Finish POPS Gui, and include https://github.com/RupertAvery/PSXPackager
This commit is contained in:
parent
157d8de398
commit
3b024948fc
|
@ -0,0 +1,180 @@
|
|||
//
|
||||
// Copyright (c) 2008-2012, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Aligns I/O to a given block size.
|
||||
/// </summary>
|
||||
/// <remarks>Uses the read-modify-write pattern to align I/O.</remarks>
|
||||
public sealed class AligningStream : WrappingMappedStream<SparseStream>
|
||||
{
|
||||
private readonly byte[] _alignmentBuffer;
|
||||
private readonly int _blockSize;
|
||||
private long _position;
|
||||
|
||||
public AligningStream(SparseStream toWrap, Ownership ownership, int blockSize)
|
||||
: base(toWrap, ownership, null)
|
||||
{
|
||||
_blockSize = blockSize;
|
||||
_alignmentBuffer = new byte[blockSize];
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int startOffset = (int)(_position % _blockSize);
|
||||
if (startOffset == 0 && (count % _blockSize == 0 || _position + count == Length))
|
||||
{
|
||||
// Aligned read - pass through to underlying stream.
|
||||
WrappedStream.Position = _position;
|
||||
int numRead = WrappedStream.Read(buffer, offset, count);
|
||||
_position += numRead;
|
||||
return numRead;
|
||||
}
|
||||
|
||||
long startPos = MathUtilities.RoundDown(_position, _blockSize);
|
||||
long endPos = MathUtilities.RoundUp(_position + count, _blockSize);
|
||||
|
||||
if (endPos - startPos > int.MaxValue)
|
||||
{
|
||||
throw new IOException("Oversized read, after alignment");
|
||||
}
|
||||
|
||||
byte[] tempBuffer = new byte[endPos - startPos];
|
||||
|
||||
WrappedStream.Position = startPos;
|
||||
int read = WrappedStream.Read(tempBuffer, 0, tempBuffer.Length);
|
||||
int available = Math.Min(count, read - startOffset);
|
||||
|
||||
Array.Copy(tempBuffer, startOffset, buffer, offset, available);
|
||||
|
||||
_position += available;
|
||||
return available;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long effectiveOffset = offset;
|
||||
if (origin == SeekOrigin.Current)
|
||||
{
|
||||
effectiveOffset += _position;
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
effectiveOffset += Length;
|
||||
}
|
||||
|
||||
if (effectiveOffset < 0)
|
||||
{
|
||||
throw new IOException("Attempt to move before beginning of stream");
|
||||
}
|
||||
_position = effectiveOffset;
|
||||
return _position;
|
||||
}
|
||||
|
||||
public override void Clear(int count)
|
||||
{
|
||||
DoOperation(
|
||||
(s, opOffset, opCount) => { s.Clear(opCount); },
|
||||
(buffer, offset, opOffset, opCount) => { Array.Clear(buffer, offset, opCount); },
|
||||
count);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
DoOperation(
|
||||
(s, opOffset, opCount) => { s.Write(buffer, offset + opOffset, opCount); },
|
||||
(tempBuffer, tempOffset, opOffset, opCount) => { Array.Copy(buffer, offset + opOffset, tempBuffer, tempOffset, opCount); },
|
||||
count);
|
||||
}
|
||||
|
||||
private void DoOperation(ModifyStream modifyStream, ModifyBuffer modifyBuffer, int count)
|
||||
{
|
||||
int startOffset = (int)(_position % _blockSize);
|
||||
if (startOffset == 0 && (count % _blockSize == 0 || _position + count == Length))
|
||||
{
|
||||
WrappedStream.Position = _position;
|
||||
modifyStream(WrappedStream, 0, count);
|
||||
_position += count;
|
||||
return;
|
||||
}
|
||||
|
||||
long unalignedEnd = _position + count;
|
||||
long alignedPos = MathUtilities.RoundDown(_position, _blockSize);
|
||||
|
||||
if (startOffset != 0)
|
||||
{
|
||||
WrappedStream.Position = alignedPos;
|
||||
WrappedStream.Read(_alignmentBuffer, 0, _blockSize);
|
||||
|
||||
modifyBuffer(_alignmentBuffer, startOffset, 0, Math.Min(count, _blockSize - startOffset));
|
||||
|
||||
WrappedStream.Position = alignedPos;
|
||||
WrappedStream.Write(_alignmentBuffer, 0, _blockSize);
|
||||
}
|
||||
|
||||
alignedPos = MathUtilities.RoundUp(_position, _blockSize);
|
||||
if (alignedPos >= unalignedEnd)
|
||||
{
|
||||
_position = unalignedEnd;
|
||||
return;
|
||||
}
|
||||
|
||||
int passthroughLength = (int)MathUtilities.RoundDown(_position + count - alignedPos, _blockSize);
|
||||
if (passthroughLength > 0)
|
||||
{
|
||||
WrappedStream.Position = alignedPos;
|
||||
modifyStream(WrappedStream, (int)(alignedPos - _position), passthroughLength);
|
||||
}
|
||||
|
||||
alignedPos += passthroughLength;
|
||||
if (alignedPos >= unalignedEnd)
|
||||
{
|
||||
_position = unalignedEnd;
|
||||
return;
|
||||
}
|
||||
|
||||
WrappedStream.Position = alignedPos;
|
||||
WrappedStream.Read(_alignmentBuffer, 0, _blockSize);
|
||||
|
||||
modifyBuffer(_alignmentBuffer, 0, (int)(alignedPos - _position), (int)Math.Min(count - (alignedPos - _position), unalignedEnd - alignedPos));
|
||||
|
||||
WrappedStream.Position = alignedPos;
|
||||
WrappedStream.Write(_alignmentBuffer, 0, _blockSize);
|
||||
|
||||
_position = unalignedEnd;
|
||||
}
|
||||
|
||||
private delegate void ModifyStream(SparseStream stream, int opOffset, int count);
|
||||
|
||||
private delegate void ModifyBuffer(byte[] buffer, int offset, int opOffset, int count);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.ApplePartitionMap
|
||||
{
|
||||
internal sealed class BlockZero : IByteArraySerializable
|
||||
{
|
||||
public uint BlockCount;
|
||||
public ushort BlockSize;
|
||||
public ushort DeviceId;
|
||||
public ushort DeviceType;
|
||||
public ushort DriverCount;
|
||||
public uint DriverData;
|
||||
public ushort Signature;
|
||||
|
||||
public int Size
|
||||
{
|
||||
get { return 512; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Signature = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0);
|
||||
BlockSize = EndianUtilities.ToUInt16BigEndian(buffer, offset + 2);
|
||||
BlockCount = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
|
||||
DeviceType = EndianUtilities.ToUInt16BigEndian(buffer, offset + 8);
|
||||
DeviceId = EndianUtilities.ToUInt16BigEndian(buffer, offset + 10);
|
||||
DriverData = EndianUtilities.ToUInt32BigEndian(buffer, offset + 12);
|
||||
DriverCount = EndianUtilities.ToUInt16LittleEndian(buffer, offset + 16);
|
||||
|
||||
return 512;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using DiscUtils.Partitions;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.ApplePartitionMap
|
||||
{
|
||||
/// <summary>
|
||||
/// Interprets Apple Partition Map structures that partition a disk.
|
||||
/// </summary>
|
||||
public sealed class PartitionMap : PartitionTable
|
||||
{
|
||||
private readonly PartitionMapEntry[] _partitions;
|
||||
private readonly Stream _stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the PartitionMap class.
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream containing the contents of a disk.</param>
|
||||
public PartitionMap(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
|
||||
stream.Position = 0;
|
||||
byte[] initialBytes = StreamUtilities.ReadExact(stream, 1024);
|
||||
|
||||
BlockZero b0 = new BlockZero();
|
||||
b0.ReadFrom(initialBytes, 0);
|
||||
|
||||
PartitionMapEntry initialPart = new PartitionMapEntry(_stream);
|
||||
initialPart.ReadFrom(initialBytes, 512);
|
||||
|
||||
byte[] partTableData = StreamUtilities.ReadExact(stream, (int)(initialPart.MapEntries - 1) * 512);
|
||||
|
||||
_partitions = new PartitionMapEntry[initialPart.MapEntries - 1];
|
||||
for (uint i = 0; i < initialPart.MapEntries - 1; ++i)
|
||||
{
|
||||
_partitions[i] = new PartitionMapEntry(_stream);
|
||||
_partitions[i].ReadFrom(partTableData, (int)(512 * i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID of the disk, always returns Guid.Empty.
|
||||
/// </summary>
|
||||
public override Guid DiskGuid
|
||||
{
|
||||
get { return Guid.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the partitions present on the disk.
|
||||
/// </summary>
|
||||
public override ReadOnlyCollection<PartitionInfo> Partitions
|
||||
{
|
||||
get { return new ReadOnlyCollection<PartitionInfo>(_partitions); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new partition that encompasses the entire disk.
|
||||
/// </summary>
|
||||
/// <param name="type">The partition type.</param>
|
||||
/// <param name="active">Whether the partition is active (bootable).</param>
|
||||
/// <returns>The index of the partition.</returns>
|
||||
/// <remarks>The partition table must be empty before this method is called,
|
||||
/// otherwise IOException is thrown.</remarks>
|
||||
public override int Create(WellKnownPartitionType type, bool active)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new partition with a target size.
|
||||
/// </summary>
|
||||
/// <param name="size">The target size (in bytes).</param>
|
||||
/// <param name="type">The partition type.</param>
|
||||
/// <param name="active">Whether the partition is active (bootable).</param>
|
||||
/// <returns>The index of the new partition.</returns>
|
||||
public override int Create(long size, WellKnownPartitionType type, bool active)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new aligned partition that encompasses the entire disk.
|
||||
/// </summary>
|
||||
/// <param name="type">The partition type.</param>
|
||||
/// <param name="active">Whether the partition is active (bootable).</param>
|
||||
/// <param name="alignment">The alignment (in byte).</param>
|
||||
/// <returns>The index of the partition.</returns>
|
||||
/// <remarks>The partition table must be empty before this method is called,
|
||||
/// otherwise IOException is thrown.</remarks>
|
||||
/// <remarks>
|
||||
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
|
||||
/// however with modern storage greater efficiency is acheived by aligning partitions on
|
||||
/// large values that are a power of two.
|
||||
/// </remarks>
|
||||
public override int CreateAligned(WellKnownPartitionType type, bool active, int alignment)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new aligned partition with a target size.
|
||||
/// </summary>
|
||||
/// <param name="size">The target size (in bytes).</param>
|
||||
/// <param name="type">The partition type.</param>
|
||||
/// <param name="active">Whether the partition is active (bootable).</param>
|
||||
/// <param name="alignment">The alignment (in byte).</param>
|
||||
/// <returns>The index of the new partition.</returns>
|
||||
/// <remarks>
|
||||
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
|
||||
/// however with modern storage greater efficiency is achieved by aligning partitions on
|
||||
/// large values that are a power of two.
|
||||
/// </remarks>
|
||||
public override int CreateAligned(long size, WellKnownPartitionType type, bool active, int alignment)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a partition at a given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the partition.</param>
|
||||
public override void Delete(int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Partitions;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.ApplePartitionMap
|
||||
{
|
||||
internal sealed class PartitionMapEntry : PartitionInfo, IByteArraySerializable
|
||||
{
|
||||
private readonly Stream _diskStream;
|
||||
public uint BootBlock;
|
||||
public uint BootBytes;
|
||||
public uint Flags;
|
||||
public uint LogicalBlocks;
|
||||
public uint LogicalBlockStart;
|
||||
public uint MapEntries;
|
||||
public string Name;
|
||||
public uint PhysicalBlocks;
|
||||
public uint PhysicalBlockStart;
|
||||
public ushort Signature;
|
||||
public string Type;
|
||||
|
||||
public PartitionMapEntry(Stream diskStream)
|
||||
{
|
||||
_diskStream = diskStream;
|
||||
}
|
||||
|
||||
public override byte BiosType
|
||||
{
|
||||
get { return 0xAF; }
|
||||
}
|
||||
|
||||
public override long FirstSector
|
||||
{
|
||||
get { return PhysicalBlockStart; }
|
||||
}
|
||||
|
||||
public override Guid GuidType
|
||||
{
|
||||
get { return Guid.Empty; }
|
||||
}
|
||||
|
||||
public override long LastSector
|
||||
{
|
||||
get { return PhysicalBlockStart + PhysicalBlocks - 1; }
|
||||
}
|
||||
|
||||
public override string TypeAsString
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
internal override PhysicalVolumeType VolumeType
|
||||
{
|
||||
get { return PhysicalVolumeType.ApplePartition; }
|
||||
}
|
||||
|
||||
public int Size
|
||||
{
|
||||
get { return 512; }
|
||||
}
|
||||
|
||||
public int ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Signature = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0);
|
||||
MapEntries = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
|
||||
PhysicalBlockStart = EndianUtilities.ToUInt32BigEndian(buffer, offset + 8);
|
||||
PhysicalBlocks = EndianUtilities.ToUInt32BigEndian(buffer, offset + 12);
|
||||
Name = EndianUtilities.BytesToString(buffer, offset + 16, 32).TrimEnd('\0');
|
||||
Type = EndianUtilities.BytesToString(buffer, offset + 48, 32).TrimEnd('\0');
|
||||
LogicalBlockStart = EndianUtilities.ToUInt32BigEndian(buffer, offset + 80);
|
||||
LogicalBlocks = EndianUtilities.ToUInt32BigEndian(buffer, offset + 84);
|
||||
Flags = EndianUtilities.ToUInt32BigEndian(buffer, offset + 88);
|
||||
BootBlock = EndianUtilities.ToUInt32BigEndian(buffer, offset + 92);
|
||||
BootBytes = EndianUtilities.ToUInt32BigEndian(buffer, offset + 96);
|
||||
|
||||
return 512;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override SparseStream Open()
|
||||
{
|
||||
return new SubStream(_diskStream, PhysicalBlockStart * 512, PhysicalBlocks * 512);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using DiscUtils.Partitions;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.ApplePartitionMap
|
||||
{
|
||||
[PartitionTableFactory]
|
||||
internal sealed class PartitionMapFactory : PartitionTableFactory
|
||||
{
|
||||
public override bool DetectIsPartitioned(Stream s)
|
||||
{
|
||||
if (s.Length < 1024)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
s.Position = 0;
|
||||
|
||||
byte[] initialBytes = StreamUtilities.ReadExact(s, 1024);
|
||||
|
||||
BlockZero b0 = new BlockZero();
|
||||
b0.ReadFrom(initialBytes, 0);
|
||||
if (b0.Signature != 0x4552)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PartitionMapEntry initialPart = new PartitionMapEntry(s);
|
||||
initialPart.ReadFrom(initialBytes, 512);
|
||||
|
||||
return initialPart.Signature == 0x504d;
|
||||
}
|
||||
|
||||
public override PartitionTable DetectPartitionTable(VirtualDisk disk)
|
||||
{
|
||||
if (!DetectIsPartitioned(disk.Content))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PartitionMap(disk.Content);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Archives
|
||||
{
|
||||
internal sealed class FileRecord
|
||||
{
|
||||
public long Length;
|
||||
public string Name;
|
||||
public long Start;
|
||||
|
||||
public FileRecord(string name, long start, long length)
|
||||
{
|
||||
Name = name;
|
||||
Start = start;
|
||||
Length = length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Archives
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimal tar file format implementation.
|
||||
/// </summary>
|
||||
public sealed class TarFile
|
||||
{
|
||||
private readonly Dictionary<string, FileRecord> _files;
|
||||
private readonly Stream _fileStream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TarFile class.
|
||||
/// </summary>
|
||||
/// <param name="fileStream">The Tar file.</param>
|
||||
public TarFile(Stream fileStream)
|
||||
{
|
||||
_fileStream = fileStream;
|
||||
_files = new Dictionary<string, FileRecord>();
|
||||
|
||||
TarHeader hdr = new TarHeader();
|
||||
byte[] hdrBuf = StreamUtilities.ReadExact(_fileStream, TarHeader.Length);
|
||||
hdr.ReadFrom(hdrBuf, 0);
|
||||
while (hdr.FileLength != 0 || !string.IsNullOrEmpty(hdr.FileName))
|
||||
{
|
||||
FileRecord record = new FileRecord(hdr.FileName, _fileStream.Position, hdr.FileLength);
|
||||
_files.Add(record.Name, record);
|
||||
_fileStream.Position += (hdr.FileLength + 511) / 512 * 512;
|
||||
|
||||
hdrBuf = StreamUtilities.ReadExact(_fileStream, TarHeader.Length);
|
||||
hdr.ReadFrom(hdrBuf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to open a file contained in the archive, if it exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file within the archive.</param>
|
||||
/// <param name="stream">A stream containing the file contents, or null.</param>
|
||||
/// <returns><c>true</c> if the file could be opened, else <c>false</c>.</returns>
|
||||
public bool TryOpenFile(string path, out Stream stream)
|
||||
{
|
||||
if (_files.ContainsKey(path))
|
||||
{
|
||||
FileRecord file = _files[path];
|
||||
stream = new SubStream(_fileStream, file.Start, file.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
stream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a file contained in the archive.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file within the archive.</param>
|
||||
/// <returns>A stream containing the file contents.</returns>
|
||||
/// <exception cref="FileNotFoundException">Thrown if the file is not found.</exception>
|
||||
public Stream OpenFile(string path)
|
||||
{
|
||||
if (_files.ContainsKey(path))
|
||||
{
|
||||
FileRecord file = _files[path];
|
||||
return new SubStream(_fileStream, file.Start, file.Length);
|
||||
}
|
||||
|
||||
throw new FileNotFoundException("File is not in archive", path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a given file exists in the archive.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path to test.</param>
|
||||
/// <returns><c>true</c> if the file is present, else <c>false</c>.</returns>
|
||||
public bool FileExists(string path)
|
||||
{
|
||||
return _files.ContainsKey(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a given directory exists in the archive.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path to test.</param>
|
||||
/// <returns><c>true</c> if the directory is present, else <c>false</c>.</returns>
|
||||
public bool DirExists(string path)
|
||||
{
|
||||
string searchStr = path;
|
||||
searchStr = searchStr.Replace(@"\", "/");
|
||||
searchStr = searchStr.EndsWith(@"/", StringComparison.Ordinal) ? searchStr : searchStr + @"/";
|
||||
|
||||
foreach (string filePath in _files.Keys)
|
||||
{
|
||||
if (filePath.StartsWith(searchStr, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal IEnumerable<FileRecord> GetFiles(string dir)
|
||||
{
|
||||
string searchStr = dir;
|
||||
searchStr = searchStr.Replace(@"\", "/");
|
||||
searchStr = searchStr.EndsWith(@"/", StringComparison.Ordinal) ? searchStr : searchStr + @"/";
|
||||
|
||||
foreach (string filePath in _files.Keys)
|
||||
{
|
||||
if (filePath.StartsWith(searchStr, StringComparison.Ordinal))
|
||||
{
|
||||
yield return _files[filePath];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Archives
|
||||
{
|
||||
/// <summary>
|
||||
/// Builder to create UNIX Tar archive files.
|
||||
/// </summary>
|
||||
public sealed class TarFileBuilder : StreamBuilder
|
||||
{
|
||||
private readonly List<UnixBuildFileRecord> _files;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TarFileBuilder"/> class.
|
||||
/// </summary>
|
||||
public TarFileBuilder()
|
||||
{
|
||||
_files = new List<UnixBuildFileRecord>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a file to the tar archive.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file.</param>
|
||||
/// <param name="buffer">The file data.</param>
|
||||
public void AddFile(string name, byte[] buffer)
|
||||
{
|
||||
_files.Add(new UnixBuildFileRecord(name, buffer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a file to the tar archive.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file.</param>
|
||||
/// <param name="buffer">The file data.</param>
|
||||
/// <param name="fileMode">The access mode of the file.</param>
|
||||
/// <param name="ownerId">The uid of the owner.</param>
|
||||
/// <param name="groupId">The gid of the owner.</param>
|
||||
/// <param name="modificationTime">The modification time for the file.</param>
|
||||
public void AddFile(
|
||||
string name, byte[] buffer, UnixFilePermissions fileMode, int ownerId, int groupId,
|
||||
DateTime modificationTime)
|
||||
{
|
||||
_files.Add(new UnixBuildFileRecord(name, buffer, fileMode, ownerId, groupId, modificationTime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a file to the tar archive.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file.</param>
|
||||
/// <param name="stream">The file data.</param>
|
||||
public void AddFile(string name, Stream stream)
|
||||
{
|
||||
_files.Add(new UnixBuildFileRecord(name, stream));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a file to the tar archive.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file.</param>
|
||||
/// <param name="stream">The file data.</param>
|
||||
/// <param name="fileMode">The access mode of the file.</param>
|
||||
/// <param name="ownerId">The uid of the owner.</param>
|
||||
/// <param name="groupId">The gid of the owner.</param>
|
||||
/// <param name="modificationTime">The modification time for the file.</param>
|
||||
public void AddFile(
|
||||
string name, Stream stream, UnixFilePermissions fileMode, int ownerId, int groupId,
|
||||
DateTime modificationTime)
|
||||
{
|
||||
_files.Add(new UnixBuildFileRecord(name, stream, fileMode, ownerId, groupId, modificationTime));
|
||||
}
|
||||
|
||||
protected override List<BuilderExtent> FixExtents(out long totalLength)
|
||||
{
|
||||
List<BuilderExtent> result = new List<BuilderExtent>(_files.Count * 2 + 2);
|
||||
long pos = 0;
|
||||
|
||||
foreach (UnixBuildFileRecord file in _files)
|
||||
{
|
||||
BuilderExtent fileContentExtent = file.Fix(pos + TarHeader.Length);
|
||||
|
||||
result.Add(new TarHeaderExtent(
|
||||
pos, file.Name, fileContentExtent.Length, file.FileMode, file.OwnerId, file.GroupId,
|
||||
file.ModificationTime));
|
||||
pos += TarHeader.Length;
|
||||
|
||||
result.Add(fileContentExtent);
|
||||
pos += MathUtilities.RoundUp(fileContentExtent.Length, 512);
|
||||
}
|
||||
|
||||
// Two empty 512-byte blocks at end of tar file.
|
||||
result.Add(new BuilderBufferExtent(pos, new byte[1024]));
|
||||
|
||||
totalLength = pos + 1024;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Archives
|
||||
{
|
||||
internal sealed class TarHeader
|
||||
{
|
||||
public const int Length = 512;
|
||||
public long FileLength;
|
||||
public UnixFilePermissions FileMode;
|
||||
|
||||
public string FileName;
|
||||
public int GroupId;
|
||||
public DateTime ModificationTime;
|
||||
public int OwnerId;
|
||||
|
||||
public void ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
FileName = ReadNullTerminatedString(buffer, offset + 0, 100);
|
||||
FileMode = (UnixFilePermissions)OctalToLong(ReadNullTerminatedString(buffer, offset + 100, 8));
|
||||
OwnerId = (int)OctalToLong(ReadNullTerminatedString(buffer, offset + 108, 8));
|
||||
GroupId = (int)OctalToLong(ReadNullTerminatedString(buffer, offset + 116, 8));
|
||||
FileLength = OctalToLong(ReadNullTerminatedString(buffer, offset + 124, 12));
|
||||
ModificationTime = OctalToLong(ReadNullTerminatedString(buffer, offset + 136, 12)).FromUnixTimeSeconds().DateTime;
|
||||
}
|
||||
|
||||
public void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Array.Clear(buffer, offset, Length);
|
||||
|
||||
EndianUtilities.StringToBytes(FileName, buffer, offset, 99);
|
||||
EndianUtilities.StringToBytes(LongToOctal((long)FileMode, 7), buffer, offset + 100, 7);
|
||||
EndianUtilities.StringToBytes(LongToOctal(OwnerId, 7), buffer, offset + 108, 7);
|
||||
EndianUtilities.StringToBytes(LongToOctal(GroupId, 7), buffer, offset + 116, 7);
|
||||
EndianUtilities.StringToBytes(LongToOctal(FileLength, 11), buffer, offset + 124, 11);
|
||||
EndianUtilities.StringToBytes(LongToOctal(Convert.ToUInt32((new DateTimeOffset(ModificationTime)).ToUnixTimeSeconds()), 11), buffer, offset + 136, 11);
|
||||
|
||||
// Checksum
|
||||
EndianUtilities.StringToBytes(new string(' ', 8), buffer, offset + 148, 8);
|
||||
long checkSum = 0;
|
||||
for (int i = 0; i < 512; ++i)
|
||||
{
|
||||
checkSum += buffer[offset + i];
|
||||
}
|
||||
|
||||
EndianUtilities.StringToBytes(LongToOctal(checkSum, 7), buffer, offset + 148, 7);
|
||||
buffer[155] = 0;
|
||||
}
|
||||
|
||||
private static string ReadNullTerminatedString(byte[] buffer, int offset, int length)
|
||||
{
|
||||
return EndianUtilities.BytesToString(buffer, offset, length).TrimEnd('\0');
|
||||
}
|
||||
|
||||
private static long OctalToLong(string value)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (int i = 0; i < value.Length; ++i)
|
||||
{
|
||||
result = result * 8 + (value[i] - '0');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string LongToOctal(long value, int length)
|
||||
{
|
||||
string result = string.Empty;
|
||||
|
||||
while (value > 0)
|
||||
{
|
||||
result = (char)('0' + value % 8) + result;
|
||||
value = value / 8;
|
||||
}
|
||||
|
||||
return new string('0', length - result.Length) + result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Archives
|
||||
{
|
||||
internal sealed class TarHeaderExtent : BuilderBufferExtent
|
||||
{
|
||||
private readonly long _fileLength;
|
||||
private readonly string _fileName;
|
||||
private readonly int _groupId;
|
||||
private readonly UnixFilePermissions _mode;
|
||||
private readonly DateTime _modificationTime;
|
||||
private readonly int _ownerId;
|
||||
|
||||
public TarHeaderExtent(long start, string fileName, long fileLength, UnixFilePermissions mode, int ownerId,
|
||||
int groupId, DateTime modificationTime)
|
||||
: base(start, 512)
|
||||
{
|
||||
_fileName = fileName;
|
||||
_fileLength = fileLength;
|
||||
_mode = mode;
|
||||
_ownerId = ownerId;
|
||||
_groupId = groupId;
|
||||
_modificationTime = modificationTime;
|
||||
}
|
||||
|
||||
public TarHeaderExtent(long start, string fileName, long fileLength)
|
||||
: this(start, fileName, fileLength, 0, 0, 0, DateTimeOffsetExtensions.UnixEpoch) {}
|
||||
|
||||
protected override byte[] GetBuffer()
|
||||
{
|
||||
byte[] buffer = new byte[TarHeader.Length];
|
||||
|
||||
TarHeader header = new TarHeader();
|
||||
header.FileName = _fileName;
|
||||
header.FileLength = _fileLength;
|
||||
header.FileMode = _mode;
|
||||
header.OwnerId = _ownerId;
|
||||
header.GroupId = _groupId;
|
||||
header.ModificationTime = _modificationTime;
|
||||
header.WriteTo(buffer, 0);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Archives
|
||||
{
|
||||
internal sealed class UnixBuildFileRecord
|
||||
{
|
||||
private readonly BuilderExtentSource _source;
|
||||
|
||||
public UnixBuildFileRecord(string name, byte[] buffer)
|
||||
: this(name, new BuilderBufferExtentSource(buffer), 0, 0, 0, DateTimeOffsetExtensions.UnixEpoch) {}
|
||||
|
||||
public UnixBuildFileRecord(string name, Stream stream)
|
||||
: this(name, new BuilderStreamExtentSource(stream), 0, 0, 0, DateTimeOffsetExtensions.UnixEpoch) {}
|
||||
|
||||
public UnixBuildFileRecord(
|
||||
string name, byte[] buffer, UnixFilePermissions fileMode, int ownerId, int groupId,
|
||||
DateTime modificationTime)
|
||||
: this(name, new BuilderBufferExtentSource(buffer), fileMode, ownerId, groupId, modificationTime) {}
|
||||
|
||||
public UnixBuildFileRecord(
|
||||
string name, Stream stream, UnixFilePermissions fileMode, int ownerId, int groupId,
|
||||
DateTime modificationTime)
|
||||
: this(name, new BuilderStreamExtentSource(stream), fileMode, ownerId, groupId, modificationTime) {}
|
||||
|
||||
public UnixBuildFileRecord(string name, BuilderExtentSource fileSource, UnixFilePermissions fileMode,
|
||||
int ownerId, int groupId, DateTime modificationTime)
|
||||
{
|
||||
Name = name;
|
||||
_source = fileSource;
|
||||
FileMode = fileMode;
|
||||
OwnerId = ownerId;
|
||||
GroupId = groupId;
|
||||
ModificationTime = modificationTime;
|
||||
}
|
||||
|
||||
public UnixFilePermissions FileMode { get; }
|
||||
|
||||
public int GroupId { get; }
|
||||
|
||||
public DateTime ModificationTime { get; }
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public int OwnerId { get; }
|
||||
|
||||
public BuilderExtent Fix(long pos)
|
||||
{
|
||||
return _source.Fix(pos);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class BaseVolumeDescriptor
|
||||
{
|
||||
public const string Iso9660StandardIdentifier = "CD001";
|
||||
|
||||
public readonly string StandardIdentifier;
|
||||
public readonly VolumeDescriptorType VolumeDescriptorType;
|
||||
public readonly byte VolumeDescriptorVersion;
|
||||
private int SectorSize;
|
||||
|
||||
public BaseVolumeDescriptor(VolumeDescriptorType type, byte version, int sectorSize)
|
||||
{
|
||||
VolumeDescriptorType = type;
|
||||
StandardIdentifier = "CD001";
|
||||
VolumeDescriptorVersion = version;
|
||||
SectorSize = sectorSize;
|
||||
}
|
||||
|
||||
public BaseVolumeDescriptor(byte[] src, int offset)
|
||||
{
|
||||
VolumeDescriptorType = (VolumeDescriptorType)src[offset + 0];
|
||||
StandardIdentifier = Encoding.ASCII.GetString(src, offset + 1, 5);
|
||||
VolumeDescriptorVersion = src[offset + 6];
|
||||
}
|
||||
|
||||
internal virtual void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Array.Clear(buffer, offset, SectorSize);
|
||||
buffer[offset] = (byte)VolumeDescriptorType;
|
||||
IsoUtilities.WriteAChars(buffer, offset + 1, 5, StandardIdentifier);
|
||||
buffer[offset + 6] = VolumeDescriptorVersion;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class Block
|
||||
{
|
||||
public int Available { get; set; }
|
||||
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public long Position { get; set; }
|
||||
|
||||
public bool Equals(Block other)
|
||||
{
|
||||
return Position == other.Position;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BlockCache<T>
|
||||
where T : Block, new()
|
||||
{
|
||||
private readonly Dictionary<long, T> _blocks;
|
||||
private int _blocksCreated;
|
||||
private readonly int _blockSize;
|
||||
|
||||
private readonly List<T> _freeBlocks;
|
||||
private readonly LinkedList<T> _lru;
|
||||
private readonly int _totalBlocks;
|
||||
|
||||
public BlockCache(int blockSize, int blockCount)
|
||||
{
|
||||
_blockSize = blockSize;
|
||||
_totalBlocks = blockCount;
|
||||
|
||||
_blocks = new Dictionary<long, T>();
|
||||
_lru = new LinkedList<T>();
|
||||
_freeBlocks = new List<T>(_totalBlocks);
|
||||
|
||||
FreeBlockCount = _totalBlocks;
|
||||
}
|
||||
|
||||
public int FreeBlockCount { get; private set; }
|
||||
|
||||
public bool ContainsBlock(long position)
|
||||
{
|
||||
return _blocks.ContainsKey(position);
|
||||
}
|
||||
|
||||
public bool TryGetBlock(long position, out T block)
|
||||
{
|
||||
if (_blocks.TryGetValue(position, out block))
|
||||
{
|
||||
_lru.Remove(block);
|
||||
_lru.AddFirst(block);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public T GetBlock(long position)
|
||||
{
|
||||
T result;
|
||||
|
||||
if (TryGetBlock(position, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = GetFreeBlock();
|
||||
result.Position = position;
|
||||
result.Available = -1;
|
||||
StoreBlock(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ReleaseBlock(long position)
|
||||
{
|
||||
T block;
|
||||
if (_blocks.TryGetValue(position, out block))
|
||||
{
|
||||
_blocks.Remove(position);
|
||||
_lru.Remove(block);
|
||||
_freeBlocks.Add(block);
|
||||
FreeBlockCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreBlock(T block)
|
||||
{
|
||||
_blocks[block.Position] = block;
|
||||
_lru.AddFirst(block);
|
||||
}
|
||||
|
||||
private T GetFreeBlock()
|
||||
{
|
||||
T block;
|
||||
|
||||
if (_freeBlocks.Count > 0)
|
||||
{
|
||||
int idx = _freeBlocks.Count - 1;
|
||||
block = _freeBlocks[idx];
|
||||
_freeBlocks.RemoveAt(idx);
|
||||
FreeBlockCount--;
|
||||
}
|
||||
else if (_blocksCreated < _totalBlocks)
|
||||
{
|
||||
block = new T();
|
||||
block.Data = new byte[_blockSize];
|
||||
_blocksCreated++;
|
||||
FreeBlockCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = _lru.Last.Value;
|
||||
_lru.RemoveLast();
|
||||
_blocks.Remove(block.Position);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings controlling BlockCache instances.
|
||||
/// </summary>
|
||||
public sealed class BlockCacheSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BlockCacheSettings class.
|
||||
/// </summary>
|
||||
public BlockCacheSettings()
|
||||
{
|
||||
BlockSize = (int)(4 * Sizes.OneKiB);
|
||||
ReadCacheSize = 4 * Sizes.OneMiB;
|
||||
LargeReadSize = 64 * Sizes.OneKiB;
|
||||
OptimumReadSize = (int)(64 * Sizes.OneKiB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BlockCacheSettings class.
|
||||
/// </summary>
|
||||
/// <param name="settings">The cache settings.</param>
|
||||
internal BlockCacheSettings(BlockCacheSettings settings)
|
||||
{
|
||||
BlockSize = settings.BlockSize;
|
||||
ReadCacheSize = settings.ReadCacheSize;
|
||||
LargeReadSize = settings.LargeReadSize;
|
||||
OptimumReadSize = settings.OptimumReadSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size (in bytes) of each cached block.
|
||||
/// </summary>
|
||||
public int BlockSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum read size that will be cached.
|
||||
/// </summary>
|
||||
/// <remarks>Large reads are not cached, on the assumption they will not
|
||||
/// be repeated. This setting controls what is considered 'large'.
|
||||
/// Any read that is more than this many bytes will not be cached.</remarks>
|
||||
public long LargeReadSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the optimum size of a read to the wrapped stream.
|
||||
/// </summary>
|
||||
/// <remarks>This value must be a multiple of BlockSize.</remarks>
|
||||
public int OptimumReadSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size (in bytes) of the read cache.
|
||||
/// </summary>
|
||||
public long ReadCacheSize { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Statistical information about the effectiveness of a BlockCache instance.
|
||||
/// </summary>
|
||||
public sealed class BlockCacheStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of free blocks in the read cache.
|
||||
/// </summary>
|
||||
public int FreeReadBlocks { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of requested 'large' reads, as defined by the LargeReadSize setting.
|
||||
/// </summary>
|
||||
public long LargeReadsIn { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times a read request was serviced (in part or whole) from the cache.
|
||||
/// </summary>
|
||||
public long ReadCacheHits { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of time a read request was serviced (in part or whole) from the wrapped stream.
|
||||
/// </summary>
|
||||
public long ReadCacheMisses { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of requested reads.
|
||||
/// </summary>
|
||||
public long TotalReadsIn { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of reads passed on by the cache.
|
||||
/// </summary>
|
||||
public long TotalReadsOut { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of requested writes.
|
||||
/// </summary>
|
||||
public long TotalWritesIn { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of requested unaligned reads.
|
||||
/// </summary>
|
||||
/// <remarks>Unaligned reads are reads where the read doesn't start on a multiple of
|
||||
/// the block size.</remarks>
|
||||
public long UnalignedReadsIn { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of requested unaligned writes.
|
||||
/// </summary>
|
||||
/// <remarks>Unaligned writes are writes where the write doesn't start on a multiple of
|
||||
/// the block size.</remarks>
|
||||
public long UnalignedWritesIn { get; internal set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,461 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// A stream implementing a block-oriented read cache.
|
||||
/// </summary>
|
||||
public sealed class BlockCacheStream : SparseStream
|
||||
{
|
||||
private bool _atEof;
|
||||
private readonly int _blocksInReadBuffer;
|
||||
|
||||
private readonly BlockCache<Block> _cache;
|
||||
private readonly Ownership _ownWrapped;
|
||||
|
||||
private long _position;
|
||||
private readonly byte[] _readBuffer;
|
||||
private readonly BlockCacheSettings _settings;
|
||||
private readonly BlockCacheStatistics _stats;
|
||||
private SparseStream _wrappedStream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BlockCacheStream class.
|
||||
/// </summary>
|
||||
/// <param name="toWrap">The stream to wrap.</param>
|
||||
/// <param name="ownership">Whether to assume ownership of <c>toWrap</c>.</param>
|
||||
public BlockCacheStream(SparseStream toWrap, Ownership ownership)
|
||||
: this(toWrap, ownership, new BlockCacheSettings()) {}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BlockCacheStream class.
|
||||
/// </summary>
|
||||
/// <param name="toWrap">The stream to wrap.</param>
|
||||
/// <param name="ownership">Whether to assume ownership of <c>toWrap</c>.</param>
|
||||
/// <param name="settings">The cache settings.</param>
|
||||
public BlockCacheStream(SparseStream toWrap, Ownership ownership, BlockCacheSettings settings)
|
||||
{
|
||||
if (!toWrap.CanRead)
|
||||
{
|
||||
throw new ArgumentException("The wrapped stream does not support reading", nameof(toWrap));
|
||||
}
|
||||
|
||||
if (!toWrap.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("The wrapped stream does not support seeking", nameof(toWrap));
|
||||
}
|
||||
|
||||
_wrappedStream = toWrap;
|
||||
_ownWrapped = ownership;
|
||||
_settings = new BlockCacheSettings(settings);
|
||||
|
||||
if (_settings.OptimumReadSize % _settings.BlockSize != 0)
|
||||
{
|
||||
throw new ArgumentException("Invalid settings, OptimumReadSize must be a multiple of BlockSize",
|
||||
nameof(settings));
|
||||
}
|
||||
|
||||
_readBuffer = new byte[_settings.OptimumReadSize];
|
||||
_blocksInReadBuffer = _settings.OptimumReadSize / _settings.BlockSize;
|
||||
|
||||
int totalBlocks = (int)(_settings.ReadCacheSize / _settings.BlockSize);
|
||||
|
||||
_cache = new BlockCache<Block>(_settings.BlockSize, totalBlocks);
|
||||
_stats = new BlockCacheStatistics();
|
||||
_stats.FreeReadBlocks = totalBlocks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication as to whether the stream can be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication as to whether the stream position can be changed.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication as to whether the stream can be written to.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _wrappedStream.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of the stream that are stored.
|
||||
/// </summary>
|
||||
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
|
||||
public override IEnumerable<StreamExtent> Extents
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _wrappedStream.Extents;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _wrappedStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the current stream position.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _position;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
CheckDisposed();
|
||||
_position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the performance statistics for this instance.
|
||||
/// </summary>
|
||||
public BlockCacheStatistics Statistics
|
||||
{
|
||||
get
|
||||
{
|
||||
_stats.FreeReadBlocks = _cache.FreeBlockCount;
|
||||
return _stats;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of a stream that are stored, within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="start">The offset of the first byte of interest.</param>
|
||||
/// <param name="count">The number of bytes of interest.</param>
|
||||
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
|
||||
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
|
||||
{
|
||||
CheckDisposed();
|
||||
return _wrappedStream.GetExtentsInRange(start, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to fill.</param>
|
||||
/// <param name="offset">The buffer offset to start from.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The number of bytes read.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
if (_position >= Length)
|
||||
{
|
||||
if (_atEof)
|
||||
{
|
||||
throw new IOException("Attempt to read beyond end of stream");
|
||||
}
|
||||
_atEof = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_stats.TotalReadsIn++;
|
||||
|
||||
if (count > _settings.LargeReadSize)
|
||||
{
|
||||
_stats.LargeReadsIn++;
|
||||
_stats.TotalReadsOut++;
|
||||
_wrappedStream.Position = _position;
|
||||
int numRead = _wrappedStream.Read(buffer, offset, count);
|
||||
_position = _wrappedStream.Position;
|
||||
|
||||
if (_position >= Length)
|
||||
{
|
||||
_atEof = true;
|
||||
}
|
||||
|
||||
return numRead;
|
||||
}
|
||||
|
||||
int totalBytesRead = 0;
|
||||
bool servicedFromCache = false;
|
||||
bool servicedOutsideCache = false;
|
||||
int blockSize = _settings.BlockSize;
|
||||
|
||||
long firstBlock = _position / blockSize;
|
||||
int offsetInNextBlock = (int)(_position % blockSize);
|
||||
long endBlock = MathUtilities.Ceil(Math.Min(_position + count, Length), blockSize);
|
||||
int numBlocks = (int)(endBlock - firstBlock);
|
||||
|
||||
if (offsetInNextBlock != 0)
|
||||
{
|
||||
_stats.UnalignedReadsIn++;
|
||||
}
|
||||
|
||||
int blocksRead = 0;
|
||||
while (blocksRead < numBlocks)
|
||||
{
|
||||
Block block;
|
||||
|
||||
// Read from the cache as much as possible
|
||||
while (blocksRead < numBlocks && _cache.TryGetBlock(firstBlock + blocksRead, out block))
|
||||
{
|
||||
int bytesToRead = Math.Min(count - totalBytesRead, block.Available - offsetInNextBlock);
|
||||
|
||||
Array.Copy(block.Data, offsetInNextBlock, buffer, offset + totalBytesRead, bytesToRead);
|
||||
offsetInNextBlock = 0;
|
||||
totalBytesRead += bytesToRead;
|
||||
_position += bytesToRead;
|
||||
blocksRead++;
|
||||
|
||||
servicedFromCache = true;
|
||||
}
|
||||
|
||||
// Now handle a sequence of (one or more) blocks that are not cached
|
||||
if (blocksRead < numBlocks && !_cache.ContainsBlock(firstBlock + blocksRead))
|
||||
{
|
||||
servicedOutsideCache = true;
|
||||
|
||||
// Figure out how many blocks to read from the wrapped stream
|
||||
int blocksToRead = 0;
|
||||
while (blocksRead + blocksToRead < numBlocks
|
||||
&& blocksToRead < _blocksInReadBuffer
|
||||
&& !_cache.ContainsBlock(firstBlock + blocksRead + blocksToRead))
|
||||
{
|
||||
++blocksToRead;
|
||||
}
|
||||
|
||||
// Allow for the end of the stream not being block-aligned
|
||||
long readPosition = (firstBlock + blocksRead) * blockSize;
|
||||
int bytesRead = (int)Math.Min(blocksToRead * (long)blockSize, Length - readPosition);
|
||||
|
||||
// Do the read
|
||||
_stats.TotalReadsOut++;
|
||||
_wrappedStream.Position = readPosition;
|
||||
StreamUtilities.ReadExact(_wrappedStream, _readBuffer, 0, bytesRead);
|
||||
|
||||
// Cache the read blocks
|
||||
for (int i = 0; i < blocksToRead; ++i)
|
||||
{
|
||||
int copyBytes = Math.Min(blockSize, bytesRead - i * blockSize);
|
||||
block = _cache.GetBlock(firstBlock + blocksRead + i);
|
||||
Array.Copy(_readBuffer, i * blockSize, block.Data, 0, copyBytes);
|
||||
block.Available = copyBytes;
|
||||
|
||||
if (copyBytes < blockSize)
|
||||
{
|
||||
Array.Clear(_readBuffer, i * blockSize + copyBytes, blockSize - copyBytes);
|
||||
}
|
||||
}
|
||||
|
||||
blocksRead += blocksToRead;
|
||||
|
||||
// Propogate the data onto the caller
|
||||
int bytesToCopy = Math.Min(count - totalBytesRead, bytesRead - offsetInNextBlock);
|
||||
Array.Copy(_readBuffer, offsetInNextBlock, buffer, offset + totalBytesRead, bytesToCopy);
|
||||
totalBytesRead += bytesToCopy;
|
||||
_position += bytesToCopy;
|
||||
offsetInNextBlock = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_position >= Length && totalBytesRead == 0)
|
||||
{
|
||||
_atEof = true;
|
||||
}
|
||||
|
||||
if (servicedFromCache)
|
||||
{
|
||||
_stats.ReadCacheHits++;
|
||||
}
|
||||
|
||||
if (servicedOutsideCache)
|
||||
{
|
||||
_stats.ReadCacheMisses++;
|
||||
}
|
||||
|
||||
return totalBytesRead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
CheckDisposed();
|
||||
_wrappedStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the stream position.
|
||||
/// </summary>
|
||||
/// <param name="offset">The origin-relative location.</param>
|
||||
/// <param name="origin">The base location.</param>
|
||||
/// <returns>The new absolute stream position.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
long effectiveOffset = offset;
|
||||
if (origin == SeekOrigin.Current)
|
||||
{
|
||||
effectiveOffset += _position;
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
effectiveOffset += Length;
|
||||
}
|
||||
|
||||
_atEof = false;
|
||||
|
||||
if (effectiveOffset < 0)
|
||||
{
|
||||
throw new IOException("Attempt to move before beginning of disk");
|
||||
}
|
||||
_position = effectiveOffset;
|
||||
return _position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the stream.
|
||||
/// </summary>
|
||||
/// <param name="value">The new length.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
CheckDisposed();
|
||||
_wrappedStream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the stream at the current location.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The data to write.</param>
|
||||
/// <param name="offset">The first byte to write from buffer.</param>
|
||||
/// <param name="count">The number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
_stats.TotalWritesIn++;
|
||||
|
||||
int blockSize = _settings.BlockSize;
|
||||
long firstBlock = _position / blockSize;
|
||||
long endBlock = MathUtilities.Ceil(Math.Min(_position + count, Length), blockSize);
|
||||
int numBlocks = (int)(endBlock - firstBlock);
|
||||
|
||||
try
|
||||
{
|
||||
_wrappedStream.Position = _position;
|
||||
_wrappedStream.Write(buffer, offset, count);
|
||||
}
|
||||
catch
|
||||
{
|
||||
InvalidateBlocks(firstBlock, numBlocks);
|
||||
throw;
|
||||
}
|
||||
|
||||
int offsetInNextBlock = (int)(_position % blockSize);
|
||||
if (offsetInNextBlock != 0)
|
||||
{
|
||||
_stats.UnalignedWritesIn++;
|
||||
}
|
||||
|
||||
// For each block touched, if it's cached, update it
|
||||
int bytesProcessed = 0;
|
||||
for (int i = 0; i < numBlocks; ++i)
|
||||
{
|
||||
int bufferPos = offset + bytesProcessed;
|
||||
int bytesThisBlock = Math.Min(count - bytesProcessed, blockSize - offsetInNextBlock);
|
||||
|
||||
Block block;
|
||||
if (_cache.TryGetBlock(firstBlock + i, out block))
|
||||
{
|
||||
Array.Copy(buffer, bufferPos, block.Data, offsetInNextBlock, bytesThisBlock);
|
||||
block.Available = Math.Max(block.Available, offsetInNextBlock + bytesThisBlock);
|
||||
}
|
||||
|
||||
offsetInNextBlock = 0;
|
||||
bytesProcessed += bytesThisBlock;
|
||||
}
|
||||
|
||||
_position += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this instance, freeing up associated resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> if invoked from <c>Dispose</c>, else <c>false</c>.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_wrappedStream != null && _ownWrapped == Ownership.Dispose)
|
||||
{
|
||||
_wrappedStream.Dispose();
|
||||
}
|
||||
|
||||
_wrappedStream = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void CheckDisposed()
|
||||
{
|
||||
if (_wrappedStream == null)
|
||||
{
|
||||
throw new ObjectDisposedException("BlockCacheStream");
|
||||
}
|
||||
}
|
||||
|
||||
private void InvalidateBlocks(long firstBlock, int numBlocks)
|
||||
{
|
||||
for (long i = firstBlock; i < firstBlock + numBlocks; ++i)
|
||||
{
|
||||
_cache.ReleaseBlock(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of boot device emulation modes.
|
||||
/// </summary>
|
||||
public enum BootDeviceEmulation : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No emulation, the boot image is just loaded and executed.
|
||||
/// </summary>
|
||||
NoEmulation = 0x0,
|
||||
|
||||
/// <summary>
|
||||
/// Emulates 1.2MB diskette image as drive A.
|
||||
/// </summary>
|
||||
Diskette1200KiB = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// Emulates 1.44MB diskette image as drive A.
|
||||
/// </summary>
|
||||
Diskette1440KiB = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// Emulates 2.88MB diskette image as drive A.
|
||||
/// </summary>
|
||||
Diskette2880KiB = 0x3,
|
||||
|
||||
/// <summary>
|
||||
/// Emulates hard disk image as drive C.
|
||||
/// </summary>
|
||||
HardDisk = 0x4
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class BootInitialEntry
|
||||
{
|
||||
public byte BootIndicator;
|
||||
public BootDeviceEmulation BootMediaType;
|
||||
public uint ImageStart;
|
||||
public ushort LoadSegment;
|
||||
public ushort SectorCount;
|
||||
public byte SystemType;
|
||||
|
||||
public BootInitialEntry() {}
|
||||
|
||||
public BootInitialEntry(byte[] buffer, int offset)
|
||||
{
|
||||
BootIndicator = buffer[offset + 0x00];
|
||||
BootMediaType = (BootDeviceEmulation)buffer[offset + 0x01];
|
||||
LoadSegment = EndianUtilities.ToUInt16LittleEndian(buffer, offset + 0x02);
|
||||
SystemType = buffer[offset + 0x04];
|
||||
SectorCount = EndianUtilities.ToUInt16LittleEndian(buffer, offset + 0x06);
|
||||
ImageStart = EndianUtilities.ToUInt32LittleEndian(buffer, offset + 0x08);
|
||||
}
|
||||
|
||||
internal void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Array.Clear(buffer, offset, 0x20);
|
||||
buffer[offset + 0x00] = BootIndicator;
|
||||
buffer[offset + 0x01] = (byte)BootMediaType;
|
||||
EndianUtilities.WriteBytesLittleEndian(LoadSegment, buffer, offset + 0x02);
|
||||
buffer[offset + 0x04] = SystemType;
|
||||
EndianUtilities.WriteBytesLittleEndian(SectorCount, buffer, offset + 0x06);
|
||||
EndianUtilities.WriteBytesLittleEndian(ImageStart, buffer, offset + 0x08);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class BootValidationEntry
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
public byte HeaderId;
|
||||
public string ManfId;
|
||||
public byte PlatformId;
|
||||
|
||||
public BootValidationEntry()
|
||||
{
|
||||
HeaderId = 1;
|
||||
PlatformId = 0;
|
||||
ManfId = ".Net DiscUtils";
|
||||
}
|
||||
|
||||
public BootValidationEntry(byte[] src, int offset)
|
||||
{
|
||||
_data = new byte[32];
|
||||
Array.Copy(src, offset, _data, 0, 32);
|
||||
|
||||
HeaderId = _data[0];
|
||||
PlatformId = _data[1];
|
||||
ManfId = EndianUtilities.BytesToString(_data, 4, 24).TrimEnd('\0').TrimEnd(' ');
|
||||
}
|
||||
|
||||
public bool ChecksumValid
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort total = 0;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
total += EndianUtilities.ToUInt16LittleEndian(_data, i * 2);
|
||||
}
|
||||
|
||||
return total == 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
Array.Clear(buffer, offset, 0x20);
|
||||
buffer[offset + 0x00] = HeaderId;
|
||||
buffer[offset + 0x01] = PlatformId;
|
||||
EndianUtilities.StringToBytes(ManfId, buffer, offset + 0x04, 24);
|
||||
buffer[offset + 0x1E] = 0x55;
|
||||
buffer[offset + 0x1F] = 0xAA;
|
||||
EndianUtilities.WriteBytesLittleEndian(CalcChecksum(buffer, offset), buffer, offset + 0x1C);
|
||||
}
|
||||
|
||||
private static ushort CalcChecksum(byte[] buffer, int offset)
|
||||
{
|
||||
ushort total = 0;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
total += EndianUtilities.ToUInt16LittleEndian(buffer, offset + i * 2);
|
||||
}
|
||||
|
||||
return (ushort)(0 - total);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class BootVolumeDescriptor : BaseVolumeDescriptor
|
||||
{
|
||||
public const string ElToritoSystemIdentifier = "EL TORITO SPECIFICATION";
|
||||
|
||||
public BootVolumeDescriptor(uint catalogSector, int sectorSize)
|
||||
: base(VolumeDescriptorType.Boot, 1, sectorSize)
|
||||
{
|
||||
CatalogSector = catalogSector;
|
||||
}
|
||||
|
||||
public BootVolumeDescriptor(byte[] src, int offset)
|
||||
: base(src, offset)
|
||||
{
|
||||
SystemId = EndianUtilities.BytesToString(src, offset + 0x7, 0x20).TrimEnd('\0');
|
||||
CatalogSector = EndianUtilities.ToUInt32LittleEndian(src, offset + 0x47);
|
||||
}
|
||||
|
||||
public uint CatalogSector { get; }
|
||||
|
||||
public string SystemId { get; }
|
||||
|
||||
internal override void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
base.WriteTo(buffer, offset);
|
||||
|
||||
EndianUtilities.StringToBytes(ElToritoSystemIdentifier, buffer, offset + 7, 0x20);
|
||||
EndianUtilities.WriteBytesLittleEndian(CatalogSector, buffer, offset + 0x47);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class BootVolumeDescriptorRegion : VolumeDescriptorDiskRegion
|
||||
{
|
||||
private readonly BootVolumeDescriptor _descriptor;
|
||||
|
||||
public BootVolumeDescriptorRegion(BootVolumeDescriptor descriptor, long start, int sectorSize)
|
||||
: base(start, sectorSize)
|
||||
{
|
||||
_descriptor = descriptor;
|
||||
}
|
||||
|
||||
protected override byte[] GetBlockData()
|
||||
{
|
||||
byte[] buffer = new byte[Length];
|
||||
_descriptor.WriteTo(buffer, 0);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
#if !NETSTANDARD
|
||||
using System;
|
||||
#endif
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for implementations of IBuffer.
|
||||
/// </summary>
|
||||
public abstract class Buffer :
|
||||
#if !NETSTANDARD
|
||||
MarshalByRefObject,
|
||||
#endif
|
||||
IBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this buffer can be read.
|
||||
/// </summary>
|
||||
public abstract bool CanRead { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this buffer can be modified.
|
||||
/// </summary>
|
||||
public abstract bool CanWrite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current capacity of the buffer, in bytes.
|
||||
/// </summary>
|
||||
public abstract long Capacity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of the stream that are stored.
|
||||
/// </summary>
|
||||
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
|
||||
public virtual IEnumerable<StreamExtent> Extents
|
||||
{
|
||||
get { return GetExtentsInRange(0, Capacity); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the buffer into a byte array.
|
||||
/// </summary>
|
||||
/// <param name="pos">The offset within the buffer to start reading.</param>
|
||||
/// <param name="buffer">The destination byte array.</param>
|
||||
/// <param name="offset">The start offset within the destination buffer.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The actual number of bytes read.</returns>
|
||||
public abstract int Read(long pos, byte[] buffer, int offset, int count);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte array into the buffer.
|
||||
/// </summary>
|
||||
/// <param name="pos">The start offset within the buffer.</param>
|
||||
/// <param name="buffer">The source byte array.</param>
|
||||
/// <param name="offset">The start offset within the source byte array.</param>
|
||||
/// <param name="count">The number of bytes to write.</param>
|
||||
public abstract void Write(long pos, byte[] buffer, int offset, int count);
|
||||
|
||||
/// <summary>
|
||||
/// Clears bytes from the buffer.
|
||||
/// </summary>
|
||||
/// <param name="pos">The start offset within the buffer.</param>
|
||||
/// <param name="count">The number of bytes to clear.</param>
|
||||
/// <remarks>
|
||||
/// <para>Logically equivalent to writing <c>count</c> null/zero bytes to the buffer, some
|
||||
/// implementations determine that some (or all) of the range indicated is not actually
|
||||
/// stored. There is no direct, automatic, correspondence to clearing bytes and them
|
||||
/// not being represented as an 'extent' - for example, the implementation of the underlying
|
||||
/// stream may not permit fine-grained extent storage.</para>
|
||||
/// <para>It is always safe to call this method to 'zero-out' a section of a buffer, regardless of
|
||||
/// the underlying buffer implementation.</para>
|
||||
/// </remarks>
|
||||
public virtual void Clear(long pos, int count)
|
||||
{
|
||||
Write(pos, new byte[count], 0, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all data to the underlying storage.
|
||||
/// </summary>
|
||||
/// <remarks>The default behaviour, implemented by this class, is to take no action.</remarks>
|
||||
public virtual void Flush() {}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the capacity of the buffer, truncating if appropriate.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired capacity of the buffer.</param>
|
||||
public abstract void SetCapacity(long value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of a buffer that are stored, within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="start">The offset of the first byte of interest.</param>
|
||||
/// <param name="count">The number of bytes of interest.</param>
|
||||
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
|
||||
public abstract IEnumerable<StreamExtent> GetExtentsInRange(long start, long count);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Buffer into a Stream.
|
||||
/// </summary>
|
||||
public class BufferStream : SparseStream
|
||||
{
|
||||
private readonly FileAccess _access;
|
||||
private readonly IBuffer _buffer;
|
||||
|
||||
private long _position;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BufferStream class.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to use.</param>
|
||||
/// <param name="access">The access permitted to clients.</param>
|
||||
public BufferStream(IBuffer buffer, FileAccess access)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_access = access;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication of whether read access is permitted.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _access != FileAccess.Write; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication of whether seeking is permitted.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication of whether write access is permitted.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _access != FileAccess.Read; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stored extents within the sparse stream.
|
||||
/// </summary>
|
||||
public override IEnumerable<StreamExtent> Extents
|
||||
{
|
||||
get { return _buffer.Extents; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the stream (the capacity of the underlying buffer).
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { return _buffer.Capacity; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the current position within the stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all data to the underlying storage.
|
||||
/// </summary>
|
||||
public override void Flush() {}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a number of bytes from the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The destination buffer.</param>
|
||||
/// <param name="offset">The start offset within the destination buffer.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The number of bytes read.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (!CanRead)
|
||||
{
|
||||
throw new IOException("Attempt to read from write-only stream");
|
||||
}
|
||||
|
||||
StreamUtilities.AssertBufferParameters(buffer, offset, count);
|
||||
|
||||
int numRead = _buffer.Read(_position, buffer, offset, count);
|
||||
_position += numRead;
|
||||
return numRead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the current stream position.
|
||||
/// </summary>
|
||||
/// <param name="offset">The origin-relative stream position.</param>
|
||||
/// <param name="origin">The origin for the stream position.</param>
|
||||
/// <returns>The new stream position.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long effectiveOffset = offset;
|
||||
if (origin == SeekOrigin.Current)
|
||||
{
|
||||
effectiveOffset += _position;
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
effectiveOffset += _buffer.Capacity;
|
||||
}
|
||||
|
||||
if (effectiveOffset < 0)
|
||||
{
|
||||
throw new IOException("Attempt to move before beginning of disk");
|
||||
}
|
||||
_position = effectiveOffset;
|
||||
return _position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the stream (the underlying buffer's capacity).
|
||||
/// </summary>
|
||||
/// <param name="value">The new length of the stream.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
_buffer.SetCapacity(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a buffer to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to write.</param>
|
||||
/// <param name="offset">The starting offset within buffer.</param>
|
||||
/// <param name="count">The number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (!CanWrite)
|
||||
{
|
||||
throw new IOException("Attempt to write to read-only stream");
|
||||
}
|
||||
|
||||
StreamUtilities.AssertBufferParameters(buffer, offset, count);
|
||||
|
||||
_buffer.Write(_position, buffer, offset, count);
|
||||
_position += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears bytes from the stream.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bytes (from the current position) to clear.</param>
|
||||
/// <remarks>
|
||||
/// <para>Logically equivalent to writing <c>count</c> null/zero bytes to the stream, some
|
||||
/// implementations determine that some (or all) of the range indicated is not actually
|
||||
/// stored. There is no direct, automatic, correspondence to clearing bytes and them
|
||||
/// not being represented as an 'extent' - for example, the implementation of the underlying
|
||||
/// stream may not permit fine-grained extent storage.</para>
|
||||
/// <para>It is always safe to call this method to 'zero-out' a section of a stream, regardless of
|
||||
/// the underlying stream implementation.</para>
|
||||
/// </remarks>
|
||||
public override void Clear(int count)
|
||||
{
|
||||
if (!CanWrite)
|
||||
{
|
||||
throw new IOException("Attempt to erase bytes in a read-only stream");
|
||||
}
|
||||
|
||||
_buffer.Clear(_position, count);
|
||||
_position += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of a stream that are stored, within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="start">The offset of the first byte of interest.</param>
|
||||
/// <param name="count">The number of bytes of interest.</param>
|
||||
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
|
||||
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
|
||||
{
|
||||
return _buffer.GetExtentsInRange(start, count);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface shared by all buffers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Buffers are very similar to streams, except the buffer has no notion of
|
||||
/// 'current position'. All I/O operations instead specify the position, as
|
||||
/// needed. Buffers also support sparse behaviour.
|
||||
/// </remarks>
|
||||
public interface IBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this buffer can be read.
|
||||
/// </summary>
|
||||
bool CanRead { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this buffer can be modified.
|
||||
/// </summary>
|
||||
bool CanWrite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current capacity of the buffer, in bytes.
|
||||
/// </summary>
|
||||
long Capacity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of the buffer that are stored.
|
||||
/// </summary>
|
||||
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
|
||||
IEnumerable<StreamExtent> Extents { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the buffer into a byte array.
|
||||
/// </summary>
|
||||
/// <param name="pos">The offset within the buffer to start reading.</param>
|
||||
/// <param name="buffer">The destination byte array.</param>
|
||||
/// <param name="offset">The start offset within the destination buffer.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The actual number of bytes read.</returns>
|
||||
int Read(long pos, byte[] buffer, int offset, int count);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte array into the buffer.
|
||||
/// </summary>
|
||||
/// <param name="pos">The start offset within the buffer.</param>
|
||||
/// <param name="buffer">The source byte array.</param>
|
||||
/// <param name="offset">The start offset within the source byte array.</param>
|
||||
/// <param name="count">The number of bytes to write.</param>
|
||||
void Write(long pos, byte[] buffer, int offset, int count);
|
||||
|
||||
/// <summary>
|
||||
/// Clears bytes from the buffer.
|
||||
/// </summary>
|
||||
/// <param name="pos">The start offset within the buffer.</param>
|
||||
/// <param name="count">The number of bytes to clear.</param>
|
||||
/// <remarks>
|
||||
/// <para>Logically equivalent to writing <c>count</c> null/zero bytes to the buffer, some
|
||||
/// implementations determine that some (or all) of the range indicated is not actually
|
||||
/// stored. There is no direct, automatic, correspondence to clearing bytes and them
|
||||
/// not being represented as an 'extent' - for example, the implementation of the underlying
|
||||
/// stream may not permit fine-grained extent storage.</para>
|
||||
/// <para>It is always safe to call this method to 'zero-out' a section of a buffer, regardless of
|
||||
/// the underlying buffer implementation.</para>
|
||||
/// </remarks>
|
||||
void Clear(long pos, int count);
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all data to the underlying storage.
|
||||
/// </summary>
|
||||
void Flush();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the capacity of the buffer, truncating if appropriate.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired capacity of the buffer.</param>
|
||||
void SetCapacity(long value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of a buffer that are stored, within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="start">The offset of the first byte of interest.</param>
|
||||
/// <param name="count">The number of bytes of interest.</param>
|
||||
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
|
||||
IEnumerable<StreamExtent> GetExtentsInRange(long start, long count);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public interface IMappedBuffer : IBuffer
|
||||
{
|
||||
long MapPosition(long position);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Class representing a portion of an existing buffer.
|
||||
/// </summary>
|
||||
public class SubBuffer : Buffer
|
||||
{
|
||||
private readonly long _first;
|
||||
private readonly long _length;
|
||||
|
||||
private readonly IBuffer _parent;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SubBuffer class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent buffer.</param>
|
||||
/// <param name="first">The first byte in <paramref name="parent"/> represented by this sub-buffer.</param>
|
||||
/// <param name="length">The number of bytes of <paramref name="parent"/> represented by this sub-buffer.</param>
|
||||
public SubBuffer(IBuffer parent, long first, long length)
|
||||
{
|
||||
_parent = parent;
|
||||
_first = first;
|
||||
_length = length;
|
||||
|
||||
if (_first + _length > _parent.Capacity)
|
||||
{
|
||||
throw new ArgumentException("Substream extends beyond end of parent stream");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can this buffer be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _parent.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can this buffer be modified.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _parent.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current capacity of the buffer, in bytes.
|
||||
/// </summary>
|
||||
public override long Capacity
|
||||
{
|
||||
get { return _length; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of the buffer that are stored.
|
||||
/// </summary>
|
||||
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
|
||||
public override IEnumerable<StreamExtent> Extents
|
||||
{
|
||||
get { return OffsetExtents(_parent.GetExtentsInRange(_first, _length)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all data to the underlying storage.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_parent.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the buffer into a byte array.
|
||||
/// </summary>
|
||||
/// <param name="pos">The offset within the buffer to start reading.</param>
|
||||
/// <param name="buffer">The destination byte array.</param>
|
||||
/// <param name="offset">The start offset within the destination buffer.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The actual number of bytes read.</returns>
|
||||
public override int Read(long pos, byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "Attempt to read negative bytes");
|
||||
}
|
||||
|
||||
if (pos >= _length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _parent.Read(pos + _first, buffer, offset,
|
||||
(int)Math.Min(count, Math.Min(_length - pos, int.MaxValue)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte array into the buffer.
|
||||
/// </summary>
|
||||
/// <param name="pos">The start offset within the buffer.</param>
|
||||
/// <param name="buffer">The source byte array.</param>
|
||||
/// <param name="offset">The start offset within the source byte array.</param>
|
||||
/// <param name="count">The number of bytes to write.</param>
|
||||
public override void Write(long pos, byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "Attempt to write negative bytes");
|
||||
}
|
||||
|
||||
if (pos + count > _length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "Attempt to write beyond end of substream");
|
||||
}
|
||||
|
||||
_parent.Write(pos + _first, buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the capacity of the buffer, truncating if appropriate.
|
||||
/// </summary>
|
||||
/// <param name="value">The desired capacity of the buffer.</param>
|
||||
public override void SetCapacity(long value)
|
||||
{
|
||||
throw new NotSupportedException("Attempt to change length of a subbuffer");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of a buffer that are stored, within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="start">The offset of the first byte of interest.</param>
|
||||
/// <param name="count">The number of bytes of interest.</param>
|
||||
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
|
||||
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
|
||||
{
|
||||
long absStart = _first + start;
|
||||
long absEnd = Math.Min(absStart + count, _first + _length);
|
||||
return OffsetExtents(_parent.GetExtentsInRange(absStart, absEnd - absStart));
|
||||
}
|
||||
|
||||
private IEnumerable<StreamExtent> OffsetExtents(IEnumerable<StreamExtent> src)
|
||||
{
|
||||
foreach (StreamExtent e in src)
|
||||
{
|
||||
yield return new StreamExtent(e.Start - _first, e.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using DiscUtils.CoreCompat;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a directory that will be built into the ISO image.
|
||||
/// </summary>
|
||||
public sealed class BuildDirectoryInfo : BuildDirectoryMember
|
||||
{
|
||||
internal static readonly Comparer<BuildDirectoryInfo> PathTableSortComparison = new PathTableComparison();
|
||||
private readonly Dictionary<string, BuildDirectoryMember> _members;
|
||||
|
||||
private readonly BuildDirectoryInfo _parent;
|
||||
private List<BuildDirectoryMember> _sortedMembers;
|
||||
private int _sectorSize;
|
||||
|
||||
internal BuildDirectoryInfo(string name, BuildDirectoryInfo parent, int sectorSize)
|
||||
: base(name, MakeShortDirName(name, parent))
|
||||
{
|
||||
_parent = parent == null ? this : parent;
|
||||
HierarchyDepth = parent == null ? 0 : parent.HierarchyDepth + 1;
|
||||
_members = new Dictionary<string, BuildDirectoryMember>();
|
||||
_sectorSize = sectorSize;
|
||||
}
|
||||
|
||||
internal int HierarchyDepth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent directory, or <c>null</c> if none.
|
||||
/// </summary>
|
||||
public override BuildDirectoryInfo Parent
|
||||
{
|
||||
get { return _parent; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified child directory or file.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file or directory to get.</param>
|
||||
/// <param name="member">The member found (or <c>null</c>).</param>
|
||||
/// <returns><c>true</c> if the specified member was found.</returns>
|
||||
internal bool TryGetMember(string name, out BuildDirectoryMember member)
|
||||
{
|
||||
return _members.TryGetValue(name, out member);
|
||||
}
|
||||
|
||||
internal void Add(BuildDirectoryMember member)
|
||||
{
|
||||
_members.Add(member.Name, member);
|
||||
_sortedMembers = null;
|
||||
}
|
||||
|
||||
internal override long GetDataSize(Encoding enc)
|
||||
{
|
||||
List<BuildDirectoryMember> sorted = GetSortedMembers();
|
||||
|
||||
long total = 34 * 2; // Two pseudo entries (self & parent)
|
||||
|
||||
foreach (BuildDirectoryMember m in sorted)
|
||||
{
|
||||
uint recordSize = m.GetDirectoryRecordSize(enc);
|
||||
|
||||
// If this record would span a sector boundary, then the current sector is
|
||||
// zero-padded, and the record goes at the start of the next sector.
|
||||
if (total % _sectorSize + recordSize > _sectorSize)
|
||||
{
|
||||
long padLength = _sectorSize - total % _sectorSize;
|
||||
total += padLength;
|
||||
}
|
||||
|
||||
total += recordSize;
|
||||
}
|
||||
|
||||
return MathUtilities.RoundUp(total, _sectorSize);
|
||||
}
|
||||
|
||||
internal uint GetPathTableEntrySize(Encoding enc)
|
||||
{
|
||||
int nameBytes = enc.GetByteCount(PickName(null, enc));
|
||||
|
||||
return (uint)(8 + nameBytes + ((nameBytes & 0x1) == 1 ? 1 : 0));
|
||||
}
|
||||
|
||||
internal int Write(byte[] buffer, int offset, Dictionary<BuildDirectoryMember, uint> locationTable, Encoding enc)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
List<BuildDirectoryMember> sorted = GetSortedMembers();
|
||||
|
||||
// Two pseudo entries, effectively '.' and '..'
|
||||
pos += WriteMember(this, "\0", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize);
|
||||
pos += WriteMember(_parent, "\x01", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize);
|
||||
|
||||
foreach (BuildDirectoryMember m in sorted)
|
||||
{
|
||||
uint recordSize = m.GetDirectoryRecordSize(enc);
|
||||
|
||||
if (pos % _sectorSize + recordSize > _sectorSize)
|
||||
{
|
||||
int padLength = _sectorSize - pos % _sectorSize;
|
||||
Array.Clear(buffer, offset + pos, padLength);
|
||||
pos += padLength;
|
||||
}
|
||||
|
||||
pos += WriteMember(m, null, enc, buffer, offset + pos, locationTable, enc, _sectorSize);
|
||||
}
|
||||
|
||||
// Ensure final padding data is zero'd
|
||||
int finalPadLength = MathUtilities.RoundUp(pos, _sectorSize) - pos;
|
||||
Array.Clear(buffer, offset + pos, finalPadLength);
|
||||
|
||||
return pos + finalPadLength;
|
||||
}
|
||||
|
||||
private static int WriteMember(BuildDirectoryMember m, string nameOverride, Encoding nameEnc, byte[] buffer, int offset, Dictionary<BuildDirectoryMember, uint> locationTable, Encoding dataEnc, int sectorSize)
|
||||
{
|
||||
DirectoryRecord dr = new DirectoryRecord();
|
||||
dr.FileIdentifier = m.PickName(nameOverride, nameEnc);
|
||||
dr.LocationOfExtent = locationTable[m];
|
||||
dr.DataLength = (uint)m.GetDataSize(dataEnc);
|
||||
dr.RecordingDateAndTime = m.CreationTime;
|
||||
dr.Flags = m is BuildDirectoryInfo ? FileFlags.Directory : FileFlags.None;
|
||||
return dr.WriteTo(buffer, offset, nameEnc);
|
||||
}
|
||||
|
||||
private static string MakeShortDirName(string longName, BuildDirectoryInfo dir)
|
||||
{
|
||||
if (IsoUtilities.IsValidDirectoryName(longName))
|
||||
{
|
||||
return longName;
|
||||
}
|
||||
|
||||
char[] shortNameChars = longName.ToUpper(CultureInfo.InvariantCulture).ToCharArray();
|
||||
for (int i = 0; i < shortNameChars.Length; ++i)
|
||||
{
|
||||
if (!IsoUtilities.IsValidDChar(shortNameChars[i]) && shortNameChars[i] != '.' && shortNameChars[i] != ';')
|
||||
{
|
||||
shortNameChars[i] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
return new string(shortNameChars);
|
||||
}
|
||||
|
||||
private List<BuildDirectoryMember> GetSortedMembers()
|
||||
{
|
||||
if (_sortedMembers == null)
|
||||
{
|
||||
List<BuildDirectoryMember> sorted = new List<BuildDirectoryMember>(_members.Values);
|
||||
sorted.Sort(SortedComparison);
|
||||
_sortedMembers = sorted;
|
||||
}
|
||||
|
||||
return _sortedMembers;
|
||||
}
|
||||
|
||||
private class PathTableComparison : Comparer<BuildDirectoryInfo>
|
||||
{
|
||||
public override int Compare(BuildDirectoryInfo x, BuildDirectoryInfo y)
|
||||
{
|
||||
if (x.HierarchyDepth != y.HierarchyDepth)
|
||||
{
|
||||
return x.HierarchyDepth - y.HierarchyDepth;
|
||||
}
|
||||
|
||||
if (x.Parent != y.Parent)
|
||||
{
|
||||
return Compare(x.Parent, y.Parent);
|
||||
}
|
||||
|
||||
return CompareNames(x.Name, y.Name, ' ');
|
||||
}
|
||||
|
||||
private static int CompareNames(string x, string y, char padChar)
|
||||
{
|
||||
int max = Math.Max(x.Length, y.Length);
|
||||
for (int i = 0; i < max; ++i)
|
||||
{
|
||||
char xChar = i < x.Length ? x[i] : padChar;
|
||||
char yChar = i < y.Length ? y[i] : padChar;
|
||||
|
||||
if (xChar != yChar)
|
||||
{
|
||||
return xChar - yChar;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the base class for <see cref="BuildFileInfo"/> and
|
||||
/// <see cref="BuildDirectoryInfo"/> objects that will be built into an
|
||||
/// ISO image.
|
||||
/// </summary>
|
||||
/// <remarks>Instances of this class have two names, a <see cref="Name"/>,
|
||||
/// which is the full-length Joliet name and a <see cref="ShortName"/>,
|
||||
/// which is the strictly compliant ISO 9660 name.</remarks>
|
||||
public abstract class BuildDirectoryMember
|
||||
{
|
||||
internal static readonly Comparer<BuildDirectoryMember> SortedComparison = new DirectorySortedComparison();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BuildDirectoryMember class.
|
||||
/// </summary>
|
||||
/// <param name="name">The Joliet compliant name of the file or directory.</param>
|
||||
/// <param name="shortName">The ISO 9660 compliant name of the file or directory.</param>
|
||||
protected BuildDirectoryMember(string name, string shortName)
|
||||
{
|
||||
Name = name;
|
||||
ShortName = shortName;
|
||||
CreationTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the creation date for the file or directory, in UTC.
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Joliet compliant name of the file or directory.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent directory, or <c>null</c> if this is the root directory.
|
||||
/// </summary>
|
||||
public abstract BuildDirectoryInfo Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ISO 9660 compliant name of the file or directory.
|
||||
/// </summary>
|
||||
public string ShortName { get; }
|
||||
|
||||
internal string PickName(string nameOverride, Encoding enc)
|
||||
{
|
||||
if (nameOverride != null)
|
||||
{
|
||||
return nameOverride;
|
||||
}
|
||||
return enc == Encoding.ASCII ? ShortName : Name;
|
||||
}
|
||||
|
||||
internal abstract long GetDataSize(Encoding enc);
|
||||
|
||||
internal uint GetDirectoryRecordSize(Encoding enc)
|
||||
{
|
||||
return DirectoryRecord.CalcLength(PickName(null, enc), enc);
|
||||
}
|
||||
|
||||
private class DirectorySortedComparison : Comparer<BuildDirectoryMember>
|
||||
{
|
||||
public override int Compare(BuildDirectoryMember x, BuildDirectoryMember y)
|
||||
{
|
||||
string[] xParts = x.Name.Split('.', ';');
|
||||
string[] yParts = y.Name.Split('.', ';');
|
||||
|
||||
string xPart;
|
||||
string yPart;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
xPart = xParts.Length > i ? xParts[i] : string.Empty;
|
||||
yPart = yParts.Length > i ? yParts[i] : string.Empty;
|
||||
int val = ComparePart(xPart, yPart, ' ');
|
||||
if (val != 0)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
xPart = xParts.Length > 2 ? xParts[2] : string.Empty;
|
||||
yPart = yParts.Length > 2 ? yParts[2] : string.Empty;
|
||||
return ComparePartBackwards(xPart, yPart, '0');
|
||||
}
|
||||
|
||||
private static int ComparePart(string x, string y, char padChar)
|
||||
{
|
||||
int max = Math.Max(x.Length, y.Length);
|
||||
for (int i = 0; i < max; ++i)
|
||||
{
|
||||
char xChar = i < x.Length ? x[i] : padChar;
|
||||
char yChar = i < y.Length ? y[i] : padChar;
|
||||
|
||||
if (xChar != yChar)
|
||||
{
|
||||
return xChar - yChar;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int ComparePartBackwards(string x, string y, char padChar)
|
||||
{
|
||||
int max = Math.Max(x.Length, y.Length);
|
||||
|
||||
int xPad = max - x.Length;
|
||||
int yPad = max - y.Length;
|
||||
|
||||
for (int i = 0; i < max; ++i)
|
||||
{
|
||||
char xChar = i >= xPad ? x[i - xPad] : padChar;
|
||||
char yChar = i >= yPad ? y[i - yPad] : padChar;
|
||||
|
||||
if (xChar != yChar)
|
||||
{
|
||||
// Note: Version numbers are in DESCENDING order!
|
||||
return yChar - xChar;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using DiscUtils.CoreCompat;
|
||||
using DiscUtils.Internal;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a file that will be built into the ISO image.
|
||||
/// </summary>
|
||||
public sealed class BuildFileInfo : BuildDirectoryMember
|
||||
{
|
||||
private readonly byte[] _contentData;
|
||||
private readonly string _contentPath;
|
||||
private readonly long _contentSize;
|
||||
private readonly Stream _contentStream;
|
||||
|
||||
internal BuildFileInfo(string name, BuildDirectoryInfo parent, byte[] content)
|
||||
: base(IsoUtilities.NormalizeFileName(name), MakeShortFileName(name, parent))
|
||||
{
|
||||
Parent = parent;
|
||||
_contentData = content;
|
||||
_contentSize = content.Length;
|
||||
}
|
||||
|
||||
internal BuildFileInfo(string name, BuildDirectoryInfo parent, string content)
|
||||
: base(IsoUtilities.NormalizeFileName(name), MakeShortFileName(name, parent))
|
||||
{
|
||||
Parent = parent;
|
||||
_contentPath = content;
|
||||
_contentSize = new FileInfo(_contentPath).Length;
|
||||
|
||||
CreationTime = new FileInfo(_contentPath).LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
internal BuildFileInfo(string name, BuildDirectoryInfo parent, Stream source)
|
||||
: base(IsoUtilities.NormalizeFileName(name), MakeShortFileName(name, parent))
|
||||
{
|
||||
Parent = parent;
|
||||
_contentStream = source;
|
||||
_contentSize = _contentStream.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The parent directory, or <c>null</c> if none.
|
||||
/// </summary>
|
||||
public override BuildDirectoryInfo Parent { get; }
|
||||
|
||||
internal override long GetDataSize(Encoding enc)
|
||||
{
|
||||
return _contentSize;
|
||||
}
|
||||
|
||||
internal Stream OpenStream()
|
||||
{
|
||||
if (_contentData != null)
|
||||
{
|
||||
return new MemoryStream(_contentData, false);
|
||||
}
|
||||
if (_contentPath != null)
|
||||
{
|
||||
var locator = new LocalFileLocator(string.Empty);
|
||||
return locator.Open(_contentPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
return _contentStream;
|
||||
}
|
||||
|
||||
internal void CloseStream(Stream s)
|
||||
{
|
||||
// Close and dispose the stream, unless it's one we were given to stream in
|
||||
// from (we might need it again).
|
||||
if (_contentStream != s)
|
||||
{
|
||||
s.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static string MakeShortFileName(string longName, BuildDirectoryInfo dir)
|
||||
{
|
||||
if (IsoUtilities.IsValidFileName(longName))
|
||||
{
|
||||
return longName;
|
||||
}
|
||||
|
||||
char[] shortNameChars = longName.ToUpper(CultureInfo.InvariantCulture).ToCharArray();
|
||||
for (int i = 0; i < shortNameChars.Length; ++i)
|
||||
{
|
||||
if (!IsoUtilities.IsValidDChar(shortNameChars[i]) && shortNameChars[i] != '.' && shortNameChars[i] != ';')
|
||||
{
|
||||
shortNameChars[i] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
string[] parts = IsoUtilities.SplitFileName(new string(shortNameChars));
|
||||
|
||||
if (parts[0].Length + parts[1].Length > 30)
|
||||
{
|
||||
parts[1] = parts[1].Substring(0, Math.Min(parts[1].Length, 3));
|
||||
}
|
||||
|
||||
if (parts[0].Length + parts[1].Length > 30)
|
||||
{
|
||||
parts[0] = parts[0].Substring(0, 30 - parts[1].Length);
|
||||
}
|
||||
|
||||
string candidate = parts[0] + '.' + parts[1] + ';' + parts[2];
|
||||
|
||||
// TODO: Make unique
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class BuildParameters
|
||||
{
|
||||
public BuildParameters()
|
||||
{
|
||||
VolumeIdentifier = string.Empty;
|
||||
UseJoliet = true;
|
||||
SectorSize = 2048;
|
||||
}
|
||||
|
||||
public int SectorSize { get; set; }
|
||||
|
||||
public bool UseJoliet { get; set; }
|
||||
|
||||
public string VolumeIdentifier { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BuilderBufferExtent : BuilderExtent
|
||||
{
|
||||
private byte[] _buffer;
|
||||
private readonly bool _fixedBuffer;
|
||||
|
||||
public BuilderBufferExtent(long start, long length)
|
||||
: base(start, length) {}
|
||||
|
||||
public BuilderBufferExtent(long start, byte[] buffer)
|
||||
: base(start, buffer.Length)
|
||||
{
|
||||
_fixedBuffer = true;
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
public override void Dispose() {}
|
||||
|
||||
public override void PrepareForRead()
|
||||
{
|
||||
if (!_fixedBuffer)
|
||||
{
|
||||
_buffer = GetBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(long diskOffset, byte[] block, int offset, int count)
|
||||
{
|
||||
int startOffset = (int)(diskOffset - Start);
|
||||
int numBytes = (int)Math.Min(Length - startOffset, count);
|
||||
Array.Copy(_buffer, startOffset, block, offset, numBytes);
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
public override void DisposeReadState()
|
||||
{
|
||||
if (!_fixedBuffer)
|
||||
{
|
||||
_buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual byte[] GetBuffer()
|
||||
{
|
||||
throw new NotSupportedException("Derived class should implement");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BuilderBufferExtentSource : BuilderExtentSource
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
|
||||
public BuilderBufferExtentSource(byte[] buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
public override BuilderExtent Fix(long pos)
|
||||
{
|
||||
return new BuilderBufferExtent(pos, _buffer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BuilderBytesExtent : BuilderExtent
|
||||
{
|
||||
protected byte[] _data;
|
||||
|
||||
public BuilderBytesExtent(long start, byte[] data)
|
||||
: base(start, data.Length)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
protected BuilderBytesExtent(long start, long length)
|
||||
: base(start, length) {}
|
||||
|
||||
public override void Dispose() {}
|
||||
|
||||
public override void PrepareForRead() {}
|
||||
|
||||
public override int Read(long diskOffset, byte[] block, int offset, int count)
|
||||
{
|
||||
int start = (int)Math.Min(diskOffset - Start, _data.Length);
|
||||
int numRead = Math.Min(count, _data.Length - start);
|
||||
|
||||
Array.Copy(_data, start, block, offset, numRead);
|
||||
|
||||
return numRead;
|
||||
}
|
||||
|
||||
public override void DisposeReadState() {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public abstract class BuilderExtent : IDisposable
|
||||
{
|
||||
public BuilderExtent(long start, long length)
|
||||
{
|
||||
Start = start;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public long Length { get; }
|
||||
|
||||
public long Start { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of the stream that are stored.
|
||||
/// </summary>
|
||||
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
|
||||
public virtual IEnumerable<StreamExtent> StreamExtents
|
||||
{
|
||||
get { return new[] { new StreamExtent(Start, Length) }; }
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract void PrepareForRead();
|
||||
|
||||
public abstract int Read(long diskOffset, byte[] block, int offset, int count);
|
||||
|
||||
public abstract void DisposeReadState();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public abstract class BuilderExtentSource
|
||||
{
|
||||
public abstract BuilderExtent Fix(long pos);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BuilderSparseStreamExtent : BuilderExtent
|
||||
{
|
||||
private readonly Ownership _ownership;
|
||||
private SparseStream _stream;
|
||||
|
||||
public BuilderSparseStreamExtent(long start, SparseStream stream)
|
||||
: this(start, stream, Ownership.None) {}
|
||||
|
||||
public BuilderSparseStreamExtent(long start, SparseStream stream, Ownership ownership)
|
||||
: base(start, stream.Length)
|
||||
{
|
||||
_stream = stream;
|
||||
_ownership = ownership;
|
||||
}
|
||||
|
||||
public override IEnumerable<StreamExtent> StreamExtents
|
||||
{
|
||||
get { return StreamExtent.Offset(_stream.Extents, Start); }
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_stream != null && _ownership == Ownership.Dispose)
|
||||
{
|
||||
_stream.Dispose();
|
||||
_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void PrepareForRead() {}
|
||||
|
||||
public override int Read(long diskOffset, byte[] block, int offset, int count)
|
||||
{
|
||||
_stream.Position = diskOffset - Start;
|
||||
return _stream.Read(block, offset, count);
|
||||
}
|
||||
|
||||
public override void DisposeReadState() {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BuilderStreamExtent : BuilderExtent
|
||||
{
|
||||
private readonly Ownership _ownership;
|
||||
private Stream _source;
|
||||
|
||||
public BuilderStreamExtent(long start, Stream source)
|
||||
: this(start, source, Ownership.None) {}
|
||||
|
||||
public BuilderStreamExtent(long start, Stream source, Ownership ownership)
|
||||
: base(start, source.Length)
|
||||
{
|
||||
_source = source;
|
||||
_ownership = ownership;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_source != null && _ownership == Ownership.Dispose)
|
||||
{
|
||||
_source.Dispose();
|
||||
_source = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void PrepareForRead() {}
|
||||
|
||||
public override int Read(long diskOffset, byte[] block, int offset, int count)
|
||||
{
|
||||
_source.Position = diskOffset - Start;
|
||||
return _source.Read(block, offset, count);
|
||||
}
|
||||
|
||||
public override void DisposeReadState() {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BuilderStreamExtentSource : BuilderExtentSource
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
|
||||
public BuilderStreamExtentSource(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
public override BuilderExtent Fix(long pos)
|
||||
{
|
||||
return new BuilderStreamExtent(pos, _stream);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class PassthroughStreamBuilder : StreamBuilder
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
|
||||
public PassthroughStreamBuilder(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
protected override List<BuilderExtent> FixExtents(out long totalLength)
|
||||
{
|
||||
_stream.Position = 0;
|
||||
List<BuilderExtent> result = new List<BuilderExtent>();
|
||||
result.Add(new BuilderStreamExtent(0, _stream));
|
||||
totalLength = _stream.Length;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for objects that can dynamically construct a stream.
|
||||
/// </summary>
|
||||
public abstract class StreamBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a new stream.
|
||||
/// </summary>
|
||||
/// <returns>The stream created by the StreamBuilder instance.</returns>
|
||||
public virtual SparseStream Build()
|
||||
{
|
||||
long totalLength;
|
||||
List<BuilderExtent> extents = FixExtents(out totalLength);
|
||||
return new BuiltStream(totalLength, extents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the stream contents to an existing stream.
|
||||
/// </summary>
|
||||
/// <param name="output">The stream to write to.</param>
|
||||
public void Build(Stream output)
|
||||
{
|
||||
using (Stream src = Build())
|
||||
{
|
||||
byte[] buffer = new byte[64 * 1024];
|
||||
int numRead = src.Read(buffer, 0, buffer.Length);
|
||||
while (numRead != 0)
|
||||
{
|
||||
output.Write(buffer, 0, numRead);
|
||||
numRead = src.Read(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the stream contents to a file.
|
||||
/// </summary>
|
||||
/// <param name="outputFile">The file to write to.</param>
|
||||
public void Build(string outputFile)
|
||||
{
|
||||
using (FileStream destStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
Build(destStream);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<BuilderExtent> FixExtents(out long totalLength);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,325 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
public class BuiltStream : SparseStream
|
||||
{
|
||||
private Stream _baseStream;
|
||||
|
||||
private BuilderExtent _currentExtent;
|
||||
private readonly List<BuilderExtent> _extents;
|
||||
private readonly long _length;
|
||||
private long _position;
|
||||
|
||||
public BuiltStream(long length, List<BuilderExtent> extents)
|
||||
{
|
||||
_baseStream = new ZeroStream(length);
|
||||
_length = length;
|
||||
_extents = extents;
|
||||
|
||||
// Make sure the extents are sorted, so binary searches will work.
|
||||
_extents.Sort(new ExtentStartComparer());
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override IEnumerable<StreamExtent> Extents
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (BuilderExtent extent in _extents)
|
||||
{
|
||||
foreach (StreamExtent streamExtent in extent.StreamExtents)
|
||||
{
|
||||
yield return streamExtent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { return _length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
public override void Flush() {}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_position >= _length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (_position + count > _length)
|
||||
{
|
||||
count = (int)(_length - _position);
|
||||
}
|
||||
|
||||
int totalRead = 0;
|
||||
while (totalRead < count && _position < _length)
|
||||
{
|
||||
// If current region is outside the area of interest, clean it up
|
||||
if (_currentExtent != null &&
|
||||
(_position < _currentExtent.Start || _position >= _currentExtent.Start + _currentExtent.Length))
|
||||
{
|
||||
_currentExtent.DisposeReadState();
|
||||
_currentExtent = null;
|
||||
}
|
||||
|
||||
// If we need to find a new region, look for it
|
||||
if (_currentExtent == null)
|
||||
{
|
||||
using (SearchExtent searchExtent = new SearchExtent(_position))
|
||||
{
|
||||
int idx = _extents.BinarySearch(searchExtent, new ExtentRangeComparer());
|
||||
if (idx >= 0)
|
||||
{
|
||||
BuilderExtent extent = _extents[idx];
|
||||
extent.PrepareForRead();
|
||||
_currentExtent = extent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int numRead = 0;
|
||||
|
||||
// If the block is outside any known extent, defer to base stream.
|
||||
if (_currentExtent == null)
|
||||
{
|
||||
_baseStream.Position = _position;
|
||||
BuilderExtent nextExtent = FindNext(_position);
|
||||
if (nextExtent != null)
|
||||
{
|
||||
numRead = _baseStream.Read(buffer, offset + totalRead,
|
||||
(int)Math.Min(count - totalRead, nextExtent.Start - _position));
|
||||
}
|
||||
else
|
||||
{
|
||||
numRead = _baseStream.Read(buffer, offset + totalRead, count - totalRead);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
numRead = _currentExtent.Read(_position, buffer, offset + totalRead, count - totalRead);
|
||||
}
|
||||
|
||||
_position += numRead;
|
||||
totalRead += numRead;
|
||||
if (numRead == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long newPos = offset;
|
||||
if (origin == SeekOrigin.Current)
|
||||
{
|
||||
newPos += _position;
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
newPos += _length;
|
||||
}
|
||||
|
||||
_position = newPos;
|
||||
return newPos;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_currentExtent != null)
|
||||
{
|
||||
_currentExtent.DisposeReadState();
|
||||
_currentExtent = null;
|
||||
}
|
||||
|
||||
if (_baseStream != null)
|
||||
{
|
||||
_baseStream.Dispose();
|
||||
_baseStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
private BuilderExtent FindNext(long pos)
|
||||
{
|
||||
int min = 0;
|
||||
int max = _extents.Count - 1;
|
||||
|
||||
if (_extents.Count == 0 || _extents[_extents.Count - 1].Start + _extents[_extents.Count - 1].Length <= pos)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (min >= max)
|
||||
{
|
||||
return _extents[min];
|
||||
}
|
||||
|
||||
int mid = (max + min) / 2;
|
||||
if (_extents[mid].Start < pos)
|
||||
{
|
||||
min = mid + 1;
|
||||
}
|
||||
else if (_extents[mid].Start > pos)
|
||||
{
|
||||
max = mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _extents[mid];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SearchExtent : BuilderExtent
|
||||
{
|
||||
public SearchExtent(long pos)
|
||||
: base(pos, 1) {}
|
||||
|
||||
public override void Dispose() {}
|
||||
|
||||
public override void PrepareForRead()
|
||||
{
|
||||
// Not valid to use this 'dummy' extent for actual construction
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(long diskOffset, byte[] block, int offset, int count)
|
||||
{
|
||||
// Not valid to use this 'dummy' extent for actual construction
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void DisposeReadState()
|
||||
{
|
||||
// Not valid to use this 'dummy' extent for actual construction
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class ExtentRangeComparer : IComparer<BuilderExtent>
|
||||
{
|
||||
public int Compare(BuilderExtent x, BuilderExtent y)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(x));
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(y));
|
||||
}
|
||||
|
||||
if (x.Start + x.Length <= y.Start)
|
||||
{
|
||||
// x < y, with no intersection
|
||||
return -1;
|
||||
}
|
||||
if (x.Start >= y.Start + y.Length)
|
||||
{
|
||||
// x > y, with no intersection
|
||||
return 1;
|
||||
}
|
||||
|
||||
// x intersects y
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class ExtentStartComparer : IComparer<BuilderExtent>
|
||||
{
|
||||
public int Compare(BuilderExtent x, BuilderExtent y)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(x));
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(y));
|
||||
}
|
||||
|
||||
long val = x.Start - y.Start;
|
||||
if (val < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (val > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,518 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that creates ISO images.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// CDBuilder builder = new CDBuilder();
|
||||
/// builder.VolumeIdentifier = "MYISO";
|
||||
/// builder.UseJoliet = true;
|
||||
/// builder.AddFile("Hello.txt", Encoding.ASCII.GetBytes("hello world!"));
|
||||
/// builder.Build(@"C:\TEMP\myiso.iso");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public sealed class CDBuilder : StreamBuilder
|
||||
{
|
||||
private const long DiskStart = 0x8000;
|
||||
private BootInitialEntry _bootEntry;
|
||||
private Stream _bootImage;
|
||||
|
||||
private readonly BuildParameters _buildParams;
|
||||
private readonly List<BuildDirectoryInfo> _dirs;
|
||||
|
||||
private readonly List<BuildFileInfo> _files;
|
||||
private readonly BuildDirectoryInfo _rootDirectory;
|
||||
private readonly int _sectorSize;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CDBuilder class.
|
||||
/// </summary>
|
||||
public CDBuilder()
|
||||
{
|
||||
_files = new List<BuildFileInfo>();
|
||||
_dirs = new List<BuildDirectoryInfo>();
|
||||
_sectorSize = 2048;
|
||||
_rootDirectory = new BuildDirectoryInfo("\0", null, _sectorSize);
|
||||
_dirs.Add(_rootDirectory);
|
||||
|
||||
_buildParams = new BuildParameters();
|
||||
_buildParams.UseJoliet = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CDBuilder class.
|
||||
/// </summary>
|
||||
public CDBuilder(int sectorSize)
|
||||
{
|
||||
_files = new List<BuildFileInfo>();
|
||||
_dirs = new List<BuildDirectoryInfo>();
|
||||
_sectorSize = sectorSize;
|
||||
_rootDirectory = new BuildDirectoryInfo("\0", null, _sectorSize);
|
||||
_dirs.Add(_rootDirectory);
|
||||
|
||||
_buildParams = new BuildParameters();
|
||||
_buildParams.UseJoliet = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to update the ISOLINUX info table at the
|
||||
/// start of the boot image. Use with ISOLINUX only.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ISOLINUX has an 'information table' at the start of the boot loader that verifies
|
||||
/// the CD has been loaded correctly by the BIOS. This table needs to be updated
|
||||
/// to match the actual ISO.
|
||||
/// </remarks>
|
||||
public bool UpdateIsolinuxBootTable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Joliet file-system extensions should be used.
|
||||
/// </summary>
|
||||
public bool UseJoliet
|
||||
{
|
||||
get { return _buildParams.UseJoliet; }
|
||||
set { _buildParams.UseJoliet = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Volume Identifier for the ISO file.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Must be a valid identifier, i.e. max 32 characters in the range A-Z, 0-9 or _.
|
||||
/// Lower-case characters are not permitted.
|
||||
/// </remarks>
|
||||
public string VolumeIdentifier
|
||||
{
|
||||
get { return _buildParams.VolumeIdentifier; }
|
||||
|
||||
set
|
||||
{
|
||||
if (value.Length > 32)
|
||||
{
|
||||
throw new ArgumentException("Not a valid volume identifier");
|
||||
}
|
||||
_buildParams.VolumeIdentifier = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the boot image for the ISO image.
|
||||
/// </summary>
|
||||
/// <param name="image">Stream containing the boot image.</param>
|
||||
/// <param name="emulation">The type of emulation requested of the BIOS.</param>
|
||||
/// <param name="loadSegment">The memory segment to load the image to (0 for default).</param>
|
||||
public void SetBootImage(Stream image, BootDeviceEmulation emulation, int loadSegment)
|
||||
{
|
||||
if (_bootEntry != null)
|
||||
{
|
||||
throw new InvalidOperationException("Boot image already set");
|
||||
}
|
||||
|
||||
_bootEntry = new BootInitialEntry();
|
||||
_bootEntry.BootIndicator = 0x88;
|
||||
_bootEntry.BootMediaType = emulation;
|
||||
_bootEntry.LoadSegment = (ushort)loadSegment;
|
||||
_bootEntry.SystemType = 0;
|
||||
_bootImage = image;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a directory to the ISO image.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the directory on the ISO image.</param>
|
||||
/// <returns>The object representing this directory.</returns>
|
||||
/// <remarks>
|
||||
/// The name is the full path to the directory, for example:
|
||||
/// <example><code>
|
||||
/// builder.AddDirectory(@"DIRA\DIRB\DIRC");
|
||||
/// </code></example>
|
||||
/// </remarks>
|
||||
public BuildDirectoryInfo AddDirectory(string name)
|
||||
{
|
||||
string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetDirectory(nameElements, nameElements.Length, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a byte array to the ISO image as a file.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file on the ISO image.</param>
|
||||
/// <param name="content">The contents of the file.</param>
|
||||
/// <returns>The object representing this file.</returns>
|
||||
/// <remarks>
|
||||
/// The name is the full path to the file, for example:
|
||||
/// <example><code>
|
||||
/// builder.AddFile(@"DIRA\DIRB\FILE.TXT;1", new byte[]{0,1,2});
|
||||
/// </code></example>
|
||||
/// <para>Note the version number at the end of the file name is optional, if not
|
||||
/// specified the default of 1 will be used.</para>
|
||||
/// </remarks>
|
||||
public BuildFileInfo AddFile(string name, byte[] content)
|
||||
{
|
||||
string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
BuildDirectoryInfo dir = GetDirectory(nameElements, nameElements.Length - 1, true);
|
||||
|
||||
BuildDirectoryMember existing;
|
||||
if (dir.TryGetMember(nameElements[nameElements.Length - 1], out existing))
|
||||
{
|
||||
throw new IOException("File already exists");
|
||||
}
|
||||
BuildFileInfo fi = new BuildFileInfo(nameElements[nameElements.Length - 1], dir, content);
|
||||
_files.Add(fi);
|
||||
dir.Add(fi);
|
||||
return fi;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a disk file to the ISO image as a file.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file on the ISO image.</param>
|
||||
/// <param name="sourcePath">The name of the file on disk.</param>
|
||||
/// <returns>The object representing this file.</returns>
|
||||
/// <remarks>
|
||||
/// The name is the full path to the file, for example:
|
||||
/// <example><code>
|
||||
/// builder.AddFile(@"DIRA\DIRB\FILE.TXT;1", @"C:\temp\tempfile.bin");
|
||||
/// </code></example>
|
||||
/// <para>Note the version number at the end of the file name is optional, if not
|
||||
/// specified the default of 1 will be used.</para>
|
||||
/// </remarks>
|
||||
public BuildFileInfo AddFile(string name, string sourcePath)
|
||||
{
|
||||
string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
BuildDirectoryInfo dir = GetDirectory(nameElements, nameElements.Length - 1, true);
|
||||
|
||||
BuildDirectoryMember existing;
|
||||
if (dir.TryGetMember(nameElements[nameElements.Length - 1], out existing))
|
||||
{
|
||||
throw new IOException("File already exists");
|
||||
}
|
||||
BuildFileInfo fi = new BuildFileInfo(nameElements[nameElements.Length - 1], dir, sourcePath);
|
||||
_files.Add(fi);
|
||||
dir.Add(fi);
|
||||
return fi;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a stream to the ISO image as a file.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file on the ISO image.</param>
|
||||
/// <param name="source">The contents of the file.</param>
|
||||
/// <returns>The object representing this file.</returns>
|
||||
/// <remarks>
|
||||
/// The name is the full path to the file, for example:
|
||||
/// <example><code>
|
||||
/// builder.AddFile(@"DIRA\DIRB\FILE.TXT;1", stream);
|
||||
/// </code></example>
|
||||
/// <para>Note the version number at the end of the file name is optional, if not
|
||||
/// specified the default of 1 will be used.</para>
|
||||
/// </remarks>
|
||||
public BuildFileInfo AddFile(string name, Stream source)
|
||||
{
|
||||
if (!source.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("source doesn't support seeking", nameof(source));
|
||||
}
|
||||
|
||||
string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
BuildDirectoryInfo dir = GetDirectory(nameElements, nameElements.Length - 1, true);
|
||||
|
||||
BuildDirectoryMember existing;
|
||||
if (dir.TryGetMember(nameElements[nameElements.Length - 1], out existing))
|
||||
{
|
||||
throw new IOException("File already exists");
|
||||
}
|
||||
BuildFileInfo fi = new BuildFileInfo(nameElements[nameElements.Length - 1], dir, source);
|
||||
_files.Add(fi);
|
||||
dir.Add(fi);
|
||||
return fi;
|
||||
}
|
||||
|
||||
protected override List<BuilderExtent> FixExtents(out long totalLength)
|
||||
{
|
||||
List<BuilderExtent> fixedRegions = new List<BuilderExtent>();
|
||||
|
||||
DateTime buildTime = DateTime.UtcNow;
|
||||
|
||||
Encoding suppEncoding = _buildParams.UseJoliet ? Encoding.BigEndianUnicode : Encoding.ASCII;
|
||||
|
||||
Dictionary<BuildDirectoryMember, uint> primaryLocationTable = new Dictionary<BuildDirectoryMember, uint>();
|
||||
Dictionary<BuildDirectoryMember, uint> supplementaryLocationTable =
|
||||
new Dictionary<BuildDirectoryMember, uint>();
|
||||
|
||||
long focus = DiskStart + 3 * _buildParams.SectorSize; // Primary, Supplementary, End (fixed at end...)
|
||||
if (_bootEntry != null)
|
||||
{
|
||||
focus += _buildParams.SectorSize;
|
||||
}
|
||||
|
||||
// ####################################################################
|
||||
// # 0. Fix boot image location
|
||||
// ####################################################################
|
||||
long bootCatalogPos = 0;
|
||||
if (_bootEntry != null)
|
||||
{
|
||||
long bootImagePos = focus;
|
||||
Stream realBootImage = PatchBootImage(_bootImage, (uint)(DiskStart / _buildParams.SectorSize),
|
||||
(uint)(bootImagePos / _buildParams.SectorSize));
|
||||
BuilderStreamExtent bootImageExtent = new BuilderStreamExtent(focus, realBootImage);
|
||||
fixedRegions.Add(bootImageExtent);
|
||||
focus += MathUtilities.RoundUp(bootImageExtent.Length, _buildParams.SectorSize);
|
||||
|
||||
bootCatalogPos = focus;
|
||||
byte[] bootCatalog = new byte[_buildParams.SectorSize];
|
||||
BootValidationEntry bve = new BootValidationEntry();
|
||||
bve.WriteTo(bootCatalog, 0x00);
|
||||
_bootEntry.ImageStart = (uint)MathUtilities.Ceil(bootImagePos, _buildParams.SectorSize);
|
||||
_bootEntry.SectorCount = (ushort)MathUtilities.Ceil(_bootImage.Length, Sizes.Sector);
|
||||
_bootEntry.WriteTo(bootCatalog, 0x20);
|
||||
fixedRegions.Add(new BuilderBufferExtent(bootCatalogPos, bootCatalog));
|
||||
focus += _buildParams.SectorSize;
|
||||
}
|
||||
|
||||
// ####################################################################
|
||||
// # 1. Fix file locations
|
||||
// ####################################################################
|
||||
|
||||
// Find end of the file data, fixing the files in place as we go
|
||||
foreach (BuildFileInfo fi in _files)
|
||||
{
|
||||
primaryLocationTable.Add(fi, (uint)(focus / _buildParams.SectorSize));
|
||||
supplementaryLocationTable.Add(fi, (uint)(focus / _buildParams.SectorSize));
|
||||
FileExtent extent = new FileExtent(fi, focus);
|
||||
|
||||
// Only remember files of non-zero length (otherwise we'll stomp on a valid file)
|
||||
if (extent.Length != 0)
|
||||
{
|
||||
fixedRegions.Add(extent);
|
||||
}
|
||||
|
||||
focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize);
|
||||
}
|
||||
|
||||
// ####################################################################
|
||||
// # 2. Fix directory locations
|
||||
// ####################################################################
|
||||
|
||||
// There are two directory tables
|
||||
// 1. Primary (std ISO9660)
|
||||
// 2. Supplementary (Joliet)
|
||||
|
||||
// Find start of the second set of directory data, fixing ASCII directories in place.
|
||||
long startOfFirstDirData = focus;
|
||||
foreach (BuildDirectoryInfo di in _dirs)
|
||||
{
|
||||
primaryLocationTable.Add(di, (uint)(focus / _buildParams.SectorSize));
|
||||
DirectoryExtent extent = new DirectoryExtent(di, primaryLocationTable, Encoding.ASCII, focus);
|
||||
fixedRegions.Add(extent);
|
||||
focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize);
|
||||
}
|
||||
|
||||
// Find end of the second directory table, fixing supplementary directories in place.
|
||||
long startOfSecondDirData = focus;
|
||||
foreach (BuildDirectoryInfo di in _dirs)
|
||||
{
|
||||
supplementaryLocationTable.Add(di, (uint)(focus / _buildParams.SectorSize));
|
||||
DirectoryExtent extent = new DirectoryExtent(di, supplementaryLocationTable, suppEncoding, focus);
|
||||
fixedRegions.Add(extent);
|
||||
focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize);
|
||||
}
|
||||
|
||||
// ####################################################################
|
||||
// # 3. Fix path tables
|
||||
// ####################################################################
|
||||
|
||||
// There are four path tables:
|
||||
// 1. LE, ASCII
|
||||
// 2. BE, ASCII
|
||||
// 3. LE, Supp Encoding (Joliet)
|
||||
// 4. BE, Supp Encoding (Joliet)
|
||||
|
||||
// Find end of the path table
|
||||
long startOfFirstPathTable = focus;
|
||||
PathTable pathTable = new PathTable(false, Encoding.ASCII, _dirs, primaryLocationTable, focus);
|
||||
fixedRegions.Add(pathTable);
|
||||
focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize);
|
||||
long primaryPathTableLength = pathTable.Length;
|
||||
|
||||
long startOfSecondPathTable = focus;
|
||||
pathTable = new PathTable(true, Encoding.ASCII, _dirs, primaryLocationTable, focus);
|
||||
fixedRegions.Add(pathTable);
|
||||
focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize);
|
||||
|
||||
long startOfThirdPathTable = focus;
|
||||
pathTable = new PathTable(false, suppEncoding, _dirs, supplementaryLocationTable, focus);
|
||||
fixedRegions.Add(pathTable);
|
||||
focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize);
|
||||
long supplementaryPathTableLength = pathTable.Length;
|
||||
|
||||
long startOfFourthPathTable = focus;
|
||||
pathTable = new PathTable(true, suppEncoding, _dirs, supplementaryLocationTable, focus);
|
||||
fixedRegions.Add(pathTable);
|
||||
focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize);
|
||||
|
||||
// Find the end of the disk
|
||||
totalLength = focus;
|
||||
|
||||
// ####################################################################
|
||||
// # 4. Prepare volume descriptors now other structures are fixed
|
||||
// ####################################################################
|
||||
int regionIdx = 0;
|
||||
focus = DiskStart;
|
||||
PrimaryVolumeDescriptor pvDesc = new PrimaryVolumeDescriptor(
|
||||
(uint)(totalLength / _buildParams.SectorSize), // VolumeSpaceSize
|
||||
(uint)primaryPathTableLength, // PathTableSize
|
||||
(uint)(startOfFirstPathTable / _buildParams.SectorSize), // TypeLPathTableLocation
|
||||
(uint)(startOfSecondPathTable / _buildParams.SectorSize), // TypeMPathTableLocation
|
||||
(uint)(startOfFirstDirData / _buildParams.SectorSize), // RootDirectory.LocationOfExtent
|
||||
(uint)_rootDirectory.GetDataSize(Encoding.ASCII), // RootDirectory.DataLength
|
||||
buildTime,
|
||||
_sectorSize);
|
||||
pvDesc.VolumeIdentifier = _buildParams.VolumeIdentifier;
|
||||
PrimaryVolumeDescriptorRegion pvdr = new PrimaryVolumeDescriptorRegion(pvDesc, focus, _sectorSize);
|
||||
fixedRegions.Insert(regionIdx++, pvdr);
|
||||
focus += _buildParams.SectorSize;
|
||||
|
||||
if (_bootEntry != null)
|
||||
{
|
||||
BootVolumeDescriptor bvDesc = new BootVolumeDescriptor(
|
||||
(uint)(bootCatalogPos / _buildParams.SectorSize), _buildParams.SectorSize);
|
||||
BootVolumeDescriptorRegion bvdr = new BootVolumeDescriptorRegion(bvDesc, focus, _buildParams.SectorSize);
|
||||
fixedRegions.Insert(regionIdx++, bvdr);
|
||||
focus += _buildParams.SectorSize;
|
||||
}
|
||||
|
||||
SupplementaryVolumeDescriptor svDesc = new SupplementaryVolumeDescriptor(
|
||||
(uint)(totalLength / _buildParams.SectorSize), // VolumeSpaceSize
|
||||
(uint)supplementaryPathTableLength, // PathTableSize
|
||||
(uint)(startOfThirdPathTable / _buildParams.SectorSize), // TypeLPathTableLocation
|
||||
(uint)(startOfFourthPathTable / _buildParams.SectorSize), // TypeMPathTableLocation
|
||||
(uint)(startOfSecondDirData / _buildParams.SectorSize), // RootDirectory.LocationOfExtent
|
||||
(uint)_rootDirectory.GetDataSize(suppEncoding), // RootDirectory.DataLength
|
||||
buildTime,
|
||||
suppEncoding,
|
||||
_sectorSize);
|
||||
svDesc.VolumeIdentifier = _buildParams.VolumeIdentifier;
|
||||
SupplementaryVolumeDescriptorRegion svdr = new SupplementaryVolumeDescriptorRegion(svDesc, focus, _sectorSize);
|
||||
fixedRegions.Insert(regionIdx++, svdr);
|
||||
focus += _buildParams.SectorSize;
|
||||
|
||||
VolumeDescriptorSetTerminator evDesc = new VolumeDescriptorSetTerminator(_sectorSize);
|
||||
VolumeDescriptorSetTerminatorRegion evdr = new VolumeDescriptorSetTerminatorRegion(evDesc, focus, _sectorSize);
|
||||
fixedRegions.Insert(regionIdx++, evdr);
|
||||
|
||||
return fixedRegions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Patches a boot image (esp. for ISOLINUX) before it is written to the disk.
|
||||
/// </summary>
|
||||
/// <param name="bootImage">The original (master) boot image.</param>
|
||||
/// <param name="pvdLba">The logical block address of the primary volume descriptor.</param>
|
||||
/// <param name="bootImageLba">The logical block address of the boot image itself.</param>
|
||||
/// <returns>A stream containing the patched boot image - does not need to be disposed.</returns>
|
||||
private Stream PatchBootImage(Stream bootImage, uint pvdLba, uint bootImageLba)
|
||||
{
|
||||
// Early-exit if no patching to do...
|
||||
if (!UpdateIsolinuxBootTable)
|
||||
{
|
||||
return bootImage;
|
||||
}
|
||||
|
||||
byte[] bootData = StreamUtilities.ReadExact(bootImage, (int)bootImage.Length);
|
||||
|
||||
Array.Clear(bootData, 8, 56);
|
||||
|
||||
uint checkSum = 0;
|
||||
for (int i = 64; i < bootData.Length; i += 4)
|
||||
{
|
||||
checkSum += EndianUtilities.ToUInt32LittleEndian(bootData, i);
|
||||
}
|
||||
|
||||
EndianUtilities.WriteBytesLittleEndian(pvdLba, bootData, 8);
|
||||
EndianUtilities.WriteBytesLittleEndian(bootImageLba, bootData, 12);
|
||||
EndianUtilities.WriteBytesLittleEndian(bootData.Length, bootData, 16);
|
||||
EndianUtilities.WriteBytesLittleEndian(checkSum, bootData, 20);
|
||||
|
||||
return new MemoryStream(bootData, false);
|
||||
}
|
||||
|
||||
private BuildDirectoryInfo GetDirectory(string[] path, int pathLength, bool createMissing)
|
||||
{
|
||||
BuildDirectoryInfo di = TryGetDirectory(path, pathLength, createMissing);
|
||||
|
||||
if (di == null)
|
||||
{
|
||||
throw new DirectoryNotFoundException("Directory not found");
|
||||
}
|
||||
|
||||
return di;
|
||||
}
|
||||
|
||||
private BuildDirectoryInfo TryGetDirectory(string[] path, int pathLength, bool createMissing)
|
||||
{
|
||||
BuildDirectoryInfo focus = _rootDirectory;
|
||||
|
||||
for (int i = 0; i < pathLength; ++i)
|
||||
{
|
||||
BuildDirectoryMember next;
|
||||
if (!focus.TryGetMember(path[i], out next))
|
||||
{
|
||||
if (createMissing)
|
||||
{
|
||||
// This directory doesn't exist, create it...
|
||||
BuildDirectoryInfo di = new BuildDirectoryInfo(path[i], focus, _sectorSize);
|
||||
focus.Add(di);
|
||||
_dirs.Add(di);
|
||||
focus = di;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildDirectoryInfo nextAsBuildDirectoryInfo = next as BuildDirectoryInfo;
|
||||
if (nextAsBuildDirectoryInfo == null)
|
||||
{
|
||||
throw new IOException("File with conflicting name exists");
|
||||
}
|
||||
focus = nextAsBuildDirectoryInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return focus;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
using DiscUtils.Vfs;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for reading existing ISO images.
|
||||
/// </summary>
|
||||
public class CDReader : VfsFileSystemFacade, IClusterBasedFileSystem, IUnixFileSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CDReader class.
|
||||
/// </summary>
|
||||
/// <param name="data">The stream to read the ISO image from.</param>
|
||||
/// <param name="joliet">Whether to read Joliet extensions.</param>
|
||||
public CDReader(Stream data, bool joliet)
|
||||
: base(new VfsCDReader(data, joliet, false, 2048))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CDReader class.
|
||||
/// </summary>
|
||||
/// <param name="data">The stream to read the ISO image from.</param>
|
||||
/// <param name="joliet">Whether to read Joliet extensions.</param>
|
||||
/// <param name="hideVersions">Hides version numbers (e.g. ";1") from the end of files.</param>
|
||||
public CDReader(Stream data, bool joliet, bool hideVersions)
|
||||
: base(new VfsCDReader(data, joliet, hideVersions, 2048)) { }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CDReader class.
|
||||
/// </summary>
|
||||
/// <param name="data">The stream to read the ISO image from.</param>
|
||||
/// <param name="joliet">Whether to read Joliet extensions.</param>
|
||||
public CDReader(Stream data, bool joliet, int sectorSize)
|
||||
: base(new VfsCDReader(data, joliet, false, sectorSize)) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CDReader class.
|
||||
/// </summary>
|
||||
/// <param name="data">The stream to read the ISO image from.</param>
|
||||
/// <param name="joliet">Whether to read Joliet extensions.</param>
|
||||
/// <param name="hideVersions">Hides version numbers (e.g. ";1") from the end of files.</param>
|
||||
public CDReader(Stream data, bool joliet, bool hideVersions, int sectorSize)
|
||||
: base(new VfsCDReader(data, joliet, hideVersions, sectorSize)) {}
|
||||
|
||||
/// <summary>
|
||||
/// Gets which of the Iso9660 variants is being used.
|
||||
/// </summary>
|
||||
public Iso9660Variant ActiveVariant
|
||||
{
|
||||
get { return GetRealFileSystem<VfsCDReader>().ActiveVariant; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the emulation requested of BIOS when the image is loaded.
|
||||
/// </summary>
|
||||
public BootDeviceEmulation BootEmulation
|
||||
{
|
||||
get { return GetRealFileSystem<VfsCDReader>().BootEmulation; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute start position (in bytes) of the boot image, or zero if not found.
|
||||
/// </summary>
|
||||
public long BootImageStart
|
||||
{
|
||||
get { return GetRealFileSystem<VfsCDReader>().BootImageStart; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the memory segment the image should be loaded into (0 for default).
|
||||
/// </summary>
|
||||
public int BootLoadSegment
|
||||
{
|
||||
get { return GetRealFileSystem<VfsCDReader>().BootLoadSegment; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a boot image is present.
|
||||
/// </summary>
|
||||
public bool HasBootImage
|
||||
{
|
||||
get { return GetRealFileSystem<VfsCDReader>().HasBootImage; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size (in bytes) of each cluster.
|
||||
/// </summary>
|
||||
public long ClusterSize
|
||||
{
|
||||
get { return GetRealFileSystem<VfsCDReader>().ClusterSize; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of clusters managed by the file system.
|
||||
/// </summary>
|
||||
public long TotalClusters
|
||||
{
|
||||
get { return GetRealFileSystem<VfsCDReader>().TotalClusters; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a cluster (index) into an absolute byte position in the underlying stream.
|
||||
/// </summary>
|
||||
/// <param name="cluster">The cluster to convert.</param>
|
||||
/// <returns>The corresponding absolute byte position.</returns>
|
||||
public long ClusterToOffset(long cluster)
|
||||
{
|
||||
return GetRealFileSystem<VfsCDReader>().ClusterToOffset(cluster);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an absolute byte position in the underlying stream to a cluster (index).
|
||||
/// </summary>
|
||||
/// <param name="offset">The byte position to convert.</param>
|
||||
/// <returns>The cluster containing the specified byte.</returns>
|
||||
public long OffsetToCluster(long offset)
|
||||
{
|
||||
return GetRealFileSystem<VfsCDReader>().OffsetToCluster(offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a file name to the list of clusters occupied by the file's data.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to inspect.</param>
|
||||
/// <returns>The clusters.</returns>
|
||||
/// <remarks>Note that in some file systems, small files may not have dedicated
|
||||
/// clusters. Only dedicated clusters will be returned.</remarks>
|
||||
public Range<long, long>[] PathToClusters(string path)
|
||||
{
|
||||
return GetRealFileSystem<VfsCDReader>().PathToClusters(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a file name to the extents containing its data.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to inspect.</param>
|
||||
/// <returns>The file extents, as absolute byte positions in the underlying stream.</returns>
|
||||
/// <remarks>Use this method with caution - not all file systems will store all bytes
|
||||
/// directly in extents. Files may be compressed, sparse or encrypted. This method
|
||||
/// merely indicates where file data is stored, not what's stored.</remarks>
|
||||
public StreamExtent[] PathToExtents(string path)
|
||||
{
|
||||
return GetRealFileSystem<VfsCDReader>().PathToExtents(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that can convert between clusters and files.
|
||||
/// </summary>
|
||||
/// <returns>The cluster map.</returns>
|
||||
public ClusterMap BuildClusterMap()
|
||||
{
|
||||
return GetRealFileSystem<VfsCDReader>().BuildClusterMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves Unix-specific information about a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the file or directory.</param>
|
||||
/// <returns>Information about the owner, group, permissions and type of the
|
||||
/// file or directory.</returns>
|
||||
public UnixFileSystemInfo GetUnixFileInfo(string path)
|
||||
{
|
||||
return GetRealFileSystem<VfsCDReader>().GetUnixFileInfo(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects if a stream contains a valid ISO file system.
|
||||
/// </summary>
|
||||
/// <param name="data">The stream to inspect.</param>
|
||||
/// <returns><c>true</c> if the stream contains an ISO file system, else false.</returns>
|
||||
public static bool Detect(Stream data, int sectorSize)
|
||||
{
|
||||
byte[] buffer = new byte[sectorSize];
|
||||
|
||||
if (data.Length < 0x8000 + sectorSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.Position = 0x8000;
|
||||
int numRead = StreamUtilities.ReadMaximum(data, buffer, 0, sectorSize);
|
||||
if (numRead != sectorSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BaseVolumeDescriptor bvd = new BaseVolumeDescriptor(buffer, 0);
|
||||
|
||||
return bvd.StandardIdentifier == BaseVolumeDescriptor.Iso9660StandardIdentifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream containing the boot image.
|
||||
/// </summary>
|
||||
/// <returns>The boot image as a stream.</returns>
|
||||
public Stream OpenBootImage()
|
||||
{
|
||||
return GetRealFileSystem<VfsCDReader>().OpenBootImage();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{78933B2F-C754-4904-804E-D3932536471C}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>CHOVY_JUAN</RootNamespace>
|
||||
<AssemblyName>CHOVY-JUAN</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AligningStream.cs" />
|
||||
<Compile Include="ApplePartitionMap\BlockZero.cs" />
|
||||
<Compile Include="ApplePartitionMap\PartitionMap.cs" />
|
||||
<Compile Include="ApplePartitionMap\PartitionMapEntry.cs" />
|
||||
<Compile Include="ApplePartitionMap\PartitionMapFactory.cs" />
|
||||
<Compile Include="Archives\FileRecord.cs" />
|
||||
<Compile Include="Archives\TarFile.cs" />
|
||||
<Compile Include="Archives\TarFileBuilder.cs" />
|
||||
<Compile Include="Archives\TarHeader.cs" />
|
||||
<Compile Include="Archives\TarHeaderExtent.cs" />
|
||||
<Compile Include="Archives\UnixBuildFileRecord.cs" />
|
||||
<Compile Include="BaseVolumeDescriptor.cs" />
|
||||
<Compile Include="Block\Block.cs" />
|
||||
<Compile Include="Block\BlockCache.cs" />
|
||||
<Compile Include="Block\BlockCacheSettings.cs" />
|
||||
<Compile Include="Block\BlockCacheStatistics.cs" />
|
||||
<Compile Include="Block\BlockCacheStream.cs" />
|
||||
<Compile Include="BootDeviceEmulation.cs" />
|
||||
<Compile Include="BootInitialEntry.cs" />
|
||||
<Compile Include="BootValidationEntry.cs" />
|
||||
<Compile Include="BootVolumeDescriptor.cs" />
|
||||
<Compile Include="BootVolumeDescriptorRegion.cs" />
|
||||
<Compile Include="Buffer\Buffer.cs" />
|
||||
<Compile Include="Buffer\BufferStream.cs" />
|
||||
<Compile Include="Buffer\IBuffer.cs" />
|
||||
<Compile Include="Buffer\IMappedBuffer.cs" />
|
||||
<Compile Include="Buffer\SubBuffer.cs" />
|
||||
<Compile Include="BuildDirectoryInfo.cs" />
|
||||
<Compile Include="BuildDirectoryMember.cs" />
|
||||
<Compile Include="Builder\BuilderBufferExtent.cs" />
|
||||
<Compile Include="Builder\BuilderBufferExtentSource.cs" />
|
||||
<Compile Include="Builder\BuilderBytesExtent.cs" />
|
||||
<Compile Include="Builder\BuilderExtent.cs" />
|
||||
<Compile Include="Builder\BuilderExtentSource.cs" />
|
||||
<Compile Include="Builder\BuilderSparseStreamExtent.cs" />
|
||||
<Compile Include="Builder\BuilderStreamExtent.cs" />
|
||||
<Compile Include="Builder\BuilderStreamExtentSource.cs" />
|
||||
<Compile Include="Builder\PassthroughStreamBuilder.cs" />
|
||||
<Compile Include="Builder\StreamBuilder.cs" />
|
||||
<Compile Include="BuildFileInfo.cs" />
|
||||
<Compile Include="BuildParameters.cs" />
|
||||
<Compile Include="BuiltStream.cs" />
|
||||
<Compile Include="CDBuilder.cs" />
|
||||
<Compile Include="CDReader.cs" />
|
||||
<Compile Include="ChsAddress.cs" />
|
||||
<Compile Include="CircularStream.cs" />
|
||||
<Compile Include="ClusterMap.cs" />
|
||||
<Compile Include="ClusterRoles.cs" />
|
||||
<Compile Include="CommonVolumeDescriptor.cs" />
|
||||
<Compile Include="Compression\Adler32.cs" />
|
||||
<Compile Include="Compression\BigEndianBitStream.cs" />
|
||||
<Compile Include="Compression\BitStream.cs" />
|
||||
<Compile Include="Compression\BlockCompressor.cs" />
|
||||
<Compile Include="Compression\BZip2BlockDecoder.cs" />
|
||||
<Compile Include="Compression\BZip2CombinedHuffmanTrees.cs" />
|
||||
<Compile Include="Compression\BZip2DecoderStream.cs" />
|
||||
<Compile Include="Compression\BZip2Randomizer.cs" />
|
||||
<Compile Include="Compression\BZip2RleStream.cs" />
|
||||
<Compile Include="Compression\CompressionResult.cs" />
|
||||
<Compile Include="Compression\DataBlockTransform.cs" />
|
||||
<Compile Include="Compression\HuffmanTree.cs" />
|
||||
<Compile Include="Compression\InverseBurrowsWheeler.cs" />
|
||||
<Compile Include="Compression\MoveToFront.cs" />
|
||||
<Compile Include="Compression\SizedDeflateStream.cs" />
|
||||
<Compile Include="Compression\ZlibBuffer.cs" />
|
||||
<Compile Include="Compression\ZlibStream.cs" />
|
||||
<Compile Include="ConcatStream.cs" />
|
||||
<Compile Include="CoreCompat\EncodingHelper.cs" />
|
||||
<Compile Include="CoreCompat\ReflectionHelper.cs" />
|
||||
<Compile Include="CoreCompat\StringExtensions.cs" />
|
||||
<Compile Include="DirectoryExtent.cs" />
|
||||
<Compile Include="DirectoryRecord.cs" />
|
||||
<Compile Include="DiscDirectoryInfo.cs" />
|
||||
<Compile Include="DiscFileInfo.cs" />
|
||||
<Compile Include="DiscFileLocator.cs" />
|
||||
<Compile Include="DiscFileSystem.cs" />
|
||||
<Compile Include="DiscFileSystemChecker.cs" />
|
||||
<Compile Include="DiscFileSystemInfo.cs" />
|
||||
<Compile Include="DiscFileSystemOptions.cs" />
|
||||
<Compile Include="DiskImageBuilder.cs" />
|
||||
<Compile Include="DiskImageFileSpecification.cs" />
|
||||
<Compile Include="ExtentStream.cs" />
|
||||
<Compile Include="File.cs" />
|
||||
<Compile Include="FileExtent.cs" />
|
||||
<Compile Include="FileFlags.cs" />
|
||||
<Compile Include="FileLocator.cs" />
|
||||
<Compile Include="FileSystemInfo.cs" />
|
||||
<Compile Include="FileSystemManager.cs" />
|
||||
<Compile Include="FileSystemParameters.cs" />
|
||||
<Compile Include="FileTransport.cs" />
|
||||
<Compile Include="FloppyDiskType.cs" />
|
||||
<Compile Include="GenericDiskAdapterType.cs" />
|
||||
<Compile Include="Geometry.cs" />
|
||||
<Compile Include="GeometryCalculation.cs" />
|
||||
<Compile Include="GeometryTranslation.cs" />
|
||||
<Compile Include="IByteArraySerializable.cs" />
|
||||
<Compile Include="IClusterBasedFileSystem.cs" />
|
||||
<Compile Include="IDiagnosticTraceable.cs" />
|
||||
<Compile Include="IFileSystem.cs" />
|
||||
<Compile Include="Internal\Crc32.cs" />
|
||||
<Compile Include="Internal\Crc32Algorithm.cs" />
|
||||
<Compile Include="Internal\Crc32BigEndian.cs" />
|
||||
<Compile Include="Internal\Crc32LittleEndian.cs" />
|
||||
<Compile Include="Internal\LocalFileLocator.cs" />
|
||||
<Compile Include="Internal\LogicalVolumeFactory.cs" />
|
||||
<Compile Include="Internal\LogicalVolumeFactoryAttribute.cs" />
|
||||
<Compile Include="Internal\ObjectCache.cs" />
|
||||
<Compile Include="Internal\Utilities.cs" />
|
||||
<Compile Include="Internal\VirtualDiskFactory.cs" />
|
||||
<Compile Include="Internal\VirtualDiskFactoryAttribute.cs" />
|
||||
<Compile Include="Internal\VirtualDiskTransport.cs" />
|
||||
<Compile Include="Internal\VirtualDiskTransportAttribute.cs" />
|
||||
<Compile Include="InvalidFileSystemException.cs" />
|
||||
<Compile Include="Iso9660Variant.cs" />
|
||||
<Compile Include="IsoContext.cs" />
|
||||
<Compile Include="IsoUtilities.cs" />
|
||||
<Compile Include="IUnixFileSystem.cs" />
|
||||
<Compile Include="IWindowsFileSystem.cs" />
|
||||
<Compile Include="LengthWrappingStream.cs" />
|
||||
<Compile Include="LogicalDiskManager\ComponentRecord.cs" />
|
||||
<Compile Include="LogicalDiskManager\Database.cs" />
|
||||
<Compile Include="LogicalDiskManager\DatabaseHeader.cs" />
|
||||
<Compile Include="LogicalDiskManager\DatabaseRecord.cs" />
|
||||
<Compile Include="LogicalDiskManager\DiskGroupRecord.cs" />
|
||||
<Compile Include="LogicalDiskManager\DiskRecord.cs" />
|
||||
<Compile Include="LogicalDiskManager\DynamicDisk.cs" />
|
||||
<Compile Include="LogicalDiskManager\DynamicDiskGroup.cs" />
|
||||
<Compile Include="LogicalDiskManager\DynamicDiskManager.cs" />
|
||||
<Compile Include="LogicalDiskManager\DynamicDiskManagerFactory.cs" />
|
||||
<Compile Include="LogicalDiskManager\DynamicVolume.cs" />
|
||||
<Compile Include="LogicalDiskManager\ExtentMergeType.cs" />
|
||||
<Compile Include="LogicalDiskManager\ExtentRecord.cs" />
|
||||
<Compile Include="LogicalDiskManager\PrivateHeader.cs" />
|
||||
<Compile Include="LogicalDiskManager\RecordType.cs" />
|
||||
<Compile Include="LogicalDiskManager\TocBlock.cs" />
|
||||
<Compile Include="LogicalDiskManager\VolumeRecord.cs" />
|
||||
<Compile Include="LogicalVolumeInfo.cs" />
|
||||
<Compile Include="LogicalVolumeStatus.cs" />
|
||||
<Compile Include="MappedStream.cs" />
|
||||
<Compile Include="MirrorStream.cs" />
|
||||
<Compile Include="NativeFileSystem.cs" />
|
||||
<Compile Include="Partitions\BiosExtendedPartitionTable.cs" />
|
||||
<Compile Include="Partitions\BiosPartitionedDiskBuilder.cs" />
|
||||
<Compile Include="Partitions\BiosPartitionInfo.cs" />
|
||||
<Compile Include="Partitions\BiosPartitionRecord.cs" />
|
||||
<Compile Include="Partitions\BiosPartitionTable.cs" />
|
||||
<Compile Include="Partitions\BiosPartitionTypes.cs" />
|
||||
<Compile Include="Partitions\DefaultPartitionTableFactory.cs" />
|
||||
<Compile Include="Partitions\GptEntry.cs" />
|
||||
<Compile Include="Partitions\GptHeader.cs" />
|
||||
<Compile Include="Partitions\GuidPartitionInfo.cs" />
|
||||
<Compile Include="Partitions\GuidPartitionTable.cs" />
|
||||
<Compile Include="Partitions\GuidPartitionTypes.cs" />
|
||||
<Compile Include="Partitions\PartitionInfo.cs" />
|
||||
<Compile Include="Partitions\PartitionTable.cs" />
|
||||
<Compile Include="Partitions\PartitionTableFactory.cs" />
|
||||
<Compile Include="Partitions\PartitionTableFactoryAttribute.cs" />
|
||||
<Compile Include="Partitions\WellKnownPartitionType.cs" />
|
||||
<Compile Include="PathTable.cs" />
|
||||
<Compile Include="PathTableRecord.cs" />
|
||||
<Compile Include="PhysicalVolumeInfo.cs" />
|
||||
<Compile Include="PhysicalVolumeType.cs" />
|
||||
<Compile Include="Plist.cs" />
|
||||
<Compile Include="PositionWrappingStream.cs" />
|
||||
<Compile Include="PrimaryVolumeDescriptor.cs" />
|
||||
<Compile Include="PrimaryVolumeDescriptorRegion.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="PumpProgressEventArgs.cs" />
|
||||
<Compile Include="Raw\Disk.cs" />
|
||||
<Compile Include="Raw\DiskFactory.cs" />
|
||||
<Compile Include="Raw\DiskImageFile.cs" />
|
||||
<Compile Include="ReaderDirectory.cs" />
|
||||
<Compile Include="ReaderDirEntry.cs" />
|
||||
<Compile Include="ReaderWriter\BigEndianDataReader.cs" />
|
||||
<Compile Include="ReaderWriter\BigEndianDataWriter.cs" />
|
||||
<Compile Include="ReaderWriter\DataReader.cs" />
|
||||
<Compile Include="ReaderWriter\DataWriter.cs" />
|
||||
<Compile Include="ReaderWriter\LittleEndianDataReader.cs" />
|
||||
<Compile Include="ReadOnlyDiscFileSystem.cs" />
|
||||
<Compile Include="ReparsePoint.cs" />
|
||||
<Compile Include="ReportLevels.cs" />
|
||||
<Compile Include="RockRidge\ChildLinkSystemUseEntry.cs" />
|
||||
<Compile Include="RockRidge\FileTimeSystemUseEntry.cs" />
|
||||
<Compile Include="RockRidge\PosixFileInfoSystemUseEntry.cs" />
|
||||
<Compile Include="RockRidge\PosixNameSystemUseEntry.cs" />
|
||||
<Compile Include="RockRidge\RockRidgeExtension.cs" />
|
||||
<Compile Include="Setup\FileOpenEventArgs.cs" />
|
||||
<Compile Include="Setup\SetupHelper.cs" />
|
||||
<Compile Include="SnapshotStream.cs" />
|
||||
<Compile Include="SparseMemoryBuffer.cs" />
|
||||
<Compile Include="SparseMemoryStream.cs" />
|
||||
<Compile Include="SparseStream.cs" />
|
||||
<Compile Include="SparseStreamOpenDelegate.cs" />
|
||||
<Compile Include="StreamBuffer.cs" />
|
||||
<Compile Include="StreamExtent.cs" />
|
||||
<Compile Include="StreamPump.cs" />
|
||||
<Compile Include="StripedStream.cs" />
|
||||
<Compile Include="SubStream.cs" />
|
||||
<Compile Include="SupplementaryVolumeDescriptor.cs" />
|
||||
<Compile Include="SupplementaryVolumeDescriptorRegion.cs" />
|
||||
<Compile Include="Susp\ContinuationSystemUseEntry.cs" />
|
||||
<Compile Include="Susp\ExtensionSelectSystemUseEntry.cs" />
|
||||
<Compile Include="Susp\ExtensionSystemUseEntry.cs" />
|
||||
<Compile Include="Susp\GenericSuspExtension.cs" />
|
||||
<Compile Include="Susp\GenericSystemUseEntry.cs" />
|
||||
<Compile Include="Susp\PaddingSystemUseEntry.cs" />
|
||||
<Compile Include="Susp\SharingProtocolSystemUseEntry.cs" />
|
||||
<Compile Include="Susp\SuspExtension.cs" />
|
||||
<Compile Include="Susp\SuspRecords.cs" />
|
||||
<Compile Include="Susp\SystemUseEntry.cs" />
|
||||
<Compile Include="System\DateTimeOffsetExtensions.cs" />
|
||||
<Compile Include="System\ExtensionAttribute.cs" />
|
||||
<Compile Include="System\Func.cs" />
|
||||
<Compile Include="System\HashSet.cs" />
|
||||
<Compile Include="System\Tuple.cs" />
|
||||
<Compile Include="ThreadSafeStream.cs" />
|
||||
<Compile Include="TimeConverter.cs" />
|
||||
<Compile Include="UnixFilePermissions.cs" />
|
||||
<Compile Include="UnixFileSystemInfo.cs" />
|
||||
<Compile Include="UnixFileType.cs" />
|
||||
<Compile Include="Util\BitCounter.cs" />
|
||||
<Compile Include="Util\EndianUtilities.cs" />
|
||||
<Compile Include="Util\MathUtilities.cs" />
|
||||
<Compile Include="Util\Numbers.cs" />
|
||||
<Compile Include="Util\Ownership.cs" />
|
||||
<Compile Include="Util\Range.cs" />
|
||||
<Compile Include="Util\Sizes.cs" />
|
||||
<Compile Include="Util\StreamUtilities.cs" />
|
||||
<Compile Include="VfsCDReader.cs" />
|
||||
<Compile Include="Vfs\IVfsDirectory.cs" />
|
||||
<Compile Include="Vfs\IVfsFile.cs" />
|
||||
<Compile Include="Vfs\IVfsFileWithStreams.cs" />
|
||||
<Compile Include="Vfs\IVfsSymlink.cs" />
|
||||
<Compile Include="Vfs\VfsContext.cs" />
|
||||
<Compile Include="Vfs\VfsDirEntry.cs" />
|
||||
<Compile Include="Vfs\VfsFileSystem.cs" />
|
||||
<Compile Include="Vfs\VfsFileSystemFacade.cs" />
|
||||
<Compile Include="Vfs\VfsFileSystemFactory.cs" />
|
||||
<Compile Include="Vfs\VfsFileSystemFactoryAttribute.cs" />
|
||||
<Compile Include="Vfs\VfsFileSystemInfo.cs" />
|
||||
<Compile Include="Vfs\VfsFileSystemOpener.cs" />
|
||||
<Compile Include="Vfs\VfsReadOnlyFileSystem.cs" />
|
||||
<Compile Include="VirtualDisk.cs" />
|
||||
<Compile Include="VirtualDiskClass.cs" />
|
||||
<Compile Include="VirtualDiskExtent.cs" />
|
||||
<Compile Include="VirtualDiskLayer.cs" />
|
||||
<Compile Include="VirtualDiskManager.cs" />
|
||||
<Compile Include="VirtualDiskParameters.cs" />
|
||||
<Compile Include="VirtualDiskTypeInfo.cs" />
|
||||
<Compile Include="VolumeDescriptorDiskRegion.cs" />
|
||||
<Compile Include="VolumeDescriptorSetTerminator.cs" />
|
||||
<Compile Include="VolumeDescriptorSetTerminatorRegion.cs" />
|
||||
<Compile Include="VolumeDescriptorType.cs" />
|
||||
<Compile Include="VolumeInfo.cs" />
|
||||
<Compile Include="VolumeManager.cs" />
|
||||
<Compile Include="WindowsFileInformation.cs" />
|
||||
<Compile Include="WrappingMappedStream.cs" />
|
||||
<Compile Include="WrappingStream.cs" />
|
||||
<Compile Include="ZeroStream.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Class whose instances represent a CHS (Cylinder, Head, Sector) address on a disk.
|
||||
/// </summary>
|
||||
/// <remarks>Instances of this class are immutable.</remarks>
|
||||
public sealed class ChsAddress
|
||||
{
|
||||
/// <summary>
|
||||
/// The address of the first sector on any disk.
|
||||
/// </summary>
|
||||
public static readonly ChsAddress First = new ChsAddress(0, 0, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ChsAddress class.
|
||||
/// </summary>
|
||||
/// <param name="cylinder">The number of cylinders of the disk.</param>
|
||||
/// <param name="head">The number of heads (aka platters) of the disk.</param>
|
||||
/// <param name="sector">The number of sectors per track/cylinder of the disk.</param>
|
||||
public ChsAddress(int cylinder, int head, int sector)
|
||||
{
|
||||
Cylinder = cylinder;
|
||||
Head = head;
|
||||
Sector = sector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cylinder number (zero-based).
|
||||
/// </summary>
|
||||
public int Cylinder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the head (zero-based).
|
||||
/// </summary>
|
||||
public int Head { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sector number (one-based).
|
||||
/// </summary>
|
||||
public int Sector { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this object is equivalent to another.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test against.</param>
|
||||
/// <returns><c>true</c> if the <paramref name="obj"/> is equivalent, else <c>false</c>.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ChsAddress other = (ChsAddress)obj;
|
||||
|
||||
return Cylinder == other.Cylinder && Head == other.Head && Sector == other.Sector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the hash code for this object.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Cylinder.GetHashCode() ^ Head.GetHashCode() ^ Sector.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of this object, in the form (C/H/S).
|
||||
/// </summary>
|
||||
/// <returns>The string representation.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "(" + Cylinder + "/" + Head + "/" + Sector + ")";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// Copyright (c) 2008-2013, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a stream that is circular, so reads and writes off the end of the stream wrap.
|
||||
/// </summary>
|
||||
public sealed class CircularStream : WrappingStream
|
||||
{
|
||||
public CircularStream(SparseStream toWrap, Ownership ownership)
|
||||
: base(toWrap, ownership) {}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
WrapPosition();
|
||||
|
||||
int read = base.Read(buffer, offset, (int)Math.Min(Length - Position, count));
|
||||
|
||||
WrapPosition();
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
WrapPosition();
|
||||
|
||||
int totalWritten = 0;
|
||||
while (totalWritten < count)
|
||||
{
|
||||
int toWrite = (int)Math.Min(count - totalWritten, Length - Position);
|
||||
|
||||
base.Write(buffer, offset + totalWritten, toWrite);
|
||||
|
||||
WrapPosition();
|
||||
|
||||
totalWritten += toWrite;
|
||||
}
|
||||
}
|
||||
|
||||
private void WrapPosition()
|
||||
{
|
||||
long pos = Position;
|
||||
long length = Length;
|
||||
|
||||
if (pos >= length)
|
||||
{
|
||||
Position = pos % length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that identifies the role of each cluster in a file system.
|
||||
/// </summary>
|
||||
public sealed class ClusterMap
|
||||
{
|
||||
private readonly object[] _clusterToFileId;
|
||||
private readonly ClusterRoles[] _clusterToRole;
|
||||
private readonly Dictionary<object, string[]> _fileIdToPaths;
|
||||
|
||||
public ClusterMap(ClusterRoles[] clusterToRole, object[] clusterToFileId,
|
||||
Dictionary<object, string[]> fileIdToPaths)
|
||||
{
|
||||
_clusterToRole = clusterToRole;
|
||||
_clusterToFileId = clusterToFileId;
|
||||
_fileIdToPaths = fileIdToPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role of a cluster within the file system.
|
||||
/// </summary>
|
||||
/// <param name="cluster">The cluster to inspect.</param>
|
||||
/// <returns>The clusters role (or roles).</returns>
|
||||
public ClusterRoles GetRole(long cluster)
|
||||
{
|
||||
if (_clusterToRole == null || _clusterToRole.Length < cluster)
|
||||
{
|
||||
return ClusterRoles.None;
|
||||
}
|
||||
return _clusterToRole[cluster];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a cluster to a list of file names.
|
||||
/// </summary>
|
||||
/// <param name="cluster">The cluster to inspect.</param>
|
||||
/// <returns>A list of paths that map to the cluster.</returns>
|
||||
/// <remarks>A list is returned because on file systems with the notion of
|
||||
/// hard links, a cluster may correspond to multiple directory entries.</remarks>
|
||||
public string[] ClusterToPaths(long cluster)
|
||||
{
|
||||
if ((GetRole(cluster) & (ClusterRoles.DataFile | ClusterRoles.SystemFile)) != 0)
|
||||
{
|
||||
object fileId = _clusterToFileId[cluster];
|
||||
return _fileIdToPaths[fileId];
|
||||
}
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of possible cluster roles.
|
||||
/// </summary>
|
||||
/// <remarks>A cluster may be in more than one role.</remarks>
|
||||
[Flags]
|
||||
public enum ClusterRoles
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown, or unspecified role.
|
||||
/// </summary>
|
||||
None = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// Cluster is free.
|
||||
/// </summary>
|
||||
Free = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Cluster is in use by a normal file.
|
||||
/// </summary>
|
||||
DataFile = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Cluster is in use by a system file.
|
||||
/// </summary>
|
||||
/// <remarks>This isn't a file marked with the 'system' attribute,
|
||||
/// rather files that form part of the file system namespace but also
|
||||
/// form part of the file system meta-data.</remarks>
|
||||
SystemFile = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// Cluster is in use for meta-data.
|
||||
/// </summary>
|
||||
Metadata = 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// Cluster contains the boot region.
|
||||
/// </summary>
|
||||
BootArea = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// Cluster is marked bad.
|
||||
/// </summary>
|
||||
Bad = 0x20
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using DiscUtils.Internal;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class CommonVolumeDescriptor : BaseVolumeDescriptor
|
||||
{
|
||||
public string AbstractFileIdentifier;
|
||||
public string ApplicationIdentifier;
|
||||
public string BibliographicFileIdentifier;
|
||||
public Encoding CharacterEncoding;
|
||||
public string CopyrightFileIdentifier;
|
||||
public DateTime CreationDateAndTime;
|
||||
public string DataPreparerIdentifier;
|
||||
public DateTime EffectiveDateAndTime;
|
||||
public DateTime ExpirationDateAndTime;
|
||||
public byte FileStructureVersion;
|
||||
public ushort LogicalBlockSize;
|
||||
public DateTime ModificationDateAndTime;
|
||||
public uint OptionalTypeLPathTableLocation;
|
||||
public uint OptionalTypeMPathTableLocation;
|
||||
public uint PathTableSize;
|
||||
public string PublisherIdentifier;
|
||||
public DirectoryRecord RootDirectory;
|
||||
|
||||
public string SystemIdentifier;
|
||||
public uint TypeLPathTableLocation;
|
||||
public uint TypeMPathTableLocation;
|
||||
public string VolumeIdentifier;
|
||||
public ushort VolumeSequenceNumber;
|
||||
public string VolumeSetIdentifier;
|
||||
public ushort VolumeSetSize;
|
||||
public uint VolumeSpaceSize;
|
||||
|
||||
public CommonVolumeDescriptor(byte[] src, int offset, Encoding enc)
|
||||
: base(src, offset)
|
||||
{
|
||||
CharacterEncoding = enc;
|
||||
|
||||
SystemIdentifier = IsoUtilities.ReadChars(src, offset + 8, 32, CharacterEncoding);
|
||||
VolumeIdentifier = IsoUtilities.ReadChars(src, offset + 40, 32, CharacterEncoding);
|
||||
VolumeSpaceSize = IsoUtilities.ToUInt32FromBoth(src, offset + 80);
|
||||
VolumeSetSize = IsoUtilities.ToUInt16FromBoth(src, offset + 120);
|
||||
VolumeSequenceNumber = IsoUtilities.ToUInt16FromBoth(src, offset + 124);
|
||||
LogicalBlockSize = IsoUtilities.ToUInt16FromBoth(src, offset + 128);
|
||||
PathTableSize = IsoUtilities.ToUInt32FromBoth(src, offset + 132);
|
||||
TypeLPathTableLocation = EndianUtilities.ToUInt32LittleEndian(src, offset + 140);
|
||||
OptionalTypeLPathTableLocation = EndianUtilities.ToUInt32LittleEndian(src, offset + 144);
|
||||
TypeMPathTableLocation = Utilities.BitSwap(EndianUtilities.ToUInt32LittleEndian(src, offset + 148));
|
||||
OptionalTypeMPathTableLocation = Utilities.BitSwap(EndianUtilities.ToUInt32LittleEndian(src, offset + 152));
|
||||
DirectoryRecord.ReadFrom(src, offset + 156, CharacterEncoding, out RootDirectory);
|
||||
VolumeSetIdentifier = IsoUtilities.ReadChars(src, offset + 190, 318 - 190, CharacterEncoding);
|
||||
PublisherIdentifier = IsoUtilities.ReadChars(src, offset + 318, 446 - 318, CharacterEncoding);
|
||||
DataPreparerIdentifier = IsoUtilities.ReadChars(src, offset + 446, 574 - 446, CharacterEncoding);
|
||||
ApplicationIdentifier = IsoUtilities.ReadChars(src, offset + 574, 702 - 574, CharacterEncoding);
|
||||
CopyrightFileIdentifier = IsoUtilities.ReadChars(src, offset + 702, 739 - 702, CharacterEncoding);
|
||||
AbstractFileIdentifier = IsoUtilities.ReadChars(src, offset + 739, 776 - 739, CharacterEncoding);
|
||||
BibliographicFileIdentifier = IsoUtilities.ReadChars(src, offset + 776, 813 - 776, CharacterEncoding);
|
||||
CreationDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 813);
|
||||
ModificationDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 830);
|
||||
ExpirationDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 847);
|
||||
EffectiveDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 864);
|
||||
FileStructureVersion = src[offset + 881];
|
||||
}
|
||||
|
||||
public CommonVolumeDescriptor(
|
||||
VolumeDescriptorType type,
|
||||
byte version,
|
||||
uint volumeSpaceSize,
|
||||
uint pathTableSize,
|
||||
uint typeLPathTableLocation,
|
||||
uint typeMPathTableLocation,
|
||||
uint rootDirExtentLocation,
|
||||
uint rootDirDataLength,
|
||||
DateTime buildTime,
|
||||
Encoding enc,
|
||||
int sectorSize)
|
||||
: base(type, version, sectorSize)
|
||||
{
|
||||
CharacterEncoding = enc;
|
||||
|
||||
SystemIdentifier = string.Empty;
|
||||
VolumeIdentifier = string.Empty;
|
||||
VolumeSpaceSize = volumeSpaceSize;
|
||||
VolumeSetSize = 1;
|
||||
VolumeSequenceNumber = 1;
|
||||
LogicalBlockSize = (ushort)sectorSize;
|
||||
PathTableSize = pathTableSize;
|
||||
TypeLPathTableLocation = typeLPathTableLocation;
|
||||
////OptionalTypeLPathTableLocation = 0;
|
||||
TypeMPathTableLocation = typeMPathTableLocation;
|
||||
////OptionalTypeMPathTableLocation = 0;
|
||||
RootDirectory = new DirectoryRecord();
|
||||
RootDirectory.ExtendedAttributeRecordLength = 0;
|
||||
RootDirectory.LocationOfExtent = rootDirExtentLocation;
|
||||
RootDirectory.DataLength = rootDirDataLength;
|
||||
RootDirectory.RecordingDateAndTime = buildTime;
|
||||
RootDirectory.Flags = FileFlags.Directory;
|
||||
RootDirectory.FileUnitSize = 0;
|
||||
RootDirectory.InterleaveGapSize = 0;
|
||||
RootDirectory.VolumeSequenceNumber = 1;
|
||||
RootDirectory.FileIdentifier = "\0";
|
||||
VolumeSetIdentifier = string.Empty;
|
||||
PublisherIdentifier = string.Empty;
|
||||
DataPreparerIdentifier = string.Empty;
|
||||
ApplicationIdentifier = string.Empty;
|
||||
CopyrightFileIdentifier = string.Empty;
|
||||
AbstractFileIdentifier = string.Empty;
|
||||
BibliographicFileIdentifier = string.Empty;
|
||||
CreationDateAndTime = buildTime;
|
||||
ModificationDateAndTime = buildTime;
|
||||
ExpirationDateAndTime = DateTime.MinValue;
|
||||
EffectiveDateAndTime = buildTime;
|
||||
FileStructureVersion = 1; // V1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of the Adler-32 checksum algorithm.
|
||||
/// </summary>
|
||||
public class Adler32
|
||||
{
|
||||
private uint _a;
|
||||
private uint _b;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Adler32 class.
|
||||
/// </summary>
|
||||
public Adler32()
|
||||
{
|
||||
_a = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the checksum of all data processed so far.
|
||||
/// </summary>
|
||||
public int Value
|
||||
{
|
||||
get { return (int)(_b << 16 | _a); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides data that should be checksummed.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer containing the data to checksum.</param>
|
||||
/// <param name="offset">Offset of the first byte to checksum.</param>
|
||||
/// <param name="count">The number of bytes to checksum.</param>
|
||||
/// <remarks>
|
||||
/// Call this method repeatedly until all checksummed
|
||||
/// data has been processed.
|
||||
/// </remarks>
|
||||
public void Process(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > buffer.Length)
|
||||
{
|
||||
throw new ArgumentException("Offset outside of array bounds", nameof(offset));
|
||||
}
|
||||
|
||||
if (count < 0 || offset + count > buffer.Length)
|
||||
{
|
||||
throw new ArgumentException("Array index out of bounds", nameof(count));
|
||||
}
|
||||
|
||||
int processed = 0;
|
||||
while (processed < count)
|
||||
{
|
||||
int innerEnd = Math.Min(count, processed + 2000);
|
||||
while (processed < innerEnd)
|
||||
{
|
||||
_a += buffer[processed++];
|
||||
_b += _a;
|
||||
}
|
||||
|
||||
_a %= 65521;
|
||||
_b %= 65521;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
//
|
||||
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal class BZip2BlockDecoder
|
||||
{
|
||||
private readonly InverseBurrowsWheeler _inverseBurrowsWheeler;
|
||||
|
||||
public BZip2BlockDecoder(int blockSize)
|
||||
{
|
||||
_inverseBurrowsWheeler = new InverseBurrowsWheeler(blockSize);
|
||||
}
|
||||
|
||||
public uint Crc { get; private set; }
|
||||
|
||||
public int Process(BitStream bitstream, byte[] outputBuffer, int outputBufferOffset)
|
||||
{
|
||||
Crc = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
Crc = (Crc << 8) | bitstream.Read(8);
|
||||
}
|
||||
|
||||
bool rand = bitstream.Read(1) != 0;
|
||||
int origPtr = (int)bitstream.Read(24);
|
||||
|
||||
int thisBlockSize = ReadBuffer(bitstream, outputBuffer, outputBufferOffset);
|
||||
|
||||
_inverseBurrowsWheeler.OriginalIndex = origPtr;
|
||||
_inverseBurrowsWheeler.Process(outputBuffer, outputBufferOffset, thisBlockSize, outputBuffer,
|
||||
outputBufferOffset);
|
||||
|
||||
if (rand)
|
||||
{
|
||||
BZip2Randomizer randomizer = new BZip2Randomizer();
|
||||
randomizer.Process(outputBuffer, outputBufferOffset, thisBlockSize, outputBuffer, outputBufferOffset);
|
||||
}
|
||||
|
||||
return thisBlockSize;
|
||||
}
|
||||
|
||||
private static int ReadBuffer(BitStream bitstream, byte[] buffer, int offset)
|
||||
{
|
||||
// The MTF state
|
||||
int numInUse = 0;
|
||||
MoveToFront moveFrontTransform = new MoveToFront();
|
||||
bool[] inUseGroups = new bool[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
inUseGroups[i] = bitstream.Read(1) != 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
if (inUseGroups[i / 16])
|
||||
{
|
||||
if (bitstream.Read(1) != 0)
|
||||
{
|
||||
moveFrontTransform.Set(numInUse, (byte)i);
|
||||
numInUse++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize 'virtual' Huffman tree from bitstream
|
||||
BZip2CombinedHuffmanTrees huffmanTree = new BZip2CombinedHuffmanTrees(bitstream, numInUse + 2);
|
||||
|
||||
// Main loop reading data
|
||||
int readBytes = 0;
|
||||
while (true)
|
||||
{
|
||||
uint symbol = huffmanTree.NextSymbol();
|
||||
|
||||
if (symbol < 2)
|
||||
{
|
||||
// RLE, with length stored in a binary-style format
|
||||
uint runLength = 0;
|
||||
int bitShift = 0;
|
||||
while (symbol < 2)
|
||||
{
|
||||
runLength += (symbol + 1) << bitShift;
|
||||
bitShift++;
|
||||
|
||||
symbol = huffmanTree.NextSymbol();
|
||||
}
|
||||
|
||||
byte b = moveFrontTransform.Head;
|
||||
while (runLength > 0)
|
||||
{
|
||||
buffer[offset + readBytes] = b;
|
||||
++readBytes;
|
||||
--runLength;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol <= numInUse)
|
||||
{
|
||||
// Single byte
|
||||
byte b = moveFrontTransform.GetAndMove((int)symbol - 1);
|
||||
buffer[offset + readBytes] = b;
|
||||
++readBytes;
|
||||
}
|
||||
else if (symbol == numInUse + 1)
|
||||
{
|
||||
// End of block marker
|
||||
return readBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("Invalid symbol from Huffman table");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
//
|
||||
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents scheme used by BZip2 where multiple Huffman trees are used as a
|
||||
/// virtual Huffman tree, with a logical selector every 50 bits in the bit stream.
|
||||
/// </summary>
|
||||
internal class BZip2CombinedHuffmanTrees
|
||||
{
|
||||
private HuffmanTree _activeTree;
|
||||
private readonly BitStream _bitstream;
|
||||
private int _nextSelector;
|
||||
private byte[] _selectors;
|
||||
private int _symbolsToNextSelector;
|
||||
private HuffmanTree[] _trees;
|
||||
|
||||
public BZip2CombinedHuffmanTrees(BitStream bitstream, int maxSymbols)
|
||||
{
|
||||
_bitstream = bitstream;
|
||||
|
||||
Initialize(maxSymbols);
|
||||
}
|
||||
|
||||
public uint NextSymbol()
|
||||
{
|
||||
if (_symbolsToNextSelector == 0)
|
||||
{
|
||||
_symbolsToNextSelector = 50;
|
||||
_activeTree = _trees[_selectors[_nextSelector]];
|
||||
_nextSelector++;
|
||||
}
|
||||
|
||||
_symbolsToNextSelector--;
|
||||
|
||||
return _activeTree.NextSymbol(_bitstream);
|
||||
}
|
||||
|
||||
private void Initialize(int maxSymbols)
|
||||
{
|
||||
int numTrees = (int)_bitstream.Read(3);
|
||||
if (numTrees < 2 || numTrees > 6)
|
||||
{
|
||||
throw new InvalidDataException("Invalid number of tables");
|
||||
}
|
||||
|
||||
int numSelectors = (int)_bitstream.Read(15);
|
||||
if (numSelectors < 1)
|
||||
{
|
||||
throw new InvalidDataException("Invalid number of selectors");
|
||||
}
|
||||
|
||||
_selectors = new byte[numSelectors];
|
||||
MoveToFront mtf = new MoveToFront(numTrees, true);
|
||||
for (int i = 0; i < numSelectors; ++i)
|
||||
{
|
||||
_selectors[i] = mtf.GetAndMove(CountSetBits(numTrees));
|
||||
}
|
||||
|
||||
_trees = new HuffmanTree[numTrees];
|
||||
for (int t = 0; t < numTrees; ++t)
|
||||
{
|
||||
uint[] lengths = new uint[maxSymbols];
|
||||
|
||||
uint len = _bitstream.Read(5);
|
||||
for (int i = 0; i < maxSymbols; ++i)
|
||||
{
|
||||
if (len < 1 || len > 20)
|
||||
{
|
||||
throw new InvalidDataException("Invalid length constructing Huffman tree");
|
||||
}
|
||||
|
||||
while (_bitstream.Read(1) != 0)
|
||||
{
|
||||
len = _bitstream.Read(1) == 0 ? len + 1 : len - 1;
|
||||
|
||||
if (len < 1 || len > 20)
|
||||
{
|
||||
throw new InvalidDataException("Invalid length constructing Huffman tree");
|
||||
}
|
||||
}
|
||||
|
||||
lengths[i] = len;
|
||||
}
|
||||
|
||||
_trees[t] = new HuffmanTree(lengths);
|
||||
}
|
||||
|
||||
_symbolsToNextSelector = 0;
|
||||
_nextSelector = 0;
|
||||
}
|
||||
|
||||
private byte CountSetBits(int max)
|
||||
{
|
||||
byte val = 0;
|
||||
while (_bitstream.Read(1) != 0)
|
||||
{
|
||||
val++;
|
||||
if (val >= max)
|
||||
{
|
||||
throw new InvalidDataException("Exceeded max number of consecutive bits");
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,348 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
//
|
||||
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Internal;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of a BZip2 decoder.
|
||||
/// </summary>
|
||||
public sealed class BZip2DecoderStream : Stream
|
||||
{
|
||||
private readonly BitStream _bitstream;
|
||||
|
||||
private readonly byte[] _blockBuffer;
|
||||
private uint _blockCrc;
|
||||
private readonly BZip2BlockDecoder _blockDecoder;
|
||||
private Crc32 _calcBlockCrc;
|
||||
private uint _calcCompoundCrc;
|
||||
private uint _compoundCrc;
|
||||
private Stream _compressedStream;
|
||||
|
||||
private bool _eof;
|
||||
private readonly Ownership _ownsCompressed;
|
||||
private long _position;
|
||||
private BZip2RleStream _rleStream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BZip2DecoderStream class.
|
||||
/// </summary>
|
||||
/// <param name="stream">The compressed input stream.</param>
|
||||
/// <param name="ownsStream">Whether ownership of stream passes to the new instance.</param>
|
||||
public BZip2DecoderStream(Stream stream, Ownership ownsStream)
|
||||
{
|
||||
_compressedStream = stream;
|
||||
_ownsCompressed = ownsStream;
|
||||
|
||||
_bitstream = new BigEndianBitStream(new BufferedStream(stream));
|
||||
|
||||
// The Magic BZh
|
||||
byte[] magic = new byte[3];
|
||||
magic[0] = (byte)_bitstream.Read(8);
|
||||
magic[1] = (byte)_bitstream.Read(8);
|
||||
magic[2] = (byte)_bitstream.Read(8);
|
||||
if (magic[0] != 0x42 || magic[1] != 0x5A || magic[2] != 0x68)
|
||||
{
|
||||
throw new InvalidDataException("Bad magic at start of stream");
|
||||
}
|
||||
|
||||
// The size of the decompression blocks in multiples of 100,000
|
||||
int blockSize = (int)_bitstream.Read(8) - 0x30;
|
||||
if (blockSize < 1 || blockSize > 9)
|
||||
{
|
||||
throw new InvalidDataException("Unexpected block size in header: " + blockSize);
|
||||
}
|
||||
|
||||
blockSize *= 100000;
|
||||
|
||||
_rleStream = new BZip2RleStream();
|
||||
_blockDecoder = new BZip2BlockDecoder(blockSize);
|
||||
_blockBuffer = new byte[blockSize];
|
||||
|
||||
if (ReadBlock() == 0)
|
||||
{
|
||||
_eof = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication of whether read access is permitted.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication of whether seeking is permitted.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication of whether write access is permitted.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the stream (the capacity of the underlying buffer).
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the current position within the stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _position; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all data to the underlying storage.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a number of bytes from the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The destination buffer.</param>
|
||||
/// <param name="offset">The start offset within the destination buffer.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The number of bytes read.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (buffer.Length < offset + count)
|
||||
{
|
||||
throw new ArgumentException("Buffer smaller than declared");
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
throw new ArgumentException("Offset less than zero", nameof(offset));
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentException("Count less than zero", nameof(count));
|
||||
}
|
||||
|
||||
if (_eof)
|
||||
{
|
||||
throw new IOException("Attempt to read beyond end of stream");
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int numRead = _rleStream.Read(buffer, offset, count);
|
||||
if (numRead == 0)
|
||||
{
|
||||
// If there was an existing block, check it's crc.
|
||||
if (_calcBlockCrc != null)
|
||||
{
|
||||
if (_blockCrc != _calcBlockCrc.Value)
|
||||
{
|
||||
throw new InvalidDataException("Decompression failed - block CRC mismatch");
|
||||
}
|
||||
|
||||
_calcCompoundCrc = ((_calcCompoundCrc << 1) | (_calcCompoundCrc >> 31)) ^ _blockCrc;
|
||||
}
|
||||
|
||||
// Read a new block (if any), if none - check the overall CRC before returning
|
||||
if (ReadBlock() == 0)
|
||||
{
|
||||
_eof = true;
|
||||
if (_calcCompoundCrc != _compoundCrc)
|
||||
{
|
||||
throw new InvalidDataException("Decompression failed - compound CRC");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
numRead = _rleStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
_calcBlockCrc.Process(buffer, offset, numRead);
|
||||
|
||||
// Pre-read next block, so a client that knows the decompressed length will still
|
||||
// have the overall CRC calculated.
|
||||
if (_rleStream.AtEof)
|
||||
{
|
||||
// If there was an existing block, check it's crc.
|
||||
if (_calcBlockCrc != null)
|
||||
{
|
||||
if (_blockCrc != _calcBlockCrc.Value)
|
||||
{
|
||||
throw new InvalidDataException("Decompression failed - block CRC mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
_calcCompoundCrc = ((_calcCompoundCrc << 1) | (_calcCompoundCrc >> 31)) ^ _blockCrc;
|
||||
if (ReadBlock() == 0)
|
||||
{
|
||||
_eof = true;
|
||||
if (_calcCompoundCrc != _compoundCrc)
|
||||
{
|
||||
throw new InvalidDataException("Decompression failed - compound CRC mismatch");
|
||||
}
|
||||
|
||||
return numRead;
|
||||
}
|
||||
}
|
||||
|
||||
_position += numRead;
|
||||
return numRead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the current stream position.
|
||||
/// </summary>
|
||||
/// <param name="offset">The origin-relative stream position.</param>
|
||||
/// <param name="origin">The origin for the stream position.</param>
|
||||
/// <returns>The new stream position.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the stream (the underlying buffer's capacity).
|
||||
/// </summary>
|
||||
/// <param name="value">The new length of the stream.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a buffer to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to write.</param>
|
||||
/// <param name="offset">The starting offset within buffer.</param>
|
||||
/// <param name="count">The number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases underlying resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Whether this method is called from Dispose.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_compressedStream != null && _ownsCompressed == Ownership.Dispose)
|
||||
{
|
||||
_compressedStream.Dispose();
|
||||
}
|
||||
|
||||
_compressedStream = null;
|
||||
|
||||
if (_rleStream != null)
|
||||
{
|
||||
_rleStream.Dispose();
|
||||
_rleStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
private int ReadBlock()
|
||||
{
|
||||
ulong marker = ReadMarker();
|
||||
if (marker == 0x314159265359)
|
||||
{
|
||||
int blockSize = _blockDecoder.Process(_bitstream, _blockBuffer, 0);
|
||||
_rleStream.Reset(_blockBuffer, 0, blockSize);
|
||||
_blockCrc = _blockDecoder.Crc;
|
||||
_calcBlockCrc = new Crc32BigEndian(Crc32Algorithm.Common);
|
||||
return blockSize;
|
||||
}
|
||||
if (marker == 0x177245385090)
|
||||
{
|
||||
_compoundCrc = ReadUint();
|
||||
return 0;
|
||||
}
|
||||
throw new InvalidDataException("Found invalid marker in stream");
|
||||
}
|
||||
|
||||
private uint ReadUint()
|
||||
{
|
||||
uint val = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
val = (val << 8) | _bitstream.Read(8);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private ulong ReadMarker()
|
||||
{
|
||||
ulong marker = 0;
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
marker = (marker << 8) | _bitstream.Read(8);
|
||||
}
|
||||
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
//
|
||||
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal class BZip2Randomizer : DataBlockTransform
|
||||
{
|
||||
private static readonly int[] RandomVals =
|
||||
{
|
||||
619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
|
||||
985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
|
||||
733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
|
||||
419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
|
||||
878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
|
||||
862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
|
||||
150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
|
||||
170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
|
||||
73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
|
||||
909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
|
||||
641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
|
||||
161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
|
||||
382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
|
||||
98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
|
||||
227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
|
||||
469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
|
||||
184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
|
||||
715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
|
||||
951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
|
||||
652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
|
||||
645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
|
||||
609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
|
||||
653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
|
||||
411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
|
||||
170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
|
||||
857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
|
||||
669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
|
||||
944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
|
||||
344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
|
||||
897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
|
||||
433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
|
||||
686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
|
||||
946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
|
||||
978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
|
||||
680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
|
||||
707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
|
||||
297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
|
||||
134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
|
||||
343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
|
||||
140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
|
||||
170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
|
||||
369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
|
||||
804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
|
||||
896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
|
||||
661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
|
||||
768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
|
||||
61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
|
||||
372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
|
||||
780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
|
||||
920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
|
||||
645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
|
||||
936, 638
|
||||
};
|
||||
|
||||
protected override bool BuffersMustNotOverlap
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
protected override int DoProcess(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
|
||||
{
|
||||
if (input != output || inputOffset != outputOffset)
|
||||
{
|
||||
Array.Copy(input, inputOffset, output, outputOffset, inputCount);
|
||||
}
|
||||
|
||||
int randIndex = 1;
|
||||
int nextByte = RandomVals[0] - 2;
|
||||
|
||||
while (nextByte < inputCount)
|
||||
{
|
||||
output[nextByte] ^= 1;
|
||||
nextByte += RandomVals[randIndex++];
|
||||
randIndex &= 0x1FF;
|
||||
}
|
||||
|
||||
return inputCount;
|
||||
}
|
||||
|
||||
protected override int MaxOutputCount(int inputCount)
|
||||
{
|
||||
return inputCount;
|
||||
}
|
||||
|
||||
protected override int MinOutputCount(int inputCount)
|
||||
{
|
||||
return inputCount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
//
|
||||
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal class BZip2RleStream : Stream
|
||||
{
|
||||
private byte[] _blockBuffer;
|
||||
private int _blockOffset;
|
||||
private int _blockRemaining;
|
||||
private byte _lastByte;
|
||||
|
||||
private int _numSame;
|
||||
private long _position;
|
||||
private int _runBytesOutstanding;
|
||||
|
||||
public bool AtEof
|
||||
{
|
||||
get { return _runBytesOutstanding == 0 && _blockRemaining == 0; }
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _position; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public void Reset(byte[] buffer, int offset, int count)
|
||||
{
|
||||
_position = 0;
|
||||
_blockBuffer = buffer;
|
||||
_blockOffset = offset;
|
||||
_blockRemaining = count;
|
||||
_numSame = -1;
|
||||
_lastByte = 0;
|
||||
_runBytesOutstanding = 0;
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int numRead = 0;
|
||||
|
||||
while (numRead < count && _runBytesOutstanding > 0)
|
||||
{
|
||||
int runCount = Math.Min(_runBytesOutstanding, count);
|
||||
for (int i = 0; i < runCount; ++i)
|
||||
{
|
||||
buffer[offset + numRead] = _lastByte;
|
||||
}
|
||||
|
||||
_runBytesOutstanding -= runCount;
|
||||
numRead += runCount;
|
||||
}
|
||||
|
||||
while (numRead < count && _blockRemaining > 0)
|
||||
{
|
||||
byte b = _blockBuffer[_blockOffset];
|
||||
++_blockOffset;
|
||||
--_blockRemaining;
|
||||
|
||||
if (_numSame == 4)
|
||||
{
|
||||
int runCount = Math.Min(b, count - numRead);
|
||||
for (int i = 0; i < runCount; ++i)
|
||||
{
|
||||
buffer[offset + numRead] = _lastByte;
|
||||
numRead++;
|
||||
}
|
||||
|
||||
_runBytesOutstanding = b - runCount;
|
||||
_numSame = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b != _lastByte || _numSame <= 0)
|
||||
{
|
||||
_lastByte = b;
|
||||
_numSame = 0;
|
||||
}
|
||||
|
||||
buffer[offset + numRead] = b;
|
||||
numRead++;
|
||||
_numSame++;
|
||||
}
|
||||
}
|
||||
|
||||
_position += numRead;
|
||||
return numRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a byte stream into a bit stream.
|
||||
/// </summary>
|
||||
internal class BigEndianBitStream : BitStream
|
||||
{
|
||||
private uint _buffer;
|
||||
private int _bufferAvailable;
|
||||
private readonly Stream _byteStream;
|
||||
|
||||
private readonly byte[] _readBuffer = new byte[2];
|
||||
|
||||
public BigEndianBitStream(Stream byteStream)
|
||||
{
|
||||
_byteStream = byteStream;
|
||||
}
|
||||
|
||||
public override int MaxReadAhead
|
||||
{
|
||||
get { return 16; }
|
||||
}
|
||||
|
||||
public override uint Read(int count)
|
||||
{
|
||||
if (count > 16)
|
||||
{
|
||||
uint result = Read(16) << (count - 16);
|
||||
return result | Read(count - 16);
|
||||
}
|
||||
|
||||
EnsureBufferFilled();
|
||||
|
||||
_bufferAvailable -= count;
|
||||
|
||||
uint mask = (uint)((1 << count) - 1);
|
||||
|
||||
return (_buffer >> _bufferAvailable) & mask;
|
||||
}
|
||||
|
||||
public override uint Peek(int count)
|
||||
{
|
||||
EnsureBufferFilled();
|
||||
|
||||
uint mask = (uint)((1 << count) - 1);
|
||||
|
||||
return (_buffer >> (_bufferAvailable - count)) & mask;
|
||||
}
|
||||
|
||||
public override void Consume(int count)
|
||||
{
|
||||
EnsureBufferFilled();
|
||||
|
||||
_bufferAvailable -= count;
|
||||
}
|
||||
|
||||
private void EnsureBufferFilled()
|
||||
{
|
||||
if (_bufferAvailable < 16)
|
||||
{
|
||||
_readBuffer[0] = 0;
|
||||
_readBuffer[1] = 0;
|
||||
_byteStream.Read(_readBuffer, 0, 2);
|
||||
|
||||
_buffer = _buffer << 16 | (uint)(_readBuffer[0] << 8) | _readBuffer[1];
|
||||
_bufferAvailable += 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for bit streams.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The rules for conversion of a byte stream to a bit stream vary
|
||||
/// between implementations.
|
||||
/// </remarks>
|
||||
internal abstract class BitStream
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the maximum number of bits that can be peeked on the stream.
|
||||
/// </summary>
|
||||
public abstract int MaxReadAhead { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads bits from the stream.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bits to read.</param>
|
||||
/// <returns>The bits as a UInt32.</returns>
|
||||
public abstract uint Read(int count);
|
||||
|
||||
/// <summary>
|
||||
/// Queries data from the stream.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bits to query.</param>
|
||||
/// <returns>The bits as a UInt32.</returns>
|
||||
/// <remarks>This method does not consume the bits (i.e. move the file pointer).</remarks>
|
||||
public abstract uint Peek(int count);
|
||||
|
||||
/// <summary>
|
||||
/// Consumes bits from the stream without returning them.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bits to consume.</param>
|
||||
public abstract void Consume(int count);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for block compression algorithms.
|
||||
/// </summary>
|
||||
public abstract class BlockCompressor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the block size parameter to the algorithm.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some algorithms may use this to control both compression and decompression, others may
|
||||
/// only use it to control compression. Some may ignore it entirely.
|
||||
/// </remarks>
|
||||
public int BlockSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Compresses some data.
|
||||
/// </summary>
|
||||
/// <param name="source">The uncompressed input.</param>
|
||||
/// <param name="sourceOffset">Offset of the input data in <c>source</c>.</param>
|
||||
/// <param name="sourceLength">The amount of uncompressed data.</param>
|
||||
/// <param name="compressed">The destination for the output compressed data.</param>
|
||||
/// <param name="compressedOffset">Offset for the output data in <c>compressed</c>.</param>
|
||||
/// <param name="compressedLength">The maximum size of the compressed data on input, and the actual size on output.</param>
|
||||
/// <returns>Indication of success, or indication the data could not compress into the requested space.</returns>
|
||||
public abstract CompressionResult Compress(byte[] source, int sourceOffset, int sourceLength, byte[] compressed,
|
||||
int compressedOffset, ref int compressedLength);
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses some data.
|
||||
/// </summary>
|
||||
/// <param name="source">The compressed input.</param>
|
||||
/// <param name="sourceOffset">Offset of the input data in <c>source</c>.</param>
|
||||
/// <param name="sourceLength">The amount of compressed data.</param>
|
||||
/// <param name="decompressed">The destination for the output decompressed data.</param>
|
||||
/// <param name="decompressedOffset">Offset for the output data in <c>decompressed</c>.</param>
|
||||
/// <returns>The amount of decompressed data.</returns>
|
||||
public abstract int Decompress(byte[] source, int sourceOffset, int sourceLength, byte[] decompressed,
|
||||
int decompressedOffset);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible results of attempting to compress data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A compression routine <i>may</i> return <c>Compressed</c>, even if the data
|
||||
/// was 'all zeros' or increased in size. The <c>AllZeros</c> and <c>Incompressible</c>
|
||||
/// values are for algorithms that include special detection for these cases.
|
||||
/// </remarks>
|
||||
public enum CompressionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The data compressed succesfully.
|
||||
/// </summary>
|
||||
Compressed,
|
||||
|
||||
/// <summary>
|
||||
/// The data was all-zero's.
|
||||
/// </summary>
|
||||
AllZeros,
|
||||
|
||||
/// <summary>
|
||||
/// The data was incompressible (could not fit into destination buffer).
|
||||
/// </summary>
|
||||
Incompressible
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal abstract class DataBlockTransform
|
||||
{
|
||||
protected abstract bool BuffersMustNotOverlap { get; }
|
||||
|
||||
public int Process(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
|
||||
{
|
||||
if (output.Length < outputOffset + (long)MinOutputCount(inputCount))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"Output buffer to small, must be at least {0} bytes may need to be {1} bytes",
|
||||
MinOutputCount(inputCount),
|
||||
MaxOutputCount(inputCount)));
|
||||
}
|
||||
|
||||
if (BuffersMustNotOverlap)
|
||||
{
|
||||
int maxOut = MaxOutputCount(inputCount);
|
||||
|
||||
if (input == output
|
||||
&& (inputOffset + (long)inputCount > outputOffset)
|
||||
&& (inputOffset <= outputOffset + (long)maxOut))
|
||||
{
|
||||
byte[] tempBuffer = new byte[maxOut];
|
||||
|
||||
int outCount = DoProcess(input, inputOffset, inputCount, tempBuffer, 0);
|
||||
Array.Copy(tempBuffer, 0, output, outputOffset, outCount);
|
||||
|
||||
return outCount;
|
||||
}
|
||||
}
|
||||
|
||||
return DoProcess(input, inputOffset, inputCount, output, outputOffset);
|
||||
}
|
||||
|
||||
protected abstract int DoProcess(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset);
|
||||
|
||||
protected abstract int MaxOutputCount(int inputCount);
|
||||
|
||||
protected abstract int MinOutputCount(int inputCount);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// A canonical Huffman tree implementation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A lookup table is created that will take any bit sequence (max tree depth in length),
|
||||
/// indicating the output symbol. In WIM files, in practice, no chunk exceeds 32768 bytes
|
||||
/// in length, so we often end up generating a bigger lookup table than the data it's
|
||||
/// encoding. This makes for exceptionally fast symbol lookups O(1), but is inefficient
|
||||
/// overall.
|
||||
/// </remarks>
|
||||
internal sealed class HuffmanTree
|
||||
{
|
||||
private readonly uint[] _buffer;
|
||||
private readonly int _numBits; // Max bits per symbol
|
||||
private readonly int _numSymbols; // Max symbols
|
||||
|
||||
public HuffmanTree(uint[] lengths)
|
||||
{
|
||||
Lengths = lengths;
|
||||
_numSymbols = lengths.Length;
|
||||
|
||||
uint maxLength = 0;
|
||||
for (int i = 0; i < Lengths.Length; ++i)
|
||||
{
|
||||
if (Lengths[i] > maxLength)
|
||||
{
|
||||
maxLength = Lengths[i];
|
||||
}
|
||||
}
|
||||
|
||||
_numBits = (int)maxLength;
|
||||
_buffer = new uint[1 << _numBits];
|
||||
|
||||
Build();
|
||||
}
|
||||
|
||||
public uint[] Lengths { get; }
|
||||
|
||||
public uint NextSymbol(BitStream bitStream)
|
||||
{
|
||||
uint symbol = _buffer[bitStream.Peek(_numBits)];
|
||||
|
||||
// We may have over-read, reset bitstream position
|
||||
bitStream.Consume((int)Lengths[symbol]);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
private void Build()
|
||||
{
|
||||
int position = 0;
|
||||
|
||||
// For each bit-length...
|
||||
for (int i = 1; i <= _numBits; ++i)
|
||||
{
|
||||
// Check each symbol
|
||||
for (uint symbol = 0; symbol < _numSymbols; ++symbol)
|
||||
{
|
||||
if (Lengths[symbol] == i)
|
||||
{
|
||||
int numToFill = 1 << (_numBits - i);
|
||||
for (int n = 0; n < numToFill; ++n)
|
||||
{
|
||||
_buffer[position + n] = symbol;
|
||||
}
|
||||
|
||||
position += numToFill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = position; i < _buffer.Length; ++i)
|
||||
{
|
||||
_buffer[i] = uint.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal sealed class InverseBurrowsWheeler : DataBlockTransform
|
||||
{
|
||||
private readonly int[] _nextPos;
|
||||
private readonly int[] _pointers;
|
||||
|
||||
public InverseBurrowsWheeler(int bufferSize)
|
||||
{
|
||||
_pointers = new int[bufferSize];
|
||||
_nextPos = new int[256];
|
||||
}
|
||||
|
||||
protected override bool BuffersMustNotOverlap
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public int OriginalIndex { get; set; }
|
||||
|
||||
protected override int DoProcess(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
|
||||
{
|
||||
int outputCount = inputCount;
|
||||
|
||||
// First find the frequency of each value
|
||||
Array.Clear(_nextPos, 0, _nextPos.Length);
|
||||
for (int i = inputOffset; i < inputOffset + inputCount; ++i)
|
||||
{
|
||||
_nextPos[input[i]]++;
|
||||
}
|
||||
|
||||
// We know they're 'sorted' in the first column, so now can figure
|
||||
// out the position of the first instance of each.
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
int tempSum = sum;
|
||||
sum += _nextPos[i];
|
||||
_nextPos[i] = tempSum;
|
||||
}
|
||||
|
||||
// For each value in the final column, put a pointer to to the
|
||||
// 'next' character in the first (sorted) column.
|
||||
for (int i = 0; i < inputCount; ++i)
|
||||
{
|
||||
_pointers[_nextPos[input[inputOffset + i]]++] = i;
|
||||
}
|
||||
|
||||
// The 'next' character after the end of the original string is the
|
||||
// first character of the original string.
|
||||
int focus = _pointers[OriginalIndex];
|
||||
|
||||
// We can now just walk the pointers to reconstruct the original string
|
||||
for (int i = 0; i < outputCount; ++i)
|
||||
{
|
||||
output[outputOffset + i] = input[inputOffset + focus];
|
||||
focus = _pointers[focus];
|
||||
}
|
||||
|
||||
return outputCount;
|
||||
}
|
||||
|
||||
protected override int MaxOutputCount(int inputCount)
|
||||
{
|
||||
return inputCount;
|
||||
}
|
||||
|
||||
protected override int MinOutputCount(int inputCount)
|
||||
{
|
||||
return inputCount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal class MoveToFront
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
|
||||
public MoveToFront()
|
||||
: this(256, false) {}
|
||||
|
||||
public MoveToFront(int size, bool autoInit)
|
||||
{
|
||||
_buffer = new byte[size];
|
||||
|
||||
if (autoInit)
|
||||
{
|
||||
for (byte i = 0; i < size; ++i)
|
||||
{
|
||||
_buffer[i] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte Head
|
||||
{
|
||||
get { return _buffer[0]; }
|
||||
}
|
||||
|
||||
public void Set(int pos, byte val)
|
||||
{
|
||||
_buffer[pos] = val;
|
||||
}
|
||||
|
||||
public byte GetAndMove(int pos)
|
||||
{
|
||||
byte val = _buffer[pos];
|
||||
|
||||
for (int i = pos; i > 0; --i)
|
||||
{
|
||||
_buffer[i] = _buffer[i - 1];
|
||||
}
|
||||
|
||||
_buffer[0] = val;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// Copyright (c) 2014, Quamotion
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal class SizedDeflateStream : DeflateStream
|
||||
{
|
||||
private readonly int _length;
|
||||
private int _position;
|
||||
|
||||
public SizedDeflateStream(Stream stream, CompressionMode mode, bool leaveOpen, int length)
|
||||
: base(stream, mode, leaveOpen)
|
||||
{
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { return _length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _position; }
|
||||
set
|
||||
{
|
||||
if (value != Position)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] array, int offset, int count)
|
||||
{
|
||||
int read = base.Read(array, offset, count);
|
||||
_position += read;
|
||||
return read;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// Copyright (c) 2014, Quamotion
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
using Buffer=DiscUtils.Streams.Buffer;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
internal class ZlibBuffer : Buffer
|
||||
{
|
||||
private Ownership _ownership;
|
||||
private readonly Stream _stream;
|
||||
private int position;
|
||||
|
||||
public ZlibBuffer(Stream stream, Ownership ownership)
|
||||
{
|
||||
_stream = stream;
|
||||
_ownership = ownership;
|
||||
position = 0;
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _stream.CanRead; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _stream.CanWrite; }
|
||||
}
|
||||
|
||||
public override long Capacity
|
||||
{
|
||||
get { return _stream.Length; }
|
||||
}
|
||||
|
||||
public override int Read(long pos, byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (pos != position)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
int read = _stream.Read(buffer, offset, count);
|
||||
position += read;
|
||||
return read;
|
||||
}
|
||||
|
||||
public override void Write(long pos, byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetCapacity(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
|
||||
{
|
||||
yield return new StreamExtent(0, _stream.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of the Zlib compression algorithm.
|
||||
/// </summary>
|
||||
/// <remarks>Only decompression is currently implemented.</remarks>
|
||||
public class ZlibStream : Stream
|
||||
{
|
||||
private readonly Adler32 _adler32;
|
||||
private readonly DeflateStream _deflateStream;
|
||||
private readonly CompressionMode _mode;
|
||||
private readonly Stream _stream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ZlibStream class.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to compress of decompress.</param>
|
||||
/// <param name="mode">Whether to compress or decompress.</param>
|
||||
/// <param name="leaveOpen">Whether closing this stream should leave <c>stream</c> open.</param>
|
||||
public ZlibStream(Stream stream, CompressionMode mode, bool leaveOpen)
|
||||
{
|
||||
_stream = stream;
|
||||
_mode = mode;
|
||||
|
||||
if (mode == CompressionMode.Decompress)
|
||||
{
|
||||
// We just sanity check against expected header values...
|
||||
byte[] headerBuffer = StreamUtilities.ReadExact(stream, 2);
|
||||
ushort header = EndianUtilities.ToUInt16BigEndian(headerBuffer, 0);
|
||||
|
||||
if (header % 31 != 0)
|
||||
{
|
||||
throw new IOException("Invalid Zlib header found");
|
||||
}
|
||||
|
||||
if ((header & 0x0F00) != 8 << 8)
|
||||
{
|
||||
throw new NotSupportedException("Zlib compression not using DEFLATE algorithm");
|
||||
}
|
||||
|
||||
if ((header & 0x0020) != 0)
|
||||
{
|
||||
throw new NotSupportedException("Zlib compression using preset dictionary");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort header =
|
||||
(8 << 8) // DEFLATE
|
||||
| (7 << 12) // 32K window size
|
||||
| 0x80; // Default algorithm
|
||||
header |= (ushort)(31 - header % 31);
|
||||
|
||||
byte[] headerBuffer = new byte[2];
|
||||
EndianUtilities.WriteBytesBigEndian(header, headerBuffer, 0);
|
||||
stream.Write(headerBuffer, 0, 2);
|
||||
}
|
||||
|
||||
_deflateStream = new DeflateStream(stream, mode, leaveOpen);
|
||||
_adler32 = new Adler32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the stream can be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _deflateStream.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the stream pointer can be changed.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the stream can be written to.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _deflateStream.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the stream position.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the stream.
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_mode == CompressionMode.Decompress)
|
||||
{
|
||||
// Can only check Adler checksum on seekable streams. Since DeflateStream
|
||||
// aggresively caches input, it normally has already consumed the footer.
|
||||
if (_stream.CanSeek)
|
||||
{
|
||||
_stream.Seek(-4, SeekOrigin.End);
|
||||
byte[] footerBuffer = StreamUtilities.ReadExact(_stream, 4);
|
||||
if (EndianUtilities.ToInt32BigEndian(footerBuffer, 0) != _adler32.Value)
|
||||
{
|
||||
throw new InvalidDataException("Corrupt decompressed data detected");
|
||||
}
|
||||
}
|
||||
|
||||
_deflateStream.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
_deflateStream.Dispose();
|
||||
|
||||
byte[] footerBuffer = new byte[4];
|
||||
EndianUtilities.WriteBytesBigEndian(_adler32.Value, footerBuffer, 0);
|
||||
_stream.Write(footerBuffer, 0, 4);
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_deflateStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to populate.</param>
|
||||
/// <param name="offset">The first byte to write.</param>
|
||||
/// <param name="count">The number of bytes requested.</param>
|
||||
/// <returns>The number of bytes read.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
CheckParams(buffer, offset, count);
|
||||
|
||||
int numRead = _deflateStream.Read(buffer, offset, count);
|
||||
_adler32.Process(buffer, offset, numRead);
|
||||
return numRead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeks to a new position.
|
||||
/// </summary>
|
||||
/// <param name="offset">Relative position to seek to.</param>
|
||||
/// <param name="origin">The origin of the seek.</param>
|
||||
/// <returns>The new position.</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the length of the stream.
|
||||
/// </summary>
|
||||
/// <param name="value">The new desired length of the stream.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer containing the data to write.</param>
|
||||
/// <param name="offset">Offset of the first byte to write.</param>
|
||||
/// <param name="count">Number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
CheckParams(buffer, offset, count);
|
||||
|
||||
_adler32.Process(buffer, offset, count);
|
||||
_deflateStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
private static void CheckParams(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > buffer.Length)
|
||||
{
|
||||
throw new ArgumentException("Offset outside of array bounds", nameof(offset));
|
||||
}
|
||||
|
||||
if (count < 0 || offset + count > buffer.Length)
|
||||
{
|
||||
throw new ArgumentException("Array index out of bounds", nameof(count));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// The concatenation of multiple streams (read-only, for now).
|
||||
/// </summary>
|
||||
public class ConcatStream : SparseStream
|
||||
{
|
||||
private readonly bool _canWrite;
|
||||
private readonly Ownership _ownsStreams;
|
||||
|
||||
private long _position;
|
||||
private SparseStream[] _streams;
|
||||
|
||||
public ConcatStream(Ownership ownsStreams, params SparseStream[] streams)
|
||||
{
|
||||
_ownsStreams = ownsStreams;
|
||||
_streams = streams;
|
||||
|
||||
// Only allow writes if all streams can be written
|
||||
_canWrite = true;
|
||||
foreach (SparseStream stream in streams)
|
||||
{
|
||||
if (!stream.CanWrite)
|
||||
{
|
||||
_canWrite = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _canWrite;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<StreamExtent> Extents
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
List<StreamExtent> extents = new List<StreamExtent>();
|
||||
|
||||
long pos = 0;
|
||||
for (int i = 0; i < _streams.Length; ++i)
|
||||
{
|
||||
foreach (StreamExtent extent in _streams[i].Extents)
|
||||
{
|
||||
extents.Add(new StreamExtent(extent.Start + pos, extent.Length));
|
||||
}
|
||||
|
||||
pos += _streams[i].Length;
|
||||
}
|
||||
|
||||
return extents;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
long length = 0;
|
||||
for (int i = 0; i < _streams.Length; ++i)
|
||||
{
|
||||
length += _streams[i].Length;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _position;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
CheckDisposed();
|
||||
_position = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
CheckDisposed();
|
||||
for (int i = 0; i < _streams.Length; ++i)
|
||||
{
|
||||
_streams[i].Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
int totalRead = 0;
|
||||
int numRead = 0;
|
||||
|
||||
do
|
||||
{
|
||||
long activeStreamStartPos;
|
||||
int activeStream = GetActiveStream(out activeStreamStartPos);
|
||||
|
||||
_streams[activeStream].Position = _position - activeStreamStartPos;
|
||||
|
||||
numRead = _streams[activeStream].Read(buffer, offset + totalRead, count - totalRead);
|
||||
|
||||
totalRead += numRead;
|
||||
_position += numRead;
|
||||
} while (numRead != 0);
|
||||
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
long effectiveOffset = offset;
|
||||
if (origin == SeekOrigin.Current)
|
||||
{
|
||||
effectiveOffset += _position;
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
effectiveOffset += Length;
|
||||
}
|
||||
|
||||
if (effectiveOffset < 0)
|
||||
{
|
||||
throw new IOException("Attempt to move before beginning of disk");
|
||||
}
|
||||
Position = effectiveOffset;
|
||||
return Position;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
long lastStreamOffset;
|
||||
int lastStream = GetStream(Length, out lastStreamOffset);
|
||||
if (value < lastStreamOffset)
|
||||
{
|
||||
throw new IOException(string.Format(CultureInfo.InvariantCulture,
|
||||
"Unable to reduce stream length to less than {0}", lastStreamOffset));
|
||||
}
|
||||
|
||||
_streams[lastStream].SetLength(value - lastStreamOffset);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
int totalWritten = 0;
|
||||
while (totalWritten != count)
|
||||
{
|
||||
// Offset of the stream = streamOffset
|
||||
long streamOffset;
|
||||
int streamIdx = GetActiveStream(out streamOffset);
|
||||
|
||||
// Offset within the stream = streamPos
|
||||
long streamPos = _position - streamOffset;
|
||||
_streams[streamIdx].Position = streamPos;
|
||||
|
||||
// Write (limited to the stream's length), except for final stream - that may be
|
||||
// extendable
|
||||
int numToWrite;
|
||||
if (streamIdx == _streams.Length - 1)
|
||||
{
|
||||
numToWrite = count - totalWritten;
|
||||
}
|
||||
else
|
||||
{
|
||||
numToWrite = (int)Math.Min(count - totalWritten, _streams[streamIdx].Length - streamPos);
|
||||
}
|
||||
|
||||
_streams[streamIdx].Write(buffer, offset + totalWritten, numToWrite);
|
||||
|
||||
totalWritten += numToWrite;
|
||||
_position += numToWrite;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (disposing && _ownsStreams == Ownership.Dispose && _streams != null)
|
||||
{
|
||||
foreach (SparseStream stream in _streams)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
_streams = null;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetActiveStream(out long startPos)
|
||||
{
|
||||
return GetStream(_position, out startPos);
|
||||
}
|
||||
|
||||
private int GetStream(long targetPos, out long streamStartPos)
|
||||
{
|
||||
// Find the stream that _position is within
|
||||
streamStartPos = 0;
|
||||
int focusStream = 0;
|
||||
while (focusStream < _streams.Length - 1 && streamStartPos + _streams[focusStream].Length <= targetPos)
|
||||
{
|
||||
streamStartPos += _streams[focusStream].Length;
|
||||
focusStream++;
|
||||
}
|
||||
|
||||
return focusStream;
|
||||
}
|
||||
|
||||
private void CheckDisposed()
|
||||
{
|
||||
if (_streams == null)
|
||||
{
|
||||
throw new ObjectDisposedException("ConcatStream");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#if NETSTANDARD
|
||||
using System.Text;
|
||||
#endif
|
||||
|
||||
namespace DiscUtils.CoreCompat
|
||||
{
|
||||
internal static class EncodingHelper
|
||||
{
|
||||
private static bool _registered;
|
||||
|
||||
public static void RegisterEncodings()
|
||||
{
|
||||
if (_registered)
|
||||
return;
|
||||
|
||||
_registered = true;
|
||||
|
||||
#if NETSTANDARD
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DiscUtils.CoreCompat
|
||||
{
|
||||
internal static class ReflectionHelper
|
||||
{
|
||||
public static bool IsEnum(Type type)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return type.GetTypeInfo().IsEnum;
|
||||
#else
|
||||
return type.IsEnum;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(PropertyInfo property, Type attributeType)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return property.GetCustomAttribute(attributeType);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(property, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(PropertyInfo property, Type attributeType, bool inherit)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return property.GetCustomAttribute(attributeType, inherit);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(property, attributeType, inherit);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(FieldInfo field, Type attributeType)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return field.GetCustomAttribute(attributeType);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(field, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(Type type, Type attributeType)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return type.GetTypeInfo().GetCustomAttribute(attributeType);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(type, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(Type type, Type attributeType, bool inherit)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return type.GetTypeInfo().GetCustomAttribute(attributeType, inherit);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(type, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static IEnumerable<Attribute> GetCustomAttributes(Type type, Type attributeType, bool inherit)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return type.GetTypeInfo().GetCustomAttributes(attributeType, inherit);
|
||||
#else
|
||||
return Attribute.GetCustomAttributes(type, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Assembly GetAssembly(Type type)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return type.GetTypeInfo().Assembly;
|
||||
#else
|
||||
return type.Assembly;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static int SizeOf<T>()
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
return Marshal.SizeOf<T>();
|
||||
#else
|
||||
return Marshal.SizeOf(typeof(T));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#if NETSTANDARD1_5
|
||||
using System.Globalization;
|
||||
|
||||
namespace DiscUtils.CoreCompat
|
||||
{
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string ToUpper(this string value, CultureInfo culture)
|
||||
{
|
||||
return value.ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class DirectoryExtent : BuilderExtent
|
||||
{
|
||||
private readonly BuildDirectoryInfo _dirInfo;
|
||||
private readonly Encoding _enc;
|
||||
private readonly Dictionary<BuildDirectoryMember, uint> _locationTable;
|
||||
|
||||
private byte[] _readCache;
|
||||
|
||||
public DirectoryExtent(BuildDirectoryInfo dirInfo, Dictionary<BuildDirectoryMember, uint> locationTable,
|
||||
Encoding enc, long start)
|
||||
: base(start, dirInfo.GetDataSize(enc))
|
||||
{
|
||||
_dirInfo = dirInfo;
|
||||
_locationTable = locationTable;
|
||||
_enc = enc;
|
||||
}
|
||||
|
||||
public override void Dispose() {}
|
||||
|
||||
public override void PrepareForRead()
|
||||
{
|
||||
_readCache = new byte[Length];
|
||||
_dirInfo.Write(_readCache, 0, _locationTable, _enc);
|
||||
}
|
||||
|
||||
public override int Read(long diskOffset, byte[] buffer, int offset, int count)
|
||||
{
|
||||
long relPos = diskOffset - Start;
|
||||
|
||||
int numRead = (int)Math.Min(count, _readCache.Length - relPos);
|
||||
|
||||
Array.Copy(_readCache, (int)relPos, buffer, offset, numRead);
|
||||
|
||||
return numRead;
|
||||
}
|
||||
|
||||
public override void DisposeReadState()
|
||||
{
|
||||
_readCache = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class DirectoryRecord
|
||||
{
|
||||
public uint DataLength;
|
||||
public byte ExtendedAttributeRecordLength;
|
||||
public string FileIdentifier;
|
||||
public byte FileUnitSize;
|
||||
public FileFlags Flags;
|
||||
public byte InterleaveGapSize;
|
||||
public uint LocationOfExtent;
|
||||
public DateTime RecordingDateAndTime;
|
||||
public byte[] SystemUseData;
|
||||
public ushort VolumeSequenceNumber;
|
||||
|
||||
public static int ReadFrom(byte[] src, int offset, Encoding enc, out DirectoryRecord record)
|
||||
{
|
||||
int length = src[offset + 0];
|
||||
|
||||
record = new DirectoryRecord();
|
||||
record.ExtendedAttributeRecordLength = src[offset + 1];
|
||||
record.LocationOfExtent = IsoUtilities.ToUInt32FromBoth(src, offset + 2);
|
||||
record.DataLength = IsoUtilities.ToUInt32FromBoth(src, offset + 10);
|
||||
record.RecordingDateAndTime = IsoUtilities.ToUTCDateTimeFromDirectoryTime(src, offset + 18);
|
||||
record.Flags = (FileFlags)src[offset + 25];
|
||||
record.FileUnitSize = src[offset + 26];
|
||||
record.InterleaveGapSize = src[offset + 27];
|
||||
record.VolumeSequenceNumber = IsoUtilities.ToUInt16FromBoth(src, offset + 28);
|
||||
byte lengthOfFileIdentifier = src[offset + 32];
|
||||
record.FileIdentifier = IsoUtilities.ReadChars(src, offset + 33, lengthOfFileIdentifier, enc);
|
||||
|
||||
int padding = (lengthOfFileIdentifier & 1) == 0 ? 1 : 0;
|
||||
int startSystemArea = lengthOfFileIdentifier + padding + 33;
|
||||
int lenSystemArea = length - startSystemArea;
|
||||
if (lenSystemArea > 0)
|
||||
{
|
||||
record.SystemUseData = new byte[lenSystemArea];
|
||||
Array.Copy(src, offset + startSystemArea, record.SystemUseData, 0, lenSystemArea);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public static uint CalcLength(string name, Encoding enc)
|
||||
{
|
||||
int nameBytes;
|
||||
if (name.Length == 1 && name[0] <= 1)
|
||||
{
|
||||
nameBytes = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nameBytes = enc.GetByteCount(name);
|
||||
}
|
||||
|
||||
return (uint)(33 + nameBytes + ((nameBytes & 0x1) == 0 ? 1 : 0));
|
||||
}
|
||||
|
||||
internal int WriteTo(byte[] buffer, int offset, Encoding enc)
|
||||
{
|
||||
uint length = CalcLength(FileIdentifier, enc);
|
||||
buffer[offset] = (byte)length;
|
||||
buffer[offset + 1] = ExtendedAttributeRecordLength;
|
||||
IsoUtilities.ToBothFromUInt32(buffer, offset + 2, LocationOfExtent);
|
||||
IsoUtilities.ToBothFromUInt32(buffer, offset + 10, DataLength);
|
||||
IsoUtilities.ToDirectoryTimeFromUTC(buffer, offset + 18, RecordingDateAndTime);
|
||||
buffer[offset + 25] = (byte)Flags;
|
||||
buffer[offset + 26] = FileUnitSize;
|
||||
buffer[offset + 27] = InterleaveGapSize;
|
||||
IsoUtilities.ToBothFromUInt16(buffer, offset + 28, VolumeSequenceNumber);
|
||||
byte lengthOfFileIdentifier;
|
||||
|
||||
if (FileIdentifier.Length == 1 && FileIdentifier[0] <= 1)
|
||||
{
|
||||
buffer[offset + 33] = (byte)FileIdentifier[0];
|
||||
lengthOfFileIdentifier = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
lengthOfFileIdentifier =
|
||||
(byte)
|
||||
IsoUtilities.WriteString(buffer, offset + 33, (int)(length - 33), false, FileIdentifier, enc);
|
||||
}
|
||||
|
||||
buffer[offset + 32] = lengthOfFileIdentifier;
|
||||
return (int)length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using DiscUtils.Internal;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides information about a directory on a disc.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class allows navigation of the disc directory/file hierarchy.
|
||||
/// </remarks>
|
||||
public sealed class DiscDirectoryInfo : DiscFileSystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DiscDirectoryInfo class.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">The file system the directory info relates to.</param>
|
||||
/// <param name="path">The path within the file system of the directory.</param>
|
||||
internal DiscDirectoryInfo(DiscFileSystem fileSystem, string path)
|
||||
: base(fileSystem, path) {}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the directory exists.
|
||||
/// </summary>
|
||||
public override bool Exists
|
||||
{
|
||||
get { return FileSystem.DirectoryExists(Path); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of the directory.
|
||||
/// </summary>
|
||||
public override string FullName
|
||||
{
|
||||
get { return base.FullName + @"\"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a directory.
|
||||
/// </summary>
|
||||
public void Create()
|
||||
{
|
||||
FileSystem.CreateDirectory(Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory, even if it's not empty.
|
||||
/// </summary>
|
||||
public override void Delete()
|
||||
{
|
||||
FileSystem.DeleteDirectory(Path, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory, with the caller choosing whether to recurse.
|
||||
/// </summary>
|
||||
/// <param name="recursive"><c>true</c> to delete all child node, <c>false</c> to fail if the directory is not empty.</param>
|
||||
public void Delete(bool recursive)
|
||||
{
|
||||
FileSystem.DeleteDirectory(Path, recursive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves a directory and it's contents to a new path.
|
||||
/// </summary>
|
||||
/// <param name="destinationDirName">The destination directory name.</param>
|
||||
public void MoveTo(string destinationDirName)
|
||||
{
|
||||
FileSystem.MoveDirectory(Path, destinationDirName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all child directories.
|
||||
/// </summary>
|
||||
/// <returns>An array of child directories.</returns>
|
||||
public DiscDirectoryInfo[] GetDirectories()
|
||||
{
|
||||
return Utilities.Map(FileSystem.GetDirectories(Path),
|
||||
p => new DiscDirectoryInfo(FileSystem, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all child directories matching a search pattern.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The search pattern.</param>
|
||||
/// <returns>An array of child directories, or empty if none match.</returns>
|
||||
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
|
||||
/// and ? (matching 1 character).</remarks>
|
||||
public DiscDirectoryInfo[] GetDirectories(string pattern)
|
||||
{
|
||||
return GetDirectories(pattern, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all descendant directories matching a search pattern.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The search pattern.</param>
|
||||
/// <param name="searchOption">Whether to search just this directory, or all children.</param>
|
||||
/// <returns>An array of descendant directories, or empty if none match.</returns>
|
||||
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
|
||||
/// and ? (matching 1 character). The option parameter determines whether only immediate
|
||||
/// children, or all children are returned.</remarks>
|
||||
public DiscDirectoryInfo[] GetDirectories(string pattern, SearchOption searchOption)
|
||||
{
|
||||
return Utilities.Map(FileSystem.GetDirectories(Path, pattern, searchOption),
|
||||
p => new DiscDirectoryInfo(FileSystem, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all files.
|
||||
/// </summary>
|
||||
/// <returns>An array of files.</returns>
|
||||
public DiscFileInfo[] GetFiles()
|
||||
{
|
||||
return Utilities.Map(FileSystem.GetFiles(Path), p => new DiscFileInfo(FileSystem, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all files matching a search pattern.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The search pattern.</param>
|
||||
/// <returns>An array of files, or empty if none match.</returns>
|
||||
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
|
||||
/// and ? (matching 1 character).</remarks>
|
||||
public DiscFileInfo[] GetFiles(string pattern)
|
||||
{
|
||||
return GetFiles(pattern, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all descendant files matching a search pattern.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The search pattern.</param>
|
||||
/// <param name="searchOption">Whether to search just this directory, or all children.</param>
|
||||
/// <returns>An array of descendant files, or empty if none match.</returns>
|
||||
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
|
||||
/// and ? (matching 1 character). The option parameter determines whether only immediate
|
||||
/// children, or all children are returned.</remarks>
|
||||
public DiscFileInfo[] GetFiles(string pattern, SearchOption searchOption)
|
||||
{
|
||||
return Utilities.Map(FileSystem.GetFiles(Path, pattern, searchOption),
|
||||
p => new DiscFileInfo(FileSystem, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all files and directories in this directory.
|
||||
/// </summary>
|
||||
/// <returns>An array of files and directories.</returns>
|
||||
public DiscFileSystemInfo[] GetFileSystemInfos()
|
||||
{
|
||||
return Utilities.Map(FileSystem.GetFileSystemEntries(Path),
|
||||
p => new DiscFileSystemInfo(FileSystem, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all files and directories in this directory.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The search pattern.</param>
|
||||
/// <returns>An array of files and directories.</returns>
|
||||
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
|
||||
/// and ? (matching 1 character).</remarks>
|
||||
public DiscFileSystemInfo[] GetFileSystemInfos(string pattern)
|
||||
{
|
||||
return Utilities.Map(FileSystem.GetFileSystemEntries(Path, pattern),
|
||||
p => new DiscFileSystemInfo(FileSystem, p));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides information about a file on a disc.
|
||||
/// </summary>
|
||||
public sealed class DiscFileInfo : DiscFileSystemInfo
|
||||
{
|
||||
internal DiscFileInfo(DiscFileSystem fileSystem, string path)
|
||||
: base(fileSystem, path) {}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of the parent directory.
|
||||
/// </summary>
|
||||
public DiscDirectoryInfo Directory
|
||||
{
|
||||
get { return Parent; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representing the directory's full path.
|
||||
/// </summary>
|
||||
public string DirectoryName
|
||||
{
|
||||
get { return Directory.FullName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file exists.
|
||||
/// </summary>
|
||||
public override bool Exists
|
||||
{
|
||||
get { return FileSystem.FileExists(Path); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the file is read-only.
|
||||
/// </summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return (Attributes & FileAttributes.ReadOnly) != 0; }
|
||||
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Attributes = Attributes | FileAttributes.ReadOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
Attributes = Attributes & ~FileAttributes.ReadOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the current file in bytes.
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get { return FileSystem.GetFileLength(Path); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file.
|
||||
/// </summary>
|
||||
public override void Delete()
|
||||
{
|
||||
FileSystem.DeleteFile(Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="StreamWriter" /> that appends text to the file represented by this <see cref="DiscFileInfo"/>.
|
||||
/// </summary>
|
||||
/// <returns>The newly created writer.</returns>
|
||||
public StreamWriter AppendText()
|
||||
{
|
||||
return new StreamWriter(Open(FileMode.Append));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing file to a new file.
|
||||
/// </summary>
|
||||
/// <param name="destinationFileName">The destination file.</param>
|
||||
public void CopyTo(string destinationFileName)
|
||||
{
|
||||
CopyTo(destinationFileName, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing file to a new file, allowing overwriting of an existing file.
|
||||
/// </summary>
|
||||
/// <param name="destinationFileName">The destination file.</param>
|
||||
/// <param name="overwrite">Whether to permit over-writing of an existing file.</param>
|
||||
public void CopyTo(string destinationFileName, bool overwrite)
|
||||
{
|
||||
FileSystem.CopyFile(Path, destinationFileName, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new file for reading and writing.
|
||||
/// </summary>
|
||||
/// <returns>The newly created stream.</returns>
|
||||
public Stream Create()
|
||||
{
|
||||
return Open(FileMode.Create);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="StreamWriter"/> that writes a new text file.
|
||||
/// </summary>
|
||||
/// <returns>A new stream writer that can write to the file contents.</returns>
|
||||
public StreamWriter CreateText()
|
||||
{
|
||||
return new StreamWriter(Open(FileMode.Create));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves a file to a new location.
|
||||
/// </summary>
|
||||
/// <param name="destinationFileName">The new name of the file.</param>
|
||||
public void MoveTo(string destinationFileName)
|
||||
{
|
||||
FileSystem.MoveFile(Path, destinationFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the current file.
|
||||
/// </summary>
|
||||
/// <param name="mode">The file mode for the created stream.</param>
|
||||
/// <returns>The newly created stream.</returns>
|
||||
/// <remarks>Read-only file systems only support <c>FileMode.Open</c>.</remarks>
|
||||
public Stream Open(FileMode mode)
|
||||
{
|
||||
return FileSystem.OpenFile(Path, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the current file.
|
||||
/// </summary>
|
||||
/// <param name="mode">The file mode for the created stream.</param>
|
||||
/// <param name="access">The access permissions for the created stream.</param>
|
||||
/// <returns>The newly created stream.</returns>
|
||||
/// <remarks>Read-only file systems only support <c>FileMode.Open</c> and <c>FileAccess.Read</c>.</remarks>
|
||||
public Stream Open(FileMode mode, FileAccess access)
|
||||
{
|
||||
return FileSystem.OpenFile(Path, mode, access);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens an existing file for read-only access.
|
||||
/// </summary>
|
||||
/// <returns>The newly created stream.</returns>
|
||||
public Stream OpenRead()
|
||||
{
|
||||
return Open(FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens an existing file for reading as UTF-8 text.
|
||||
/// </summary>
|
||||
/// <returns>The newly created reader.</returns>
|
||||
public StreamReader OpenText()
|
||||
{
|
||||
return new StreamReader(OpenRead());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a file for writing.
|
||||
/// </summary>
|
||||
/// <returns>The newly created stream.</returns>
|
||||
public Stream OpenWrite()
|
||||
{
|
||||
return Open(FileMode.Open, FileAccess.Write);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Internal;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
internal sealed class DiscFileLocator : FileLocator
|
||||
{
|
||||
private readonly string _basePath;
|
||||
private readonly DiscFileSystem _fileSystem;
|
||||
|
||||
public DiscFileLocator(DiscFileSystem fileSystem, string basePath)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_basePath = basePath;
|
||||
}
|
||||
|
||||
public override bool Exists(string fileName)
|
||||
{
|
||||
return _fileSystem.FileExists(Utilities.CombinePaths(_basePath, fileName));
|
||||
}
|
||||
|
||||
protected override Stream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
return _fileSystem.OpenFile(Utilities.CombinePaths(_basePath, fileName), mode, access);
|
||||
}
|
||||
|
||||
public override FileLocator GetRelativeLocator(string path)
|
||||
{
|
||||
return new DiscFileLocator(_fileSystem, Utilities.CombinePaths(_basePath, path));
|
||||
}
|
||||
|
||||
public override string GetFullPath(string path)
|
||||
{
|
||||
return Utilities.CombinePaths(_basePath, path);
|
||||
}
|
||||
|
||||
public override string GetDirectoryFromPath(string path)
|
||||
{
|
||||
return Utilities.GetDirectoryFromPath(path);
|
||||
}
|
||||
|
||||
public override string GetFileFromPath(string path)
|
||||
{
|
||||
return Utilities.GetFileFromPath(path);
|
||||
}
|
||||
|
||||
public override DateTime GetLastWriteTimeUtc(string path)
|
||||
{
|
||||
return _fileSystem.GetLastWriteTimeUtc(Utilities.CombinePaths(_basePath, path));
|
||||
}
|
||||
|
||||
public override bool HasCommonRoot(FileLocator other)
|
||||
{
|
||||
DiscFileLocator otherDiscLocator = other as DiscFileLocator;
|
||||
|
||||
if (otherDiscLocator == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Common root if the same file system instance.
|
||||
return ReferenceEquals(otherDiscLocator._fileSystem, _fileSystem);
|
||||
}
|
||||
|
||||
public override string ResolveRelativePath(string path)
|
||||
{
|
||||
return Utilities.ResolveRelativePath(_basePath, path);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,509 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the base class for all file systems.
|
||||
/// </summary>
|
||||
public abstract class DiscFileSystem :
|
||||
#if !NETSTANDARD
|
||||
MarshalByRefObject,
|
||||
#endif
|
||||
IFileSystem, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DiscFileSystem class.
|
||||
/// </summary>
|
||||
protected DiscFileSystem()
|
||||
{
|
||||
Options = new DiscFileSystemOptions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DiscFileSystem class.
|
||||
/// </summary>
|
||||
/// <param name="defaultOptions">The options instance to use for this file system instance.</param>
|
||||
protected DiscFileSystem(DiscFileSystemOptions defaultOptions)
|
||||
{
|
||||
Options = defaultOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the DiscFileSystem class.
|
||||
/// </summary>
|
||||
~DiscFileSystem()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file system options, which can be modified.
|
||||
/// </summary>
|
||||
public virtual DiscFileSystemOptions Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a friendly description of the file system type.
|
||||
/// </summary>
|
||||
public abstract string FriendlyName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file system is read-only or read-write.
|
||||
/// </summary>
|
||||
/// <returns>true if the file system is read-write.</returns>
|
||||
public abstract bool CanWrite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root directory of the file system.
|
||||
/// </summary>
|
||||
public virtual DiscDirectoryInfo Root
|
||||
{
|
||||
get { return new DiscDirectoryInfo(this, string.Empty); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the volume label.
|
||||
/// </summary>
|
||||
public virtual string VolumeLabel
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file system is thread-safe.
|
||||
/// </summary>
|
||||
public virtual bool IsThreadSafe
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing file to a new file.
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">The source file.</param>
|
||||
/// <param name="destinationFile">The destination file.</param>
|
||||
public virtual void CopyFile(string sourceFile, string destinationFile)
|
||||
{
|
||||
CopyFile(sourceFile, destinationFile, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing file to a new file, allowing overwriting of an existing file.
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">The source file.</param>
|
||||
/// <param name="destinationFile">The destination file.</param>
|
||||
/// <param name="overwrite">Whether to permit over-writing of an existing file.</param>
|
||||
public abstract void CopyFile(string sourceFile, string destinationFile, bool overwrite);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the new directory.</param>
|
||||
public abstract void CreateDirectory(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory to delete.</param>
|
||||
public abstract void DeleteDirectory(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory, optionally with all descendants.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory to delete.</param>
|
||||
/// <param name="recursive">Determines if the all descendants should be deleted.</param>
|
||||
public virtual void DeleteDirectory(string path, bool recursive)
|
||||
{
|
||||
if (recursive)
|
||||
{
|
||||
foreach (string dir in GetDirectories(path))
|
||||
{
|
||||
DeleteDirectory(dir, true);
|
||||
}
|
||||
|
||||
foreach (string file in GetFiles(path))
|
||||
{
|
||||
DeleteFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteDirectory(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file to delete.</param>
|
||||
public abstract void DeleteFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the directory exists.</returns>
|
||||
public abstract bool DirectoryExists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a file exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the file exists.</returns>
|
||||
public abstract bool FileExists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a file or directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the file or directory exists.</returns>
|
||||
public virtual bool Exists(string path)
|
||||
{
|
||||
return FileExists(path) || DirectoryExists(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of subdirectories in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <returns>Array of directories.</returns>
|
||||
public virtual string[] GetDirectories(string path)
|
||||
{
|
||||
return GetDirectories(path, "*.*", SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of subdirectories in a specified directory matching a specified
|
||||
/// search pattern.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <returns>Array of directories matching the search pattern.</returns>
|
||||
public virtual string[] GetDirectories(string path, string searchPattern)
|
||||
{
|
||||
return GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of subdirectories in a specified directory matching a specified
|
||||
/// search pattern, using a value to determine whether to search subdirectories.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
|
||||
/// <returns>Array of directories matching the search pattern.</returns>
|
||||
public abstract string[] GetDirectories(string path, string searchPattern, SearchOption searchOption);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <returns>Array of files.</returns>
|
||||
public virtual string[] GetFiles(string path)
|
||||
{
|
||||
return GetFiles(path, "*.*", SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <returns>Array of files matching the search pattern.</returns>
|
||||
public virtual string[] GetFiles(string path, string searchPattern)
|
||||
{
|
||||
return GetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files in a specified directory matching a specified
|
||||
/// search pattern, using a value to determine whether to search subdirectories.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
|
||||
/// <returns>Array of files matching the search pattern.</returns>
|
||||
public abstract string[] GetFiles(string path, string searchPattern, SearchOption searchOption);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all files and subdirectories in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
|
||||
public abstract string[] GetFileSystemEntries(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files and subdirectories in a specified directory matching a specified
|
||||
/// search pattern.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
|
||||
public abstract string[] GetFileSystemEntries(string path, string searchPattern);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a directory.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectoryName">The directory to move.</param>
|
||||
/// <param name="destinationDirectoryName">The target directory name.</param>
|
||||
public abstract void MoveDirectory(string sourceDirectoryName, string destinationDirectoryName);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a file.
|
||||
/// </summary>
|
||||
/// <param name="sourceName">The file to move.</param>
|
||||
/// <param name="destinationName">The target file name.</param>
|
||||
public virtual void MoveFile(string sourceName, string destinationName)
|
||||
{
|
||||
MoveFile(sourceName, destinationName, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves a file, allowing an existing file to be overwritten.
|
||||
/// </summary>
|
||||
/// <param name="sourceName">The file to move.</param>
|
||||
/// <param name="destinationName">The target file name.</param>
|
||||
/// <param name="overwrite">Whether to permit a destination file to be overwritten.</param>
|
||||
public abstract void MoveFile(string sourceName, string destinationName, bool overwrite);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">The file mode for the created stream.</param>
|
||||
/// <returns>The new stream.</returns>
|
||||
public virtual SparseStream OpenFile(string path, FileMode mode)
|
||||
{
|
||||
return OpenFile(path, mode, FileAccess.ReadWrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">The file mode for the created stream.</param>
|
||||
/// <param name="access">The access permissions for the created stream.</param>
|
||||
/// <returns>The new stream.</returns>
|
||||
public abstract SparseStream OpenFile(string path, FileMode mode, FileAccess access);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The file or directory to inspect.</param>
|
||||
/// <returns>The attributes of the file or directory.</returns>
|
||||
public abstract FileAttributes GetAttributes(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the attributes of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The file or directory to change.</param>
|
||||
/// <param name="newValue">The new attributes of the file or directory.</param>
|
||||
public abstract void SetAttributes(string path, FileAttributes newValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The creation time.</returns>
|
||||
public virtual DateTime GetCreationTime(string path)
|
||||
{
|
||||
return GetCreationTimeUtc(path).ToLocalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the creation time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
public virtual void SetCreationTime(string path, DateTime newTime)
|
||||
{
|
||||
SetCreationTimeUtc(path, newTime.ToUniversalTime());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The creation time.</returns>
|
||||
public abstract DateTime GetCreationTimeUtc(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the creation time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
public abstract void SetCreationTimeUtc(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last access time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last access time.</returns>
|
||||
public virtual DateTime GetLastAccessTime(string path)
|
||||
{
|
||||
return GetLastAccessTimeUtc(path).ToLocalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last access time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
public virtual void SetLastAccessTime(string path, DateTime newTime)
|
||||
{
|
||||
SetLastAccessTimeUtc(path, newTime.ToUniversalTime());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last access time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last access time.</returns>
|
||||
public abstract DateTime GetLastAccessTimeUtc(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last access time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
public abstract void SetLastAccessTimeUtc(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modification time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last write time.</returns>
|
||||
public virtual DateTime GetLastWriteTime(string path)
|
||||
{
|
||||
return GetLastWriteTimeUtc(path).ToLocalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last modification time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
public virtual void SetLastWriteTime(string path, DateTime newTime)
|
||||
{
|
||||
SetLastWriteTimeUtc(path, newTime.ToUniversalTime());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modification time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last write time.</returns>
|
||||
public abstract DateTime GetLastWriteTimeUtc(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last modification time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
public abstract void SetLastWriteTimeUtc(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file.</param>
|
||||
/// <returns>The length in bytes.</returns>
|
||||
public abstract long GetFileLength(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object representing a possible file.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path.</param>
|
||||
/// <returns>The representing object.</returns>
|
||||
/// <remarks>The file does not need to exist.</remarks>
|
||||
public virtual DiscFileInfo GetFileInfo(string path)
|
||||
{
|
||||
return new DiscFileInfo(this, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object representing a possible directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The directory path.</param>
|
||||
/// <returns>The representing object.</returns>
|
||||
/// <remarks>The directory does not need to exist.</remarks>
|
||||
public virtual DiscDirectoryInfo GetDirectoryInfo(string path)
|
||||
{
|
||||
return new DiscDirectoryInfo(this, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object representing a possible file system object (file or directory).
|
||||
/// </summary>
|
||||
/// <param name="path">The file system path.</param>
|
||||
/// <returns>The representing object.</returns>
|
||||
/// <remarks>The file system object does not need to exist.</remarks>
|
||||
public virtual DiscFileSystemInfo GetFileSystemInfo(string path)
|
||||
{
|
||||
return new DiscFileSystemInfo(this, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the boot code of the file system into a byte array.
|
||||
/// </summary>
|
||||
/// <returns>The boot code, or <c>null</c> if not available.</returns>
|
||||
public virtual byte[] ReadBootCode()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Size of the Filesystem in bytes
|
||||
/// </summary>
|
||||
public abstract long Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used space of the Filesystem in bytes
|
||||
/// </summary>
|
||||
public abstract long UsedSpace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Available space of the Filesystem in bytes
|
||||
/// </summary>
|
||||
public abstract long AvailableSpace { get; }
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this instance, releasing all resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this instance.
|
||||
/// </summary>
|
||||
/// <param name="disposing">The value <c>true</c> if Disposing.</param>
|
||||
protected virtual void Dispose(bool disposing) {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for objects that validate file system integrity.
|
||||
/// </summary>
|
||||
/// <remarks>Instances of this class do not offer the ability to fix/correct
|
||||
/// file system issues, just to perform a limited number of checks on
|
||||
/// integrity of the file system.</remarks>
|
||||
public abstract class DiscFileSystemChecker
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks the integrity of a file system held in a stream.
|
||||
/// </summary>
|
||||
/// <param name="reportOutput">A report on issues found.</param>
|
||||
/// <param name="levels">The amount of detail to report.</param>
|
||||
/// <returns><c>true</c> if the file system appears valid, else <c>false</c>.</returns>
|
||||
public abstract bool Check(TextWriter reportOutput, ReportLevels levels);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Internal;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the base class for both <see cref="DiscFileInfo"/> and <see cref="DiscDirectoryInfo"/> objects.
|
||||
/// </summary>
|
||||
public class DiscFileSystemInfo
|
||||
{
|
||||
internal DiscFileSystemInfo(DiscFileSystem fileSystem, string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
}
|
||||
|
||||
FileSystem = fileSystem;
|
||||
Path = path.Trim('\\');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="System.IO.FileAttributes"/> of the current <see cref="DiscFileSystemInfo"/> object.
|
||||
/// </summary>
|
||||
public virtual FileAttributes Attributes
|
||||
{
|
||||
get { return FileSystem.GetAttributes(Path); }
|
||||
set { FileSystem.SetAttributes(Path, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the creation time (in local time) of the current <see cref="DiscFileSystemInfo"/> object.
|
||||
/// </summary>
|
||||
public virtual DateTime CreationTime
|
||||
{
|
||||
get { return CreationTimeUtc.ToLocalTime(); }
|
||||
set { CreationTimeUtc = value.ToUniversalTime(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the creation time (in UTC) of the current <see cref="DiscFileSystemInfo"/> object.
|
||||
/// </summary>
|
||||
public virtual DateTime CreationTimeUtc
|
||||
{
|
||||
get { return FileSystem.GetCreationTimeUtc(Path); }
|
||||
set { FileSystem.SetCreationTimeUtc(Path, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file system object exists.
|
||||
/// </summary>
|
||||
public virtual bool Exists
|
||||
{
|
||||
get { return FileSystem.Exists(Path); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the extension part of the file or directory name.
|
||||
/// </summary>
|
||||
public virtual string Extension
|
||||
{
|
||||
get
|
||||
{
|
||||
string name = Name;
|
||||
int sepIdx = name.LastIndexOf('.');
|
||||
if (sepIdx >= 0)
|
||||
{
|
||||
return name.Substring(sepIdx + 1);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file system the referenced file or directory exists on.
|
||||
/// </summary>
|
||||
public DiscFileSystem FileSystem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of the file or directory.
|
||||
/// </summary>
|
||||
public virtual string FullName
|
||||
{
|
||||
get { return Path; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last time (in local time) the file or directory was accessed.
|
||||
/// </summary>
|
||||
/// <remarks>Read-only file systems will never update this value, it will remain at a fixed value.</remarks>
|
||||
public virtual DateTime LastAccessTime
|
||||
{
|
||||
get { return LastAccessTimeUtc.ToLocalTime(); }
|
||||
set { LastAccessTimeUtc = value.ToUniversalTime(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last time (in UTC) the file or directory was accessed.
|
||||
/// </summary>
|
||||
/// <remarks>Read-only file systems will never update this value, it will remain at a fixed value.</remarks>
|
||||
public virtual DateTime LastAccessTimeUtc
|
||||
{
|
||||
get { return FileSystem.GetLastAccessTimeUtc(Path); }
|
||||
set { FileSystem.SetLastAccessTimeUtc(Path, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last time (in local time) the file or directory was written to.
|
||||
/// </summary>
|
||||
public virtual DateTime LastWriteTime
|
||||
{
|
||||
get { return LastWriteTimeUtc.ToLocalTime(); }
|
||||
set { LastWriteTimeUtc = value.ToUniversalTime(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last time (in UTC) the file or directory was written to.
|
||||
/// </summary>
|
||||
public virtual DateTime LastWriteTimeUtc
|
||||
{
|
||||
get { return FileSystem.GetLastWriteTimeUtc(Path); }
|
||||
set { FileSystem.SetLastWriteTimeUtc(Path, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the file or directory.
|
||||
/// </summary>
|
||||
public virtual string Name
|
||||
{
|
||||
get { return Utilities.GetFileFromPath(Path); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DiscDirectoryInfo"/> of the directory containing the current <see cref="DiscFileSystemInfo"/> object.
|
||||
/// </summary>
|
||||
public virtual DiscDirectoryInfo Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DiscDirectoryInfo(FileSystem, Utilities.GetDirectoryFromPath(Path));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the referenced file.
|
||||
/// </summary>
|
||||
protected string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file or directory.
|
||||
/// </summary>
|
||||
public virtual void Delete()
|
||||
{
|
||||
if ((Attributes & FileAttributes.Directory) != 0)
|
||||
{
|
||||
FileSystem.DeleteDirectory(Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileSystem.DeleteFile(Path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if <paramref name="obj"/> is equivalent to this object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj"/> is equivalent, else <c>false</c>.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
DiscFileSystemInfo asInfo = obj as DiscFileSystemInfo;
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return string.Compare(Path, asInfo.Path, StringComparison.Ordinal) == 0 &&
|
||||
Equals(FileSystem, asInfo.FileSystem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code for this object.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Path.GetHashCode() ^ FileSystem.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Common file system options.
|
||||
/// </summary>
|
||||
/// <remarks>Not all options are honoured by all file systems.</remarks>
|
||||
public class DiscFileSystemOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the random number generator the file system should use.
|
||||
/// </summary>
|
||||
/// <remarks>This option is normally <c>null</c>, which is fine for most purposes.
|
||||
/// Use this option when you need to finely control the filesystem for
|
||||
/// reproducibility of behaviour (for example in a test harness).</remarks>
|
||||
public Random RandomNumberGenerator { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using DiscUtils.CoreCompat;
|
||||
using DiscUtils.Internal;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all disk image builders.
|
||||
/// </summary>
|
||||
public abstract class DiskImageBuilder
|
||||
{
|
||||
private static Dictionary<string, VirtualDiskFactory> _typeMap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the geometry of this disk, as reported by the BIOS, will be implied from the content stream if not set.
|
||||
/// </summary>
|
||||
public Geometry BiosGeometry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content for this disk, implying the size of the disk.
|
||||
/// </summary>
|
||||
public SparseStream Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the adapter type for created virtual disk, for file formats that encode this information.
|
||||
/// </summary>
|
||||
public virtual GenericDiskAdapterType GenericAdapterType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the geometry of this disk, will be implied from the content stream if not set.
|
||||
/// </summary>
|
||||
public Geometry Geometry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this file format preserves BIOS geometry information.
|
||||
/// </summary>
|
||||
public virtual bool PreservesBiosGeometry
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
private static Dictionary<string, VirtualDiskFactory> TypeMap
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_typeMap == null)
|
||||
{
|
||||
InitializeMaps();
|
||||
}
|
||||
|
||||
return _typeMap;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance that constructs the specified type (and variant) of virtual disk image.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of image to build (VHD, VMDK, etc).</param>
|
||||
/// <param name="variant">The variant type (differencing/dynamic, fixed/static, etc).</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
public static DiskImageBuilder GetBuilder(string type, string variant)
|
||||
{
|
||||
VirtualDiskFactory factory;
|
||||
if (!TypeMap.TryGetValue(type, out factory))
|
||||
{
|
||||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unknown disk type '{0}'", type), nameof(type));
|
||||
}
|
||||
|
||||
return factory.GetImageBuilder(variant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the construction of the disk image.
|
||||
/// </summary>
|
||||
/// <param name="baseName">The base name for the disk images.</param>
|
||||
/// <returns>A set of one or more logical files that constitute the
|
||||
/// disk image. The first file is the 'primary' file that is normally attached to VMs.</returns>
|
||||
/// <remarks>The supplied <c>baseName</c> is the start of the file name, with no file
|
||||
/// extension. The set of file specifications will indicate the actual name corresponding
|
||||
/// to each logical file that comprises the disk image. For example, given a base name
|
||||
/// 'foo', the files 'foo.vmdk' and 'foo-flat.vmdk' could be returned.</remarks>
|
||||
public abstract DiskImageFileSpecification[] Build(string baseName);
|
||||
|
||||
private static void InitializeMaps()
|
||||
{
|
||||
Dictionary<string, VirtualDiskFactory> typeMap = new Dictionary<string, VirtualDiskFactory>();
|
||||
|
||||
foreach (Type type in ReflectionHelper.GetAssembly(typeof(VirtualDisk)).GetTypes())
|
||||
{
|
||||
VirtualDiskFactoryAttribute attr = (VirtualDiskFactoryAttribute)ReflectionHelper.GetCustomAttribute(type, typeof(VirtualDiskFactoryAttribute), false);
|
||||
if (attr != null)
|
||||
{
|
||||
VirtualDiskFactory factory = (VirtualDiskFactory)Activator.CreateInstance(type);
|
||||
typeMap.Add(attr.Type, factory);
|
||||
}
|
||||
}
|
||||
|
||||
_typeMap = typeMap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a particular file that is a constituent part of a virtual disk.
|
||||
/// </summary>
|
||||
public sealed class DiskImageFileSpecification
|
||||
{
|
||||
private readonly StreamBuilder _builder;
|
||||
|
||||
internal DiskImageFileSpecification(string name, StreamBuilder builder)
|
||||
{
|
||||
Name = name;
|
||||
_builder = builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets name of the file.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object that provides access to the file's content.
|
||||
/// </summary>
|
||||
/// <returns>A stream object that contains the file's content.</returns>
|
||||
public SparseStream OpenStream()
|
||||
{
|
||||
return _builder.Build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class ExtentStream : Stream
|
||||
{
|
||||
private readonly uint _dataLength;
|
||||
private readonly byte _fileUnitSize;
|
||||
private readonly byte _interleaveGapSize;
|
||||
|
||||
private readonly Stream _isoStream;
|
||||
private long _position;
|
||||
|
||||
private readonly uint _startBlock;
|
||||
private readonly int _sectorSize;
|
||||
|
||||
public ExtentStream(Stream isoStream, uint startBlock, uint dataLength, byte fileUnitSize,
|
||||
byte interleaveGapSize, int sectorSize)
|
||||
{
|
||||
_isoStream = isoStream;
|
||||
_startBlock = startBlock;
|
||||
_dataLength = dataLength;
|
||||
_fileUnitSize = fileUnitSize;
|
||||
_interleaveGapSize = interleaveGapSize;
|
||||
_sectorSize = sectorSize;
|
||||
|
||||
if (_fileUnitSize != 0 || _interleaveGapSize != 0)
|
||||
{
|
||||
throw new NotSupportedException("Non-contiguous extents not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { return _dataLength; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
public override void Flush() {}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_position > _dataLength)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int toRead = (int)Math.Min((uint)count, _dataLength - _position);
|
||||
|
||||
_isoStream.Position = _position + _startBlock * (long)_sectorSize;
|
||||
int numRead = _isoStream.Read(buffer, offset, toRead);
|
||||
_position += numRead;
|
||||
return numRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long newPos = offset;
|
||||
if (origin == SeekOrigin.Current)
|
||||
{
|
||||
newPos += _position;
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
newPos += _dataLength;
|
||||
}
|
||||
|
||||
_position = newPos;
|
||||
return newPos;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Vfs;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class File : IVfsFile
|
||||
{
|
||||
protected IsoContext _context;
|
||||
protected ReaderDirEntry _dirEntry;
|
||||
|
||||
public File(IsoContext context, ReaderDirEntry dirEntry)
|
||||
{
|
||||
_context = context;
|
||||
_dirEntry = dirEntry;
|
||||
}
|
||||
|
||||
public virtual byte[] SystemUseData
|
||||
{
|
||||
get { return _dirEntry.Record.SystemUseData; }
|
||||
}
|
||||
|
||||
public UnixFileSystemInfo UnixFileInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_context.SuspDetected || string.IsNullOrEmpty(_context.RockRidgeIdentifier))
|
||||
{
|
||||
throw new InvalidOperationException("No RockRidge file information available");
|
||||
}
|
||||
|
||||
SuspRecords suspRecords = new SuspRecords(_context, SystemUseData, 0);
|
||||
|
||||
PosixFileInfoSystemUseEntry pfi =
|
||||
suspRecords.GetEntry<PosixFileInfoSystemUseEntry>(_context.RockRidgeIdentifier, "PX");
|
||||
if (pfi != null)
|
||||
{
|
||||
return new UnixFileSystemInfo
|
||||
{
|
||||
FileType = (UnixFileType)((pfi.FileMode >> 12) & 0xff),
|
||||
Permissions = (UnixFilePermissions)(pfi.FileMode & 0xfff),
|
||||
UserId = (int)pfi.UserId,
|
||||
GroupId = (int)pfi.GroupId,
|
||||
Inode = pfi.Inode,
|
||||
LinkCount = (int)pfi.NumLinks
|
||||
};
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No RockRidge file information available for this file");
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime LastAccessTimeUtc
|
||||
{
|
||||
get { return _dirEntry.LastAccessTimeUtc; }
|
||||
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public DateTime LastWriteTimeUtc
|
||||
{
|
||||
get { return _dirEntry.LastWriteTimeUtc; }
|
||||
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public DateTime CreationTimeUtc
|
||||
{
|
||||
get { return _dirEntry.CreationTimeUtc; }
|
||||
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public FileAttributes FileAttributes
|
||||
{
|
||||
get { return _dirEntry.FileAttributes; }
|
||||
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public long FileLength
|
||||
{
|
||||
get { return _dirEntry.Record.DataLength; }
|
||||
}
|
||||
|
||||
public IBuffer FileContent
|
||||
{
|
||||
get
|
||||
{
|
||||
ExtentStream es = new ExtentStream(_context.DataStream, _dirEntry.Record.LocationOfExtent,
|
||||
_dirEntry.Record.DataLength, _dirEntry.Record.FileUnitSize, _dirEntry.Record.InterleaveGapSize, _context.SectorSize);
|
||||
return new StreamBuffer(es, Ownership.Dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
internal class FileExtent : BuilderExtent
|
||||
{
|
||||
private readonly BuildFileInfo _fileInfo;
|
||||
|
||||
private Stream _readStream;
|
||||
|
||||
public FileExtent(BuildFileInfo fileInfo, long start)
|
||||
: base(start, fileInfo.GetDataSize(Encoding.ASCII))
|
||||
{
|
||||
_fileInfo = fileInfo;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_readStream != null)
|
||||
{
|
||||
_fileInfo.CloseStream(_readStream);
|
||||
_readStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void PrepareForRead()
|
||||
{
|
||||
_readStream = _fileInfo.OpenStream();
|
||||
}
|
||||
|
||||
public override int Read(long diskOffset, byte[] block, int offset, int count)
|
||||
{
|
||||
long relPos = diskOffset - Start;
|
||||
int totalRead = 0;
|
||||
|
||||
// Don't arbitrarily set position, just in case stream implementation is
|
||||
// non-seeking, and we're doing sequential reads
|
||||
if (_readStream.Position != relPos)
|
||||
{
|
||||
_readStream.Position = relPos;
|
||||
}
|
||||
|
||||
// Read up to EOF
|
||||
int numRead = _readStream.Read(block, offset, count);
|
||||
totalRead += numRead;
|
||||
while (numRead > 0 && totalRead < count)
|
||||
{
|
||||
numRead = _readStream.Read(block, offset + totalRead, count - totalRead);
|
||||
totalRead += numRead;
|
||||
}
|
||||
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
public override void DisposeReadState()
|
||||
{
|
||||
_fileInfo.CloseStream(_readStream);
|
||||
_readStream = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace DiscUtils.Iso9660
|
||||
{
|
||||
[Flags]
|
||||
internal enum FileFlags : byte
|
||||
{
|
||||
None = 0x00,
|
||||
Hidden = 0x01,
|
||||
Directory = 0x02,
|
||||
AssociatedFile = 0x04,
|
||||
Record = 0x08,
|
||||
Protection = 0x10,
|
||||
MultiExtent = 0x80
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Internal;
|
||||
using DiscUtils.Setup;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
public abstract class FileLocator
|
||||
{
|
||||
public abstract bool Exists(string fileName);
|
||||
|
||||
public Stream Open(string fileName, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
var args = new FileOpenEventArgs(fileName, mode, access, share, OpenFile);
|
||||
SetupHelper.OnOpeningFile(this, args);
|
||||
if (args.Result != null)
|
||||
return args.Result;
|
||||
return OpenFile(args.FileName, args.FileMode, args.FileAccess, args.FileShare);
|
||||
}
|
||||
|
||||
protected abstract Stream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share);
|
||||
|
||||
public abstract FileLocator GetRelativeLocator(string path);
|
||||
|
||||
public abstract string GetFullPath(string path);
|
||||
|
||||
public abstract string GetDirectoryFromPath(string path);
|
||||
|
||||
public abstract string GetFileFromPath(string path);
|
||||
|
||||
public abstract DateTime GetLastWriteTimeUtc(string path);
|
||||
|
||||
public abstract bool HasCommonRoot(FileLocator other);
|
||||
|
||||
public abstract string ResolveRelativePath(string path);
|
||||
|
||||
internal string MakeRelativePath(FileLocator fileLocator, string path)
|
||||
{
|
||||
if (!HasCommonRoot(fileLocator))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string ourFullPath = GetFullPath(string.Empty) + @"\";
|
||||
string otherFullPath = fileLocator.GetFullPath(path);
|
||||
|
||||
return Utilities.MakeRelativePath(otherFullPath, ourFullPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class holding information about a file system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// File system implementations derive from this class, to provide information about the file system.
|
||||
/// </remarks>
|
||||
public abstract class FileSystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a one-line description of the file system.
|
||||
/// </summary>
|
||||
public abstract string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the file system.
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens a volume using the file system.
|
||||
/// </summary>
|
||||
/// <param name="volume">The volume to access.</param>
|
||||
/// <returns>A file system instance.</returns>
|
||||
public DiscFileSystem Open(VolumeInfo volume)
|
||||
{
|
||||
return Open(volume, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream using the file system.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to access.</param>
|
||||
/// <returns>A file system instance.</returns>
|
||||
public DiscFileSystem Open(Stream stream)
|
||||
{
|
||||
return Open(stream, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a volume using the file system.
|
||||
/// </summary>
|
||||
/// <param name="volume">The volume to access.</param>
|
||||
/// <param name="parameters">Parameters for the file system.</param>
|
||||
/// <returns>A file system instance.</returns>
|
||||
public abstract DiscFileSystem Open(VolumeInfo volume, FileSystemParameters parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream using the file system.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to access.</param>
|
||||
/// <param name="parameters">Parameters for the file system.</param>
|
||||
/// <returns>A file system instance.</returns>
|
||||
public abstract DiscFileSystem Open(Stream stream, FileSystemParameters parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the file system.
|
||||
/// </summary>
|
||||
/// <returns>The file system name.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using DiscUtils.CoreCompat;
|
||||
using DiscUtils.Vfs;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// FileSystemManager determines which file systems are present on a volume.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The static detection methods detect default file systems. To plug in additional
|
||||
/// file systems, create an instance of this class and call RegisterFileSystems.
|
||||
/// </remarks>
|
||||
public static class FileSystemManager
|
||||
{
|
||||
private static readonly List<VfsFileSystemFactory> _factories;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the FileSystemManager class.
|
||||
/// </summary>
|
||||
static FileSystemManager()
|
||||
{
|
||||
_factories = new List<VfsFileSystemFactory>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers new file systems with an instance of this class.
|
||||
/// </summary>
|
||||
/// <param name="factory">The detector for the new file systems.</param>
|
||||
public static void RegisterFileSystems(VfsFileSystemFactory factory)
|
||||
{
|
||||
_factories.Add(factory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers new file systems detected in an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly to inspect.</param>
|
||||
/// <remarks>
|
||||
/// To be detected, the <c>VfsFileSystemFactory</c> instances must be marked with the
|
||||
/// <c>VfsFileSystemFactoryAttribute</c>> attribute.
|
||||
/// </remarks>
|
||||
public static void RegisterFileSystems(Assembly assembly)
|
||||
{
|
||||
_factories.AddRange(DetectFactories(assembly));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect which file systems are present on a volume.
|
||||
/// </summary>
|
||||
/// <param name="volume">The volume to inspect.</param>
|
||||
/// <returns>The list of file systems detected.</returns>
|
||||
public static FileSystemInfo[] DetectFileSystems(VolumeInfo volume)
|
||||
{
|
||||
using (Stream s = volume.Open())
|
||||
{
|
||||
return DoDetect(s, volume);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect which file systems are present in a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to inspect.</param>
|
||||
/// <returns>The list of file systems detected.</returns>
|
||||
public static FileSystemInfo[] DetectFileSystems(Stream stream)
|
||||
{
|
||||
return DoDetect(stream, null);
|
||||
}
|
||||
|
||||
private static IEnumerable<VfsFileSystemFactory> DetectFactories(Assembly assembly)
|
||||
{
|
||||
foreach (Type type in assembly.GetTypes())
|
||||
{
|
||||
Attribute attrib = ReflectionHelper.GetCustomAttribute(type, typeof(VfsFileSystemFactoryAttribute), false);
|
||||
if (attrib == null)
|
||||
continue;
|
||||
|
||||
yield return (VfsFileSystemFactory)Activator.CreateInstance(type);
|
||||
}
|
||||
}
|
||||
|
||||
private static FileSystemInfo[] DoDetect(Stream stream, VolumeInfo volume)
|
||||
{
|
||||
BufferedStream detectStream = new BufferedStream(stream);
|
||||
List<FileSystemInfo> detected = new List<FileSystemInfo>();
|
||||
|
||||
foreach (VfsFileSystemFactory factory in _factories)
|
||||
{
|
||||
detected.AddRange(factory.Detect(detectStream, volume));
|
||||
}
|
||||
|
||||
return detected.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Class with generic file system parameters.
|
||||
/// </summary>
|
||||
/// <remarks>Note - not all parameters apply to all types of file system.</remarks>
|
||||
public sealed class FileSystemParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the character encoding for file names, or <c>null</c> for default.
|
||||
/// </summary>
|
||||
/// <remarks>Some file systems, such as FAT, don't specify a particular character set for
|
||||
/// file names. This parameter determines the character set that will be used for such
|
||||
/// file systems.</remarks>
|
||||
public Encoding FileNameEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the algorithm to convert file system time to UTC.
|
||||
/// </summary>
|
||||
/// <remarks>Some file system, such as FAT, don't have a defined way to convert from file system
|
||||
/// time (local time where the file system is authored) to UTC time. This parameter determines
|
||||
/// the algorithm to use.</remarks>
|
||||
public TimeConverter TimeConverter { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using DiscUtils.Internal;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
[VirtualDiskTransport("file")]
|
||||
internal sealed class FileTransport : VirtualDiskTransport
|
||||
{
|
||||
private string _extraInfo;
|
||||
private string _path;
|
||||
|
||||
public override bool IsRawDisk
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override void Connect(Uri uri, string username, string password)
|
||||
{
|
||||
_path = uri.LocalPath;
|
||||
_extraInfo = uri.Fragment.TrimStart('#');
|
||||
|
||||
if (!Directory.Exists(Path.GetDirectoryName(_path)))
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
string.Format(CultureInfo.InvariantCulture, "No such file '{0}'", uri.OriginalString), _path);
|
||||
}
|
||||
}
|
||||
|
||||
public override VirtualDisk OpenDisk(FileAccess access)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override FileLocator GetFileLocator()
|
||||
{
|
||||
return new LocalFileLocator(Path.GetDirectoryName(_path) + @"\");
|
||||
}
|
||||
|
||||
public override string GetFileName()
|
||||
{
|
||||
return Path.GetFileName(_path);
|
||||
}
|
||||
|
||||
public override string GetExtraInfo()
|
||||
{
|
||||
return _extraInfo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// The supported Floppy Disk logical formats.
|
||||
/// </summary>
|
||||
public enum FloppyDiskType
|
||||
{
|
||||
/// <summary>
|
||||
/// 720KiB capacity disk.
|
||||
/// </summary>
|
||||
DoubleDensity = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 1440KiB capacity disk.
|
||||
/// </summary>
|
||||
HighDensity = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 2880KiB capacity disk.
|
||||
/// </summary>
|
||||
Extended = 2
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Well known hard disk adaptor types.
|
||||
/// </summary>
|
||||
public enum GenericDiskAdapterType
|
||||
{
|
||||
/// <summary>
|
||||
/// IDE adaptor.
|
||||
/// </summary>
|
||||
Ide = 0,
|
||||
|
||||
/// <summary>
|
||||
/// SCSI adaptor.
|
||||
/// </summary>
|
||||
Scsi = 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,477 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Class whose instances represent disk geometries.
|
||||
/// </summary>
|
||||
/// <remarks>Instances of this class are immutable.</remarks>
|
||||
public sealed class Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Geometry class. The default 512 bytes per sector is assumed.
|
||||
/// </summary>
|
||||
/// <param name="cylinders">The number of cylinders of the disk.</param>
|
||||
/// <param name="headsPerCylinder">The number of heads (aka platters) of the disk.</param>
|
||||
/// <param name="sectorsPerTrack">The number of sectors per track/cylinder of the disk.</param>
|
||||
public Geometry(int cylinders, int headsPerCylinder, int sectorsPerTrack)
|
||||
{
|
||||
Cylinders = cylinders;
|
||||
HeadsPerCylinder = headsPerCylinder;
|
||||
SectorsPerTrack = sectorsPerTrack;
|
||||
BytesPerSector = 512;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Geometry class.
|
||||
/// </summary>
|
||||
/// <param name="cylinders">The number of cylinders of the disk.</param>
|
||||
/// <param name="headsPerCylinder">The number of heads (aka platters) of the disk.</param>
|
||||
/// <param name="sectorsPerTrack">The number of sectors per track/cylinder of the disk.</param>
|
||||
/// <param name="bytesPerSector">The number of bytes per sector of the disk.</param>
|
||||
public Geometry(int cylinders, int headsPerCylinder, int sectorsPerTrack, int bytesPerSector)
|
||||
{
|
||||
Cylinders = cylinders;
|
||||
HeadsPerCylinder = headsPerCylinder;
|
||||
SectorsPerTrack = sectorsPerTrack;
|
||||
BytesPerSector = bytesPerSector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Geometry class.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The total capacity of the disk.</param>
|
||||
/// <param name="headsPerCylinder">The number of heads (aka platters) of the disk.</param>
|
||||
/// <param name="sectorsPerTrack">The number of sectors per track/cylinder of the disk.</param>
|
||||
/// <param name="bytesPerSector">The number of bytes per sector of the disk.</param>
|
||||
public Geometry(long capacity, int headsPerCylinder, int sectorsPerTrack, int bytesPerSector)
|
||||
{
|
||||
Cylinders = (int)(capacity / (headsPerCylinder * (long)sectorsPerTrack * bytesPerSector));
|
||||
HeadsPerCylinder = headsPerCylinder;
|
||||
SectorsPerTrack = sectorsPerTrack;
|
||||
BytesPerSector = bytesPerSector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes in each sector.
|
||||
/// </summary>
|
||||
public int BytesPerSector { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total capacity of the disk (in bytes).
|
||||
/// </summary>
|
||||
public long Capacity
|
||||
{
|
||||
get { return TotalSectorsLong * BytesPerSector; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of cylinders.
|
||||
/// </summary>
|
||||
public int Cylinders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of heads (aka platters).
|
||||
/// </summary>
|
||||
public int HeadsPerCylinder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the Geometry is representable both by the BIOS and by IDE.
|
||||
/// </summary>
|
||||
public bool IsBiosAndIdeSafe
|
||||
{
|
||||
get { return Cylinders <= 1024 && HeadsPerCylinder <= 16 && SectorsPerTrack <= 63; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the Geometry is consistent with the values a BIOS can support.
|
||||
/// </summary>
|
||||
public bool IsBiosSafe
|
||||
{
|
||||
get { return Cylinders <= 1024 && HeadsPerCylinder <= 255 && SectorsPerTrack <= 63; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the Geometry is consistent with the values IDE can represent.
|
||||
/// </summary>
|
||||
public bool IsIdeSafe
|
||||
{
|
||||
get { return Cylinders <= 65536 && HeadsPerCylinder <= 16 && SectorsPerTrack <= 255; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the last sector on the disk.
|
||||
/// </summary>
|
||||
public ChsAddress LastSector
|
||||
{
|
||||
get { return new ChsAddress(Cylinders - 1, HeadsPerCylinder - 1, SectorsPerTrack); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a null geometry, which has 512-byte sectors but zero sectors, tracks or cylinders.
|
||||
/// </summary>
|
||||
public static Geometry Null
|
||||
{
|
||||
get { return new Geometry(0, 0, 0, 512); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of sectors per track.
|
||||
/// </summary>
|
||||
public int SectorsPerTrack { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size of the disk (in sectors).
|
||||
/// </summary>
|
||||
[Obsolete("Use TotalSectorsLong instead, to support very large disks.")]
|
||||
public int TotalSectors
|
||||
{
|
||||
get { return Cylinders * HeadsPerCylinder * SectorsPerTrack; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size of the disk (in sectors).
|
||||
/// </summary>
|
||||
public long TotalSectorsLong
|
||||
{
|
||||
get { return Cylinders * (long)HeadsPerCylinder * SectorsPerTrack; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the 'Large' BIOS geometry for a disk, given it's physical geometry.
|
||||
/// </summary>
|
||||
/// <param name="ideGeometry">The physical (aka IDE) geometry of the disk.</param>
|
||||
/// <returns>The geometry a BIOS using the 'Large' method for calculating disk geometry will indicate for the disk.</returns>
|
||||
public static Geometry LargeBiosGeometry(Geometry ideGeometry)
|
||||
{
|
||||
int cylinders = ideGeometry.Cylinders;
|
||||
int heads = ideGeometry.HeadsPerCylinder;
|
||||
int sectors = ideGeometry.SectorsPerTrack;
|
||||
|
||||
while (cylinders > 1024 && heads <= 127)
|
||||
{
|
||||
cylinders >>= 1;
|
||||
heads <<= 1;
|
||||
}
|
||||
|
||||
return new Geometry(cylinders, heads, sectors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the 'LBA Assisted' BIOS geometry for a disk, given it's capacity.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The capacity of the disk.</param>
|
||||
/// <returns>The geometry a BIOS using the 'LBA Assisted' method for calculating disk geometry will indicate for the disk.</returns>
|
||||
public static Geometry LbaAssistedBiosGeometry(long capacity)
|
||||
{
|
||||
int heads;
|
||||
if (capacity <= 504 * Sizes.OneMiB)
|
||||
{
|
||||
heads = 16;
|
||||
}
|
||||
else if (capacity <= 1008 * Sizes.OneMiB)
|
||||
{
|
||||
heads = 32;
|
||||
}
|
||||
else if (capacity <= 2016 * Sizes.OneMiB)
|
||||
{
|
||||
heads = 64;
|
||||
}
|
||||
else if (capacity <= 4032 * Sizes.OneMiB)
|
||||
{
|
||||
heads = 128;
|
||||
}
|
||||
else
|
||||
{
|
||||
heads = 255;
|
||||
}
|
||||
|
||||
int sectors = 63;
|
||||
int cylinders = (int)Math.Min(1024, capacity / (sectors * (long)heads * Sizes.Sector));
|
||||
return new Geometry(cylinders, heads, sectors, Sizes.Sector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a geometry into one that is BIOS-safe, if not already.
|
||||
/// </summary>
|
||||
/// <param name="geometry">The geometry to make BIOS-safe.</param>
|
||||
/// <param name="capacity">The capacity of the disk.</param>
|
||||
/// <returns>The new geometry.</returns>
|
||||
/// <remarks>This method returns the LBA-Assisted geometry if the given geometry isn't BIOS-safe.</remarks>
|
||||
public static Geometry MakeBiosSafe(Geometry geometry, long capacity)
|
||||
{
|
||||
if (geometry == null)
|
||||
{
|
||||
return LbaAssistedBiosGeometry(capacity);
|
||||
}
|
||||
if (geometry.IsBiosSafe)
|
||||
{
|
||||
return geometry;
|
||||
}
|
||||
return LbaAssistedBiosGeometry(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a sensible disk geometry for a disk capacity using the VHD algorithm (errs under).
|
||||
/// </summary>
|
||||
/// <param name="capacity">The desired capacity of the disk.</param>
|
||||
/// <returns>The appropriate disk geometry.</returns>
|
||||
/// <remarks>The geometry returned tends to produce a disk with less capacity
|
||||
/// than requested (an exact capacity is not always possible). The geometry returned is the IDE
|
||||
/// (aka Physical) geometry of the disk, not necessarily the geometry used by the BIOS.</remarks>
|
||||
public static Geometry FromCapacity(long capacity)
|
||||
{
|
||||
return FromCapacity(capacity, Sizes.Sector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a sensible disk geometry for a disk capacity using the VHD algorithm (errs under).
|
||||
/// </summary>
|
||||
/// <param name="capacity">The desired capacity of the disk.</param>
|
||||
/// <param name="sectorSize">The logical sector size of the disk.</param>
|
||||
/// <returns>The appropriate disk geometry.</returns>
|
||||
/// <remarks>The geometry returned tends to produce a disk with less capacity
|
||||
/// than requested (an exact capacity is not always possible). The geometry returned is the IDE
|
||||
/// (aka Physical) geometry of the disk, not necessarily the geometry used by the BIOS.</remarks>
|
||||
public static Geometry FromCapacity(long capacity, int sectorSize)
|
||||
{
|
||||
int totalSectors;
|
||||
int cylinders;
|
||||
int headsPerCylinder;
|
||||
int sectorsPerTrack;
|
||||
|
||||
// If more than ~128GB truncate at ~128GB
|
||||
if (capacity > 65535 * (long)16 * 255 * sectorSize)
|
||||
{
|
||||
totalSectors = 65535 * 16 * 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalSectors = (int)(capacity / sectorSize);
|
||||
}
|
||||
|
||||
// If more than ~32GB, break partition table compatibility.
|
||||
// Partition table has max 63 sectors per track. Otherwise
|
||||
// we're looking for a geometry that's valid for both BIOS
|
||||
// and ATA.
|
||||
if (totalSectors > 65535 * 16 * 63)
|
||||
{
|
||||
sectorsPerTrack = 255;
|
||||
headsPerCylinder = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
sectorsPerTrack = 17;
|
||||
int cylindersTimesHeads = totalSectors / sectorsPerTrack;
|
||||
headsPerCylinder = (cylindersTimesHeads + 1023) / 1024;
|
||||
|
||||
if (headsPerCylinder < 4)
|
||||
{
|
||||
headsPerCylinder = 4;
|
||||
}
|
||||
|
||||
// If we need more than 1023 cylinders, or 16 heads, try more sectors per track
|
||||
if (cylindersTimesHeads >= headsPerCylinder * 1024U || headsPerCylinder > 16)
|
||||
{
|
||||
sectorsPerTrack = 31;
|
||||
headsPerCylinder = 16;
|
||||
cylindersTimesHeads = totalSectors / sectorsPerTrack;
|
||||
}
|
||||
|
||||
// We need 63 sectors per track to keep the cylinder count down
|
||||
if (cylindersTimesHeads >= headsPerCylinder * 1024U)
|
||||
{
|
||||
sectorsPerTrack = 63;
|
||||
headsPerCylinder = 16;
|
||||
}
|
||||
}
|
||||
|
||||
cylinders = totalSectors / sectorsPerTrack / headsPerCylinder;
|
||||
|
||||
return new Geometry(cylinders, headsPerCylinder, sectorsPerTrack, sectorSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a CHS (Cylinder,Head,Sector) address to a LBA (Logical Block Address).
|
||||
/// </summary>
|
||||
/// <param name="chsAddress">The CHS address to convert.</param>
|
||||
/// <returns>The Logical Block Address (in sectors).</returns>
|
||||
public long ToLogicalBlockAddress(ChsAddress chsAddress)
|
||||
{
|
||||
return ToLogicalBlockAddress(chsAddress.Cylinder, chsAddress.Head, chsAddress.Sector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a CHS (Cylinder,Head,Sector) address to a LBA (Logical Block Address).
|
||||
/// </summary>
|
||||
/// <param name="cylinder">The cylinder of the address.</param>
|
||||
/// <param name="head">The head of the address.</param>
|
||||
/// <param name="sector">The sector of the address.</param>
|
||||
/// <returns>The Logical Block Address (in sectors).</returns>
|
||||
public long ToLogicalBlockAddress(int cylinder, int head, int sector)
|
||||
{
|
||||
if (cylinder < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(cylinder), cylinder, "cylinder number is negative");
|
||||
}
|
||||
|
||||
if (head >= HeadsPerCylinder)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(head), head, "head number is larger than disk geometry");
|
||||
}
|
||||
|
||||
if (head < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(head), head, "head number is negative");
|
||||
}
|
||||
|
||||
if (sector > SectorsPerTrack)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sector), sector,
|
||||
"sector number is larger than disk geometry");
|
||||
}
|
||||
|
||||
if (sector < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sector), sector,
|
||||
"sector number is less than one (sectors are 1-based)");
|
||||
}
|
||||
|
||||
return (cylinder * (long)HeadsPerCylinder + head) * SectorsPerTrack + sector - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a LBA (Logical Block Address) to a CHS (Cylinder, Head, Sector) address.
|
||||
/// </summary>
|
||||
/// <param name="logicalBlockAddress">The logical block address (in sectors).</param>
|
||||
/// <returns>The address in CHS form.</returns>
|
||||
public ChsAddress ToChsAddress(long logicalBlockAddress)
|
||||
{
|
||||
if (logicalBlockAddress < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(logicalBlockAddress), logicalBlockAddress,
|
||||
"Logical Block Address is negative");
|
||||
}
|
||||
|
||||
int cylinder = (int)(logicalBlockAddress / (HeadsPerCylinder * SectorsPerTrack));
|
||||
int temp = (int)(logicalBlockAddress % (HeadsPerCylinder * SectorsPerTrack));
|
||||
int head = temp / SectorsPerTrack;
|
||||
int sector = temp % SectorsPerTrack + 1;
|
||||
|
||||
return new ChsAddress(cylinder, head, sector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates an IDE (aka Physical) geometry to a BIOS (aka Logical) geometry.
|
||||
/// </summary>
|
||||
/// <param name="translation">The translation to perform.</param>
|
||||
/// <returns>The translated disk geometry.</returns>
|
||||
public Geometry TranslateToBios(GeometryTranslation translation)
|
||||
{
|
||||
return TranslateToBios(0, translation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates an IDE (aka Physical) geometry to a BIOS (aka Logical) geometry.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The capacity of the disk, required if the geometry is an approximation on the actual disk size.</param>
|
||||
/// <param name="translation">The translation to perform.</param>
|
||||
/// <returns>The translated disk geometry.</returns>
|
||||
public Geometry TranslateToBios(long capacity, GeometryTranslation translation)
|
||||
{
|
||||
if (capacity <= 0)
|
||||
{
|
||||
capacity = TotalSectorsLong * 512L;
|
||||
}
|
||||
|
||||
switch (translation)
|
||||
{
|
||||
case GeometryTranslation.None:
|
||||
return this;
|
||||
|
||||
case GeometryTranslation.Auto:
|
||||
if (IsBiosSafe)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
return LbaAssistedBiosGeometry(capacity);
|
||||
|
||||
case GeometryTranslation.Lba:
|
||||
return LbaAssistedBiosGeometry(capacity);
|
||||
|
||||
case GeometryTranslation.Large:
|
||||
return LargeBiosGeometry(this);
|
||||
|
||||
default:
|
||||
throw new ArgumentException(
|
||||
string.Format(CultureInfo.InvariantCulture, "Translation mode '{0}' not yet implemented",
|
||||
translation), nameof(translation));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this object is equivalent to another.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test against.</param>
|
||||
/// <returns><c>true</c> if the <paramref name="obj"/> is equivalent, else <c>false</c>.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Geometry other = (Geometry)obj;
|
||||
|
||||
return Cylinders == other.Cylinders && HeadsPerCylinder == other.HeadsPerCylinder
|
||||
&& SectorsPerTrack == other.SectorsPerTrack && BytesPerSector == other.BytesPerSector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the hash code for this object.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Cylinders.GetHashCode() ^ HeadsPerCylinder.GetHashCode()
|
||||
^ SectorsPerTrack.GetHashCode() ^ BytesPerSector.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of this object, in the form (C/H/S).
|
||||
/// </summary>
|
||||
/// <returns>The string representation.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (BytesPerSector == 512)
|
||||
{
|
||||
return "(" + Cylinders + "/" + HeadsPerCylinder + "/" + SectorsPerTrack + ")";
|
||||
}
|
||||
return "(" + Cylinders + "/" + HeadsPerCylinder + "/" + SectorsPerTrack + ":" + BytesPerSector + ")";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for calculating a disk geometry from a capacity.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The disk capacity to convert.</param>
|
||||
/// <returns>The appropriate geometry for the disk.</returns>
|
||||
public delegate Geometry GeometryCalculation(long capacity);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of standard BIOS disk geometry translation methods.
|
||||
/// </summary>
|
||||
public enum GeometryTranslation
|
||||
{
|
||||
/// <summary>
|
||||
/// Apply no translation.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Automatic, based on the physical geometry select the most appropriate translation.
|
||||
/// </summary>
|
||||
Auto = 1,
|
||||
|
||||
/// <summary>
|
||||
/// LBA assisted translation, based on just the disk capacity.
|
||||
/// </summary>
|
||||
Lba = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Bit-shifting translation, based on the physical geometry of the disk.
|
||||
/// </summary>
|
||||
Large = 3
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Common interface for reading structures to/from byte arrays.
|
||||
/// </summary>
|
||||
public interface IByteArraySerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the total number of bytes the structure occupies.
|
||||
/// </summary>
|
||||
int Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the structure from a byte array.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to read from.</param>
|
||||
/// <param name="offset">The buffer offset to start reading from.</param>
|
||||
/// <returns>The number of bytes read.</returns>
|
||||
int ReadFrom(byte[] buffer, int offset);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a structure to a byte array.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to write to.</param>
|
||||
/// <param name="offset">The buffer offset to start writing at.</param>
|
||||
void WriteTo(byte[] buffer, int offset);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all file systems based on a cluster model.
|
||||
/// </summary>
|
||||
public interface IClusterBasedFileSystem : IFileSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the size (in bytes) of each cluster.
|
||||
/// </summary>
|
||||
long ClusterSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of clusters managed by the file system.
|
||||
/// </summary>
|
||||
long TotalClusters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a cluster (index) into an absolute byte position in the underlying stream.
|
||||
/// </summary>
|
||||
/// <param name="cluster">The cluster to convert.</param>
|
||||
/// <returns>The corresponding absolute byte position.</returns>
|
||||
long ClusterToOffset(long cluster);
|
||||
|
||||
/// <summary>
|
||||
/// Converts an absolute byte position in the underlying stream to a cluster (index).
|
||||
/// </summary>
|
||||
/// <param name="offset">The byte position to convert.</param>
|
||||
/// <returns>The cluster containing the specified byte.</returns>
|
||||
long OffsetToCluster(long offset);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a file name to the list of clusters occupied by the file's data.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to inspect.</param>
|
||||
/// <returns>The clusters.</returns>
|
||||
/// <remarks>Note that in some file systems, small files may not have dedicated
|
||||
/// clusters. Only dedicated clusters will be returned.</remarks>
|
||||
Range<long, long>[] PathToClusters(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a file name to the extents containing its data.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to inspect.</param>
|
||||
/// <returns>The file extents, as absolute byte positions in the underlying stream.</returns>
|
||||
/// <remarks>Use this method with caution - not all file systems will store all bytes
|
||||
/// directly in extents. Files may be compressed, sparse or encrypted. This method
|
||||
/// merely indicates where file data is stored, not what's stored.</remarks>
|
||||
StreamExtent[] PathToExtents(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that can convert between clusters and files.
|
||||
/// </summary>
|
||||
/// <returns>The cluster map.</returns>
|
||||
ClusterMap BuildClusterMap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface exposed by objects that can provide a structured trace of their content.
|
||||
/// </summary>
|
||||
public interface IDiagnosticTraceable
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a diagnostic report about the state of the object to a writer.
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to send the report to.</param>
|
||||
/// <param name="linePrefix">The prefix to place at the start of each line.</param>
|
||||
void Dump(TextWriter writer, string linePrefix);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Common interface for all file systems.
|
||||
/// </summary>
|
||||
public interface IFileSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file system is read-only or read-write.
|
||||
/// </summary>
|
||||
/// <returns>true if the file system is read-write.</returns>
|
||||
bool CanWrite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file system is thread-safe.
|
||||
/// </summary>
|
||||
bool IsThreadSafe { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root directory of the file system.
|
||||
/// </summary>
|
||||
DiscDirectoryInfo Root { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing file to a new file.
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">The source file.</param>
|
||||
/// <param name="destinationFile">The destination file.</param>
|
||||
void CopyFile(string sourceFile, string destinationFile);
|
||||
|
||||
/// <summary>
|
||||
/// Copies an existing file to a new file, allowing overwriting of an existing file.
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">The source file.</param>
|
||||
/// <param name="destinationFile">The destination file.</param>
|
||||
/// <param name="overwrite">Whether to permit over-writing of an existing file.</param>
|
||||
void CopyFile(string sourceFile, string destinationFile, bool overwrite);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the new directory.</param>
|
||||
void CreateDirectory(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory to delete.</param>
|
||||
void DeleteDirectory(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory, optionally with all descendants.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory to delete.</param>
|
||||
/// <param name="recursive">Determines if the all descendants should be deleted.</param>
|
||||
void DeleteDirectory(string path, bool recursive);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file to delete.</param>
|
||||
void DeleteFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the directory exists.</returns>
|
||||
bool DirectoryExists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a file exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the file exists.</returns>
|
||||
bool FileExists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a file or directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the file or directory exists.</returns>
|
||||
bool Exists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of subdirectories in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <returns>Array of directories.</returns>
|
||||
string[] GetDirectories(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of subdirectories in a specified directory matching a specified
|
||||
/// search pattern.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <returns>Array of directories matching the search pattern.</returns>
|
||||
string[] GetDirectories(string path, string searchPattern);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of subdirectories in a specified directory matching a specified
|
||||
/// search pattern, using a value to determine whether to search subdirectories.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
|
||||
/// <returns>Array of directories matching the search pattern.</returns>
|
||||
string[] GetDirectories(string path, string searchPattern, SearchOption searchOption);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <returns>Array of files.</returns>
|
||||
string[] GetFiles(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <returns>Array of files matching the search pattern.</returns>
|
||||
string[] GetFiles(string path, string searchPattern);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files in a specified directory matching a specified
|
||||
/// search pattern, using a value to determine whether to search subdirectories.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
|
||||
/// <returns>Array of files matching the search pattern.</returns>
|
||||
string[] GetFiles(string path, string searchPattern, SearchOption searchOption);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all files and subdirectories in a specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
|
||||
string[] GetFileSystemEntries(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of files and subdirectories in a specified directory matching a specified
|
||||
/// search pattern.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to search.</param>
|
||||
/// <param name="searchPattern">The search string to match against.</param>
|
||||
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
|
||||
string[] GetFileSystemEntries(string path, string searchPattern);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a directory.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectoryName">The directory to move.</param>
|
||||
/// <param name="destinationDirectoryName">The target directory name.</param>
|
||||
void MoveDirectory(string sourceDirectoryName, string destinationDirectoryName);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a file.
|
||||
/// </summary>
|
||||
/// <param name="sourceName">The file to move.</param>
|
||||
/// <param name="destinationName">The target file name.</param>
|
||||
void MoveFile(string sourceName, string destinationName);
|
||||
|
||||
/// <summary>
|
||||
/// Moves a file, allowing an existing file to be overwritten.
|
||||
/// </summary>
|
||||
/// <param name="sourceName">The file to move.</param>
|
||||
/// <param name="destinationName">The target file name.</param>
|
||||
/// <param name="overwrite">Whether to permit a destination file to be overwritten.</param>
|
||||
void MoveFile(string sourceName, string destinationName, bool overwrite);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">The file mode for the created stream.</param>
|
||||
/// <returns>The new stream.</returns>
|
||||
SparseStream OpenFile(string path, FileMode mode);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">The file mode for the created stream.</param>
|
||||
/// <param name="access">The access permissions for the created stream.</param>
|
||||
/// <returns>The new stream.</returns>
|
||||
SparseStream OpenFile(string path, FileMode mode, FileAccess access);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The file or directory to inspect.</param>
|
||||
/// <returns>The attributes of the file or directory.</returns>
|
||||
FileAttributes GetAttributes(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the attributes of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The file or directory to change.</param>
|
||||
/// <param name="newValue">The new attributes of the file or directory.</param>
|
||||
void SetAttributes(string path, FileAttributes newValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The creation time.</returns>
|
||||
DateTime GetCreationTime(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the creation time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
void SetCreationTime(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The creation time.</returns>
|
||||
DateTime GetCreationTimeUtc(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the creation time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
void SetCreationTimeUtc(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last access time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last access time.</returns>
|
||||
DateTime GetLastAccessTime(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last access time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
void SetLastAccessTime(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last access time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last access time.</returns>
|
||||
DateTime GetLastAccessTimeUtc(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last access time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
void SetLastAccessTimeUtc(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modification time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last write time.</returns>
|
||||
DateTime GetLastWriteTime(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last modification time (in local time) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
void SetLastWriteTime(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modification time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <returns>The last write time.</returns>
|
||||
DateTime GetLastWriteTimeUtc(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last modification time (in UTC) of a file or directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file or directory.</param>
|
||||
/// <param name="newTime">The new time to set.</param>
|
||||
void SetLastWriteTimeUtc(string path, DateTime newTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file.</param>
|
||||
/// <returns>The length in bytes.</returns>
|
||||
long GetFileLength(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object representing a possible file.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path.</param>
|
||||
/// <returns>The representing object.</returns>
|
||||
/// <remarks>The file does not need to exist.</remarks>
|
||||
DiscFileInfo GetFileInfo(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object representing a possible directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The directory path.</param>
|
||||
/// <returns>The representing object.</returns>
|
||||
/// <remarks>The directory does not need to exist.</remarks>
|
||||
DiscDirectoryInfo GetDirectoryInfo(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object representing a possible file system object (file or directory).
|
||||
/// </summary>
|
||||
/// <param name="path">The file system path.</param>
|
||||
/// <returns>The representing object.</returns>
|
||||
/// <remarks>The file system object does not need to exist.</remarks>
|
||||
DiscFileSystemInfo GetFileSystemInfo(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the boot code of the file system into a byte array.
|
||||
/// </summary>
|
||||
/// <returns>The boot code, or <c>null</c> if not available.</returns>
|
||||
byte[] ReadBootCode();
|
||||
|
||||
/// <summary>
|
||||
/// Size of the Filesystem in bytes
|
||||
/// </summary>
|
||||
long Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used space of the Filesystem in bytes
|
||||
/// </summary>
|
||||
long UsedSpace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Available space of the Filesystem in bytes
|
||||
/// </summary>
|
||||
long AvailableSpace { get; }
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue