Remove .NET Framework 4.6.2/4.7.2 (#24)

* Remove < .NET 4.8, general cleanup

* Abstract

* Tango

* Banner

* Scan no more

* Common

* Application

* Access

* Filter-feeder

* Graffiti

* Paint-over

* Law and Order

* XOR-o

* Unused staircase

* Maybe

* Maybe not

* Delete this

* The word is "no"

* Emit

* Improper

* Aye aye

* Fence

* Barrier

* Monkey

* Pail

* Lines
This commit is contained in:
Matt Nadareski
2020-07-15 09:41:59 -07:00
committed by GitHub
parent 1a718a3915
commit 4e406604c2
82 changed files with 8975 additions and 11172 deletions

View File

@@ -3,6 +3,7 @@ using System.IO;
using SabreTools.Library.Data;
using SabreTools.Library.DatItems;
using SabreTools.Library.Tools;
namespace SabreTools.Library.FileTypes
{
@@ -34,6 +35,83 @@ namespace SabreTools.Library.FileTypes
{
}
/// <summary>
/// Create an archive object from a filename, if possible
/// </summary>
/// <param name="input">Name of the file to create the archive from</param>
/// <returns>Archive object representing the inputs</returns>
public static BaseArchive Create(string input)
{
BaseArchive archive = null;
// First get the archive type
FileType? at = input.GetFileType();
// If we got back null, then it's not an archive, so we we return
if (at == null)
return archive;
// Create the archive based on the type
Globals.Logger.Verbose($"Found archive of type: {at}");
switch (at)
{
case FileType.GZipArchive:
archive = new GZipArchive(input);
break;
case FileType.RarArchive:
archive = new RarArchive(input);
break;
case FileType.SevenZipArchive:
archive = new SevenZipArchive(input);
break;
case FileType.TapeArchive:
archive = new TapeArchive(input);
break;
case FileType.ZipArchive:
archive = new ZipArchive(input);
break;
default:
// We ignore all other types for now
break;
}
return archive;
}
/// <summary>
/// Create an archive object of the specified type, if possible
/// </summary>
/// <param name="archiveType">SharpCompress.Common.ArchiveType representing the archive to create</param>
/// <returns>Archive object representing the inputs</returns>
public static BaseArchive Create(FileType archiveType)
{
switch (archiveType)
{
case FileType.GZipArchive:
return new GZipArchive();
case FileType.RarArchive:
return new RarArchive();
case FileType.SevenZipArchive:
return new SevenZipArchive();
case FileType.TapeArchive:
return new TapeArchive();
case FileType.ZipArchive:
return new ZipArchive();
default:
return null;
}
}
#endregion
#region Extraction

View File

@@ -17,7 +17,9 @@ namespace SabreTools.Library.FileTypes
public long? Size { get; set; }
public byte[] CRC { get; set; }
public byte[] MD5 { get; set; }
#if NET_FRAMEWORK
public byte[] RIPEMD160 { get; set; }
#endif
public byte[] SHA1 { get; set; }
public byte[] SHA256 { get; set; }
public byte[] SHA384 { get; set; }
@@ -45,14 +47,16 @@ namespace SabreTools.Library.FileTypes
if (getHashes)
{
BaseFile temp = Utilities.GetFileInfo(this.Filename);
BaseFile temp = FileExtensions.GetInfo(this.Filename);
if (temp != null)
{
this.Parent = temp.Parent;
this.Date = temp.Date;
this.CRC = temp.CRC;
this.MD5 = temp.MD5;
#if NET_FRAMEWORK
this.RIPEMD160 = temp.RIPEMD160;
#endif
this.SHA1 = temp.SHA1;
this.SHA256 = temp.SHA256;
this.SHA384 = temp.SHA384;
@@ -73,21 +77,23 @@ namespace SabreTools.Library.FileTypes
if (getHashes)
{
BaseFile temp = Utilities.GetStreamInfo(stream, stream.Length);
if(temp != null)
BaseFile temp = stream.GetInfo();
if (temp != null)
{
this.Parent = temp.Parent;
this.Date = temp.Date;
this.CRC = temp.CRC;
this.MD5 = temp.MD5;
#if NET_FRAMEWORK
this.RIPEMD160 = temp.RIPEMD160;
#endif
this.SHA1 = temp.SHA1;
this.SHA256 = temp.SHA256;
this.SHA384 = temp.SHA384;
this.SHA512 = temp.SHA512;
}
}
}
#endregion

View File

@@ -1,4 +1,6 @@
using System.IO;
using System;
using System.IO;
using System.Text;
using SabreTools.Library.Data;
using SabreTools.Library.Tools;
@@ -9,477 +11,137 @@ namespace SabreTools.Library.FileTypes
/// This is code adapted from chd.h and chd.cpp in MAME
/// Additional archival code from https://github.com/rtissera/libchdr/blob/master/src/chd.h
/// </summary>
/// <remarks>
/// ----------------------------------------------
/// Common CHD Header:
/// 0x00-0x07 - CHD signature
/// 0x08-0x0B - Header size
/// 0x0C-0x0F - CHD version
/// ----------------------------------------------
/// CHD v1 header layout:
/// 0x10-0x13 - Flags (1: Has parent MD5, 2: Disallow writes)
/// 0x14-0x17 - Compression
/// 0x18-0x1B - 512-byte sectors per hunk
/// 0x1C-0x1F - Hunk count
/// 0x20-0x23 - Hard disk cylinder count
/// 0x24-0x27 - Hard disk head count
/// 0x28-0x2B - Hard disk sector count
/// 0x2C-0x3B - MD5
/// 0x3C-0x4B - Parent MD5
/// ----------------------------------------------
/// CHD v2 header layout:
/// 0x10-0x13 - Flags (1: Has parent MD5, 2: Disallow writes)
/// 0x14-0x17 - Compression
/// 0x18-0x1B - seclen-byte sectors per hunk
/// 0x1C-0x1F - Hunk count
/// 0x20-0x23 - Hard disk cylinder count
/// 0x24-0x27 - Hard disk head count
/// 0x28-0x2B - Hard disk sector count
/// 0x2C-0x3B - MD5
/// 0x3C-0x4B - Parent MD5
/// 0x4C-0x4F - Number of bytes per sector (seclen)
/// ----------------------------------------------
/// CHD v3 header layout:
/// 0x10-0x13 - Flags (1: Has parent SHA-1, 2: Disallow writes)
/// 0x14-0x17 - Compression
/// 0x18-0x1B - Hunk count
/// 0x1C-0x23 - Logical Bytes
/// 0x24-0x2C - Metadata Offset
/// ...
/// 0x4C-0x4F - Hunk Bytes
/// 0x50-0x63 - SHA-1
/// 0x64-0x77 - Parent SHA-1
/// 0x78-0x87 - Map
/// ----------------------------------------------
/// CHD v4 header layout:
/// 0x10-0x13 - Flags (1: Has parent SHA-1, 2: Disallow writes)
/// 0x14-0x17 - Compression
/// 0x18-0x1B - Hunk count
/// 0x1C-0x23 - Logical Bytes
/// 0x24-0x2C - Metadata Offset
/// ...
/// 0x2C-0x2F - Hunk Bytes
/// 0x30-0x43 - SHA-1
/// 0x44-0x57 - Parent SHA-1
/// 0x58-0x6b - Raw SHA-1
/// 0x6c-0x7b - Map
/// ----------------------------------------------
/// CHD v5 header layout:
/// 0x10-0x13 - Compression format 1
/// 0x14-0x17 - Compression format 2
/// 0x18-0x1B - Compression format 3
/// 0x1C-0x1F - Compression format 4
/// 0x20-0x27 - Logical Bytes
/// 0x28-0x2F - Map Offset
/// 0x30-0x37 - Metadata Offset
/// 0x38-0x3B - Hunk Bytes
/// 0x3C-0x3F - Unit Bytes
/// 0x40-0x53 - Raw SHA-1
/// 0x54-0x67 - SHA-1
/// 0x68-0x7b - Parent SHA-1
/// ----------------------------------------------
/// </remarks>
public class CHDFile : BaseFile
public abstract class CHDFile : BaseFile
{
#region Private instance variables
// Core parameters from the header
private byte[] m_signature; // signature
private uint m_headersize; // size of the header
private uint m_version; // version of the header
private ulong m_logicalbytes; // logical size of the raw CHD data in bytes
private ulong m_mapoffset; // offset of map
private ulong m_metaoffset; // offset to first metadata bit
private uint m_sectorsperhunk; // number of sectors per hunk
private uint m_hunkbytes; // size of each raw hunk in bytes
private ulong m_hunkcount; // number of hunks represented
private uint m_unitbytes; // size of each unit in bytes
private ulong m_unitcount; // number of units represented
private CHD_CODEC[] m_compression = new CHD_CODEC[4]; // array of compression types used
// map information
private uint m_mapentrybytes; // length of each entry in a map
// additional required vars
private uint? _headerVersion;
private BinaryReader m_br; // Binary reader representing the CHD stream
#endregion
#region Pubically facing variables
public uint? Version
{
get
{
if (_headerVersion == null)
{
_headerVersion = ValidateHeaderVersion();
}
return _headerVersion;
}
}
// Common header fields
protected char[] tag = new char[8]; // 'MComprHD'
protected uint length; // length of header (including tag and length fields)
protected uint version; // drive format version
#endregion
#region Constructors
/// <summary>
/// Create a new, blank CHDFile
/// </summary>
public CHDFile()
{
this.Type = FileType.CHD;
}
/// <summary>
/// Create a new CHDFile from an input file
/// </summary>
/// <param name="filename"></param>
public CHDFile(string filename)
: this(Utilities.TryOpenRead(filename))
/// <param name="filename">Filename respresenting the CHD file</param>
public static CHDFile Create(string filename)
{
using (FileStream fs = FileExtensions.TryOpenRead(filename))
{
return Create(fs);
}
}
/// <summary>
/// Create a new CHDFile from an input stream
/// </summary>
/// <param name="chdstream">Stream representing the CHD file</param>
public CHDFile(Stream chdstream)
public static CHDFile Create(Stream chdstream)
{
this.Type = FileType.CHD;
m_br = new BinaryReader(chdstream);
// Read the standard CHD headers
(char[] tag, uint length, uint version) = GetHeaderValues(chdstream);
chdstream.Seek(-16, SeekOrigin.Current); // Seek back to start
_headerVersion = ValidateHeaderVersion();
if (_headerVersion != null)
{
byte[] hash = GetHashFromHeader();
if (hash != null)
{
if (hash.Length == Constants.MD5Length)
this.MD5 = hash;
else if (hash.Length == Constants.SHA1Length)
this.SHA1 = hash;
}
}
// Validate that this is actually a valid CHD
uint validatedVersion = ValidateHeader(tag, length, version);
if (validatedVersion == 0)
return null;
// Read and retrun the current CHD
CHDFile generated = ReadAsVersion(chdstream, version);
if (generated != null)
generated.Type = FileType.CHD;
return generated;
}
#endregion
#region Abstract functionality
/// <summary>
/// Return the best-available hash for a particular CHD version
/// </summary>
public abstract byte[] GetHash();
#endregion
#region Header Parsing
/// <summary>
/// Validate the initial signature, version, and header size
/// Get the generic header values of a CHD, if possible
/// </summary>
/// <returns>Unsigned int containing the version number, null if invalid</returns>
private uint? ValidateHeaderVersion()
/// <param name="stream"></param>
/// <returns></returns>
private static (char[] tag, uint length, uint version) GetHeaderValues(Stream stream)
{
try
char[] parsedTag = new char[8];
uint parsedLength = 0;
uint parsedVersion = 0;
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
{
// Seek to the beginning to make sure we're reading the correct bytes
m_br.BaseStream.Seek(0, SeekOrigin.Begin);
parsedTag = br.ReadCharsBigEndian(8);
parsedLength = br.ReadUInt32BigEndian();
parsedVersion = br.ReadUInt32BigEndian();
}
// Read and verify the CHD signature
m_signature = m_br.ReadBytes(8);
return (parsedTag, parsedLength, parsedVersion);
}
// If no signature could be read, return null
if (m_signature == null || m_signature.Length == 0)
/// <summary>
/// Validate the header values
/// </summary>
/// <returns>Matching version, 0 if none</returns>
private static uint ValidateHeader(char[] tag, uint length, uint version)
{
if (!string.Equals(new string(tag), "MComprHD", StringComparison.Ordinal))
return 0;
switch (version)
{
case 1:
return length == CHDFileV1.HeaderSize ? version : 0;
case 2:
return length == CHDFileV2.HeaderSize ? version : 0;
case 3:
return length == CHDFileV3.HeaderSize ? version : 0;
case 4:
return length == CHDFileV4.HeaderSize ? version : 0;
case 5:
return length == CHDFileV5.HeaderSize ? version : 0;
default:
return 0;
}
}
/// <summary>
/// Read a stream as a particular CHD version
/// </summary>
/// <param name="stream">CHD file as a stream</param>
/// <param name="version">CHD version to parse</param>
/// <returns>Populated CHD file, null on failure</returns>
private static CHDFile ReadAsVersion(Stream stream, uint version)
{
switch (version)
{
case 1:
return CHDFileV1.Deserialize(stream);
case 2:
return CHDFileV2.Deserialize(stream);
case 3:
return CHDFileV3.Deserialize(stream);
case 4:
return CHDFileV4.Deserialize(stream);
case 5:
return CHDFileV5.Deserialize(stream);
default:
return null;
if (!m_signature.StartsWith(Constants.CHDSignature, exact: true))
{
// throw CHDERR_INVALID_FILE;
return null;
}
// Get the header size and version
m_headersize = m_br.ReadUInt32Reverse();
m_version = m_br.ReadUInt32Reverse();
// If we have an invalid combination of size and version
if ((m_version == 1 && m_headersize != Constants.CHD_V1_HEADER_SIZE)
|| (m_version == 2 && m_headersize != Constants.CHD_V2_HEADER_SIZE)
|| (m_version == 3 && m_headersize != Constants.CHD_V3_HEADER_SIZE)
|| (m_version == 4 && m_headersize != Constants.CHD_V4_HEADER_SIZE)
|| (m_version == 5 && m_headersize != Constants.CHD_V5_HEADER_SIZE)
|| (m_version < 1 || m_version > 5))
{
// throw CHDERR_UNSUPPORTED_VERSION;
return null;
}
return m_version;
}
catch
{
return null;
}
}
/// <summary>
/// Get the internal MD5 (v1, v2) or SHA-1 (v3, v4, v5) from the CHD
/// </summary>
/// <returns>MD5 as a byte array, null on error</returns>
private byte[] GetHashFromHeader()
{
// Validate the header by default just in case
uint? version = ValidateHeaderVersion();
// Now get the hash, if possible
byte[] hash;
// Now parse the rest of the header according to the version
try
{
switch (version)
{
case 1:
hash = ParseCHDv1Header();
break;
case 2:
hash = ParseCHDv2Header();
break;
case 3:
hash = ParseCHDv3Header();
break;
case 4:
hash = ParseCHDv4Header();
break;
case 5:
hash = ParseCHDv5Header();
break;
case null:
default:
// throw CHDERR_INVALID_FILE;
return null;
}
}
catch
{
// throw CHDERR_INVALID_FILE;
return null;
}
return hash;
}
/// <summary>
/// Parse a CHD v1 header
/// </summary>
/// <returns>The extracted MD5 on success, null otherwise</returns>
private byte[] ParseCHDv1Header()
{
// Seek to after the signature to make sure we're reading the correct bytes
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
// Set the blank MD5 hash
byte[] md5 = new byte[16];
// Set offsets and defaults
m_mapoffset = 0;
m_mapentrybytes = 0;
// Read the CHD flags
uint flags = m_br.ReadUInt32Reverse();
// Determine compression
switch (m_br.ReadUInt32())
{
case 0: m_compression[0] = CHD_CODEC.NONE; break;
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
}
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
m_sectorsperhunk = m_br.ReadUInt32Reverse();
m_hunkcount = m_br.ReadUInt32Reverse();
m_br.ReadUInt32Reverse(); // Cylinder count
m_br.ReadUInt32Reverse(); // Head count
m_br.ReadUInt32Reverse(); // Sector count
md5 = m_br.ReadBytes(16);
m_br.ReadBytes(16); // Parent MD5
return md5;
}
/// <summary>
/// Parse a CHD v2 header
/// </summary>
/// <returns>The extracted MD5 on success, null otherwise</returns>
private byte[] ParseCHDv2Header()
{
// Seek to after the signature to make sure we're reading the correct bytes
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
// Set the blank MD5 hash
byte[] md5 = new byte[16];
// Set offsets and defaults
m_mapoffset = 0;
m_mapentrybytes = 0;
// Read the CHD flags
uint flags = m_br.ReadUInt32Reverse();
// Determine compression
switch (m_br.ReadUInt32())
{
case 0: m_compression[0] = CHD_CODEC.NONE; break;
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
}
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
m_sectorsperhunk = m_br.ReadUInt32Reverse();
m_hunkcount = m_br.ReadUInt32Reverse();
m_br.ReadUInt32Reverse(); // Cylinder count
m_br.ReadUInt32Reverse(); // Head count
m_br.ReadUInt32Reverse(); // Sector count
md5 = m_br.ReadBytes(16);
m_br.ReadBytes(16); // Parent MD5
m_br.ReadUInt32Reverse(); // Sector size
return md5;
}
/// <summary>
/// Parse a CHD v3 header
/// </summary>
/// <returns>The extracted SHA-1 on success, null otherwise</returns>
private byte[] ParseCHDv3Header()
{
// Seek to after the signature to make sure we're reading the correct bytes
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
// Set the blank SHA-1 hash
byte[] sha1 = new byte[20];
// Set offsets and defaults
m_mapoffset = 120;
m_mapentrybytes = 16;
// Read the CHD flags
uint flags = m_br.ReadUInt32Reverse();
// Determine compression
switch (m_br.ReadUInt32())
{
case 0: m_compression[0] = CHD_CODEC.NONE; break;
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
}
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
m_hunkcount = m_br.ReadUInt32Reverse();
m_logicalbytes = m_br.ReadUInt64Reverse();
m_metaoffset = m_br.ReadUInt32Reverse();
m_br.BaseStream.Seek(76, SeekOrigin.Begin);
m_hunkbytes = m_br.ReadUInt32Reverse();
m_br.BaseStream.Seek(Constants.CHDv3SHA1Offset, SeekOrigin.Begin);
sha1 = m_br.ReadBytes(20);
// guess at the units based on snooping the metadata
// m_unitbytes = guess_unitbytes();
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
return sha1;
}
/// <summary>
/// Parse a CHD v4 header
/// </summary>
/// <returns>The extracted SHA-1 on success, null otherwise</returns>
private byte[] ParseCHDv4Header()
{
// Seek to after the signature to make sure we're reading the correct bytes
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
// Set the blank SHA-1 hash
byte[] sha1 = new byte[20];
// Set offsets and defaults
m_mapoffset = 108;
m_mapentrybytes = 16;
// Read the CHD flags
uint flags = m_br.ReadUInt32Reverse();
// Determine compression
switch (m_br.ReadUInt32())
{
case 0: m_compression[0] = CHD_CODEC.NONE; break;
case 1: m_compression[0] = CHD_CODEC.ZLIB; break;
case 2: m_compression[0] = CHD_CODEC.ZLIB; break;
case 3: m_compression[0] = CHD_CODEC.AVHUFF; break;
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return null;
}
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC.NONE;
m_hunkcount = m_br.ReadUInt32Reverse();
m_logicalbytes = m_br.ReadUInt64Reverse();
m_metaoffset = m_br.ReadUInt32Reverse();
m_br.BaseStream.Seek(44, SeekOrigin.Begin);
m_hunkbytes = m_br.ReadUInt32Reverse();
m_br.BaseStream.Seek(Constants.CHDv4SHA1Offset, SeekOrigin.Begin);
sha1 = m_br.ReadBytes(20);
// guess at the units based on snooping the metadata
// m_unitbytes = guess_unitbytes();
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
return sha1;
}
/// <summary>
/// Parse a CHD v5 header
/// </summary>
/// <returns>The extracted SHA-1 on success, null otherwise</returns>
private byte[] ParseCHDv5Header()
{
// Seek to after the signature to make sure we're reading the correct bytes
m_br.BaseStream.Seek(16, SeekOrigin.Begin);
// Set the blank SHA-1 hash
byte[] sha1 = new byte[20];
// Determine compression
m_compression[0] = (CHD_CODEC)m_br.ReadUInt32Reverse();
m_compression[1] = (CHD_CODEC)m_br.ReadUInt32Reverse();
m_compression[2] = (CHD_CODEC)m_br.ReadUInt32Reverse();
m_compression[3] = (CHD_CODEC)m_br.ReadUInt32Reverse();
m_logicalbytes = m_br.ReadUInt64Reverse();
m_mapoffset = m_br.ReadUInt64Reverse();
m_metaoffset = m_br.ReadUInt64Reverse();
m_hunkbytes = m_br.ReadUInt32Reverse();
m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes;
m_unitbytes = m_br.ReadUInt32Reverse();
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
// m_allow_writes = !compressed();
// determine properties of map entries
// m_mapentrybytes = compressed() ? 12 : 4;
m_br.BaseStream.Seek(Constants.CHDv5SHA1Offset, SeekOrigin.Begin);
sha1 = m_br.ReadBytes(20);
return sha1;
}
#endregion

View File

@@ -0,0 +1,90 @@
using System;
using System.IO;
using System.Text;
using SabreTools.Library.Tools;
namespace SabreTools.Library.FileTypes
{
/// <summary>
/// CHD V1 File
/// </summary>
public class CHDFileV1 : CHDFile
{
/// <summary>
/// CHD flags
/// </summary>
[Flags]
public enum Flags : uint
{
DriveHasParent = 0x00000001,
DriveAllowsWrites = 0x00000002,
}
/// <summary>
/// Compression being used in CHD
/// </summary>
public enum Compression : uint
{
CHDCOMPRESSION_NONE = 0,
CHDCOMPRESSION_ZLIB = 1,
}
/// <summary>
/// Map format
/// </summary>
public class Map
{
public ulong offset; // 44; starting offset within the file
public ulong length; // 20; length of data; if == hunksize, data is uncompressed
}
public const int HeaderSize = 76;
public const uint Version = 1;
// V1-specific header values
public Flags flags; // flags (see above)
public Compression compression; // compression type
public uint hunksize; // 512-byte sectors per hunk
public uint totalhunks; // total # of hunks represented
public uint cylinders; // number of cylinders on hard disk
public uint heads; // number of heads on hard disk
public uint sectors; // number of sectors on hard disk
public byte[] md5 = new byte[16]; // MD5 checksum of raw data
public byte[] parentmd5 = new byte[16]; // MD5 checksum of parent file
/// <summary>
/// Parse and validate the header as if it's V1
/// </summary>
public static CHDFileV1 Deserialize(Stream stream)
{
CHDFileV1 chd = new CHDFileV1();
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
{
chd.tag = br.ReadCharsBigEndian(8);
chd.length = br.ReadUInt32BigEndian();
chd.version = br.ReadUInt32BigEndian();
chd.flags = (Flags)br.ReadUInt32BigEndian();
chd.compression = (Compression)br.ReadUInt32BigEndian();
chd.hunksize = br.ReadUInt32BigEndian();
chd.totalhunks = br.ReadUInt32BigEndian();
chd.cylinders = br.ReadUInt32BigEndian();
chd.heads = br.ReadUInt32BigEndian();
chd.sectors = br.ReadUInt32BigEndian();
chd.md5 = br.ReadBytesBigEndian(16);
chd.parentmd5 = br.ReadBytesBigEndian(16);
}
return chd;
}
/// <summary>
/// Return internal MD5 hash
/// </summary>
public override byte[] GetHash()
{
return md5;
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.IO;
using System.Text;
using SabreTools.Library.Tools;
namespace SabreTools.Library.FileTypes
{
/// <summary>
/// CHD V2 File
/// </summary>
public class CHDFileV2 : CHDFile
{
/// <summary>
/// CHD flags
/// </summary>
[Flags]
public enum Flags : uint
{
DriveHasParent = 0x00000001,
DriveAllowsWrites = 0x00000002,
}
/// <summary>
/// Compression being used in CHD
/// </summary>
public enum Compression : uint
{
CHDCOMPRESSION_NONE = 0,
CHDCOMPRESSION_ZLIB = 1,
}
/// <summary>
/// Map format
/// </summary>
public class Map
{
public ulong offset; // 44; starting offset within the file
public ulong length; // 20; length of data; if == hunksize, data is uncompressed
}
public const int HeaderSize = 80;
public const uint Version = 2;
// V2-specific header values
public Flags flags; // flags (see above)
public Compression compression; // compression type
public uint hunksize; // 512-byte sectors per hunk
public uint totalhunks; // total # of hunks represented
public uint cylinders; // number of cylinders on hard disk
public uint heads; // number of heads on hard disk
public uint sectors; // number of sectors on hard disk
public byte[] md5 = new byte[16]; // MD5 checksum of raw data
public byte[] parentmd5 = new byte[16]; // MD5 checksum of parent file
public uint seclen; // number of bytes per sector
/// <summary>
/// Parse and validate the header as if it's V2
/// </summary>
public static CHDFileV2 Deserialize(Stream stream)
{
CHDFileV2 chd = new CHDFileV2();
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
{
chd.tag = br.ReadCharsBigEndian(8);
chd.length = br.ReadUInt32BigEndian();
chd.version = br.ReadUInt32BigEndian();
chd.flags = (Flags)br.ReadUInt32BigEndian();
chd.compression = (Compression)br.ReadUInt32BigEndian();
chd.hunksize = br.ReadUInt32BigEndian();
chd.totalhunks = br.ReadUInt32BigEndian();
chd.cylinders = br.ReadUInt32BigEndian();
chd.heads = br.ReadUInt32BigEndian();
chd.sectors = br.ReadUInt32BigEndian();
chd.md5 = br.ReadBytesBigEndian(16);
chd.parentmd5 = br.ReadBytesBigEndian(16);
chd.seclen = br.ReadUInt32BigEndian();
}
return chd;
}
/// <summary>
/// Return internal MD5 hash
/// </summary>
public override byte[] GetHash()
{
return md5;
}
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.IO;
using System.Text;
using SabreTools.Library.Tools;
namespace SabreTools.Library.FileTypes
{
/// <summary>
/// CHD V3 File
/// </summary>
public class CHDFileV3 : CHDFile
{
/// <summary>
/// CHD flags
/// </summary>
[Flags]
public enum Flags : uint
{
DriveHasParent = 0x00000001,
DriveAllowsWrites = 0x00000002,
}
/// <summary>
/// Compression being used in CHD
/// </summary>
public enum Compression : uint
{
CHDCOMPRESSION_NONE = 0,
CHDCOMPRESSION_ZLIB = 1,
CHDCOMPRESSION_ZLIB_PLUS = 2,
}
/// <summary>
/// Map format
/// </summary>
public class Map
{
public ulong offset; // starting offset within the file
public uint crc32; // 32-bit CRC of the uncompressed data
public ushort length_lo; // lower 16 bits of length
public byte length_hi; // upper 8 bits of length
public byte flags; // flags, indicating compression info
}
public const int HeaderSize = 120;
public const uint Version = 3;
// V3-specific header values
public Flags flags; // flags (see above)
public Compression compression; // compression type
public uint totalhunks; // total # of hunks represented
public ulong logicalbytes; // logical size of the data (in bytes)
public ulong metaoffset; // offset to the first blob of metadata
public byte[] md5 = new byte[16]; // MD5 checksum of raw data
public byte[] parentmd5 = new byte[16]; // MD5 checksum of parent file
public uint hunkbytes; // number of bytes per hunk
public byte[] sha1 = new byte[20]; // SHA1 checksum of raw data
public byte[] parentsha1 = new byte[20]; // SHA1 checksum of parent file
/// <summary>
/// Parse and validate the header as if it's V3
/// </summary>
public static CHDFileV3 Deserialize(Stream stream)
{
CHDFileV3 chd = new CHDFileV3();
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
{
chd.tag = br.ReadCharsBigEndian(8);
chd.length = br.ReadUInt32BigEndian();
chd.version = br.ReadUInt32BigEndian();
chd.flags = (Flags)br.ReadUInt32BigEndian();
chd.compression = (Compression)br.ReadUInt32BigEndian();
chd.totalhunks = br.ReadUInt32BigEndian();
chd.logicalbytes = br.ReadUInt64BigEndian();
chd.metaoffset = br.ReadUInt64BigEndian();
chd.md5 = br.ReadBytesBigEndian(16);
chd.parentmd5 = br.ReadBytesBigEndian(16);
chd.hunkbytes = br.ReadUInt32BigEndian();
chd.sha1 = br.ReadBytesBigEndian(20);
chd.parentsha1 = br.ReadBytesBigEndian(20);
}
return chd;
}
/// <summary>
/// Return internal SHA1 hash
/// </summary>
public override byte[] GetHash()
{
return sha1;
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
using System.IO;
using System.Text;
using SabreTools.Library.Tools;
namespace SabreTools.Library.FileTypes
{
/// <summary>
/// CHD V4 File
/// </summary>
public class CHDFileV4 : CHDFile
{
/// <summary>
/// CHD flags
/// </summary>
[Flags]
public enum Flags : uint
{
DriveHasParent = 0x00000001,
DriveAllowsWrites = 0x00000002,
}
/// <summary>
/// Compression being used in CHD
/// </summary>
public enum Compression : uint
{
CHDCOMPRESSION_NONE = 0,
CHDCOMPRESSION_ZLIB = 1,
CHDCOMPRESSION_ZLIB_PLUS = 2,
CHDCOMPRESSION_AV = 3,
}
/// <summary>
/// Map format
/// </summary>
public class Map
{
public ulong offset; // starting offset within the file
public uint crc32; // 32-bit CRC of the uncompressed data
public ushort length_lo; // lower 16 bits of length
public byte length_hi; // upper 8 bits of length
public byte flags; // flags, indicating compression info
}
public const int HeaderSize = 108;
public const uint Version = 4;
// V4-specific header values
public Flags flags; // flags (see above)
public Compression compression; // compression type
public uint totalhunks; // total # of hunks represented
public ulong logicalbytes; // logical size of the data (in bytes)
public ulong metaoffset; // offset to the first blob of metadata
public uint hunkbytes; // number of bytes per hunk
public byte[] sha1 = new byte[20]; // combined raw+meta SHA1
public byte[] parentsha1 = new byte[20]; // combined raw+meta SHA1 of parent
public byte[] rawsha1 = new byte[20]; // raw data SHA1
/// <summary>
/// Parse and validate the header as if it's V4
/// </summary>
public static CHDFileV4 Deserialize(Stream stream)
{
CHDFileV4 chd = new CHDFileV4();
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
{
chd.tag = br.ReadCharsBigEndian(8);
chd.length = br.ReadUInt32BigEndian();
chd.version = br.ReadUInt32BigEndian();
chd.flags = (Flags)br.ReadUInt32BigEndian();
chd.compression = (Compression)br.ReadUInt32BigEndian();
chd.totalhunks = br.ReadUInt32BigEndian();
chd.logicalbytes = br.ReadUInt64BigEndian();
chd.metaoffset = br.ReadUInt64BigEndian();
chd.hunkbytes = br.ReadUInt32BigEndian();
chd.sha1 = br.ReadBytesBigEndian(20);
chd.parentsha1 = br.ReadBytesBigEndian(20);
chd.rawsha1 = br.ReadBytesBigEndian(20);
}
return chd;
}
/// <summary>
/// Return internal SHA1 hash
/// </summary>
public override byte[] GetHash()
{
return sha1;
}
}
}

View File

@@ -0,0 +1,98 @@
using System.IO;
using System.Text;
using SabreTools.Library.Tools;
namespace SabreTools.Library.FileTypes
{
/// <summary>
/// CHD V5 File
/// </summary>
public class CHDFileV5 : CHDFile
{
/// <summary>
/// Uncompressed map format
/// </summary>
private class UncompressedMap
{
public uint offset; // starting offset within the file
}
/// <summary>
/// Compressed map header format
/// </summary>
private class CompressedMapHeader
{
public uint length; // length of compressed map
public byte[] datastart = new byte[12]; // UINT48; offset of first block
public ushort crc; // crc-16 of the map
public byte lengthbits; // bits used to encode complength
public byte hunkbits; // bits used to encode self-refs
public byte parentunitbits; // bits used to encode parent unit refs
public byte reserved; // future use
}
/// <summary>
/// Compressed map entry format
/// </summary>
private class CompressedMapEntry
{
public byte compression; // compression type
public byte[] complength = new byte[6]; // UINT24; compressed length
public byte[] offset = new byte[12]; // UINT48; offset
public ushort crc; // crc-16 of the data
}
public const int HeaderSize = 124;
public const uint Version = 5;
// V5-specific header values
public uint[] compressors = new uint[4]; // which custom compressors are used?
public ulong logicalbytes; // logical size of the data (in bytes)
public ulong mapoffset; // offset to the map
public ulong metaoffset; // offset to the first blob of metadata
public uint hunkbytes; // number of bytes per hunk
public uint unitbytes; // number of bytes per unit within each hunk
public byte[] rawsha1 = new byte[20]; // raw data SHA1
public byte[] sha1 = new byte[20]; // combined raw+meta SHA1
public byte[] parentsha1 = new byte[20]; // combined raw+meta SHA1 of parent
/// <summary>
/// Parse and validate the header as if it's V5
/// </summary>
public static CHDFileV5 Deserialize(Stream stream)
{
CHDFileV5 chd = new CHDFileV5();
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
{
chd.tag = br.ReadCharsBigEndian(8);
chd.length = br.ReadUInt32BigEndian();
chd.version = br.ReadUInt32BigEndian();
chd.compressors = new uint[4];
for (int i = 0; i < 4; i++)
{
chd.compressors[i] = br.ReadUInt32BigEndian();
}
chd.logicalbytes = br.ReadUInt64BigEndian();
chd.mapoffset = br.ReadUInt64BigEndian();
chd.metaoffset = br.ReadUInt64BigEndian();
chd.hunkbytes = br.ReadUInt32BigEndian();
chd.unitbytes = br.ReadUInt32BigEndian();
chd.rawsha1 = br.ReadBytesBigEndian(20);
chd.sha1 = br.ReadBytesBigEndian(20);
chd.parentsha1 = br.ReadBytesBigEndian(20);
}
return chd;
}
/// <summary>
/// Return internal SHA1 hash
/// </summary>
public override byte[] GetHash()
{
return sha1;
}
}
}

View File

@@ -1,340 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.Library.Data;
using SabreTools.Library.DatItems;
/// <summary>
/// This code is based on the header format described at http://www.rarlab.com/technote.htm#srvheaders
/// </summary>
/// <remarks>
/// ---------------------------------------------
/// vint
///
/// Variable length integer. Can include one or more bytes, where lower 7 bits of every byte contain integer data
/// and highest bit in every byte is the continuation flag.If highest bit is 0, this is the last byte in sequence.
/// So first byte contains 7 least significant bits of integer and continuation flag. Second byte, if present,
/// contains next 7 bits and so on.
///
/// Currently RAR format uses vint to store up to 64 bit integers, resulting in 10 bytes maximum. This value may
/// be increased in the future if necessary for some reason.
///
/// Sometimes RAR needs to pre-allocate space for vint before knowing its exact value. In such situation it can
/// allocate more space than really necessary and then fill several leading bytes with 0x80 hexadecimal, which means
/// 0 with continuation flag set.
/// ----------------------------------------------
/// General archive layout:
///
/// Self-extracting module(optional) (RAR assumes the maximum SFX module size to not exceed 1 MB, but this value
/// can be increased in the future.
/// RAR 5.0 signature (RAR 5.0 signature consists of 8 bytes: 0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00.
/// You need to search for this signature in supposed archive from beginning and up to maximum SFX
/// module size. Just for comparison this is RAR 4.x 7 byte length signature: 0x52 0x61 0x72 0x21 0x1A 0x07 0x00.)
/// Archive encryption header(optional)
/// Main archive header
/// Archive comment service header(optional)
/// File header 1
/// Service headers(NTFS ACL, streams, etc.) for preceding file(optional).
/// ...
/// File header N
/// Service headers(NTFS ACL, streams, etc.) for preceding file(optional).
/// Recovery record(optional).
/// End of archive header.
/// ----------------------------------------------
/// General archive block format:
///
/// Header CRC32: uint32 (CRC32 of header data starting from Header size field and up to and including the optional extra area.)
/// Header size: vint (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.)
/// Header type: vint (Type of archive header. Possible values are: )
/// 1 Main archive header.
/// 2 File header.
/// 3 Service header.
/// 4 Archive encryption header.
/// 5 End of archive header.
/// Header flags: vint (Flags common for all headers:)
/// 0x0001 Extra area is present in the end of header.
/// 0x0002 Data area is present in the end of header.
/// 0x0004 Blocks with unknown type and this flag must be skipped when updating an archive.
/// 0x0008 Data area is continuing from previous volume.
/// 0x0010 Data area is continuing in next volume.
/// 0x0020 Block depends on preceding file block.
/// 0x0040 Preserve a child block if host block is modified.
/// Extra area size: vint (Size of extra area. Optional field, present only if 0x0001 header flag is set.)
/// Data size: vint (Size of data area. Optional field, present only if 0x0002 header flag is set.)
/// ...: ... (Fields specific for current block type. See concrete block type descriptions for details)
/// Extra data: ... (Optional area containing additional header fields, present only if 0x0001 header flag is set.)
/// Data area: vint (Optional data area, present only if 0x0002 header flag is set. Used to store large data amounts, such as
/// compressed file data. Not counted in Header CRC and Header size fields.
/// ----------------------------------------------
/// General extra area format
///
/// Size: vint (Size of record data starting from Type.)
/// Type: vint (Record type. Different archive blocks have different associated extra area record types. Read the
/// concrete archive block description for details. New record types can be added in the future, so unknown
/// record types need to be skipped without interrupting an operation.)
/// Data: ... (Record dependent data. May be missing if record consists only from size and type.)
/// ----------------------------------------------
/// Archive encryption header:
///
/// Header CRC32: uint32
/// Header size: vint
/// Header type: vint (4)
/// Header flags: vint
/// Encryption version: vint (Version of encryption algorithm. Now only 0 version(AES-256) is supported.)
/// Encryption flags: vint
/// 0x0001 Password check data is present.
/// KDF count: 1 byte (Binary logarithm of iteration number for PBKDF2 function.RAR can refuse to process
/// KDF count exceeding some threshold. Concrete value of threshold is a version dependent.)
/// Salt: 16 bytes (Salt value used globally for all encrypted archive headers.)
/// Check value: 12 bytes (Value used to verify the password validity. Present only if 0x0001 encryption
/// flag is set.First 8 bytes are calculated using additional PBKDF2 rounds, 4 last bytes is the additional
/// checksum. Together with the standard header CRC32 we have 64 bit checksum to reliably verify this field
/// integrity and distinguish invalid password and damaged data. Further details can be found in UnRAR source code.)
/// ----------------------------------------------
/// Main archive header:
///
/// Header CRC32: uint32 (CRC32 of header data starting from Header size field and up to and including the optional extra area.)
/// Header size: vint (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.)
/// Header type: vint (1)
/// Header flags: vint (Flags common for all headers)
/// Extra area size: vint (Size of extra area. Optional field, present only if 0x0001 header flag is set.)
/// Archive flags: vint
/// 0x0001 Volume.Archive is a part of multivolume set.
/// 0x0002 Volume number field is present.This flag is present in all volumes except first.
/// 0x0004 Solid archive.
/// 0x0008 Recovery record is present.
/// 0x0010 Locked archive.
/// Volume number: vint (Optional field, present only if 0x0002 archive flag is set. Not present for first volume,
/// 1 for second volume, 2 for third and so on.)
/// Extra area: ... (Optional area containing additional header fields, present only if 0x0001 header flag is set.)
/// [Extra area of main archive header can contain following record types
/// Type Name Description
/// 0x01 Locator Contains positions of different service blocks, so they can be accessed quickly, without scanning
/// the entire archive.This record is optional.If it is missing, it is still necessary to scan the entire archive
/// to verify presence of service blocks.]
/// ----------------------------------------------
/// </remarks>
namespace SabreTools.Library.FileTypes
{
public class CoreRarArchive : BaseArchive
{
// SFX Module Information
public byte[] SFX;
// Standard Header Information
public uint HeaderCRC32;
public uint HeaderSize; // vint
public RarHeaderFlags HeaderFlags; // vint
public uint ExtraAreaSize; // vint
public RarArchiveFlags ArchiveFlags; // vint
public uint VolumeNumber; // vint
public byte[] ExtraArea;
// Encryption Header Information
public uint EncryptionHeaderCRC32;
public uint EncryptionHeaderSize; // vint
public RarHeaderFlags EncryptionHeaderFlags; // vint
public uint EncryptionVersion; // vint
public uint EncryptionFlags; // vint
public byte KDFCount;
public byte[] Salt = new byte[16];
public byte[] CheckValue = new byte[12];
// Locator Information
public uint LocatorSize; // vint
public uint LocatorFlags; // vint
public uint QuickOpenOffset; // vint
public uint RecoveryRecordOffset; // vint
// Entry Information
public List<CoreRarArchiveEntry> Entries = new List<CoreRarArchiveEntry>();
#region Unimplemented methods
public override bool CopyAll(string outDir)
{
throw new NotImplementedException();
}
public override string CopyToFile(string entryName, string outDir)
{
throw new NotImplementedException();
}
public override (MemoryStream, string) CopyToStream(string entryName)
{
throw new NotImplementedException();
}
public override List<BaseFile> GetChildren(Hash omitFromScan = Hash.DeepHashes, bool date = false)
{
throw new NotImplementedException();
}
public override List<string> GetEmptyFolders()
{
throw new NotImplementedException();
}
public override bool IsTorrent()
{
throw new NotImplementedException();
}
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
{
throw new NotImplementedException();
}
public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false)
{
throw new NotImplementedException();
}
public override bool Write(List<string> inputFiles, string outDir, List<Rom> roms, bool date = false, bool romba = false)
{
throw new NotImplementedException();
}
#endregion
}
public class CoreRarArchiveEntry
{
// Standard Entry Information
public uint HeaderCRC32;
public uint HeaderSize; // vint
public RarHeaderType HeaderType; // vint
public RarHeaderFlags HeaderFlags; // vint
public uint ExtraAreaSize; // vint
public uint DataAreaSize; // vint
public RarFileFlags FileFlags; // vint
public uint UnpackedSize; // vint
public uint Attributes; // vint
public uint mtime;
public uint DataCRC32;
public uint CompressionInformation; // vint
public uint HostOS; // vint
public uint NameLength; // vint
public byte[] Name;
public byte[] DataArea;
// File Encryption Information
public uint EncryptionSize; // vint
public RarEncryptionFlags EncryptionFlags; // vint
public byte KDFCount;
public byte[] Salt = new byte[16];
public byte[] IV = new byte[16];
public byte[] CheckValue = new byte[12];
// File Hash Information
public uint HashSize; // vint
public uint HashType; // vint
public byte[] HashData = new byte[32];
// File Time Information
public uint TimeSize; // vint
public RarTimeFlags TimeFlags; // vint
public uint TimeMtime;
public ulong TimeMtime64;
public uint TimeCtime;
public ulong TimeCtime64;
public uint TimeLtime;
public ulong TimeLtime64;
// File Version Information
public uint VersionSize; // vint
public const uint VersionFlags = 0; // vint
public uint VersionNumber; // vint
// File System Redirection Record
public uint RedirectionSize; // vint
public RarRedirectionType RedirectionType; // vint
public uint RedirectionFlags; // vint
public uint RedirectionNameLength; // vint
public byte[] RedirectionName;
// Unix Owner Record
public uint UnixOwnerSize; // vint
public RarUnixOwnerRecordFlags UnixOwnerFlags; // vint
public uint UnixOwnerUserNameLength; // vint
public byte[] UnixOwnerUserName;
public uint UnixOwnerGroupNameLength; // vint
public byte[] UnixOwnerGroupName;
public uint UnixOwnerUserId; // vint
public uint UnixOwnerGroupId; // vint
// Service Data Information
public uint ServiceSize; // vint
public byte[] ServiceData;
}
// BELOW ARE CONCRETE IMPLEMENTATIONS OF HEADER DETAILS
/// <summary>
/// General archive block format used by all RAR block types
/// </summary>
public class GeneralArchiveBlockFormat
{
public uint HeaderCRC32;
public uint HeaderSize; // vint
public HeaderType HeaderType;
public HeaderFlags HeaderFlags;
public ulong ExtraAreaSize; // vint
public ulong DataAreaSize; // vint
public byte[] ExtraArea;
public byte[] DataArea;
}
/// <summary>
/// General extra area format used by all RAR extra area records
/// </summary>
public class GeneralExtraAreaFormat
{
public ulong Size; // vint
public ulong Type; // vint
public byte[] Data;
}
/// <summary>
/// Encryption header only present in encrypted archives
///
/// Every proceeding header is started from 16 byte AES-256
/// initialization vectors followed by encrypted header data
/// </summary>
public class ArchiveEncryptionHeader : GeneralArchiveBlockFormat
{
public new HeaderType HeaderType = HeaderType.ArchiveEncryptionHeader;
public ulong EncryptionVersion; // vint
public ulong EncryptionFlags; // vint
}
/// <summary>
/// Types of archive header
/// </summary>
public enum HeaderType : ulong // vint
{
MainArchiveHeader = 1,
FileHeader = 2,
ServiceHeader = 3,
ArchiveEncryptionHeader = 4,
EndOfArchiveHeader = 5,
}
/// <summary>
/// Flags common for all headers
/// </summary>
[Flags]
public enum HeaderFlags : ulong // vint
{
ExtraAreaIsPresentInEndOfHeader = 0x0001,
DataAreaIsPresentInEndOfHeader = 0x0002,
BlocksWithUnknownType = 0x0004, // this flag must be skipped when updating an archive
DataAreaIsContinuingFromPreviousVolume = 0x0008,
DataAreaIsContinuingInNextVolume = 0x0010,
BlockDependsOnPrecedingFileBlock = 0x0020,
PreserveChildBlockIfHostBlockIsModified = 0x0040,
}
}

View File

@@ -43,6 +43,55 @@ namespace SabreTools.Library.FileTypes
this.Type = FileType.Folder;
}
/// <summary>
/// Create an folder object of the specified type, if possible
/// </summary>
/// <param name="outputFormat">SabreTools.Library.Data.OutputFormat representing the archive to create</param>
/// <returns>Archive object representing the inputs</returns>
public static Folder Create(OutputFormat outputFormat)
{
switch (outputFormat)
{
case OutputFormat.Folder:
return new Folder();
case OutputFormat.TapeArchive:
return new TapeArchive();
case OutputFormat.Torrent7Zip:
return new SevenZipArchive();
case OutputFormat.TorrentGzip:
case OutputFormat.TorrentGzipRomba:
return new GZipArchive();
case OutputFormat.TorrentLRZip:
return new LRZipArchive();
case OutputFormat.TorrentLZ4:
return new LZ4Archive();
case OutputFormat.TorrentRar:
return new RarArchive();
case OutputFormat.TorrentXZ:
case OutputFormat.TorrentXZRomba:
return new XZArchive();
case OutputFormat.TorrentZip:
return new ZipArchive();
case OutputFormat.TorrentZPAQ:
return new ZPAQArchive();
case OutputFormat.TorrentZstd:
return new ZstdArchive();
default:
return null;
}
}
#endregion
#region Extraction
@@ -129,7 +178,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(outDir);
// Get all files from the input directory
List<string> files = Utilities.RetrieveFiles(this.Filename, new List<string>());
List<string> files = DirectoryExtensions.GetFilesOrdered(this.Filename);
// Now sort through to find the first file that matches
string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault();
@@ -168,7 +217,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(this.Filename);
// Get all files from the input directory
List<string> files = Utilities.RetrieveFiles(this.Filename, new List<string>());
List<string> files = DirectoryExtensions.GetFilesOrdered(this.Filename);
// Now sort through to find the first file that matches
string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault();
@@ -176,7 +225,7 @@ namespace SabreTools.Library.FileTypes
// If we had a file, copy that over to the new name
if (!string.IsNullOrWhiteSpace(match))
{
Utilities.TryOpenRead(match).CopyTo(ms);
FileExtensions.TryOpenRead(match).CopyTo(ms);
realentry = match;
}
}
@@ -207,7 +256,7 @@ namespace SabreTools.Library.FileTypes
_children = new List<BaseFile>();
foreach (string file in Directory.EnumerateFiles(this.Filename, "*", SearchOption.TopDirectoryOnly))
{
BaseFile nf = Utilities.GetFileInfo(file, omitFromScan: omitFromScan, date: date);
BaseFile nf = FileExtensions.GetInfo(file, omitFromScan: omitFromScan, date: date);
_children.Add(nf);
}
@@ -228,7 +277,7 @@ namespace SabreTools.Library.FileTypes
/// <returns>List of empty folders in the folder</returns>
public virtual List<string> GetEmptyFolders()
{
return Utilities.GetEmptyDirectories(this.Filename).ToList();
return DirectoryExtensions.ListEmpty(this.Filename);
}
#endregion
@@ -247,7 +296,7 @@ namespace SabreTools.Library.FileTypes
/// <remarks>This works for now, but it can be sped up by using Ionic.Zip or another zlib wrapper that allows for header values built-in. See edc's code.</remarks>
public virtual bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
{
FileStream fs = Utilities.TryOpenRead(inputFile);
FileStream fs = FileExtensions.TryOpenRead(inputFile);
return Write(fs, outDir, rom, date, romba);
}
@@ -281,7 +330,7 @@ namespace SabreTools.Library.FileTypes
FileStream outputStream = null;
// Get the output folder name from the first rebuild rom
string fileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName), Utilities.RemovePathUnsafeCharacters(rom.Name));
string fileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName), Sanitizer.RemovePathUnsafeCharacters(rom.Name));
try
{
@@ -292,7 +341,7 @@ namespace SabreTools.Library.FileTypes
}
// Overwrite output files by default
outputStream = Utilities.TryCreate(fileName);
outputStream = FileExtensions.TryCreate(fileName);
// If the output stream isn't null
if (outputStream != null)

View File

@@ -60,7 +60,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(outDir);
// Decompress the _filename stream
FileStream outstream = Utilities.TryCreate(Path.Combine(outDir, Path.GetFileNameWithoutExtension(this.Filename)));
FileStream outstream = FileExtensions.TryCreate(Path.Combine(outDir, Path.GetFileNameWithoutExtension(this.Filename)));
var gz = new gZip();
ZipReturn ret = gz.ZipFileOpen(this.Filename);
ret = gz.ZipFileOpenReadStream(0, out Stream gzstream, out ulong streamSize);
@@ -110,7 +110,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
// Now open and write the file if possible
FileStream fs = Utilities.TryCreate(realEntry);
FileStream fs = FileExtensions.TryCreate(realEntry);
if (fs != null)
{
ms.Seek(0, SeekOrigin.Begin);
@@ -145,7 +145,7 @@ namespace SabreTools.Library.FileTypes
public override (MemoryStream, string) CopyToStream(string entryName)
{
MemoryStream ms = new MemoryStream();
string realEntry = null;
string realEntry;
try
{
@@ -215,11 +215,10 @@ namespace SabreTools.Library.FileTypes
{
Filename = gamename,
};
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
br.BaseStream.Seek(-8, SeekOrigin.End);
byte[] headercrc = br.ReadBytesReverse(4);
tempRom.CRC = headercrc;
tempRom.Size = br.ReadInt32Reverse();
tempRom.CRC = br.ReadBytesBigEndian(4);
tempRom.Size = br.ReadInt32BigEndian();
br.Dispose();
_children.Add(tempRom);
@@ -230,7 +229,7 @@ namespace SabreTools.Library.FileTypes
var gz = new gZip();
ZipReturn ret = gz.ZipFileOpen(this.Filename);
ret = gz.ZipFileOpenReadStream(0, out Stream gzstream, out ulong streamSize);
BaseFile gzipEntryRom = Utilities.GetStreamInfo(gzstream, gzstream.Length, omitFromScan);
BaseFile gzipEntryRom = gzstream.GetInfo(omitFromScan: omitFromScan);
gzipEntryRom.Filename = gz.Filename(0);
gzipEntryRom.Parent = gamename;
gzipEntryRom.Date = (date && gz.TimeStamp > 0 ? gz.TimeStamp.ToString() : null);
@@ -267,9 +266,7 @@ namespace SabreTools.Library.FileTypes
{
// Check for the file existing first
if (!File.Exists(this.Filename))
{
return false;
}
string datum = Path.GetFileName(this.Filename).ToLowerInvariant();
long filesize = new FileInfo(this.Filename).Length;
@@ -296,15 +293,11 @@ namespace SabreTools.Library.FileTypes
}
// Get the Romba-specific header data
byte[] header; // Get preamble header for checking
byte[] headermd5; // MD5
byte[] headercrc; // CRC
ulong headersz; // Int64 size
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
header = br.ReadBytes(12);
headermd5 = br.ReadBytes(16);
headercrc = br.ReadBytes(4);
headersz = br.ReadUInt64();
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
byte[] header = br.ReadBytes(12); // Get preamble header for checking
br.ReadBytes(16); // headermd5
br.ReadBytes(4); // headercrc
br.ReadUInt64(); // headersz
br.Dispose();
// If the header is not correct, return a blank rom
@@ -313,15 +306,13 @@ namespace SabreTools.Library.FileTypes
{
// This is a temp fix to ignore the modification time and OS until romba can be fixed
if (i == 4 || i == 5 || i == 6 || i == 7 || i == 9)
{
continue;
}
correct &= (header[i] == Constants.TorrentGZHeader[i]);
}
if (!correct)
{
return false;
}
return true;
}
@@ -367,7 +358,7 @@ namespace SabreTools.Library.FileTypes
byte[] headermd5; // MD5
byte[] headercrc; // CRC
ulong headersz; // Int64 size
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
header = br.ReadBytes(12);
headermd5 = br.ReadBytes(16);
headercrc = br.ReadBytes(4);
@@ -429,10 +420,11 @@ namespace SabreTools.Library.FileTypes
Globals.Logger.Warning($"File '{inputFile}' does not exist!");
return false;
}
inputFile = Path.GetFullPath(inputFile);
// Get the file stream for the file and write out
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date, romba);
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date, romba);
}
/// <summary>
@@ -451,27 +443,24 @@ namespace SabreTools.Library.FileTypes
// If the stream is not readable, return
if (!inputStream.CanRead)
{
return success;
}
// Make sure the output directory exists
if (!Directory.Exists(outDir))
{
Directory.CreateDirectory(outDir);
}
outDir = Path.GetFullPath(outDir);
// Now get the Rom info for the file so we have hashes and size
rom = new Rom(Utilities.GetStreamInfo(inputStream, inputStream.Length, keepReadOpen: true));
rom = new Rom(inputStream.GetInfo(keepReadOpen: true));
// Get the output file name
string outfile = null;
string outfile;
// If we have a romba output, add the romba path
if (romba)
{
outfile = Path.Combine(outDir, Utilities.GetRombaPath(rom.SHA1)); // TODO: When updating to SHA-256, this needs to update to SHA256
outfile = Path.Combine(outDir, PathExtensions.GetRombaPath(rom.SHA1)); // TODO: When updating to SHA-256, this needs to update to SHA256
// Check to see if the folder needs to be created
if (!Directory.Exists(Path.GetDirectoryName(outfile)))
@@ -489,7 +478,7 @@ namespace SabreTools.Library.FileTypes
if (!File.Exists(outfile))
{
// Compress the input stream
FileStream outputStream = Utilities.TryCreate(outfile);
FileStream outputStream = FileExtensions.TryCreate(outfile);
// Open the output file for writing
BinaryWriter sw = new BinaryWriter(outputStream);

View File

@@ -104,7 +104,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
// Now open and write the file if possible
FileStream fs = Utilities.TryCreate(realEntry);
FileStream fs = FileExtensions.TryCreate(realEntry);
if (fs != null)
{
ms.Seek(0, SeekOrigin.Begin);
@@ -183,7 +183,7 @@ namespace SabreTools.Library.FileTypes
try
{
SharpCompress.Archives.Rar.RarArchive ra = SharpCompress.Archives.Rar.RarArchive.Open(Utilities.TryOpenRead(this.Filename));
SharpCompress.Archives.Rar.RarArchive ra = SharpCompress.Archives.Rar.RarArchive.Open(FileExtensions.TryOpenRead(this.Filename));
foreach (RarArchiveEntry entry in ra.Entries.Where(e => e != null && !e.IsDirectory))
{
// If secure hashes are disabled, do a quickscan
@@ -203,7 +203,7 @@ namespace SabreTools.Library.FileTypes
else
{
Stream entryStream = entry.OpenEntryStream();
BaseFile rarEntryRom = Utilities.GetStreamInfo(entryStream, entry.Size, omitFromScan);
BaseFile rarEntryRom = entryStream.GetInfo(entry.Size, omitFromScan);
rarEntryRom.Filename = entry.Key;
rarEntryRom.Parent = gamename;
rarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
@@ -224,223 +224,6 @@ namespace SabreTools.Library.FileTypes
return found;
}
/// <summary>
/// (INCOMPLETE) Retrieve file information for a RAR file
/// </summary>
/// TODO: Write the rest of this RAR file handling
public void GetRarFileInfo()
{
if (!File.Exists(this.Filename))
{
return;
}
BinaryReader br = new BinaryReader(Utilities.TryOpenRead(this.Filename));
// Check for the signature first (Skipping the SFX Module)
byte[] signature = br.ReadBytes(8);
int startpos = 0;
while (startpos < Constants.MibiByte && !signature.StartsWith(Constants.RarSignature, exact: true) && !signature.StartsWith(Constants.RarFiveSignature, exact: true))
{
startpos++;
br.BaseStream.Position = startpos;
signature = br.ReadBytes(8);
}
if (!signature.StartsWith(Constants.RarSignature, exact: true) && !signature.StartsWith(Constants.RarFiveSignature, exact: true))
{
return;
}
CoreRarArchive cra = new CoreRarArchive();
if (startpos > 0)
{
br.BaseStream.Position = 0;
cra.SFX = br.ReadBytes(startpos);
}
// Get all archive header information
cra.HeaderCRC32 = br.ReadUInt32();
cra.HeaderSize = br.ReadUInt32();
uint headerType = br.ReadUInt32();
// Special encryption information
bool hasEncryptionHeader = false;
// If it's encrypted
if (headerType == (uint)RarHeaderType.ArchiveEncryption)
{
hasEncryptionHeader = true;
cra.EncryptionHeaderCRC32 = cra.HeaderCRC32;
cra.EncryptionHeaderSize = cra.HeaderSize;
cra.EncryptionHeaderFlags = (RarHeaderFlags)br.ReadUInt32();
cra.EncryptionVersion = br.ReadUInt32();
cra.EncryptionFlags = br.ReadUInt32();
cra.KDFCount = br.ReadByte();
cra.Salt = br.ReadBytes(16);
cra.CheckValue = br.ReadBytes(12);
cra.HeaderCRC32 = br.ReadUInt32();
cra.HeaderSize = br.ReadUInt32();
headerType = br.ReadUInt32();
}
cra.HeaderFlags = (RarHeaderFlags)br.ReadUInt32();
if ((cra.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0)
{
cra.ExtraAreaSize = br.ReadUInt32();
}
cra.ArchiveFlags = (RarArchiveFlags)br.ReadUInt32();
if ((cra.ArchiveFlags & RarArchiveFlags.VolumeNumberField) != 0)
{
cra.VolumeNumber = br.ReadUInt32();
}
if (((cra.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0) && cra.ExtraAreaSize != 0)
{
cra.ExtraArea = br.ReadBytes((int)cra.ExtraAreaSize);
}
// Archive Comment Service Header
// Now for file headers
for (; ; )
{
CoreRarArchiveEntry crae = new CoreRarArchiveEntry();
crae.HeaderCRC32 = br.ReadUInt32();
crae.HeaderSize = br.ReadUInt32();
crae.HeaderType = (RarHeaderType)br.ReadUInt32();
if (crae.HeaderType == RarHeaderType.EndOfArchive)
{
break;
}
crae.HeaderFlags = (RarHeaderFlags)br.ReadUInt32();
if ((crae.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0)
{
crae.ExtraAreaSize = br.ReadUInt32();
}
if ((crae.HeaderFlags & RarHeaderFlags.DataAreaPresent) != 0)
{
crae.DataAreaSize = br.ReadUInt32();
}
crae.FileFlags = (RarFileFlags)br.ReadUInt32();
crae.UnpackedSize = br.ReadUInt32();
if ((crae.FileFlags & RarFileFlags.UnpackedSizeUnknown) != 0)
{
crae.UnpackedSize = 0;
}
crae.Attributes = br.ReadUInt32();
crae.mtime = br.ReadUInt32();
crae.DataCRC32 = br.ReadUInt32();
crae.CompressionInformation = br.ReadUInt32();
crae.HostOS = br.ReadUInt32();
crae.NameLength = br.ReadUInt32();
crae.Name = br.ReadBytes((int)crae.NameLength);
if ((crae.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0)
{
uint extraSize = br.ReadUInt32();
switch (br.ReadUInt32()) // Extra Area Type
{
case 0x01: // File encryption information
crae.EncryptionSize = extraSize;
crae.EncryptionFlags = (RarEncryptionFlags)br.ReadUInt32();
crae.KDFCount = br.ReadByte();
crae.Salt = br.ReadBytes(16);
crae.IV = br.ReadBytes(16);
crae.CheckValue = br.ReadBytes(12);
break;
case 0x02: // File data hash
crae.HashSize = extraSize;
crae.HashType = br.ReadUInt32();
crae.HashData = br.ReadBytes(32);
break;
case 0x03: // High precision file time
crae.TimeSize = extraSize;
crae.TimeFlags = (RarTimeFlags)br.ReadUInt32();
if ((crae.TimeFlags & RarTimeFlags.TimeInUnixFormat) != 0)
{
if ((crae.TimeFlags & RarTimeFlags.ModificationTimePresent) != 0)
{
crae.TimeMtime64 = br.ReadUInt64();
}
if ((crae.TimeFlags & RarTimeFlags.CreationTimePresent) != 0)
{
crae.TimeCtime64 = br.ReadUInt64();
}
if ((crae.TimeFlags & RarTimeFlags.LastAccessTimePresent) != 0)
{
crae.TimeLtime64 = br.ReadUInt64();
}
}
else
{
if ((crae.TimeFlags & RarTimeFlags.ModificationTimePresent) != 0)
{
crae.TimeMtime = br.ReadUInt32();
}
if ((crae.TimeFlags & RarTimeFlags.CreationTimePresent) != 0)
{
crae.TimeCtime = br.ReadUInt32();
}
if ((crae.TimeFlags & RarTimeFlags.LastAccessTimePresent) != 0)
{
crae.TimeLtime = br.ReadUInt32();
}
}
break;
case 0x04: // File version number
crae.VersionSize = extraSize;
/* crae.VersionFlags = */
br.ReadUInt32();
crae.VersionNumber = br.ReadUInt32();
break;
case 0x05: // File system redirection
crae.RedirectionSize = extraSize;
crae.RedirectionType = (RarRedirectionType)br.ReadUInt32();
crae.RedirectionFlags = br.ReadUInt32();
crae.RedirectionNameLength = br.ReadUInt32();
crae.RedirectionName = br.ReadBytes((int)crae.RedirectionNameLength);
break;
case 0x06: // Unix owner and group information
crae.UnixOwnerSize = extraSize;
crae.UnixOwnerFlags = (RarUnixOwnerRecordFlags)br.ReadUInt32();
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.UserNameStringIsPresent) != 0)
{
crae.UnixOwnerUserNameLength = br.ReadUInt32();
crae.UnixOwnerUserName = br.ReadBytes((int)crae.UnixOwnerUserNameLength);
}
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.GroupNameStringIsPresent) != 0)
{
crae.UnixOwnerGroupNameLength = br.ReadUInt32();
crae.UnixOwnerGroupName = br.ReadBytes((int)crae.UnixOwnerGroupNameLength);
}
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.NumericUserIdIsPresent) != 0)
{
crae.UnixOwnerUserId = br.ReadUInt32();
}
if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.NumericGroupIdIsPresent) != 0)
{
crae.UnixOwnerGroupId = br.ReadUInt32();
}
break;
case 0x07: // Service header data array
break;
}
}
if ((crae.HeaderFlags & RarHeaderFlags.DataAreaPresent) != 0)
{
crae.DataArea = br.ReadBytes((int)crae.DataAreaSize);
}
}
}
/// <summary>
/// Generate a list of empty folders in an archive
/// </summary>
@@ -505,7 +288,7 @@ namespace SabreTools.Library.FileTypes
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
{
// Get the file stream for the file and write out
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date, romba);
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date, romba);
}
/// <summary>

View File

@@ -85,7 +85,7 @@ namespace SabreTools.Library.FileTypes
continue;
}
FileStream writeStream = Utilities.TryCreate(Path.Combine(outDir, zf.Filename(i)));
FileStream writeStream = FileExtensions.TryCreate(Path.Combine(outDir, zf.Filename(i)));
// If the stream is smaller than the buffer, just run one loop through to avoid issues
if (streamsize < _bufferSize)
@@ -152,7 +152,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
// Now open and write the file if possible
FileStream fs = Utilities.TryCreate(realEntry);
FileStream fs = FileExtensions.TryCreate(realEntry);
if (fs != null)
{
ms.Seek(0, SeekOrigin.Begin);
@@ -314,7 +314,7 @@ namespace SabreTools.Library.FileTypes
// Otherwise, use the stream directly
else
{
BaseFile zipEntryRom = Utilities.GetStreamInfo(readStream, (long)zf.UncompressedSize(i), omitFromScan, true);
BaseFile zipEntryRom = readStream.GetInfo((long)zf.UncompressedSize(i), omitFromScan, true);
zipEntryRom.Filename = zf.Filename(i);
zipEntryRom.Parent = gamename;
found.Add(zipEntryRom);
@@ -417,7 +417,7 @@ namespace SabreTools.Library.FileTypes
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
{
// Get the file stream for the file and write out
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date: date);
}
/// <summary>
@@ -450,7 +450,7 @@ namespace SabreTools.Library.FileTypes
inputStream.Seek(0, SeekOrigin.Begin);
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
// Set internal variables
Stream writeStream = null;
@@ -615,7 +615,7 @@ namespace SabreTools.Library.FileTypes
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
FileExtensions.TryDelete(archiveFileName);
}
File.Move(tempFile, archiveFileName);
@@ -658,7 +658,7 @@ namespace SabreTools.Library.FileTypes
}
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
// Set internal variables
Stream writeStream = null;
@@ -697,7 +697,7 @@ namespace SabreTools.Library.FileTypes
int index = inputIndexMap[key];
// Open the input file for reading
Stream freadStream = Utilities.TryOpenRead(inputFiles[index]);
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[index]);
ulong istreamSize = (ulong)(new FileInfo(inputFiles[index]).Length);
DateTime dt = DateTime.Now;
@@ -780,7 +780,7 @@ namespace SabreTools.Library.FileTypes
if (index < 0)
{
// Open the input file for reading
Stream freadStream = Utilities.TryOpenRead(inputFiles[-index - 1]);
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[-index - 1]);
ulong istreamSize = (ulong)(new FileInfo(inputFiles[-index - 1]).Length);
DateTime dt = DateTime.Now;
@@ -842,7 +842,7 @@ namespace SabreTools.Library.FileTypes
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
FileExtensions.TryDelete(archiveFileName);
}
File.Move(tempFile, archiveFileName);

