From 420742e74b6f4b1cfdfce2e4e501646f0b609275 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 3 May 2020 13:43:40 -0400 Subject: [PATCH] various enhancements and improvements to WinRAR DataFormat --- .../WinRAR/Blocks/RARArchiveBlock.cs | 44 ++ .../FileSystem/WinRAR/Blocks/RAREndBlock.cs | 42 ++ .../FileSystem/WinRAR/Blocks/RARFileBlock.cs | 63 ++ .../DataFormats/FileSystem/WinRAR/RARBlock.cs | 59 ++ .../FileSystem/WinRAR/RARBlockDataFormat.cs | 306 +++++++++ .../FileSystem/WinRAR/RARBlockFlags.cs | 57 ++ .../FileSystem/WinRAR/RARBlockObjectModel.cs | 43 ++ .../FileSystem/WinRAR/RARBlockType.cs | 56 ++ .../FileSystem/WinRAR/RARDataFormat.cs | 160 +++-- .../FileSystem/WinRAR/RARFormatVersion.cs | 42 ++ .../RARArchiveBlockFlagsV4.cs} | 4 +- .../RARBlockTypeV4.cs} | 4 +- .../WinRAR/V5/RARArchiveBlockFlagsV5.cs | 51 ++ .../FileSystem/WinRAR/V5/RARBlockTypeV5.cs | 47 ++ .../FileSystem/WinRAR/V5/RAREndBlockFlags.cs | 32 + .../FileSystem/WinRAR/V5/RARFileAttributes.cs | 31 + .../FileSystem/WinRAR/V5/RARFileBlockFlags.cs | 49 ++ .../FileSystem/WinRAR/VM/RAROpcode.cs | 90 +++ .../FileSystem/WinRAR/VM/RAROpcodeFlags.cs | 39 ++ .../FileSystem/WinRAR/VM/RAROpcodeType.cs | 67 ++ .../DataFormats/FileSystem/WinRAR/VM/RARVM.cs | 614 ++++++++++++++++++ .../FileSystem/WinRAR/VM/RARVMFlags.cs | 32 + .../FileSystem/WinRAR/VM/RARVMFunctions.cs | 27 + .../UniversalEditor.Plugins.FileSystem.csproj | 28 +- 24 files changed, 1897 insertions(+), 90 deletions(-) create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARArchiveBlock.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RAREndBlock.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARFileBlock.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlock.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockDataFormat.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockFlags.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockObjectModel.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockType.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARFormatVersion.cs rename Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/{RARHeaderFlags.cs => V4/RARArchiveBlockFlagsV4.cs} (95%) rename Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/{RARHeaderType.cs => V4/RARBlockTypeV4.cs} (92%) create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARArchiveBlockFlagsV5.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARBlockTypeV5.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RAREndBlockFlags.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileAttributes.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileBlockFlags.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcode.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeFlags.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeType.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVM.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFlags.cs create mode 100644 Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFunctions.cs diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARArchiveBlock.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARArchiveBlock.cs new file mode 100644 index 00000000..4e935d2e --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARArchiveBlock.cs @@ -0,0 +1,44 @@ +// +// RARArchiveHeaderV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.Blocks +{ + public class RARArchiveBlock : RARBlock + { + public V5.RARArchiveBlockFlagsV5 archiveFlags; + public long volumeNumber; + + public override object Clone() + { + RARArchiveBlock clone = new RARArchiveBlock(); + clone.crc = crc; + clone.size = size; + clone.headerType = headerType; + clone.headerFlags = headerFlags; + clone.extraAreaSize = extraAreaSize; + clone.dataSize = dataSize; + + clone.archiveFlags = archiveFlags; + clone.volumeNumber = volumeNumber; + return clone; + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RAREndBlock.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RAREndBlock.cs new file mode 100644 index 00000000..b99773b7 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RAREndBlock.cs @@ -0,0 +1,42 @@ +// +// RAREndHeaderV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.Blocks +{ + public class RAREndBlock : RARBlock + { + public V5.RAREndBlockFlags endOfArchiveFlags; + + public override object Clone() + { + RAREndBlock clone = new RAREndBlock(); + clone.crc = crc; + clone.size = size; + clone.headerType = headerType; + clone.headerFlags = headerFlags; + clone.extraAreaSize = extraAreaSize; + clone.dataSize = dataSize; + + clone.endOfArchiveFlags = endOfArchiveFlags; + return clone; + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARFileBlock.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARFileBlock.cs new file mode 100644 index 00000000..b4649653 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/Blocks/RARFileBlock.cs @@ -0,0 +1,63 @@ +// +// RARFileHeaderV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.Blocks +{ + public class RARFileBlock : RARBlock + { + public V5.RARFileBlockFlags fileFlags; + public long unpackedSize; + public V5.RARFileAttributes attributes; + public uint mtime; + public uint dataCrc; + public long compressionFlags; + public RARHostOperatingSystem hostOperatingSystem; + public long fileNameLength; + public string fileName; + + public long dataOffset; + + public override object Clone() + { + RARFileBlock clone = new RARFileBlock(); + clone.crc = crc; + clone.size = size; + clone.headerType = headerType; + clone.headerFlags = headerFlags; + clone.extraAreaSize = extraAreaSize; + clone.dataSize = dataSize; + + clone.fileFlags = fileFlags; + clone.unpackedSize = unpackedSize; + clone.attributes = attributes; + clone.mtime = mtime; + clone.dataCrc = dataCrc; + clone.compressionFlags = compressionFlags; + clone.hostOperatingSystem = hostOperatingSystem; + clone.fileNameLength = fileNameLength; + clone.fileName = fileName; + + clone.dataOffset = dataOffset; + + return clone; + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlock.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlock.cs new file mode 100644 index 00000000..7cbd0343 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlock.cs @@ -0,0 +1,59 @@ +// +// RARBlockHeaderV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.IO; + +namespace UniversalEditor.DataFormats.FileSystem.WinRAR +{ + public abstract class RARBlock : ICloneable + { + public class RARBlockCollection + : System.Collections.ObjectModel.Collection + { + + } + + /// + /// CRC32 of header data starting from Header size field and up to and including the optional extra area. + /// + public uint crc; + /// + /// Size of header data starting from Header type field and up to and including the optional extra area. This field must not be longer than 3 bytes in + /// current implementation, resulting in 2 MB maximum header size. + /// + public long size; + /// + /// Type of archive header. + /// + public RARBlockType headerType; + public RARBlockFlags headerFlags; + /// + /// Size of extra area. Optional field, present only if 0x0001 header flag is set. + /// + public long extraAreaSize; + /// + /// Size of data area.Optional field, present only if 0x0002 header flag is set. + /// + public long dataSize; + + public abstract object Clone(); + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockDataFormat.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockDataFormat.cs new file mode 100644 index 00000000..393d08ab --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockDataFormat.cs @@ -0,0 +1,306 @@ +// +// RARBlockDataFormat.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.DataFormats.FileSystem.WinRAR.Blocks; +using UniversalEditor.IO; + +namespace UniversalEditor.DataFormats.FileSystem.WinRAR +{ + public class RARBlockDataFormat : DataFormat + { + public RARFormatVersion FormatVersion { get; set; } = RARFormatVersion.Modern; + + private long FindStartOfRar(Reader reader) + { + while (!reader.EndOfStream) + { + string Rar = reader.ReadFixedLengthString(4); + if (Rar == "\x52\x45\x7e\x5e") + { + FormatVersion = RARFormatVersion.Ancient; + return reader.Accessor.Position; + } + else if (Rar == "Rar!") + { + ushort a10 = reader.ReadUInt16(); + if (a10 != 0x071A) throw new InvalidDataFormatException("Invalid block header"); + + byte a11 = reader.ReadByte(); // 01? + if (a11 == 1) + { + byte a12 = reader.ReadByte(); + if (a12 != 0) throw new InvalidDataFormatException("Invalid block header"); + + FormatVersion = RARFormatVersion.Enhanced; + return reader.Accessor.Position; + } + else if (a11 == 0) + { + } + else + { + throw new InvalidDataFormatException("Invalid block header"); + } + + FormatVersion = RARFormatVersion.Modern; + return reader.Accessor.Position; + } + else + { + reader.Seek(-3, SeekOrigin.Current); + continue; + } + } + return -1; + } + + protected override void LoadInternal(ref ObjectModel objectModel) + { + RARBlockObjectModel bom = (objectModel as RARBlockObjectModel); + + Reader reader = Accessor.Reader; + long rarStart = FindStartOfRar(reader); + if (rarStart == -1) + throw new InvalidDataFormatException("could not find start of RAR data"); + + reader.Accessor.Seek(rarStart, SeekOrigin.Begin); + + while (!reader.EndOfStream) + { + switch (FormatVersion) + { + case RARFormatVersion.Modern: + { + //RARv4 + ushort head_crc = reader.ReadUInt16(); + V4.RARBlockTypeV4 headerType = (V4.RARBlockTypeV4)reader.ReadByte(); + V4.RARArchiveBlockFlagsV4 head_flags = (V4.RARArchiveBlockFlagsV4)reader.ReadUInt16(); + ushort head_size = reader.ReadUInt16(); + + if (reader.EndOfStream) break; + + switch (headerType) + { + case V4.RARBlockTypeV4.Archive: + { + RARArchiveBlock block = new RARArchiveBlock(); + block.crc = head_crc; + block.headerType = RARBlockTypeV4ToRARBlockType(headerType); + block.size = head_size; + + ushort reserved1 = reader.ReadUInt16(); + uint reserved2 = reader.ReadUInt32(); + + bom.Blocks.Add(block); + break; + } + case V4.RARBlockTypeV4.File: + { + RARFileBlock block = new RARFileBlock(); + block.crc = head_crc; + block.headerType = RARBlockTypeV4ToRARBlockType(headerType); + block.size = head_size; + + block.dataSize = reader.ReadUInt32(); + block.unpackedSize = reader.ReadUInt32(); + block.hostOperatingSystem = (RARHostOperatingSystem)reader.ReadByte(); + block.dataCrc = reader.ReadUInt32(); + block.mtime = reader.ReadUInt32(); + + // Version number is encoded as 10 * Major version + minor version. + byte requiredVersionToUnpack = reader.ReadByte(); + + RARCompressionMethod compressionMethod = (RARCompressionMethod)reader.ReadByte(); + block.fileNameLength = reader.ReadUInt16(); + uint fileAttributes = reader.ReadUInt32(); + + if (((RARFileHeaderFlags)head_flags & RARFileHeaderFlags.SupportLargeFiles) == RARFileHeaderFlags.SupportLargeFiles) + { + // High 4 bytes of 64 bit value of compressed file size. + uint highPackSize = reader.ReadUInt32(); + // High 4 bytes of 64 bit value of uncompressed file size. + uint highUnpackSize = reader.ReadUInt32(); + } + + block.fileName = reader.ReadFixedLengthString(block.fileNameLength); + byte nul = reader.ReadByte(); + + if (((RARFileHeaderFlags)head_flags & RARFileHeaderFlags.EncryptionSaltPresent) == RARFileHeaderFlags.EncryptionSaltPresent) + { + long salt = reader.ReadInt64(); + } + + if (((RARFileHeaderFlags)head_flags & RARFileHeaderFlags.ExtendedTimeFieldPresent) == RARFileHeaderFlags.ExtendedTimeFieldPresent) + { + uint exttime = reader.ReadUInt32(); + + } + + long offset = reader.Accessor.Position; + block.dataOffset = offset; + + reader.Seek(block.dataSize, SeekOrigin.Current); + + if (!(block.unpackedSize == 0 && block.dataSize == 0)) + { + // if both these fields are zero, we don't care - it's a folder record + // otherwise, it's a file record and we need to add it + bom.Blocks.Add(block); + } + break; + } + } + + break; + } + case RARFormatVersion.Enhanced: + { + //RARv5 + uint crc = reader.ReadUInt32(); + long size = reader.Read7BitEncodedInt(); + V5.RARBlockTypeV5 headerType = (V5.RARBlockTypeV5)reader.Read7BitEncodedInt(); + RARBlockFlags headerFlags = (RARBlockFlags)reader.Read7BitEncodedInt(); + long extraAreaSize = 0, dataSize = 0; + + if ((headerFlags & RARBlockFlags.ExtraAreaPresent) == RARBlockFlags.ExtraAreaPresent) + { + extraAreaSize = reader.Read7BitEncodedInt(); + } + if ((headerFlags & RARBlockFlags.DataAreaPresent) == RARBlockFlags.DataAreaPresent) + { + dataSize = reader.Read7BitEncodedInt(); + } + + switch (headerType) + { + case V5.RARBlockTypeV5.Main: + { + RARArchiveBlock header = new RARArchiveBlock(); + header.crc = crc; + header.size = size; + header.headerType = RARBlockTypeV5ToRARBlockType(headerType); + header.headerFlags = headerFlags; + header.extraAreaSize = extraAreaSize; + header.dataSize = dataSize; + + ((RARArchiveBlock)header).archiveFlags = (V5.RARArchiveBlockFlagsV5)reader.Read7BitEncodedInt(); + if ((((RARArchiveBlock)header).archiveFlags & V5.RARArchiveBlockFlagsV5.VolumeNumberFieldPresent) == V5.RARArchiveBlockFlagsV5.VolumeNumberFieldPresent) + { + ((RARArchiveBlock)header).volumeNumber = reader.Read7BitEncodedInt(); + } + + bom.Blocks.Add(header); + break; + } + case V5.RARBlockTypeV5.File: + case V5.RARBlockTypeV5.Service: + { + RARFileBlock header = new RARFileBlock(); + header.crc = crc; + header.size = size; + header.headerType = RARBlockTypeV5ToRARBlockType(headerType); + header.headerFlags = headerFlags; + header.extraAreaSize = extraAreaSize; + header.dataSize = dataSize; + + ((RARFileBlock)header).fileFlags = (V5.RARFileBlockFlags)reader.Read7BitEncodedInt(); + ((RARFileBlock)header).unpackedSize = reader.Read7BitEncodedInt(); + ((RARFileBlock)header).attributes = (V5.RARFileAttributes)reader.Read7BitEncodedInt(); + if ((((RARFileBlock)header).fileFlags & V5.RARFileBlockFlags.TimeFieldPresent) == V5.RARFileBlockFlags.TimeFieldPresent) + { + ((RARFileBlock)header).mtime = reader.ReadUInt32(); + } + if ((((RARFileBlock)header).fileFlags & V5.RARFileBlockFlags.CRC32Present) == V5.RARFileBlockFlags.CRC32Present) + { + ((RARFileBlock)header).dataCrc = reader.ReadUInt32(); + } + ((RARFileBlock)header).compressionFlags = reader.Read7BitEncodedInt(); + ((RARFileBlock)header).hostOperatingSystem = (RARHostOperatingSystem)reader.Read7BitEncodedInt(); + ((RARFileBlock)header).fileNameLength = reader.Read7BitEncodedInt(); + ((RARFileBlock)header).fileName = reader.ReadFixedLengthString(((RARFileBlock)header).fileNameLength); + ((RARFileBlock)header).dataOffset = reader.Accessor.Position + extraAreaSize; + + bom.Blocks.Add(header); + break; + } + case V5.RARBlockTypeV5.End: + { + RAREndBlock header = new RAREndBlock(); + header.crc = crc; + header.size = size; + header.headerType = RARBlockTypeV5ToRARBlockType(headerType); + header.headerFlags = headerFlags; + ((RAREndBlock)header).endOfArchiveFlags = (V5.RAREndBlockFlags)reader.Read7BitEncodedInt(); + header.extraAreaSize = extraAreaSize; + header.dataSize = dataSize; + + bom.Blocks.Add(header); + break; + } + } + + // extra area... + reader.Seek(extraAreaSize, SeekOrigin.Current); + // data area... + reader.Seek(dataSize, SeekOrigin.Current); + + break; + } + } + } + } + + private RARBlockType RARBlockTypeV5ToRARBlockType(V5.RARBlockTypeV5 headerType) + { + switch (headerType) + { + case V5.RARBlockTypeV5.Encryption: return RARBlockType.Encryption; + case V5.RARBlockTypeV5.End: return RARBlockType.End; + case V5.RARBlockTypeV5.File: return RARBlockType.File; + case V5.RARBlockTypeV5.Main: return RARBlockType.Main; + case V5.RARBlockTypeV5.Service: return RARBlockType.Service; + } + return RARBlockType.Unknown; + } + + private RARBlockType RARBlockTypeV4ToRARBlockType(V4.RARBlockTypeV4 headerType) + { + switch (headerType) + { + case V4.RARBlockTypeV4.Archive: return RARBlockType.Main; + case V4.RARBlockTypeV4.File: return RARBlockType.File; + case V4.RARBlockTypeV4.Marker: return RARBlockType.Marker; + case V4.RARBlockTypeV4.OldAuthenticity: return RARBlockType.OldAuthenticity; + case V4.RARBlockTypeV4.OldAuthenticity2:return RARBlockType.OldAuthenticity2; + case V4.RARBlockTypeV4.OldComment: return RARBlockType.OldComment; + case V4.RARBlockTypeV4.OldRecoveryRecord: return RARBlockType.OldRecoveryRecord; + case V4.RARBlockTypeV4.OldSubblock: return RARBlockType.OldSubblock; + case V4.RARBlockTypeV4.Subblock: return RARBlockType.Subblock; + } + return RARBlockType.Unknown; + } + + protected override void SaveInternal(ObjectModel objectModel) + { + throw new NotImplementedException(); + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockFlags.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockFlags.cs new file mode 100644 index 00000000..d8f4dad2 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockFlags.cs @@ -0,0 +1,57 @@ +// +// RARHeaderFlagsV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR +{ + [Flags()] + public enum RARBlockFlags + { + None = 0x0000, + /// + /// Extra area is present in the end of header. + /// + ExtraAreaPresent = 0x0001, + /// + /// Data area is present in the end of header. + /// + DataAreaPresent = 0x0002, + /// + /// Blocks with unknown type and this flag must be skipped when updating an archive. + /// + SkipUnknownBlocks = 0x0004, + /// + /// Data area is continuing from previous volume. + /// + ContinuedFromPrevious = 0x0008, + /// + /// Data area is continuing in next volume. + /// + ContinuedInNext = 0x0010, + /// + /// Block depends on preceding file block. + /// + DependentBlock = 0x0020, + /// + /// Preserve a child block if host block is modified. + /// + PreserveChild = 0x0040 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockObjectModel.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockObjectModel.cs new file mode 100644 index 00000000..e63d6b5c --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockObjectModel.cs @@ -0,0 +1,43 @@ +// +// RARBlockObjectModel.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR +{ + public class RARBlockObjectModel : ObjectModel + { + public RARBlock.RARBlockCollection Blocks { get; } = new RARBlock.RARBlockCollection(); + + public override void Clear() + { + Blocks.Clear(); + } + public override void CopyTo(ObjectModel where) + { + RARBlockObjectModel clone = (where as RARBlockObjectModel); + if (clone == null) throw new ObjectModelNotSupportedException(); + + for (int i = 0; i < Blocks.Count; i++) + { + clone.Blocks.Add(Blocks[i].Clone() as RARBlock); + } + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockType.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockType.cs new file mode 100644 index 00000000..943fe72b --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARBlockType.cs @@ -0,0 +1,56 @@ +// +// RARBlockType.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR +{ + public enum RARBlockType + { + Unknown = -1, + + Marker, + /// + /// Main archive header + /// + Main, + /// + /// File header + /// + File, + /// + /// Service header + /// + Service, + /// + /// Archive encryption header + /// + Encryption, + /// + /// End of archive header + /// + End, + OldComment, + OldAuthenticity, + OldSubblock, + OldRecoveryRecord, + OldAuthenticity2, + Subblock + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARDataFormat.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARDataFormat.cs index 02ca17d8..e73f5ebc 100644 --- a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARDataFormat.cs +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARDataFormat.cs @@ -19,8 +19,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -using System; - +using System.Collections.Generic; +using UniversalEditor.DataFormats.FileSystem.WinRAR.Blocks; +using UniversalEditor.IO; using UniversalEditor.ObjectModels.FileSystem; namespace UniversalEditor.DataFormats.FileSystem.WinRAR @@ -33,7 +34,7 @@ namespace UniversalEditor.DataFormats.FileSystem.WinRAR /// published unrar source in the development of this . It may end up that this is only able /// to handle uncompressed (i.e. stored) RAR files, and until there exists a better solution to the unrar licensing problem, it will have to do. /// - public class RARDataFormat : DataFormat + public class RARDataFormat : RARBlockDataFormat { private static DataFormatReference _dfr = null; protected override DataFormatReference MakeReferenceInternal() @@ -46,92 +47,83 @@ namespace UniversalEditor.DataFormats.FileSystem.WinRAR return _dfr; } - protected override void LoadInternal(ref ObjectModel objectModel) + void F_DataRequest(object sender, DataRequestEventArgs e) { - FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel); + File f = (File)sender; + Reader reader = (Reader)f.Properties["reader"]; + long offset = (long)f.Properties["offset"]; + long CompressedLength = (long)f.Properties["CompressedLength"]; + long DecompressedLength = (long)f.Properties["DecompressedLength"]; - IO.Reader br = base.Accessor.Reader; - br.Accessor.Position = 0; - - #region marker block - string Rar = br.ReadFixedLengthString(4); - if (Rar != "Rar!") throw new InvalidDataFormatException("File does not begin with \"Rar!\""); - - ushort a10 = br.ReadUInt16(); - byte a11 = br.ReadByte(); - if (a10 != 0x071A || a11 != 0x00) throw new InvalidDataFormatException("Invalid block header"); - #endregion - - #region archive header - { - ushort head_crc = br.ReadUInt16(); - RARHeaderType head_type = (RARHeaderType)br.ReadByte(); - RARHeaderFlags head_flags = (RARHeaderFlags)br.ReadUInt16(); - - ushort head_size = br.ReadUInt16(); - ushort reserved1 = br.ReadUInt16(); - uint reserved2 = br.ReadUInt32(); - } - #endregion - - #region File Entry - while (br.Accessor.Position + 3 < br.Accessor.Length) - { - ushort head_crc = br.ReadUInt16(); - RARHeaderType head_type = (RARHeaderType)br.ReadByte(); - RARFileHeaderFlags head_flags = (RARFileHeaderFlags)br.ReadUInt16(); - - ushort head_size = br.ReadUInt16(); - - if (br.EndOfStream) break; - - uint compressedSize = br.ReadUInt32(); - uint decompressedSize = br.ReadUInt32(); - RARHostOperatingSystem hostOS = (RARHostOperatingSystem)br.ReadByte(); - uint fileCRC = br.ReadUInt32(); - uint dateTimeDOS = br.ReadUInt32(); - - // Version number is encoded as 10 * Major version + minor version. - byte requiredVersionToUnpack = br.ReadByte(); - - RARCompressionMethod compressionMethod = (RARCompressionMethod)br.ReadByte(); - ushort fileNameSize = br.ReadUInt16(); - uint fileAttributes = br.ReadUInt32(); - - if ((head_flags & RARFileHeaderFlags.SupportLargeFiles) == RARFileHeaderFlags.SupportLargeFiles) - { - // High 4 bytes of 64 bit value of compressed file size. - uint highPackSize = br.ReadUInt32(); - // High 4 bytes of 64 bit value of uncompressed file size. - uint highUnpackSize = br.ReadUInt32(); - } - - string filename = br.ReadFixedLengthString(fileNameSize); - byte nul = br.ReadByte(); - - if ((head_flags & RARFileHeaderFlags.EncryptionSaltPresent) == RARFileHeaderFlags.EncryptionSaltPresent) - { - long salt = br.ReadInt64(); - } - - if ((head_flags & RARFileHeaderFlags.ExtendedTimeFieldPresent) == RARFileHeaderFlags.ExtendedTimeFieldPresent) - { - uint exttime = br.ReadUInt32(); - - } - - byte[] compressedData = br.ReadBytes(compressedSize); - - byte[] decompressedData = compressedData; - - fsom.Files.Add(filename, decompressedData); - } - #endregion + reader.Seek(offset, SeekOrigin.Begin); + byte[] compressedData = reader.ReadBytes(CompressedLength); + byte[] decompressedData = compressedData; + e.Data = decompressedData; } - protected override void SaveInternal(ObjectModel objectModel) + protected override void BeforeLoadInternal(Stack objectModels) { - throw new NotImplementedException(); + base.BeforeLoadInternal(objectModels); + objectModels.Push(new RARBlockObjectModel()); + } + protected override void AfterLoadInternal(Stack objectModels) + { + base.AfterLoadInternal(objectModels); + + RARBlockObjectModel bom = objectModels.Pop() as RARBlockObjectModel; + + FileSystemObjectModel fsom = (objectModels.Pop() as FileSystemObjectModel); + if (fsom == null) + throw new ObjectModelNotSupportedException(); + + Reader reader = Accessor.Reader; + + bool endOfArchiveReached = false; + for (int i = 0; i < bom.Blocks.Count; i++) + { + RARBlock header = bom.Blocks[i]; + if (header is RARFileBlock fh) + { + if (fh.headerType == RARBlockType.File) + { + File f = fsom.AddFile(fh.fileName); + f.Properties["reader"] = reader; + f.Properties["offset"] = fh.dataOffset; + f.Properties["CompressedLength"] = fh.dataSize; + f.Properties["DecompressedLength"] = fh.unpackedSize; + f.Size = fh.unpackedSize; + f.DataRequest += F_DataRequest; + } + else if (fh.headerType == RARBlockType.Service) + { + if (fh.fileName == "CMT") + { + + } + else if (fh.fileName == "QO") + { + + } + } + } + else if (header is RAREndBlock eh) + { + endOfArchiveReached = true; + } + } + + if (!endOfArchiveReached) + { + UserInterface.HostApplication.Messages.Add(UserInterface.HostApplicationMessageSeverity.Warning, "end of file reached before end of archive marker", Accessor.GetFileName()); + } + } + + protected override void BeforeSaveInternal(Stack objectModels) + { + base.BeforeSaveInternal(objectModels); + + RARBlockObjectModel bom = new RARBlockObjectModel(); + objectModels.Push(bom); } } } diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARFormatVersion.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARFormatVersion.cs new file mode 100644 index 00000000..0561e117 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARFormatVersion.cs @@ -0,0 +1,42 @@ +// +// RARFormatVersion.cs - indicates the format version for a RAR archive +// +// Author: +// Michael Becker +// +// Copyright (c) 2011-2020 Mike Becker's Software +// +// 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 . + +namespace UniversalEditor.DataFormats.FileSystem.WinRAR +{ + /// + /// Indicates the format version for a RAR archive. + /// + public enum RARFormatVersion + { + /// + /// The ancient RAR format beginning with "RE~". + /// + Ancient, + /// + /// The modern RAR format beginning with "Rar!". + /// + Modern, + /// + /// The modern RAR format beginning with "Rar!" and containing a V5 archive header signal. + /// + Enhanced + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARHeaderFlags.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V4/RARArchiveBlockFlagsV4.cs similarity index 95% rename from Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARHeaderFlags.cs rename to Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V4/RARArchiveBlockFlagsV4.cs index 58366f70..c6645cbc 100644 --- a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARHeaderFlags.cs +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V4/RARArchiveBlockFlagsV4.cs @@ -21,13 +21,13 @@ using System; -namespace UniversalEditor.DataFormats.FileSystem.WinRAR +namespace UniversalEditor.DataFormats.FileSystem.WinRAR.V4 { /// /// Indicates header attributes for a RAR archive. /// [Flags()] - public enum RARHeaderFlags + public enum RARArchiveBlockFlagsV4 { ArchiveVolume = 0x0001, /// diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARHeaderType.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V4/RARBlockTypeV4.cs similarity index 92% rename from Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARHeaderType.cs rename to Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V4/RARBlockTypeV4.cs index 19380243..432c8358 100644 --- a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/RARHeaderType.cs +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V4/RARBlockTypeV4.cs @@ -19,12 +19,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -namespace UniversalEditor.DataFormats.FileSystem.WinRAR +namespace UniversalEditor.DataFormats.FileSystem.WinRAR.V4 { /// /// Indicates the type of header in a RAR file. /// - public enum RARHeaderType : byte + public enum RARBlockTypeV4 : byte { Marker = 0x72, Archive = 0x73, diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARArchiveBlockFlagsV5.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARArchiveBlockFlagsV5.cs new file mode 100644 index 00000000..d34c1012 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARArchiveBlockFlagsV5.cs @@ -0,0 +1,51 @@ +// +// RARArchiveHeaderFlagsV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.V5 +{ + [Flags()] + public enum RARArchiveBlockFlagsV5 + { + None = 0x0000, + /// + /// Volume. Archive is a part of multivolume set. + /// + Volume = 0x0001, + /// + /// Volume number field is present. This flag is present in all volumes except first. + /// + VolumeNumberFieldPresent = 0x0002, + /// + /// Solid archive. + /// + Solid = 0x0004, + /// + /// Recovery record is present. + /// + RecoveryRecordPresent = 0x0008, + /// + /// Locked archive. + /// + Locked = 0x0010 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARBlockTypeV5.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARBlockTypeV5.cs new file mode 100644 index 00000000..c54fd971 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARBlockTypeV5.cs @@ -0,0 +1,47 @@ +// +// RARHeaderTypeV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.V5 +{ + public enum RARBlockTypeV5 + { + /// + /// Main archive header + /// + Main = 0x01, + /// + /// File header + /// + File = 0x02, + /// + /// Service header + /// + Service = 0x03, + /// + /// Archive encryption header + /// + Encryption = 0x04, + /// + /// End of archive header + /// + End = 0x05 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RAREndBlockFlags.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RAREndBlockFlags.cs new file mode 100644 index 00000000..6c3e0c18 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RAREndBlockFlags.cs @@ -0,0 +1,32 @@ +// +// RAREndHeaderFlagsV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.V5 +{ + public enum RAREndBlockFlags + { + None = 0x0000, + /// + /// Archive is volume and is not the last volume in the set. + /// + Continued = 0x0001 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileAttributes.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileAttributes.cs new file mode 100644 index 00000000..63ad5baf --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileAttributes.cs @@ -0,0 +1,31 @@ +// +// RARFileAttributesV5.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.V5 +{ + [Flags()] + public enum RARFileAttributes + { + None = 0x0000 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileBlockFlags.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileBlockFlags.cs new file mode 100644 index 00000000..bd4a55aa --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/V5/RARFileBlockFlags.cs @@ -0,0 +1,49 @@ +// +// V5RARFileFlags.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.V5 +{ + [Flags()] + public enum RARFileBlockFlags + { + /// + /// Directory file system object (file header only). + /// + DirectoryFileSystemObject = 0x0001, + /// + /// Time field in Unix format is present. + /// + TimeFieldPresent = 0x0002, + /// + /// CRC32 field is present. + /// + CRC32Present = 0x0004, + /// + /// Unpacked size is unknown. + /// + /// + /// If flag 0x0008 () is set, unpacked size field is still present, but must be ignored and extraction must be + /// performed until reaching the end of compression stream. This flag can be set if actual file size is larger than reported by OS or if file size is + /// unknown such as for all volumes except last when archiving from stdin to multivolume archive. + /// + UnpackedSizeUnknown = 0x0008 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcode.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcode.cs new file mode 100644 index 00000000..4ff5dbdc --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcode.cs @@ -0,0 +1,90 @@ +// +// RAROpcode.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.VM +{ + public abstract class RAROpcode + { + public abstract RAROpcodeType Type { get; } + + public uint value1; + public uint value2; + + public bool bytemode; + public byte addressingmode1; + public byte addressingmode2; + + public RARGetterFunction operand1getter; + public RARSetterFunction operand1setter; + + public RARGetterFunction operand2getter; + public RARSetterFunction operand2setter; + + public static readonly RAROpcodeFlags[] InstructionFlags = new RAROpcodeFlags[] + { + /*/*[RARMovInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag, + /*[RARCmpInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARAddInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARSubInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARJzInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsUnconditionalJumpFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARJnzInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARIncInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARDecInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARJmpInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag, + /*[RARXorInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARAndInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RAROrInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARTestInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARJsInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARJnsInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARJbInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARJbeInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARJaInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARJaeInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARPushInstruction]*/ RAROpcodeFlags.RAR1OperandFlag, + /*[RARPopInstruction]*/ RAROpcodeFlags.RAR1OperandFlag, + /*[RARCallInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARIsRelativeJumpFlag, + /*[RARRetInstruction]*/ RAROpcodeFlags.RAR0OperandsFlag | RAROpcodeFlags.RARIsUnconditionalJumpFlag, + /*[RARNotInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag, + /*[RARShlInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARShrInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARSarInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARNegInstruction]*/ RAROpcodeFlags.RAR1OperandFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARPushaInstruction]*/ RAROpcodeFlags.RAR0OperandsFlag, + /*[RARPopaInstruction]*/ RAROpcodeFlags.RAR0OperandsFlag, + /*[RARPushfInstruction]*/ RAROpcodeFlags.RAR0OperandsFlag | RAROpcodeFlags.RARReadsStatusFlag, + /*[RARPopfInstruction]*/ RAROpcodeFlags.RAR0OperandsFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARMovzxInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARWritesFirstOperandFlag, + /*[RARMovsxInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARWritesFirstOperandFlag, + /*[RARXchgInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARWritesSecondOperandFlag | RAROpcodeFlags.RARHasByteModeFlag, + /*[RARMulInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag, + /*[RARDivInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag, + /*[RARAdcInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARReadsStatusFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARSbbInstruction]*/ RAROpcodeFlags.RAR2OperandsFlag | RAROpcodeFlags.RARHasByteModeFlag | RAROpcodeFlags.RARWritesFirstOperandFlag | RAROpcodeFlags.RARReadsStatusFlag | RAROpcodeFlags.RARWritesStatusFlag, + /*[RARPrintInstruction]*/ RAROpcodeFlags.RAR0OperandsFlag + }; + + public bool RARInstructionWritesSecondOperand() + { + return ((RAROpcode.InstructionFlags[(int)Type] & RAROpcodeFlags.RARWritesSecondOperandFlag) == RAROpcodeFlags.RARWritesSecondOperandFlag); + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeFlags.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeFlags.cs new file mode 100644 index 00000000..3db38d1a --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeFlags.cs @@ -0,0 +1,39 @@ +// +// RAROpcodeFlags.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.VM +{ + [Flags()] + public enum RAROpcodeFlags + { + RAR0OperandsFlag = 0, + RAR1OperandFlag = 1, + RAR2OperandsFlag = 2, + RAROperandsFlag = 3, + RARHasByteModeFlag = 4, + RARIsUnconditionalJumpFlag = 8, + RARIsRelativeJumpFlag = 16, + RARWritesFirstOperandFlag = 32, + RARWritesSecondOperandFlag = 64, + RARReadsStatusFlag = 128, + RARWritesStatusFlag = 256 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeType.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeType.cs new file mode 100644 index 00000000..4313d4cd --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RAROpcodeType.cs @@ -0,0 +1,67 @@ +// +// RAROpcodeType.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.VM +{ + public enum RAROpcodeType : byte + { + Mov = 0, + Cmp = 1, + Add = 2, + Sub = 3, + Jz = 4, + Jnz = 5, + Inc = 6, + Dec = 7, + Jmp = 8, + Xor = 9, + And = 10, + Or = 11, + Test = 12, + Js = 13, + Jns = 14, + Jb = 15, + Jbe = 16, + Ja = 17, + Jae = 18, + Push = 19, + Pop = 20, + Call = 21, + Ret = 22, + Not = 23, + Shl = 24, + Shr = 25, + Sar = 26, + Neg = 27, + Pusha = 28, + Popa = 29, + Pushf = 30, + Popf = 31, + Movzx = 32, + Movsx = 33, + Xchg = 34, + Mul = 35, + Div = 36, + Adc = 37, + Sbb = 38, + Print = 39 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVM.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVM.cs new file mode 100644 index 00000000..bc822fc1 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVM.cs @@ -0,0 +1,614 @@ +// +// RARVM.cs - work in progress to implement WinRAR VM from the unarchiver +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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 System.Diagnostics.Contracts; + +namespace UniversalEditor.DataFormats.FileSystem.WinRAR.VM +{ + /// + /// Work in progress to implement WinRAR VM from the unarchiver. + /// + public class RARVM + { + private uint[] registers = new uint[8]; + public void SetRegisters(uint[] registers) + { + Contract.Requires(registers.Length == 8); + + this.registers = registers; + } + + static int RARRegisterAddressingMode(int n) + { + return (0 + (n)); + } + static int RARRegisterIndirectAddressingMode(int n) + { + return (8 + (n)); + } + static int RARIndexedAbsoluteAddressingMode(int n) + { + return (16 + (n)); + } + const int RARAbsoluteAddressingMode = 24; + const int RARImmediateAddressingMode = 25; + const int RARNumberOfAddressingModes = 26; + + static RARGetterFunction[] OperandGetters_32 = new RARGetterFunction[RARNumberOfAddressingModes]; + static RARGetterFunction[] OperandGetters_8 = new RARGetterFunction[RARNumberOfAddressingModes]; + static RARSetterFunction[] OperandSetters_32 = new RARSetterFunction[RARNumberOfAddressingModes]; + static RARSetterFunction[] OperandSetters_8 = new RARSetterFunction[RARNumberOfAddressingModes]; + + static readonly byte RARNumberOfInstructions = (byte)Enum.GetValues(typeof(RAROpcodeType)).Length; + + int NumberOfRARInstructionOperands(RAROpcodeType instruction) + { + if ((int)instruction >= RARNumberOfInstructions) return 0; + return (int)(RAROpcode.InstructionFlags[(int)instruction] & RAROpcodeFlags.RAROperandsFlag); + } + + bool RARInstructionHasByteMode(RAROpcodeType instruction) + { + if ((int)instruction >= RARNumberOfInstructions) return false; + return (RAROpcode.InstructionFlags[(int)instruction] & RAROpcodeFlags.RARHasByteModeFlag) != 0; + } + bool RARInstructionWritesFirstOperand(RAROpcodeType instruction) + { + if ((int)instruction >= RARNumberOfInstructions) return false; + return (RAROpcode.InstructionFlags[(int)instruction] & RAROpcodeFlags.RARWritesFirstOperandFlag) != 0; + } + + + public uint GetOperand1() + { + return _opcodes[_ip].operand1getter(this, _opcodes[_ip].value1); + } + public uint GetOperand2() + { + return _opcodes[_ip].operand2getter(this, _opcodes[_ip].value2); + } + public void SetOperand1(uint data) + { + _opcodes[_ip].operand1setter(this, _opcodes[_ip].value1, data); + } + public void SetOperand2(uint data) + { + _opcodes[_ip].operand2setter(this, _opcodes[_ip].value2, data); + } + + private bool Init(RAROpcode[] opcodes) + { + for (int i = 0; i < opcodes.Length; i++) + { + if ((byte)opcodes[i].Type >= RARNumberOfInstructions) return false; + + RARInstructionLabel[] instructionlabels; + RARInstructionLabel[] instructionlabels_32; + RARInstructionLabel[] instructionlabels_8; + + RARSetterFunction[] setterfunctions; + RARGetterFunction[] getterfunctions; + + if (opcodes[i].Type == RAROpcodeType.Movsx || opcodes[i].Type == RAROpcodeType.Movzx) + { + // instructionlabels = instructionlabels_32; + getterfunctions = OperandGetters_8; + setterfunctions = OperandSetters_32; + } + else if (opcodes[i].bytemode) + { + if (!RARInstructionHasByteMode(opcodes[i].Type)) return false; + + // instructionlabels = instructionlabels_8; + getterfunctions = OperandGetters_8; + setterfunctions = OperandSetters_8; + } + else + { + // instructionlabels = instructionlabels_32; + getterfunctions = OperandGetters_32; + setterfunctions = OperandSetters_32; + } + + // opcodes[i].instructionlabel = instructionlabels[opcodes[i].Type]; + + int numoperands = NumberOfRARInstructionOperands(opcodes[i].Type); + + if (numoperands >= 1) + { + if (opcodes[i].addressingmode1 >= RARNumberOfAddressingModes) return false; + opcodes[i].operand1getter = getterfunctions[opcodes[i].addressingmode1]; + opcodes[i].operand1setter = setterfunctions[opcodes[i].addressingmode1]; + + if (opcodes[i].addressingmode1 == RARImmediateAddressingMode) + { + if (RARInstructionWritesFirstOperand(opcodes[i].Type)) return false; + } + else if (opcodes[i].addressingmode1 == RARAbsoluteAddressingMode) + { + opcodes[i].value1 &= RARProgramMemoryMask; + } + } + + if (numoperands == 2) + { + if (opcodes[i].addressingmode2 >= RARNumberOfAddressingModes) return false; + opcodes[i].operand2getter = getterfunctions[opcodes[i].addressingmode2]; + opcodes[i].operand2setter = setterfunctions[opcodes[i].addressingmode2]; + + if (opcodes[i].addressingmode2 == RARImmediateAddressingMode) + { + if (opcodes[i].RARInstructionWritesSecondOperand()) return false; + } + else if (opcodes[i].addressingmode2 == RARAbsoluteAddressingMode) + { + opcodes[i].value2 &= RARProgramMemoryMask; + } + } + } + return true; + } + + private RARVMFlags flags = RARVMFlags.None; + + public void SetFlagsWithCarry(uint res, uint carry) + { + uint result = res; + flags = (RARVMFlags)((result == 0 ? (uint)RARVMFlags.ZeroFlag : (result & (uint)RARVMFlags.SignFlag)) | carry); + } + + private RAROpcode[] _opcodes = new RAROpcode[0]; + private int _ip = 0; + + public void Run(RAROpcode[] opcodes) + { + _opcodes = opcodes; + _ip = -1; + NextInstruction(); + } + + public void NextInstruction() + { + _ip++; + // Debug(); + Execute(_opcodes[_ip]); // goto *opcode->instructionlabel; + } + + private uint _RARRead32(byte[] b, uint ofs) + { + return ((uint)b[ofs + 3] << 24) | ((uint)b[ofs + 2] << 16) | ((uint)b[ofs + 1] << 8) | (uint)b[ofs + 0]; + } + private void _RARWrite32(byte[] b, uint ofs, uint n) + { + b[ofs + 3] = (byte)((n >> 24) & 0xff); + b[ofs + 2] = (byte)((n >> 16) & 0xff); + b[ofs + 1] = (byte)((n >> 8) & 0xff); + b[ofs + 0] = (byte)(n & 0xff); + } + + private const int RARProgramMemorySize = 0x40000; + private const int RARProgramMemoryMask = (RARProgramMemorySize - 1); + + private byte[] memory = new byte[0]; + private uint RARVirtualMachineRead32(uint address) + { + return _RARRead32(memory, address & RARProgramMemoryMask); + } + private void RARVirtualMachineWrite32(uint address, uint val) + { + _RARWrite32(memory, address & RARProgramMemoryMask, val); + } + + private uint SignExtend(uint a) + { + return ((uint)((byte)(a))); + } + + private void SetByteFlagsWithCarry(uint res, uint carry) + { + uint result = (res); + flags = (RARVMFlags)((result == 0 ? (uint)RARVMFlags.ZeroFlag : (SignExtend(result) & (uint)RARVMFlags.SignFlag)) | (carry)); + } + private void SetFlags(uint res) + { + SetFlagsWithCarry(res, 0); + } + + private void SetOperand1AndFlagsWithCarry(uint res, uint carry) + { + uint r = (res); + SetFlagsWithCarry(r, carry); + SetOperand1(r); + } + private void SetOperand1AndByteFlagsWithCarry(uint res, uint carry) + { + uint r = (res); + SetByteFlagsWithCarry(r, carry); + SetOperand1(r); + } + private void SetOperand1AndFlags(uint res) + { + uint r = (res); + SetFlags(r); + SetOperand1(r); + } + + public bool Jump(uint ofs) + { + uint o = ofs; + if (o >= _opcodes.Length) return false; + _ip = (int)o; + + // Debug(); + + Execute(_opcodes[_ip]); + return true; + } + + public void Execute(RAROpcode opcode) + { + /* + RAROpcode* opcode; + uint flags = self->flags; + + Jump(0); + */ + switch (opcode.Type) + { + case RAROpcodeType.Mov: + { + SetOperand1(GetOperand2()); + NextInstruction(); + break; + } + case RAROpcodeType.Cmp: + { + uint term1 = GetOperand1(); + uint result = term1 - GetOperand2(); + SetFlagsWithCarry(result, (uint)(result > term1 ? 1 : 0)); + NextInstruction(); + break; + } + case RAROpcodeType.Add: + { + uint term1 = GetOperand1(); + uint result = term1 + GetOperand2(); + SetOperand1AndFlagsWithCarry(result, (uint)(result < term1 ? 1 : 0)); + NextInstruction(); + break; + } + /* + case RAROpcodeType.AddByte: + { + uint term1 = GetOperand1(); + SetOperand1AndByteFlagsWithCarry(term1 + GetOperand2() & 0xff, result < term1); + NextInstruction(); + break; + } + */ + case RAROpcodeType.Sub: + { + uint term1 = GetOperand1(); + uint result = term1 - GetOperand2(); + SetOperand1AndFlagsWithCarry(result, (uint)(result > term1 ? 1 : 0)); + NextInstruction(); + break; + } + /* + case RAROpcodeType.SubByte: // Not correctly implemented in the RAR VM + { + uint term1 = GetOperand1(); + SetOperandAndByteFlagsWithCarry(term1-GetOperand2() & 0xFF, result > term1); + NextInstruction(); + break; + } + */ + case RAROpcodeType.Jz: + { + if ((flags & RARVMFlags.ZeroFlag) == RARVMFlags.ZeroFlag) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Jnz: + { + if ((flags & RARVMFlags.ZeroFlag) != RARVMFlags.ZeroFlag) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Inc: + { + SetOperand1AndFlags(GetOperand1() + 1); + NextInstruction(); + break; + } + /* + case RAROpcodeType.IncByte: + { + SetOperand1AndFlags(GetOperand1() + 1 & 0xff); + NextInstruction(); + break; + } + */ + case RAROpcodeType.Dec: + { + SetOperand1AndFlags(GetOperand1() - 1); + NextInstruction(); + break; + } + /* + case RAROpcodeType.DecByte: + { + SetOperand1AndFlags(GetOperand1() - 1 & 0xff); + NextInstruction(); + break; + } + */ + case RAROpcodeType.Jmp: + { + Jump(GetOperand1()); + break; + } + case RAROpcodeType.Xor: + { + SetOperand1AndFlags(GetOperand1() ^ GetOperand2()); + NextInstruction(); + break; + } + case RAROpcodeType.And: + { + SetOperand1AndFlags(GetOperand1() & GetOperand2()); + NextInstruction(); + break; + } + case RAROpcodeType.Or: + { + SetOperand1AndFlags(GetOperand1() | GetOperand2()); + NextInstruction(); + break; + } + case RAROpcodeType.Test: + { + SetFlags(GetOperand1() & GetOperand2()); + NextInstruction(); + break; + } + case RAROpcodeType.Js: + { + if ((flags & RARVMFlags.SignFlag) == RARVMFlags.SignFlag) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Jns: + { + if ((flags & RARVMFlags.SignFlag) != RARVMFlags.SignFlag) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Jb: + { + if ((flags & RARVMFlags.CarryFlag) == RARVMFlags.CarryFlag) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Jbe: + { + if ((flags & (RARVMFlags.CarryFlag | RARVMFlags.ZeroFlag)) == (RARVMFlags.CarryFlag | RARVMFlags.ZeroFlag)) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Ja: + { + if (!((flags & (RARVMFlags.CarryFlag | RARVMFlags.ZeroFlag)) == (RARVMFlags.CarryFlag | RARVMFlags.ZeroFlag))) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Jae: + { + if ((flags & RARVMFlags.CarryFlag) != RARVMFlags.CarryFlag) Jump(GetOperand1()); + else NextInstruction(); + break; + } + case RAROpcodeType.Push: + { + registers[7] -= 4; + RARVirtualMachineWrite32(registers[7], GetOperand1()); + NextInstruction(); + break; + } + case RAROpcodeType.Pop: + { + SetOperand1(RARVirtualMachineRead32(registers[7])); + registers[7] += 4; + NextInstruction(); + break; + } + case RAROpcodeType.Call: + { + registers[7] -= 4; + throw new NotImplementedException(); + // RARVirtualMachineWrite32(registers[7], opcode - opcodes + 1); + Jump(GetOperand1()); + break; + } + case RAROpcodeType.Ret: + { + if (registers[7] >= RARProgramMemorySize) + { + // this.flags = flags; + // return true; + return; + } + uint retaddr = RARVirtualMachineRead32(registers[7]); + registers[7] += 4; + Jump(retaddr); + break; + } + case RAROpcodeType.Not: + { + SetOperand1(~GetOperand1()); + NextInstruction(); + break; + } + case RAROpcodeType.Shl: + { + uint op1 = GetOperand1(); + uint op2 = GetOperand2(); + SetOperand1AndFlagsWithCarry(op1 << (ushort)op2, (uint)(((op1 << (ushort)(op2 - 1)) & 0x80000000) != 0 ? 1 : 0)); + NextInstruction(); + break; + } + case RAROpcodeType.Shr: + { + uint op1 = GetOperand1(); + uint op2 = GetOperand2(); + SetOperand1AndFlagsWithCarry(op1 >> (ushort)op2, (uint)(((op1 >> (ushort)(op2 - 1)) & 1) != 0 ? 1 : 0)); + NextInstruction(); + break; + } + case RAROpcodeType.Sar: + { + uint op1 = GetOperand1(); + uint op2 = GetOperand2(); + SetOperand1AndFlagsWithCarry(op1 >> (ushort)op2, (uint)((((op1 >> (ushort)(op2 - 1)) & 1) != 0) ? 1 : 0)); + NextInstruction(); + break; + } + case RAROpcodeType.Neg: + { + long result = -GetOperand1(); + SetOperand1AndFlagsWithCarry((uint)result, (uint)(result != 0 ? 1 : 0)); + NextInstruction(); + break; + } + case RAROpcodeType.Pusha: + { + for (int i = 0; i < 8; i++) RARVirtualMachineWrite32((uint)(registers[7] - 4 - i * 4), registers[i]); + registers[7] -= 32; + + NextInstruction(); + break; + } + case RAROpcodeType.Popa: + { + for (int i = 0; i < 8; i++) + { + registers[i] = RARVirtualMachineRead32((uint)(registers[7] + 28 - i * 4)); + } + NextInstruction(); + break; + } + case RAROpcodeType.Pushf: + { + registers[7] -= 4; + RARVirtualMachineWrite32(registers[7], (uint)flags); + NextInstruction(); + break; + } + case RAROpcodeType.Popf: + { + flags = (RARVMFlags)RARVirtualMachineRead32((uint)(registers[7])); + registers[7] += 4; + NextInstruction(); + break; + } + case RAROpcodeType.Movzx: + { + SetOperand1(GetOperand2()); + NextInstruction(); + break; + } + case RAROpcodeType.Movsx: + { + SetOperand1(SignExtend(GetOperand2())); + NextInstruction(); + break; + } + case RAROpcodeType.Xchg: + { + uint op1 = GetOperand1(); + uint op2 = GetOperand2(); + SetOperand1(op2); + SetOperand2(op1); + NextInstruction(); + break; + } + case RAROpcodeType.Mul: + { + SetOperand1(GetOperand1() * GetOperand2()); + NextInstruction(); + break; + } + case RAROpcodeType.Div: + { + uint denominator = GetOperand2(); + if (denominator != 0) SetOperand1(GetOperand1() / denominator); + NextInstruction(); + break; + } + case RAROpcodeType.Adc: + { + uint term1 = GetOperand1(); + uint carry = (uint)(flags & RARVMFlags.CarryFlag); + uint result = term1 + GetOperand2() + carry; + SetOperand1AndFlagsWithCarry(result, (uint)((result < term1 || (result == term1 && (carry != 0))) ? 1 : 0)); + NextInstruction(); + break; + } + /* + case RAROpcodeType.AdcByte: + { + uint term1 = GetOperand1(); + uint carry = (uint)(flags & RARVMFlags.CarryFlag); + SetOperand1AndFlagsWithCarry(term1 + GetOperand2() + carry & 0xff, result < term1 || result == term1 && carry); // Does not correctly set sign bit. + NextInstruction(); + break; + } + */ + case RAROpcodeType.Sbb: + { + uint term1 = GetOperand1(); + uint carry = (uint)(flags & RARVMFlags.CarryFlag); + uint result = (term1 - GetOperand2() - carry); + SetOperand1AndFlagsWithCarry(result, (uint)((result > term1 || (result == term1 && (carry != 0))) ? 1 : 0)); + NextInstruction(); + break; + } + /* + case RAROpcodeType.SbbByte: + { + uint term1 = GetOperand1(); + uint carry = (uint)(flags & RARVMFlags.CarryFlag); + uint result = (term1 - GetOperand2() - carry & 0xff); + SetOperand1AndFlagsWithCarry(result, (uint)(result > term1 || (result == term1 && (carry != 0)) ? 1 : 0)); // Does not correctly set sign bit. + NextInstruction(); + break; + } + */ + case RAROpcodeType.Print: + { + NextInstruction(); + break; + } + } + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFlags.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFlags.cs new file mode 100644 index 00000000..3fe40f89 --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFlags.cs @@ -0,0 +1,32 @@ +// +// RARVMFlags.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.VM +{ + [Flags()] + public enum RARVMFlags : uint + { + None = 0x00000000, + CarryFlag = 0x00000001, + ZeroFlag = 0x00000002, + SignFlag = 0x80000000 + } +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFunctions.cs b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFunctions.cs new file mode 100644 index 00000000..bd407e3c --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/WinRAR/VM/RARVMFunctions.cs @@ -0,0 +1,27 @@ +// +// RARVMFunctions.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.WinRAR.VM +{ + public delegate void RARInstructionLabel(); + public delegate uint RARGetterFunction(RARVM vm, uint value); + public delegate void RARSetterFunction(RARVM vm, uint value, uint data); +} diff --git a/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj b/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj index 123c00b8..530eca3d 100644 --- a/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj +++ b/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj @@ -199,8 +199,6 @@ - - @@ -240,6 +238,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -282,6 +302,10 @@ + + + +