View File

@@ -108,7 +108,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
// Now open and write the file if possible
FileStream fs = Utilities.TryCreate(realEntry);
FileStream fs = FileExtensions.TryCreate(realEntry);
if (fs != null)
{
ms.Seek(0, SeekOrigin.Begin);
@@ -187,7 +187,7 @@ namespace SabreTools.Library.FileTypes
try
{
TarArchive ta = TarArchive.Open(Utilities.TryOpenRead(this.Filename));
TarArchive ta = TarArchive.Open(FileExtensions.TryOpenRead(this.Filename));
foreach (TarArchiveEntry entry in ta.Entries.Where(e => e != null && !e.IsDirectory))
{
// If secure hashes are disabled, do a quickscan
@@ -207,7 +207,7 @@ namespace SabreTools.Library.FileTypes
else
{
Stream entryStream = entry.OpenEntryStream();
BaseFile tarEntryRom = Utilities.GetStreamInfo(entryStream, entry.Size, omitFromScan);
BaseFile tarEntryRom = entryStream.GetInfo(entry.Size, omitFromScan);
tarEntryRom.Filename = entry.Key;
tarEntryRom.Parent = gamename;
tarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
@@ -292,7 +292,7 @@ namespace SabreTools.Library.FileTypes
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
{
// Get the file stream for the file and write out
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date: date);
}
/// <summary>
@@ -322,7 +322,7 @@ namespace SabreTools.Library.FileTypes
}
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
// Set internal variables
TarArchive oldTarFile = TarArchive.Create();
@@ -441,7 +441,7 @@ namespace SabreTools.Library.FileTypes
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
FileExtensions.TryDelete(archiveFileName);
}
File.Move(tempFile, archiveFileName);
@@ -484,7 +484,7 @@ namespace SabreTools.Library.FileTypes
}
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".tar") ? string.Empty : ".tar"));
// Set internal variables
TarArchive oldTarFile = TarArchive.Create();
@@ -526,7 +526,7 @@ namespace SabreTools.Library.FileTypes
}
// Copy the input stream to the output
tarFile.AddEntry(roms[index].Name, Utilities.TryOpenRead(inputFiles[index]), size: roms[index].Size, modified: usableDate);
tarFile.AddEntry(roms[index].Name, FileExtensions.TryOpenRead(inputFiles[index]), size: roms[index].Size, modified: usableDate);
}
}
@@ -586,7 +586,7 @@ namespace SabreTools.Library.FileTypes
}
// Copy the input file to the output
tarFile.AddEntry(roms[-index - 1].Name, Utilities.TryOpenRead(inputFiles[-index - 1]), size: roms[-index - 1].Size, modified: usableDate);
tarFile.AddEntry(roms[-index - 1].Name, FileExtensions.TryOpenRead(inputFiles[-index - 1]), size: roms[-index - 1].Size, modified: usableDate);
}
// Otherwise, copy the file from the old archive
@@ -622,7 +622,7 @@ namespace SabreTools.Library.FileTypes
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
FileExtensions.TryDelete(archiveFileName);
}
File.Move(tempFile, archiveFileName);

View File

@@ -1,23 +1,18 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using SabreTools.Library.Data;
using SabreTools.Library.DatItems;
using SabreTools.Library.Tools;
using Compress.ZipFile;
using SevenZip;
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Readers;
using SharpCompress.Compressors.Xz;
namespace SabreTools.Library.FileTypes
{
/// <summary>
/// Represents a TorrentXZ archive for reading and writing
/// </summary>
/// TODO: Wait for XZ write to be enabled by SevenZipSharp library
public class XZArchive : BaseArchive
{
#region Constructors
@@ -61,14 +56,16 @@ namespace SabreTools.Library.FileTypes
// Create the temp directory
Directory.CreateDirectory(outDir);
// Extract all files to the temp directory
SharpCompress.Archives.SevenZip.SevenZipArchive sza = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(Utilities.TryOpenRead(this.Filename));
foreach (SevenZipArchiveEntry entry in sza.Entries)
{
entry.WriteToDirectory(outDir, new SharpCompress.Common.ExtractionOptions { PreserveFileTime = true, ExtractFullPath = true, Overwrite = true });
}
// Decompress the _filename stream
FileStream outstream = FileExtensions.TryCreate(Path.Combine(outDir, Path.GetFileNameWithoutExtension(this.Filename)));
var xz = new XZStream(File.OpenRead(this.Filename));
xz.CopyTo(outstream);
// Dispose of the streams
outstream.Dispose();
xz.Dispose();
encounteredErrors = false;
sza.Dispose();
}
catch (EndOfStreamException)
{
@@ -107,7 +104,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
// Now open and write the file if possible
FileStream fs = Utilities.TryCreate(realEntry);
FileStream fs = FileExtensions.TryCreate(realEntry);
if (fs != null)
{
ms.Seek(0, SeekOrigin.Begin);
@@ -142,22 +139,26 @@ namespace SabreTools.Library.FileTypes
public override (MemoryStream, string) CopyToStream(string entryName)
{
MemoryStream ms = new MemoryStream();
string realEntry = null;
string realEntry;
try
{
SharpCompress.Archives.SevenZip.SevenZipArchive sza = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(this.Filename, new ReaderOptions { LeaveStreamOpen = false, });
foreach (SevenZipArchiveEntry entry in sza.Entries)
// Decompress the _filename stream
realEntry = Path.GetFileNameWithoutExtension(this.Filename);
var xz = new XZStream(File.OpenRead(this.Filename));
// Write the file out
byte[] xbuffer = new byte[_bufferSize];
int xlen;
while ((xlen = xz.Read(xbuffer, 0, _bufferSize)) > 0)
{
if (entry != null && !entry.IsDirectory && entry.Key.Contains(entryName))
{
// Write the file out
realEntry = entry.Key;
entry.WriteTo(ms);
break;
}
ms.Write(xbuffer, 0, xlen);
ms.Flush();
}
sza.Dispose();
// Dispose of the streams
xz.Dispose();
}
catch (Exception ex)
{
@@ -182,7 +183,58 @@ namespace SabreTools.Library.FileTypes
/// <remarks>TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually</remarks>
public override List<BaseFile> GetChildren(Hash omitFromScan = Hash.DeepHashes, bool date = false)
{
throw new NotImplementedException();
if (_children == null || _children.Count == 0)
{
_children = new List<BaseFile>();
string gamename = Path.GetFileNameWithoutExtension(this.Filename);
BaseFile possibleTxz = GetTorrentXZFileInfo();
// If it was, then add it to the outputs and continue
if (possibleTxz != null && possibleTxz.Filename != null)
{
_children.Add(possibleTxz);
}
else
{
try
{
// If secure hashes are disabled, do a quickscan
if (omitFromScan == Hash.SecureHashes)
{
BaseFile tempRom = new BaseFile()
{
Filename = gamename,
};
BinaryReader br = new BinaryReader(FileExtensions.TryOpenRead(this.Filename));
br.BaseStream.Seek(-8, SeekOrigin.End);
tempRom.CRC = br.ReadBytesBigEndian(4);
tempRom.Size = br.ReadInt32BigEndian();
br.Dispose();
_children.Add(tempRom);
}
// Otherwise, use the stream directly
else
{
var xzStream = new XZStream(File.OpenRead(this.Filename));
BaseFile xzEntryRom = xzStream.GetInfo(omitFromScan: omitFromScan);
xzEntryRom.Filename = gamename;
xzEntryRom.Parent = gamename;
_children.Add(xzEntryRom);
xzStream.Dispose();
}
}
catch (Exception ex)
{
Globals.Logger.Error(ex.ToString());
return null;
}
}
}
return _children;
}
/// <summary>
@@ -192,7 +244,8 @@ namespace SabreTools.Library.FileTypes
/// <returns>List of empty folders in the archive</returns>
public override List<string> GetEmptyFolders()
{
throw new NotImplementedException();
// XZ files don't contain directories
return new List<string>();
}
/// <summary>
@@ -200,7 +253,50 @@ namespace SabreTools.Library.FileTypes
/// </summary>
public override bool IsTorrent()
{
throw new NotImplementedException();
// Check for the file existing first
if (!File.Exists(this.Filename))
return false;
string datum = Path.GetFileName(this.Filename).ToLowerInvariant();
// Check if the name is the right length
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz")) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length
{
Globals.Logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(this.Filename)}'");
return false;
}
return true;
}
/// <summary>
/// Retrieve file information for a single torrent XZ file
/// </summary>
/// <returns>Populated DatItem object if success, empty one on error</returns>
public BaseFile GetTorrentXZFileInfo()
{
// Check for the file existing first
if (!File.Exists(this.Filename))
return null;
string datum = Path.GetFileName(this.Filename).ToLowerInvariant();
// Check if the name is the right length
if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.xz")) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length
{
Globals.Logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(this.Filename)}'");
return null;
}
BaseFile baseFile = new BaseFile
{
Filename = Path.GetFileNameWithoutExtension(this.Filename).ToLowerInvariant(),
SHA1 = Utilities.StringToByteArray(Path.GetFileNameWithoutExtension(this.Filename)), // TODO: When updating to SHA-256, this needs to update to SHA256
Parent = Path.GetFileNameWithoutExtension(this.Filename).ToLowerInvariant(),
};
return baseFile;
}
#endregion
@@ -219,8 +315,17 @@ namespace SabreTools.Library.FileTypes
/// <remarks>This works for now, but it can be sped up by using Ionic.Zip or another zlib wrapper that allows for header values built-in. See edc's code.</remarks>
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
{
// Check that the input file exists
if (!File.Exists(inputFile))
{
Globals.Logger.Warning($"File '{inputFile}' does not exist!");
return false;
}
inputFile = Path.GetFullPath(inputFile);
// Get the file stream for the file and write out
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date, romba);
}
/// <summary>
@@ -235,185 +340,48 @@ namespace SabreTools.Library.FileTypes
public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false)
{
bool success = false;
string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}");
// If either input is null or empty, return
if (inputStream == null || rom == null || rom.Name == null)
{
return success;
}
// If the stream is not readable, return
if (!inputStream.CanRead)
{
return success;
// Make sure the output directory exists
if (!Directory.Exists(outDir))
Directory.CreateDirectory(outDir);
outDir = Path.GetFullPath(outDir);
// Now get the Rom info for the file so we have hashes and size
rom = new Rom(inputStream.GetInfo(keepReadOpen: true));
// Get the output file name
string outfile;
// If we have a romba output, add the romba path
if (romba)
{
outfile = Path.Combine(outDir, PathExtensions.GetRombaPath(rom.SHA1)); // TODO: When updating to SHA-256, this needs to update to SHA256
// Check to see if the folder needs to be created
if (!Directory.Exists(Path.GetDirectoryName(outfile)))
Directory.CreateDirectory(Path.GetDirectoryName(outfile));
}
// Otherwise, we're just rebuilding to the main directory
else
{
outfile = Path.Combine(outDir, rom.SHA1 + ".xz"); // TODO: When updating to SHA-256, this needs to update to SHA256
}
// Seek to the beginning of the stream
inputStream.Seek(0, SeekOrigin.Begin);
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".xz") ? string.Empty : ".xz"));
// Set internal variables
SevenZipBase.SetLibraryPath("7za.dll");
SevenZipExtractor oldZipFile = null;
SevenZipCompressor zipFile;
try
// If the output file exists, don't try to write again
if (!File.Exists(outfile))
{
// If the full output path doesn't exist, create it
if (!Directory.Exists(Path.GetDirectoryName(tempFile)))
{
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
}
// Compress the input stream
XZStream outputStream = new XZStream(FileExtensions.TryCreate(outfile));
inputStream.CopyTo(outputStream);
// If the archive doesn't exist, create it and put the single file
if (!File.Exists(archiveFileName))
{
zipFile = new SevenZipCompressor()
{
ArchiveFormat = OutArchiveFormat.XZ,
CompressionLevel = CompressionLevel.Normal,
};
// Create the temp directory
string tempPath = Path.Combine(outDir, Guid.NewGuid().ToString());
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
// Create a stream dictionary
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
dict.Add(rom.Name, inputStream);
// Now add the stream
zipFile.CompressStreamDictionary(dict, tempFile);
}
// Otherwise, sort the input files and write out in the correct order
else
{
// Open the old archive for reading
using (oldZipFile = new SevenZipExtractor(archiveFileName))
{
// Map all inputs to index
Dictionary<string, int> inputIndexMap = new Dictionary<string, int>();
// If the old one doesn't contain the new file, then add it
if (!oldZipFile.ArchiveFileNames.Contains(rom.Name.Replace('\\', '/')))
{
inputIndexMap.Add(rom.Name.Replace('\\', '/'), -1);
}
// Then add all of the old entries to it too
for (int i = 0; i < oldZipFile.FilesCount; i++)
{
inputIndexMap.Add(oldZipFile.ArchiveFileNames[i], i);
}
// If the number of entries is the same as the old archive, skip out
if (inputIndexMap.Keys.Count <= oldZipFile.FilesCount)
{
success = true;
return success;
}
// Otherwise, process the old zipfile
zipFile = new SevenZipCompressor()
{
ArchiveFormat = OutArchiveFormat.XZ,
CompressionLevel = CompressionLevel.Normal,
};
// Get the order for the entries with the new file
List<string> keys = inputIndexMap.Keys.ToList();
keys.Sort(ZipFile.TrrntZipStringCompare);
// Copy over all files to the new archive
foreach (string key in keys)
{
// Get the index mapped to the key
int index = inputIndexMap[key];
// If we have the input file, add it now
if (index < 0)
{
// Create a stream dictionary
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
dict.Add(rom.Name, inputStream);
// Now add the stream
zipFile.CompressStreamDictionary(dict, tempFile);
}
// Otherwise, copy the file from the old archive
else
{
Stream oldZipFileEntryStream = new MemoryStream();
oldZipFile.ExtractFile(index, oldZipFileEntryStream);
oldZipFileEntryStream.Seek(0, SeekOrigin.Begin);
// Create a stream dictionary
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
dict.Add(oldZipFile.ArchiveFileNames[index], oldZipFileEntryStream);
// Now add the stream
zipFile.CompressStreamDictionary(dict, tempFile);
oldZipFileEntryStream.Dispose();
}
// After the first file, make sure we're in append mode
zipFile.CompressionMode = CompressionMode.Append;
}
}
}
success = true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
success = false;
}
finally
{
inputStream?.Dispose();
}
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
}
File.Move(tempFile, archiveFileName);
// Now make the file T7Z
// TODO: Add ACTUAL T7Z compatible code
BinaryWriter bw = new BinaryWriter(Utilities.TryOpenReadWrite(archiveFileName));
bw.Seek(0, SeekOrigin.Begin);
bw.Write(Constants.Torrent7ZipHeader);
bw.Seek(0, SeekOrigin.End);
using (oldZipFile = new SevenZipExtractor(Utilities.TryOpenReadWrite(archiveFileName)))
{
// Get the correct signature to use (Default 0, Unicode 1, SingleFile 2, StripFileNames 4)
byte[] tempsig = Constants.Torrent7ZipSignature;
if (oldZipFile.FilesCount > 1)
{
tempsig[16] = 0x2;
}
else
{
tempsig[16] = 0;
}
bw.Write(tempsig);
bw.Flush();
bw.Dispose();
// Dispose of everything
outputStream.Dispose();
inputStream.Dispose();
}
return true;
@@ -430,216 +398,7 @@ namespace SabreTools.Library.FileTypes
/// <returns>True if the archive was written properly, false otherwise</returns>
public override bool Write(List<string> inputFiles, string outDir, List<Rom> roms, bool date = false, bool romba = false)
{
bool success = false;
string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}");
// If either list of roms is null or empty, return
if (inputFiles == null || roms == null || inputFiles.Count == 0 || roms.Count == 0)
{
return success;
}
// If the number of inputs is less than the number of available roms, return
if (inputFiles.Count < roms.Count)
{
return success;
}
// If one of the files doesn't exist, return
foreach (string file in inputFiles)
{
if (!File.Exists(file))
{
return success;
}
}
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".xz") ? string.Empty : ".xz"));
// Set internal variables
SevenZipBase.SetLibraryPath("7za.dll");
SevenZipExtractor oldZipFile;
SevenZipCompressor zipFile;
try
{
// If the full output path doesn't exist, create it
if (!Directory.Exists(Path.GetDirectoryName(archiveFileName)))
{
Directory.CreateDirectory(Path.GetDirectoryName(archiveFileName));
}
// If the archive doesn't exist, create it and put the single file
if (!File.Exists(archiveFileName))
{
zipFile = new SevenZipCompressor()
{
ArchiveFormat = OutArchiveFormat.XZ,
CompressionLevel = CompressionLevel.Normal,
};
// Map all inputs to index
Dictionary<string, int> inputIndexMap = new Dictionary<string, int>();
for (int i = 0; i < inputFiles.Count; i++)
{
inputIndexMap.Add(roms[i].Name.Replace('\\', '/'), i);
}
// Sort the keys in TZIP order
List<string> keys = inputIndexMap.Keys.ToList();
keys.Sort(ZipFile.TrrntZipStringCompare);
// Create the temp directory
string tempPath = Path.Combine(outDir, Guid.NewGuid().ToString());
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
// Now add all of the files in order
foreach (string key in keys)
{
string newkey = Path.Combine(tempPath, key);
File.Move(inputFiles[inputIndexMap[key]], newkey);
zipFile.CompressFiles(tempFile, newkey);
File.Move(newkey, inputFiles[inputIndexMap[key]]);
// After the first file, make sure we're in append mode
zipFile.CompressionMode = CompressionMode.Append;
}
Utilities.CleanDirectory(tempPath);
Utilities.TryDeleteDirectory(tempPath);
}
// Otherwise, sort the input files and write out in the correct order
else
{
// Open the old archive for reading
using (oldZipFile = new SevenZipExtractor(archiveFileName))
{
// Map all inputs to index
Dictionary<string, int> inputIndexMap = new Dictionary<string, int>();
for (int i = 0; i < inputFiles.Count; i++)
{
// If the old one contains the new file, then just skip out
if (oldZipFile.ArchiveFileNames.Contains(roms[i].Name.Replace('\\', '/')))
{
continue;
}
inputIndexMap.Add(roms[i].Name.Replace('\\', '/'), -(i + 1));
}
// Then add all of the old entries to it too
for (int i = 0; i < oldZipFile.FilesCount; i++)
{
inputIndexMap.Add(oldZipFile.ArchiveFileNames[i], i);
}
// If the number of entries is the same as the old archive, skip out
if (inputIndexMap.Keys.Count <= oldZipFile.FilesCount)
{
success = true;
return success;
}
// Otherwise, process the old zipfile
zipFile = new SevenZipCompressor()
{
ArchiveFormat = OutArchiveFormat.XZ,
CompressionLevel = CompressionLevel.Normal,
};
// Get the order for the entries with the new file
List<string> keys = inputIndexMap.Keys.ToList();
keys.Sort(ZipFile.TrrntZipStringCompare);
// Copy over all files to the new archive
foreach (string key in keys)
{
// Get the index mapped to the key
int index = inputIndexMap[key];
// If we have the input file, add it now
if (index < 0)
{
FileStream inputStream = Utilities.TryOpenRead(inputFiles[-index - 1]);
// Create a stream dictionary
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
dict.Add(key, inputStream);
// Now add the stream
zipFile.CompressStreamDictionary(dict, tempFile);
}
// Otherwise, copy the file from the old archive
else
{
Stream oldZipFileEntryStream = new MemoryStream();
oldZipFile.ExtractFile(index, oldZipFileEntryStream);
oldZipFileEntryStream.Seek(0, SeekOrigin.Begin);
// Create a stream dictionary
Dictionary<string, Stream> dict = new Dictionary<string, Stream>();
dict.Add(oldZipFile.ArchiveFileNames[index], oldZipFileEntryStream);
// Now add the stream
zipFile.CompressStreamDictionary(dict, tempFile);
oldZipFileEntryStream.Dispose();
}
// After the first file, make sure we're in append mode
zipFile.CompressionMode = CompressionMode.Append;
}
}
}
success = true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
success = false;
}
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
}
File.Move(tempFile, archiveFileName);
// Now make the file T7Z
// TODO: Add ACTUAL T7Z compatible code
BinaryWriter bw = new BinaryWriter(Utilities.TryOpenReadWrite(archiveFileName));
bw.Seek(0, SeekOrigin.Begin);
bw.Write(Constants.Torrent7ZipHeader);
bw.Seek(0, SeekOrigin.End);
using (oldZipFile = new SevenZipExtractor(Utilities.TryOpenReadWrite(archiveFileName)))
{
// Get the correct signature to use (Default 0, Unicode 1, SingleFile 2, StripFileNames 4)
byte[] tempsig = Constants.Torrent7ZipSignature;
if (oldZipFile.FilesCount > 1)
{
tempsig[16] = 0x2;
}
else
{
tempsig[16] = 0;
}
bw.Write(tempsig);
bw.Flush();
bw.Dispose();
}
return true;
throw new NotImplementedException();
}
#endregion

View File

@@ -86,7 +86,7 @@ namespace SabreTools.Library.FileTypes
continue;
}
FileStream writeStream = Utilities.TryCreate(Path.Combine(outDir, zf.Filename(i)));
FileStream writeStream = FileExtensions.TryCreate(Path.Combine(outDir, zf.Filename(i)));
// If the stream is smaller than the buffer, just run one loop through to avoid issues
if (streamsize < _bufferSize)
@@ -153,7 +153,7 @@ namespace SabreTools.Library.FileTypes
Directory.CreateDirectory(Path.GetDirectoryName(realEntry));
// Now open and write the file if possible
FileStream fs = Utilities.TryCreate(realEntry);
FileStream fs = FileExtensions.TryCreate(realEntry);
if (fs != null)
{
ms.Seek(0, SeekOrigin.Begin);
@@ -317,7 +317,7 @@ namespace SabreTools.Library.FileTypes
// Otherwise, use the stream directly
else
{
BaseFile zipEntryRom = Utilities.GetStreamInfo(readStream, (long)zf.UncompressedSize(i), omitFromScan, true);
BaseFile zipEntryRom = readStream.GetInfo((long)zf.UncompressedSize(i), omitFromScan, true);
zipEntryRom.Filename = zf.Filename(i);
zipEntryRom.Parent = gamename;
string convertedDate = zf.LastModified(i).ToString("yyyy/MM/dd hh:mm:ss");
@@ -422,7 +422,7 @@ namespace SabreTools.Library.FileTypes
public override bool Write(string inputFile, string outDir, Rom rom, bool date = false, bool romba = false)
{
// Get the file stream for the file and write out
return Write(Utilities.TryOpenRead(inputFile), outDir, rom, date: date);
return Write(FileExtensions.TryOpenRead(inputFile), outDir, rom, date: date);
}
/// <summary>
@@ -455,7 +455,7 @@ namespace SabreTools.Library.FileTypes
inputStream.Seek(0, SeekOrigin.Begin);
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
// Set internal variables
Stream writeStream = null;
@@ -621,7 +621,7 @@ namespace SabreTools.Library.FileTypes
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
FileExtensions.TryDelete(archiveFileName);
}
File.Move(tempFile, archiveFileName);
@@ -664,7 +664,7 @@ namespace SabreTools.Library.FileTypes
}
// Get the output archive name from the first rebuild rom
string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
string archiveFileName = Path.Combine(outDir, Sanitizer.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip"));
// Set internal variables
Stream writeStream = null;
@@ -703,7 +703,7 @@ namespace SabreTools.Library.FileTypes
int index = inputIndexMap[key];
// Open the input file for reading
Stream freadStream = Utilities.TryOpenRead(inputFiles[index]);
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[index]);
ulong istreamSize = (ulong)(new FileInfo(inputFiles[index]).Length);
DateTime dt = DateTime.Now;
@@ -786,7 +786,7 @@ namespace SabreTools.Library.FileTypes
if (index < 0)
{
// Open the input file for reading
Stream freadStream = Utilities.TryOpenRead(inputFiles[-index - 1]);
Stream freadStream = FileExtensions.TryOpenRead(inputFiles[-index - 1]);
ulong istreamSize = (ulong)(new FileInfo(inputFiles[-index - 1]).Length);
DateTime dt = DateTime.Now;
@@ -849,7 +849,7 @@ namespace SabreTools.Library.FileTypes
// If the old file exists, delete it and replace
if (File.Exists(archiveFileName))
{
Utilities.TryDeleteFile(archiveFileName);
FileExtensions.TryDelete(archiveFileName);
}
File.Move(tempFile, archiveFileName);