mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add Nand project
This commit is contained in:
parent
ace593a19d
commit
f3c7d04bb0
12
NandReader/NandReader.csproj
Normal file
12
NandReader/NandReader.csproj
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.1;net45</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libhac.Nand\libhac.Nand.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
53
NandReader/Program.cs
Normal file
53
NandReader/Program.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using libhac;
|
||||
using libhac.Nand;
|
||||
|
||||
namespace NandReader
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
DumpTickets();
|
||||
}
|
||||
|
||||
private static void DumpTickets()
|
||||
{
|
||||
using (var logger = new ProgressBar())
|
||||
using (var stream = new FileStream(@"F:\rawnand.bin", FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
var keyset = OpenKeyset();
|
||||
var nand = new Nand(stream, keyset, logger);
|
||||
}
|
||||
}
|
||||
|
||||
private static Keyset OpenKeyset()
|
||||
{
|
||||
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
var homeKeyFile = Path.Combine(home, ".switch", "prod.keys");
|
||||
var homeTitleKeyFile = Path.Combine(home, ".switch", "title.keys");
|
||||
var homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
|
||||
string keyFile = null;
|
||||
string titleKeyFile = null;
|
||||
string consoleKeyFile = null;
|
||||
|
||||
if (File.Exists(homeKeyFile))
|
||||
{
|
||||
keyFile = homeKeyFile;
|
||||
}
|
||||
|
||||
if (File.Exists(homeTitleKeyFile))
|
||||
{
|
||||
titleKeyFile = homeTitleKeyFile;
|
||||
}
|
||||
|
||||
if (File.Exists(homeConsoleKeyFile))
|
||||
{
|
||||
consoleKeyFile = homeConsoleKeyFile;
|
||||
}
|
||||
|
||||
return ExternalKeys.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile);
|
||||
}
|
||||
}
|
||||
}
|
61
libhac.Nand/DiscUtils.Core/ApplePartitionMap/BlockZero.cs
Normal file
61
libhac.Nand/DiscUtils.Core/ApplePartitionMap/BlockZero.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
153
libhac.Nand/DiscUtils.Core/ApplePartitionMap/PartitionMap.cs
Normal file
153
libhac.Nand/DiscUtils.Core/ApplePartitionMap/PartitionMap.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
38
libhac.Nand/DiscUtils.Core/Archives/FileRecord.cs
Normal file
38
libhac.Nand/DiscUtils.Core/Archives/FileRecord.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
144
libhac.Nand/DiscUtils.Core/Archives/TarFile.cs
Normal file
144
libhac.Nand/DiscUtils.Core/Archives/TarFile.cs
Normal file
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
122
libhac.Nand/DiscUtils.Core/Archives/TarFileBuilder.cs
Normal file
122
libhac.Nand/DiscUtils.Core/Archives/TarFileBuilder.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
102
libhac.Nand/DiscUtils.Core/Archives/TarHeader.cs
Normal file
102
libhac.Nand/DiscUtils.Core/Archives/TarHeader.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
68
libhac.Nand/DiscUtils.Core/Archives/TarHeaderExtent.cs
Normal file
68
libhac.Nand/DiscUtils.Core/Archives/TarHeaderExtent.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
75
libhac.Nand/DiscUtils.Core/Archives/UnixBuildFileRecord.cs
Normal file
75
libhac.Nand/DiscUtils.Core/Archives/UnixBuildFileRecord.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
99
libhac.Nand/DiscUtils.Core/ChsAddress.cs
Normal file
99
libhac.Nand/DiscUtils.Core/ChsAddress.cs
Normal file
@ -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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
75
libhac.Nand/DiscUtils.Core/ClusterMap.cs
Normal file
75
libhac.Nand/DiscUtils.Core/ClusterMap.cs
Normal file
@ -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;
|
||||
|
||||
internal 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];
|
||||
}
|
||||
}
|
||||
}
|
50
libhac.Nand/DiscUtils.Core/ClusterRoles.cs
Normal file
50
libhac.Nand/DiscUtils.Core/ClusterRoles.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
93
libhac.Nand/DiscUtils.Core/Compression/Adler32.cs
Normal file
93
libhac.Nand/DiscUtils.Core/Compression/Adler32.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
141
libhac.Nand/DiscUtils.Core/Compression/BZip2BlockDecoder.cs
Normal file
141
libhac.Nand/DiscUtils.Core/Compression/BZip2BlockDecoder.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
348
libhac.Nand/DiscUtils.Core/Compression/BZip2DecoderStream.cs
Normal file
348
libhac.Nand/DiscUtils.Core/Compression/BZip2DecoderStream.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
124
libhac.Nand/DiscUtils.Core/Compression/BZip2Randomizer.cs
Normal file
124
libhac.Nand/DiscUtils.Core/Compression/BZip2Randomizer.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
157
libhac.Nand/DiscUtils.Core/Compression/BZip2RleStream.cs
Normal file
157
libhac.Nand/DiscUtils.Core/Compression/BZip2RleStream.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
94
libhac.Nand/DiscUtils.Core/Compression/BigEndianBitStream.cs
Normal file
94
libhac.Nand/DiscUtils.Core/Compression/BigEndianBitStream.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
libhac.Nand/DiscUtils.Core/Compression/BitStream.cs
Normal file
60
libhac.Nand/DiscUtils.Core/Compression/BitStream.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
64
libhac.Nand/DiscUtils.Core/Compression/BlockCompressor.cs
Normal file
64
libhac.Nand/DiscUtils.Core/Compression/BlockCompressor.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
50
libhac.Nand/DiscUtils.Core/Compression/CompressionResult.cs
Normal file
50
libhac.Nand/DiscUtils.Core/Compression/CompressionResult.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
70
libhac.Nand/DiscUtils.Core/Compression/DataBlockTransform.cs
Normal file
70
libhac.Nand/DiscUtils.Core/Compression/DataBlockTransform.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
102
libhac.Nand/DiscUtils.Core/Compression/HuffmanTree.cs
Normal file
102
libhac.Nand/DiscUtils.Core/Compression/HuffmanTree.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
68
libhac.Nand/DiscUtils.Core/Compression/MoveToFront.cs
Normal file
68
libhac.Nand/DiscUtils.Core/Compression/MoveToFront.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
64
libhac.Nand/DiscUtils.Core/Compression/SizedDeflateStream.cs
Normal file
64
libhac.Nand/DiscUtils.Core/Compression/SizedDeflateStream.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
86
libhac.Nand/DiscUtils.Core/Compression/ZlibBuffer.cs
Normal file
86
libhac.Nand/DiscUtils.Core/Compression/ZlibBuffer.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
240
libhac.Nand/DiscUtils.Core/Compression/ZlibStream.cs
Normal file
240
libhac.Nand/DiscUtils.Core/Compression/ZlibStream.cs
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
libhac.Nand/DiscUtils.Core/CoreCompat/EncodingHelper.cs
Normal file
23
libhac.Nand/DiscUtils.Core/CoreCompat/EncodingHelper.cs
Normal file
@ -0,0 +1,23 @@
|
||||
#if NETCORE
|
||||
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 NETCORE
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
91
libhac.Nand/DiscUtils.Core/CoreCompat/ReflectionHelper.cs
Normal file
91
libhac.Nand/DiscUtils.Core/CoreCompat/ReflectionHelper.cs
Normal file
@ -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 NETCORE
|
||||
return type.GetTypeInfo().IsEnum;
|
||||
#else
|
||||
return type.IsEnum;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(PropertyInfo property, Type attributeType)
|
||||
{
|
||||
#if NETCORE
|
||||
return property.GetCustomAttribute(attributeType);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(property, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(PropertyInfo property, Type attributeType, bool inherit)
|
||||
{
|
||||
#if NETCORE
|
||||
return property.GetCustomAttribute(attributeType, inherit);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(property, attributeType, inherit);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(FieldInfo field, Type attributeType)
|
||||
{
|
||||
#if NETCORE
|
||||
return field.GetCustomAttribute(attributeType);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(field, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(Type type, Type attributeType)
|
||||
{
|
||||
#if NETCORE
|
||||
return type.GetTypeInfo().GetCustomAttribute(attributeType);
|
||||
#else
|
||||
return Attribute.GetCustomAttribute(type, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Attribute GetCustomAttribute(Type type, Type attributeType, bool inherit)
|
||||
{
|
||||
#if NETCORE
|
||||
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 NETCORE
|
||||
return type.GetTypeInfo().GetCustomAttributes(attributeType, inherit);
|
||||
#else
|
||||
return Attribute.GetCustomAttributes(type, attributeType);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Assembly GetAssembly(Type type)
|
||||
{
|
||||
#if NETCORE
|
||||
return type.GetTypeInfo().Assembly;
|
||||
#else
|
||||
return type.Assembly;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static int SizeOf<T>()
|
||||
{
|
||||
#if NETCORE
|
||||
return Marshal.SizeOf<T>();
|
||||
#else
|
||||
return Marshal.SizeOf(typeof(T));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
15
libhac.Nand/DiscUtils.Core/CoreCompat/StringExtensions.cs
Normal file
15
libhac.Nand/DiscUtils.Core/CoreCompat/StringExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
||||
#if NETCORE
|
||||
using System.Globalization;
|
||||
|
||||
namespace DiscUtils.CoreCompat
|
||||
{
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string ToUpper(this string value, CultureInfo culture)
|
||||
{
|
||||
return value.ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
190
libhac.Nand/DiscUtils.Core/DiscDirectoryInfo.cs
Normal file
190
libhac.Nand/DiscUtils.Core/DiscDirectoryInfo.cs
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
200
libhac.Nand/DiscUtils.Core/DiscFileInfo.cs
Normal file
200
libhac.Nand/DiscUtils.Core/DiscFileInfo.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
93
libhac.Nand/DiscUtils.Core/DiscFileLocator.cs
Normal file
93
libhac.Nand/DiscUtils.Core/DiscFileLocator.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
509
libhac.Nand/DiscUtils.Core/DiscFileSystem.cs
Normal file
509
libhac.Nand/DiscUtils.Core/DiscFileSystem.cs
Normal file
@ -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 !NETCORE
|
||||
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
|
||||
}
|
||||
}
|
43
libhac.Nand/DiscUtils.Core/DiscFileSystemChecker.cs
Normal file
43
libhac.Nand/DiscUtils.Core/DiscFileSystemChecker.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
219
libhac.Nand/DiscUtils.Core/DiscFileSystemInfo.cs
Normal file
219
libhac.Nand/DiscUtils.Core/DiscFileSystemInfo.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
41
libhac.Nand/DiscUtils.Core/DiscFileSystemOptions.cs
Normal file
41
libhac.Nand/DiscUtils.Core/DiscFileSystemOptions.cs
Normal file
@ -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; }
|
||||
}
|
||||
}
|
70
libhac.Nand/DiscUtils.Core/DiscUtils.Core.csproj
Normal file
70
libhac.Nand/DiscUtils.Core/DiscUtils.Core.csproj
Normal file
@ -0,0 +1,70 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Implementation of the ISO, UDF, FAT and NTFS file systems is now fairly stable. VHD, XVA, VMDK and VDI disk formats are implemented, as well as read/write Registry support. The library also includes a simple iSCSI initiator, for accessing disks via iSCSI and an NFS client implementation.</Description>
|
||||
<AssemblyTitle>DiscUtils (for .NET and .NET Core), core library that supports parts of DiscUtils</AssemblyTitle>
|
||||
<Authors>Kenneth Bell;Quamotion;LordMike</Authors>
|
||||
<TargetFrameworks>netstandard1.5;net20;net40;net45</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>DiscUtils.Core</AssemblyName>
|
||||
<AssemblyOriginatorKeyFile>../../SigningKey.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<PackageId>DiscUtils.Core</PackageId>
|
||||
<PackageTags>DiscUtils;VHD;VDI;XVA;VMDK;ISO;NTFS;EXT2FS</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/DiscUtils/DiscUtils</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/DiscUtils/DiscUtils/blob/master/LICENSE.txt</PackageLicenseUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/DiscUtils/DiscUtils</RepositoryUrl>
|
||||
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<GenerateNeutralResourcesLanguageAttribute>false</GenerateNeutralResourcesLanguageAttribute>
|
||||
<GeneratePackageOnBuild Condition="'$(Configuration)' != 'Debug'">True</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
|
||||
<PackageReference Include="System.Security.AccessControl" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net20' ">
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.5'">
|
||||
<PackageReference Include="System.IO.FileSystem.DriveInfo">
|
||||
<Version>4.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Text.Encoding.CodePages">
|
||||
<Version>4.3.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
|
||||
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net20' ">
|
||||
<DefineConstants>$(DefineConstants);NET20</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DiscUtils.Streams\DiscUtils.Streams.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
126
libhac.Nand/DiscUtils.Core/DiskImageBuilder.cs
Normal file
126
libhac.Nand/DiscUtils.Core/DiskImageBuilder.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
54
libhac.Nand/DiscUtils.Core/DiskImageFileSpecification.cs
Normal file
54
libhac.Nand/DiscUtils.Core/DiskImageFileSpecification.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
72
libhac.Nand/DiscUtils.Core/FileLocator.cs
Normal file
72
libhac.Nand/DiscUtils.Core/FileLocator.cs
Normal file
@ -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
|
||||
{
|
||||
internal 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);
|
||||
}
|
||||
}
|
||||
}
|
90
libhac.Nand/DiscUtils.Core/FileSystemInfo.cs
Normal file
90
libhac.Nand/DiscUtils.Core/FileSystemInfo.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
121
libhac.Nand/DiscUtils.Core/FileSystemManager.cs
Normal file
121
libhac.Nand/DiscUtils.Core/FileSystemManager.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
49
libhac.Nand/DiscUtils.Core/FileSystemParameters.cs
Normal file
49
libhac.Nand/DiscUtils.Core/FileSystemParameters.cs
Normal file
@ -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; }
|
||||
}
|
||||
}
|
73
libhac.Nand/DiscUtils.Core/FileTransport.cs
Normal file
73
libhac.Nand/DiscUtils.Core/FileTransport.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
45
libhac.Nand/DiscUtils.Core/FloppyDiskType.cs
Normal file
45
libhac.Nand/DiscUtils.Core/FloppyDiskType.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
40
libhac.Nand/DiscUtils.Core/GenericDiskAdapterType.cs
Normal file
40
libhac.Nand/DiscUtils.Core/GenericDiskAdapterType.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
477
libhac.Nand/DiscUtils.Core/Geometry.cs
Normal file
477
libhac.Nand/DiscUtils.Core/Geometry.cs
Normal file
@ -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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
9
libhac.Nand/DiscUtils.Core/GeometryCalculation.cs
Normal file
9
libhac.Nand/DiscUtils.Core/GeometryCalculation.cs
Normal file
@ -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);
|
||||
}
|
50
libhac.Nand/DiscUtils.Core/GeometryTranslation.cs
Normal file
50
libhac.Nand/DiscUtils.Core/GeometryTranslation.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
81
libhac.Nand/DiscUtils.Core/IClusterBasedFileSystem.cs
Normal file
81
libhac.Nand/DiscUtils.Core/IClusterBasedFileSystem.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
39
libhac.Nand/DiscUtils.Core/IDiagnosticTraceable.cs
Normal file
39
libhac.Nand/DiscUtils.Core/IDiagnosticTraceable.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
367
libhac.Nand/DiscUtils.Core/IFileSystem.cs
Normal file
367
libhac.Nand/DiscUtils.Core/IFileSystem.cs
Normal file
@ -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; }
|
||||
}
|
||||
}
|
38
libhac.Nand/DiscUtils.Core/IUnixFileSystem.cs
Normal file
38
libhac.Nand/DiscUtils.Core/IUnixFileSystem.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the base class for all file systems that support Unix semantics.
|
||||
/// </summary>
|
||||
public interface IUnixFileSystem : IFileSystem
|
||||
{
|
||||
/// <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>
|
||||
UnixFileSystemInfo GetUnixFileInfo(string path);
|
||||
}
|
||||
}
|
43
libhac.Nand/DiscUtils.Core/Internal/Crc32.cs
Normal file
43
libhac.Nand/DiscUtils.Core/Internal/Crc32.cs
Normal file
@ -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.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Internal
|
||||
{
|
||||
internal abstract class Crc32
|
||||
{
|
||||
protected readonly uint[] Table;
|
||||
protected uint _value;
|
||||
|
||||
protected Crc32(uint[] table)
|
||||
{
|
||||
Table = table;
|
||||
_value = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
public uint Value
|
||||
{
|
||||
get { return _value ^ 0xFFFFFFFF; }
|
||||
}
|
||||
|
||||
public abstract void Process(byte[] buffer, int offset, int count);
|
||||
}
|
||||
}
|
47
libhac.Nand/DiscUtils.Core/Internal/Crc32Algorithm.cs
Normal file
47
libhac.Nand/DiscUtils.Core/Internal/Crc32Algorithm.cs
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// 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.Internal
|
||||
{
|
||||
internal enum Crc32Algorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Used in Ethernet, PKZIP, BZIP2, Gzip, PNG, etc. (aka CRC32).
|
||||
/// </summary>
|
||||
Common = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Used in iSCSI, SCTP, Btrfs, Vhdx. (aka CRC32C).
|
||||
/// </summary>
|
||||
Castagnoli = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown usage. (aka CRC32K).
|
||||
/// </summary>
|
||||
Koopman = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Used in AIXM. (aka CRC32Q).
|
||||
/// </summary>
|
||||
Aeronautical = 3
|
||||
}
|
||||
}
|
94
libhac.Nand/DiscUtils.Core/Internal/Crc32BigEndian.cs
Normal file
94
libhac.Nand/DiscUtils.Core/Internal/Crc32BigEndian.cs
Normal file
@ -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.
|
||||
//
|
||||
|
||||
namespace DiscUtils.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates CRC32 of buffers.
|
||||
/// </summary>
|
||||
internal sealed class Crc32BigEndian : Crc32
|
||||
{
|
||||
private static readonly uint[][] Tables;
|
||||
|
||||
static Crc32BigEndian()
|
||||
{
|
||||
Tables = new uint[4][];
|
||||
|
||||
Tables[(int)Crc32Algorithm.Common] = CalcTable(0x04C11DB7);
|
||||
Tables[(int)Crc32Algorithm.Castagnoli] = CalcTable(0x1EDC6F41);
|
||||
Tables[(int)Crc32Algorithm.Koopman] = CalcTable(0x741B8CD7);
|
||||
Tables[(int)Crc32Algorithm.Aeronautical] = CalcTable(0x814141AB);
|
||||
}
|
||||
|
||||
public Crc32BigEndian(Crc32Algorithm algorithm)
|
||||
: base(Tables[(int)algorithm]) {}
|
||||
|
||||
public static uint Compute(Crc32Algorithm algorithm, byte[] buffer, int offset, int count)
|
||||
{
|
||||
return Process(Tables[(int)algorithm], 0xFFFFFFFF, buffer, offset, count) ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
public override void Process(byte[] buffer, int offset, int count)
|
||||
{
|
||||
_value = Process(Table, _value, buffer, offset, count);
|
||||
}
|
||||
|
||||
private static uint[] CalcTable(uint polynomial)
|
||||
{
|
||||
uint[] table = new uint[256];
|
||||
|
||||
for (uint i = 0; i < 256; ++i)
|
||||
{
|
||||
uint crc = i << 24;
|
||||
|
||||
for (int j = 8; j > 0; --j)
|
||||
{
|
||||
if ((crc & 0x80000000) != 0)
|
||||
{
|
||||
crc = (crc << 1) ^ polynomial;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
table[i] = crc;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private static uint Process(uint[] table, uint accumulator, byte[] buffer, int offset, int count)
|
||||
{
|
||||
uint value = accumulator;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte b = buffer[offset + i];
|
||||
value = table[(value >> 24) ^ b] ^ (value << 8);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
98
libhac.Nand/DiscUtils.Core/Internal/Crc32LittleEndian.cs
Normal file
98
libhac.Nand/DiscUtils.Core/Internal/Crc32LittleEndian.cs
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// 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.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates CRC32 of buffers.
|
||||
/// </summary>
|
||||
internal sealed class Crc32LittleEndian : Crc32
|
||||
{
|
||||
private static readonly uint[][] Tables;
|
||||
|
||||
static Crc32LittleEndian()
|
||||
{
|
||||
Tables = new uint[4][];
|
||||
|
||||
Tables[(int)Crc32Algorithm.Common] = CalcTable(0xEDB88320);
|
||||
Tables[(int)Crc32Algorithm.Castagnoli] = CalcTable(0x82F63B78);
|
||||
Tables[(int)Crc32Algorithm.Koopman] = CalcTable(0xEB31D82E);
|
||||
Tables[(int)Crc32Algorithm.Aeronautical] = CalcTable(0xD5828281);
|
||||
}
|
||||
|
||||
public Crc32LittleEndian(Crc32Algorithm algorithm)
|
||||
: base(Tables[(int)algorithm]) {}
|
||||
|
||||
public static uint Compute(Crc32Algorithm algorithm, byte[] buffer, int offset, int count)
|
||||
{
|
||||
return Process(Tables[(int)algorithm], 0xFFFFFFFF, buffer, offset, count) ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
public override void Process(byte[] buffer, int offset, int count)
|
||||
{
|
||||
_value = Process(Table, _value, buffer, offset, count);
|
||||
}
|
||||
|
||||
private static uint[] CalcTable(uint polynomial)
|
||||
{
|
||||
uint[] table = new uint[256];
|
||||
|
||||
table[0] = 0;
|
||||
for (uint i = 0; i <= 255; ++i)
|
||||
{
|
||||
uint crc = i;
|
||||
|
||||
for (int j = 8; j > 0; --j)
|
||||
{
|
||||
if ((crc & 1) != 0)
|
||||
{
|
||||
crc = (crc >> 1) ^ polynomial;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
table[i] = crc;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private static uint Process(uint[] table, uint accumulator, byte[] buffer, int offset, int count)
|
||||
{
|
||||
uint value = accumulator;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte b = buffer[offset + i];
|
||||
|
||||
uint temp1 = (value >> 8) & 0x00FFFFFF;
|
||||
uint temp2 = table[(value ^ b) & 0xFF];
|
||||
value = temp1 ^ temp2;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
108
libhac.Nand/DiscUtils.Core/Internal/LocalFileLocator.cs
Normal file
108
libhac.Nand/DiscUtils.Core/Internal/LocalFileLocator.cs
Normal file
@ -0,0 +1,108 @@
|
||||
//
|
||||
// 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.Internal
|
||||
{
|
||||
internal sealed class LocalFileLocator : FileLocator
|
||||
{
|
||||
private readonly string _dir;
|
||||
|
||||
public LocalFileLocator(string dir)
|
||||
{
|
||||
_dir = dir;
|
||||
}
|
||||
|
||||
public override bool Exists(string fileName)
|
||||
{
|
||||
return File.Exists(Path.Combine(_dir, fileName));
|
||||
}
|
||||
|
||||
protected override Stream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
return new FileStream(Path.Combine(_dir, fileName), mode, access, share);
|
||||
}
|
||||
|
||||
public override FileLocator GetRelativeLocator(string path)
|
||||
{
|
||||
return new LocalFileLocator(Path.Combine(_dir, path));
|
||||
}
|
||||
|
||||
public override string GetFullPath(string path)
|
||||
{
|
||||
string combinedPath = Path.Combine(_dir, path);
|
||||
if (string.IsNullOrEmpty(combinedPath))
|
||||
{
|
||||
#if NETCORE
|
||||
return Directory.GetCurrentDirectory();
|
||||
#else
|
||||
return Environment.CurrentDirectory;
|
||||
#endif
|
||||
}
|
||||
return Path.GetFullPath(combinedPath);
|
||||
}
|
||||
|
||||
public override string GetDirectoryFromPath(string path)
|
||||
{
|
||||
return Path.GetDirectoryName(path);
|
||||
}
|
||||
|
||||
public override string GetFileFromPath(string path)
|
||||
{
|
||||
return Path.GetFileName(path);
|
||||
}
|
||||
|
||||
public override DateTime GetLastWriteTimeUtc(string path)
|
||||
{
|
||||
return File.GetLastWriteTimeUtc(Path.Combine(_dir, path));
|
||||
}
|
||||
|
||||
public override bool HasCommonRoot(FileLocator other)
|
||||
{
|
||||
LocalFileLocator otherLocal = other as LocalFileLocator;
|
||||
if (otherLocal == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the paths have drive specifiers, then common root depends on them having a common
|
||||
// drive letter.
|
||||
string otherDir = otherLocal._dir;
|
||||
if (otherDir.Length >= 2 && _dir.Length >= 2)
|
||||
{
|
||||
if (otherDir[1] == ':' && _dir[1] == ':')
|
||||
{
|
||||
return char.ToUpperInvariant(otherDir[0]) == char.ToUpperInvariant(_dir[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ResolveRelativePath(string path)
|
||||
{
|
||||
return Utilities.ResolveRelativePath(_dir, path);
|
||||
}
|
||||
}
|
||||
}
|
33
libhac.Nand/DiscUtils.Core/Internal/LogicalVolumeFactory.cs
Normal file
33
libhac.Nand/DiscUtils.Core/Internal/LogicalVolumeFactory.cs
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// 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.Internal
|
||||
{
|
||||
internal abstract class LogicalVolumeFactory
|
||||
{
|
||||
public abstract bool HandlesPhysicalVolume(PhysicalVolumeInfo volume);
|
||||
|
||||
public abstract void MapDisks(IEnumerable<VirtualDisk> disks, Dictionary<string, LogicalVolumeInfo> result);
|
||||
}
|
||||
}
|
@ -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.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Internal
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
internal sealed class LogicalVolumeFactoryAttribute : Attribute {}
|
||||
}
|
149
libhac.Nand/DiscUtils.Core/Internal/ObjectCache.cs
Normal file
149
libhac.Nand/DiscUtils.Core/Internal/ObjectCache.cs
Normal file
@ -0,0 +1,149 @@
|
||||
//
|
||||
// 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.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Caches objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="K">The type of the object key.</typeparam>
|
||||
/// <typeparam name="V">The type of the objects to cache.</typeparam>
|
||||
/// <remarks>
|
||||
/// Can be use for two purposes - to ensure there is only one instance of a given object,
|
||||
/// and to prevent the need to recreate objects that are expensive to create.
|
||||
/// </remarks>
|
||||
internal class ObjectCache<K, V>
|
||||
{
|
||||
private const int MostRecentListSize = 20;
|
||||
private const int PruneGap = 500;
|
||||
|
||||
private readonly Dictionary<K, WeakReference> _entries;
|
||||
private int _nextPruneCount;
|
||||
private readonly List<KeyValuePair<K, V>> _recent;
|
||||
|
||||
public ObjectCache()
|
||||
{
|
||||
_entries = new Dictionary<K, WeakReference>();
|
||||
_recent = new List<KeyValuePair<K, V>>();
|
||||
}
|
||||
|
||||
public V this[K key]
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 0; i < _recent.Count; ++i)
|
||||
{
|
||||
KeyValuePair<K, V> recentEntry = _recent[i];
|
||||
if (recentEntry.Key.Equals(key))
|
||||
{
|
||||
MakeMostRecent(i);
|
||||
return recentEntry.Value;
|
||||
}
|
||||
}
|
||||
|
||||
WeakReference wRef;
|
||||
if (_entries.TryGetValue(key, out wRef))
|
||||
{
|
||||
V val = (V)wRef.Target;
|
||||
if (val != null)
|
||||
{
|
||||
MakeMostRecent(key, val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
return default(V);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_entries[key] = new WeakReference(value);
|
||||
MakeMostRecent(key, value);
|
||||
PruneEntries();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Remove(K key)
|
||||
{
|
||||
for (int i = 0; i < _recent.Count; ++i)
|
||||
{
|
||||
if (_recent[i].Key.Equals(key))
|
||||
{
|
||||
_recent.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_entries.Remove(key);
|
||||
}
|
||||
|
||||
private void PruneEntries()
|
||||
{
|
||||
_nextPruneCount++;
|
||||
|
||||
if (_nextPruneCount > PruneGap)
|
||||
{
|
||||
List<K> toPrune = new List<K>();
|
||||
foreach (KeyValuePair<K, WeakReference> entry in _entries)
|
||||
{
|
||||
if (!entry.Value.IsAlive)
|
||||
{
|
||||
toPrune.Add(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (K key in toPrune)
|
||||
{
|
||||
_entries.Remove(key);
|
||||
}
|
||||
|
||||
_nextPruneCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeMostRecent(int i)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
KeyValuePair<K, V> entry = _recent[i];
|
||||
_recent.RemoveAt(i);
|
||||
_recent.Insert(0, entry);
|
||||
}
|
||||
|
||||
private void MakeMostRecent(K key, V val)
|
||||
{
|
||||
while (_recent.Count >= MostRecentListSize)
|
||||
{
|
||||
_recent.RemoveAt(_recent.Count - 1);
|
||||
}
|
||||
|
||||
_recent.Insert(0, new KeyValuePair<K, V>(key, val));
|
||||
}
|
||||
}
|
||||
}
|
467
libhac.Nand/DiscUtils.Core/Internal/Utilities.cs
Normal file
467
libhac.Nand/DiscUtils.Core/Internal/Utilities.cs
Normal file
@ -0,0 +1,467 @@
|
||||
//
|
||||
// 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 System.Text.RegularExpressions;
|
||||
|
||||
namespace DiscUtils.Internal
|
||||
{
|
||||
internal static class Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts between two arrays.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the elements of the source array.</typeparam>
|
||||
/// <typeparam name="U">The type of the elements of the destination array.</typeparam>
|
||||
/// <param name="source">The source array.</param>
|
||||
/// <param name="func">The function to map from source type to destination type.</param>
|
||||
/// <returns>The resultant array.</returns>
|
||||
public static U[] Map<T, U>(ICollection<T> source, Func<T, U> func)
|
||||
{
|
||||
U[] result = new U[source.Count];
|
||||
int i = 0;
|
||||
|
||||
foreach (T sVal in source)
|
||||
{
|
||||
result[i++] = func(sVal);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts between two arrays.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the elements of the source array.</typeparam>
|
||||
/// <typeparam name="U">The type of the elements of the destination array.</typeparam>
|
||||
/// <param name="source">The source array.</param>
|
||||
/// <param name="func">The function to map from source type to destination type.</param>
|
||||
/// <returns>The resultant array.</returns>
|
||||
public static U[] Map<T, U>(IEnumerable<T> source, Func<T, U> func)
|
||||
{
|
||||
List<U> result = new List<U>();
|
||||
|
||||
foreach (T sVal in source)
|
||||
{
|
||||
result.Add(func(sVal));
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters a collection into a new collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="C">The type of the new collection.</typeparam>
|
||||
/// <typeparam name="T">The type of the collection entries.</typeparam>
|
||||
/// <param name="source">The collection to filter.</param>
|
||||
/// <param name="predicate">The predicate to select which entries are carried over.</param>
|
||||
/// <returns>The new collection, containing all entries where the predicate returns <c>true</c>.</returns>
|
||||
public static C Filter<C, T>(ICollection<T> source, Func<T, bool> predicate) where C : ICollection<T>, new()
|
||||
{
|
||||
C result = new C();
|
||||
foreach (T val in source)
|
||||
{
|
||||
if (predicate(val))
|
||||
{
|
||||
result.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if two ranges overlap.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the ordinals.</typeparam>
|
||||
/// <param name="xFirst">The lowest ordinal of the first range (inclusive).</param>
|
||||
/// <param name="xLast">The highest ordinal of the first range (exclusive).</param>
|
||||
/// <param name="yFirst">The lowest ordinal of the second range (inclusive).</param>
|
||||
/// <param name="yLast">The highest ordinal of the second range (exclusive).</param>
|
||||
/// <returns><c>true</c> if the ranges overlap, else <c>false</c>.</returns>
|
||||
public static bool RangesOverlap<T>(T xFirst, T xLast, T yFirst, T yLast) where T : IComparable<T>
|
||||
{
|
||||
return !((xLast.CompareTo(yFirst) <= 0) || (xFirst.CompareTo(yLast) >= 0));
|
||||
}
|
||||
|
||||
#region Bit Twiddling
|
||||
|
||||
public static bool IsAllZeros(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int end = offset + count;
|
||||
for (int i = offset; i < end; ++i)
|
||||
{
|
||||
if (buffer[i] != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsPowerOfTwo(uint val)
|
||||
{
|
||||
if (val == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((val & 1) != 1)
|
||||
{
|
||||
val >>= 1;
|
||||
}
|
||||
|
||||
return val == 1;
|
||||
}
|
||||
|
||||
public static bool IsPowerOfTwo(long val)
|
||||
{
|
||||
if (val == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((val & 1) != 1)
|
||||
{
|
||||
val >>= 1;
|
||||
}
|
||||
|
||||
return val == 1;
|
||||
}
|
||||
|
||||
public static bool AreEqual(byte[] a, byte[] b)
|
||||
{
|
||||
if (a.Length != b.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.Length; ++i)
|
||||
{
|
||||
if (a[i] != b[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static ushort BitSwap(ushort value)
|
||||
{
|
||||
return (ushort)(((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8));
|
||||
}
|
||||
|
||||
public static uint BitSwap(uint value)
|
||||
{
|
||||
return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0x00FF0000) >> 8) |
|
||||
((value & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
public static ulong BitSwap(ulong value)
|
||||
{
|
||||
return ((ulong)BitSwap((uint)(value & 0xFFFFFFFF)) << 32) | BitSwap((uint)(value >> 32));
|
||||
}
|
||||
|
||||
public static short BitSwap(short value)
|
||||
{
|
||||
return (short)BitSwap((ushort)value);
|
||||
}
|
||||
|
||||
public static int BitSwap(int value)
|
||||
{
|
||||
return (int)BitSwap((uint)value);
|
||||
}
|
||||
|
||||
public static long BitSwap(long value)
|
||||
{
|
||||
return (long)BitSwap((ulong)value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Path Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the directory part of a path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to process.</param>
|
||||
/// <returns>The directory part.</returns>
|
||||
public static string GetDirectoryFromPath(string path)
|
||||
{
|
||||
string trimmed = path.TrimEnd('\\');
|
||||
|
||||
int index = trimmed.LastIndexOf('\\');
|
||||
if (index < 0)
|
||||
{
|
||||
return string.Empty; // No directory, just a file name
|
||||
}
|
||||
|
||||
return trimmed.Substring(0, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the file part of a path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to process.</param>
|
||||
/// <returns>The file part of the path.</returns>
|
||||
public static string GetFileFromPath(string path)
|
||||
{
|
||||
string trimmed = path.Trim('\\');
|
||||
|
||||
int index = trimmed.LastIndexOf('\\');
|
||||
if (index < 0)
|
||||
{
|
||||
return trimmed; // No directory, just a file name
|
||||
}
|
||||
|
||||
return trimmed.Substring(index + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines two paths.
|
||||
/// </summary>
|
||||
/// <param name="a">The first part of the path.</param>
|
||||
/// <param name="b">The second part of the path.</param>
|
||||
/// <returns>The combined path.</returns>
|
||||
public static string CombinePaths(string a, string b)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a) || (b.Length > 0 && b[0] == '\\'))
|
||||
{
|
||||
return b;
|
||||
}
|
||||
if (string.IsNullOrEmpty(b))
|
||||
{
|
||||
return a;
|
||||
}
|
||||
return a.TrimEnd('\\') + '\\' + b.TrimStart('\\');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a relative path into an absolute one.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The base path to resolve from.</param>
|
||||
/// <param name="relativePath">The relative path.</param>
|
||||
/// <returns>The absolute path. If no <paramref name="basePath"/> is specified
|
||||
/// then relativePath is returned as-is. If <paramref name="relativePath"/>
|
||||
/// contains more '..' characters than the base path contains levels of
|
||||
/// directory, the resultant string be the root drive followed by the file name.
|
||||
/// If no the basePath starts with '\' (no drive specified) then the returned
|
||||
/// path will also start with '\'.
|
||||
/// For example: (\TEMP\Foo.txt, ..\..\Bar.txt) gives (\Bar.txt).
|
||||
/// </returns>
|
||||
public static string ResolveRelativePath(string basePath, string relativePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(basePath))
|
||||
{
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
if (!basePath.EndsWith(@"\"))
|
||||
basePath = Path.GetDirectoryName(basePath);
|
||||
|
||||
string merged = Path.GetFullPath(Path.Combine(basePath, relativePath));
|
||||
|
||||
if (basePath.StartsWith(@"\") && merged.Length > 2 && merged[1].Equals(':'))
|
||||
{
|
||||
return merged.Substring(2);
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
public static string ResolvePath(string basePath, string path)
|
||||
{
|
||||
if (!path.StartsWith("\\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ResolveRelativePath(basePath, path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string MakeRelativePath(string path, string basePath)
|
||||
{
|
||||
List<string> pathElements =
|
||||
new List<string>(path.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
List<string> basePathElements =
|
||||
new List<string>(basePath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
|
||||
if (!basePath.EndsWith("\\", StringComparison.Ordinal) && basePathElements.Count > 0)
|
||||
{
|
||||
basePathElements.RemoveAt(basePathElements.Count - 1);
|
||||
}
|
||||
|
||||
// Find first part of paths that don't match
|
||||
int i = 0;
|
||||
while (i < Math.Min(pathElements.Count - 1, basePathElements.Count))
|
||||
{
|
||||
if (pathElements[i].ToUpperInvariant() != basePathElements[i].ToUpperInvariant())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
// For each remaining part of the base path, insert '..'
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (i == basePathElements.Count)
|
||||
{
|
||||
result.Append(@".\");
|
||||
}
|
||||
else if (i < basePathElements.Count)
|
||||
{
|
||||
for (int j = 0; j < basePathElements.Count - i; ++j)
|
||||
{
|
||||
result.Append(@"..\");
|
||||
}
|
||||
}
|
||||
|
||||
// For each remaining part of the path, add the path element
|
||||
for (int j = i; j < pathElements.Count - 1; ++j)
|
||||
{
|
||||
result.Append(pathElements[j]);
|
||||
result.Append(@"\");
|
||||
}
|
||||
|
||||
result.Append(pathElements[pathElements.Count - 1]);
|
||||
|
||||
// If the target was a directory, put the terminator back
|
||||
if (path.EndsWith(@"\", StringComparison.Ordinal))
|
||||
{
|
||||
result.Append(@"\");
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filesystem Support
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a file name matches the 8.3 pattern.
|
||||
/// </summary>
|
||||
/// <param name="name">The name to test.</param>
|
||||
/// <returns><c>true</c> if the name is 8.3, otherwise <c>false</c>.</returns>
|
||||
public static bool Is8Dot3(string name)
|
||||
{
|
||||
if (name.Length > 12)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string[] split = name.Split('.');
|
||||
|
||||
if (split.Length > 2 || split.Length < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (split[0].Length > 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (char ch in split[0])
|
||||
{
|
||||
if (!Is8Dot3Char(ch))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (split.Length > 1)
|
||||
{
|
||||
if (split[1].Length > 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (char ch in split[1])
|
||||
{
|
||||
if (!Is8Dot3Char(ch))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Is8Dot3Char(char ch)
|
||||
{
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || "_^$~!#%£-{}()@'`&".IndexOf(ch) != -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a 'standard' wildcard file/path specification into a regular expression.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The wildcard pattern to convert.</param>
|
||||
/// <returns>The resultant regular expression.</returns>
|
||||
/// <remarks>
|
||||
/// The wildcard * (star) matches zero or more characters (including '.'), and ?
|
||||
/// (question mark) matches precisely one character (except '.').
|
||||
/// </remarks>
|
||||
public static Regex ConvertWildcardsToRegEx(string pattern)
|
||||
{
|
||||
if (!pattern.Contains("."))
|
||||
{
|
||||
pattern += ".";
|
||||
}
|
||||
|
||||
string query = "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", "[^.]") + "$";
|
||||
return new Regex(query, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
public static FileAttributes FileAttributesFromUnixFileType(UnixFileType fileType)
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case UnixFileType.Fifo:
|
||||
return FileAttributes.Device | FileAttributes.System;
|
||||
case UnixFileType.Character:
|
||||
return FileAttributes.Device | FileAttributes.System;
|
||||
case UnixFileType.Directory:
|
||||
return FileAttributes.Directory;
|
||||
case UnixFileType.Block:
|
||||
return FileAttributes.Device | FileAttributes.System;
|
||||
case UnixFileType.Regular:
|
||||
return FileAttributes.Normal;
|
||||
case UnixFileType.Link:
|
||||
return FileAttributes.ReparsePoint;
|
||||
case UnixFileType.Socket:
|
||||
return FileAttributes.Device | FileAttributes.System;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
56
libhac.Nand/DiscUtils.Core/Internal/VirtualDiskFactory.cs
Normal file
56
libhac.Nand/DiscUtils.Core/Internal/VirtualDiskFactory.cs
Normal file
@ -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.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Internal
|
||||
{
|
||||
internal abstract class VirtualDiskFactory
|
||||
{
|
||||
public abstract string[] Variants { get; }
|
||||
|
||||
public abstract VirtualDiskTypeInfo GetDiskTypeInformation(string variant);
|
||||
|
||||
public abstract DiskImageBuilder GetImageBuilder(string variant);
|
||||
|
||||
public abstract VirtualDisk CreateDisk(FileLocator locator, string variant, string path,
|
||||
VirtualDiskParameters diskParameters);
|
||||
|
||||
public abstract VirtualDisk OpenDisk(string path, FileAccess access);
|
||||
|
||||
public abstract VirtualDisk OpenDisk(FileLocator locator, string path, FileAccess access);
|
||||
|
||||
public virtual VirtualDisk OpenDisk(FileLocator locator, string path, string extraInfo,
|
||||
Dictionary<string, string> parameters, FileAccess access)
|
||||
{
|
||||
return OpenDisk(locator, path, access);
|
||||
}
|
||||
|
||||
public VirtualDisk OpenDisk(DiscFileSystem fileSystem, string path, FileAccess access)
|
||||
{
|
||||
return OpenDisk(new DiscFileLocator(fileSystem, @"\"), path, access);
|
||||
}
|
||||
|
||||
public abstract VirtualDiskLayer OpenDiskLayer(FileLocator locator, string path, FileAccess access);
|
||||
}
|
||||
}
|
@ -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.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscUtils.Internal
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
internal sealed class VirtualDiskFactoryAttribute : Attribute
|
||||
{
|
||||
public VirtualDiskFactoryAttribute(string type, string fileExtensions)
|
||||
{
|
||||
Type = type;
|
||||
FileExtensions = fileExtensions.Replace(".", string.Empty).Split(',');
|
||||
}
|
||||
|
||||
public string[] FileExtensions { get; }
|
||||
|
||||
public string Type { get; }
|
||||
}
|
||||
}
|
50
libhac.Nand/DiscUtils.Core/Internal/VirtualDiskTransport.cs
Normal file
50
libhac.Nand/DiscUtils.Core/Internal/VirtualDiskTransport.cs
Normal file
@ -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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace DiscUtils.Internal
|
||||
{
|
||||
internal abstract class VirtualDiskTransport : IDisposable
|
||||
{
|
||||
public abstract bool IsRawDisk { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public abstract void Connect(Uri uri, string username, string password);
|
||||
|
||||
public abstract VirtualDisk OpenDisk(FileAccess access);
|
||||
|
||||
public abstract FileLocator GetFileLocator();
|
||||
|
||||
public abstract string GetFileName();
|
||||
|
||||
public abstract string GetExtraInfo();
|
||||
|
||||
protected virtual void Dispose(bool disposing) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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.Internal
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
internal sealed class VirtualDiskTransportAttribute : Attribute
|
||||
{
|
||||
public VirtualDiskTransportAttribute(string scheme)
|
||||
{
|
||||
Scheme = scheme;
|
||||
}
|
||||
|
||||
public string Scheme { get; }
|
||||
}
|
||||
}
|
73
libhac.Nand/DiscUtils.Core/InvalidFileSystemException.cs
Normal file
73
libhac.Nand/DiscUtils.Core/InvalidFileSystemException.cs
Normal file
@ -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.IO;
|
||||
|
||||
#if !NETCORE
|
||||
using System.Runtime.Serialization;
|
||||
#endif
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when some invalid file system data is found, indicating probably corruption.
|
||||
/// </summary>
|
||||
#if !NETCORE
|
||||
[Serializable]
|
||||
#endif
|
||||
public class InvalidFileSystemException : IOException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the InvalidFileSystemException class.
|
||||
/// </summary>
|
||||
public InvalidFileSystemException() {}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the InvalidFileSystemException class.
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message.</param>
|
||||
public InvalidFileSystemException(string message)
|
||||
: base(message) {}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the InvalidFileSystemException class.
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message.</param>
|
||||
/// <param name="innerException">The inner exception.</param>
|
||||
public InvalidFileSystemException(string message, Exception innerException)
|
||||
: base(message, innerException) {}
|
||||
|
||||
#if !NETCORE
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the InvalidFileSystemException class.
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info.</param>
|
||||
/// <param name="context">The streaming context.</param>
|
||||
protected InvalidFileSystemException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -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.LogicalDiskManager
|
||||
{
|
||||
internal sealed class ComponentRecord : DatabaseRecord
|
||||
{
|
||||
public uint LinkId; // Identical on mirrors
|
||||
public ExtentMergeType MergeType; // (02 Spanned, Simple, Mirrored) (01 on striped)
|
||||
public ulong NumExtents; // Could be num disks
|
||||
public string StatusString;
|
||||
public long StripeSizeSectors;
|
||||
public long StripeStride; // aka num partitions
|
||||
public uint Unknown1; // Zero
|
||||
public uint Unknown2; // Zero
|
||||
public ulong Unknown3; // 00 .. 00
|
||||
public ulong Unknown4; // ??
|
||||
public ulong VolumeId;
|
||||
|
||||
protected override void DoReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
base.DoReadFrom(buffer, offset);
|
||||
|
||||
int pos = offset + 0x18;
|
||||
|
||||
Id = ReadVarULong(buffer, ref pos);
|
||||
Name = ReadVarString(buffer, ref pos);
|
||||
StatusString = ReadVarString(buffer, ref pos);
|
||||
MergeType = (ExtentMergeType)ReadByte(buffer, ref pos);
|
||||
Unknown1 = ReadUInt(buffer, ref pos); // Zero
|
||||
NumExtents = ReadVarULong(buffer, ref pos);
|
||||
Unknown2 = ReadUInt(buffer, ref pos);
|
||||
LinkId = ReadUInt(buffer, ref pos);
|
||||
Unknown3 = ReadULong(buffer, ref pos); // Zero
|
||||
VolumeId = ReadVarULong(buffer, ref pos);
|
||||
Unknown4 = ReadVarULong(buffer, ref pos); // Zero
|
||||
|
||||
if ((Flags & 0x1000) != 0)
|
||||
{
|
||||
StripeSizeSectors = ReadVarLong(buffer, ref pos);
|
||||
StripeStride = ReadVarLong(buffer, ref pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
178
libhac.Nand/DiscUtils.Core/LogicalDiskManager/Database.cs
Normal file
178
libhac.Nand/DiscUtils.Core/LogicalDiskManager/Database.cs
Normal file
@ -0,0 +1,178 @@
|
||||
//
|
||||
// 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.LogicalDiskManager
|
||||
{
|
||||
internal class Database
|
||||
{
|
||||
private readonly Dictionary<ulong, DatabaseRecord> _records;
|
||||
private readonly DatabaseHeader _vmdb;
|
||||
|
||||
public Database(Stream stream)
|
||||
{
|
||||
long dbStart = stream.Position;
|
||||
|
||||
byte[] buffer = new byte[Sizes.Sector];
|
||||
stream.Read(buffer, 0, buffer.Length);
|
||||
_vmdb = new DatabaseHeader();
|
||||
_vmdb.ReadFrom(buffer, 0);
|
||||
|
||||
stream.Position = dbStart + _vmdb.HeaderSize;
|
||||
|
||||
buffer = StreamUtilities.ReadExact(stream, (int)(_vmdb.BlockSize * _vmdb.NumVBlks));
|
||||
|
||||
_records = new Dictionary<ulong, DatabaseRecord>();
|
||||
for (int i = 0; i < _vmdb.NumVBlks; ++i)
|
||||
{
|
||||
DatabaseRecord rec = DatabaseRecord.ReadFrom(buffer, (int)(i * _vmdb.BlockSize));
|
||||
if (rec != null)
|
||||
{
|
||||
_records.Add(rec.Id, rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<DiskRecord> Disks
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (DatabaseRecord record in _records.Values)
|
||||
{
|
||||
if (record.RecordType == RecordType.Disk)
|
||||
{
|
||||
yield return (DiskRecord)record;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<VolumeRecord> Volumes
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (DatabaseRecord record in _records.Values)
|
||||
{
|
||||
if (record.RecordType == RecordType.Volume)
|
||||
{
|
||||
yield return (VolumeRecord)record;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DiskGroupRecord GetDiskGroup(Guid guid)
|
||||
{
|
||||
foreach (DatabaseRecord record in _records.Values)
|
||||
{
|
||||
if (record.RecordType == RecordType.DiskGroup)
|
||||
{
|
||||
DiskGroupRecord dgRecord = (DiskGroupRecord)record;
|
||||
if (new Guid(dgRecord.GroupGuidString) == guid || guid == Guid.Empty)
|
||||
{
|
||||
return dgRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal IEnumerable<ComponentRecord> GetVolumeComponents(ulong volumeId)
|
||||
{
|
||||
foreach (DatabaseRecord record in _records.Values)
|
||||
{
|
||||
if (record.RecordType == RecordType.Component)
|
||||
{
|
||||
ComponentRecord cmpntRecord = (ComponentRecord)record;
|
||||
if (cmpntRecord.VolumeId == volumeId)
|
||||
{
|
||||
yield return cmpntRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<ExtentRecord> GetComponentExtents(ulong componentId)
|
||||
{
|
||||
foreach (DatabaseRecord record in _records.Values)
|
||||
{
|
||||
if (record.RecordType == RecordType.Extent)
|
||||
{
|
||||
ExtentRecord extentRecord = (ExtentRecord)record;
|
||||
if (extentRecord.ComponentId == componentId)
|
||||
{
|
||||
yield return extentRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DiskRecord GetDisk(ulong diskId)
|
||||
{
|
||||
return (DiskRecord)_records[diskId];
|
||||
}
|
||||
|
||||
internal VolumeRecord GetVolume(ulong volumeId)
|
||||
{
|
||||
return (VolumeRecord)_records[volumeId];
|
||||
}
|
||||
|
||||
internal VolumeRecord GetVolume(Guid id)
|
||||
{
|
||||
return FindRecord<VolumeRecord>(r => r.VolumeGuid == id, RecordType.Volume);
|
||||
}
|
||||
|
||||
internal IEnumerable<VolumeRecord> GetVolumes()
|
||||
{
|
||||
foreach (DatabaseRecord record in _records.Values)
|
||||
{
|
||||
if (record.RecordType == RecordType.Volume)
|
||||
{
|
||||
yield return (VolumeRecord)record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal T FindRecord<T>(Predicate<T> pred, RecordType typeId)
|
||||
where T : DatabaseRecord
|
||||
{
|
||||
foreach (DatabaseRecord record in _records.Values)
|
||||
{
|
||||
if (record.RecordType == typeId)
|
||||
{
|
||||
T t = (T)record;
|
||||
if (pred(t))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
internal class DatabaseHeader
|
||||
{
|
||||
public uint BlockSize; // 00 00 00 80
|
||||
public long CommittedSequence; // 0xA
|
||||
public string DiskGroupId;
|
||||
public string GroupName;
|
||||
public uint HeaderSize; // 00 00 02 00
|
||||
public uint NumVBlks; // 00 00 17 24
|
||||
public long PendingSequence; // 0xA
|
||||
public string Signature; // VMDB
|
||||
public DateTime Timestamp;
|
||||
public ushort Unknown1; // 00 01
|
||||
public uint Unknown2; // 1
|
||||
public uint Unknown3; // 1
|
||||
public uint Unknown4; // 3
|
||||
public uint Unknown5; // 3
|
||||
public long Unknown6; // 0
|
||||
public long Unknown7; // 1
|
||||
public uint Unknown8; // 1
|
||||
public uint Unknown9; // 3
|
||||
public uint UnknownA; // 3
|
||||
public long UnknownB; // 0
|
||||
public uint UnknownC; // 0
|
||||
public ushort VersionDenom; // 00 0a
|
||||
public ushort VersionNum; // 00 04
|
||||
|
||||
public void ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 4);
|
||||
NumVBlks = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x04);
|
||||
BlockSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
|
||||
HeaderSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x0C);
|
||||
Unknown1 = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0x10);
|
||||
VersionNum = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0x12);
|
||||
VersionDenom = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0x14);
|
||||
GroupName = EndianUtilities.BytesToString(buffer, offset + 0x16, 31).Trim('\0');
|
||||
DiskGroupId = EndianUtilities.BytesToString(buffer, offset + 0x35, 0x40).Trim('\0');
|
||||
|
||||
// May be wrong way round...
|
||||
CommittedSequence = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x75);
|
||||
PendingSequence = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x7D);
|
||||
|
||||
Unknown2 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x85);
|
||||
Unknown3 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x89);
|
||||
Unknown4 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x8D);
|
||||
Unknown5 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x91);
|
||||
Unknown6 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x95);
|
||||
Unknown7 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x9D);
|
||||
Unknown8 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xA5);
|
||||
Unknown9 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xA9);
|
||||
UnknownA = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xAD);
|
||||
|
||||
UnknownB = EndianUtilities.ToInt64BigEndian(buffer, offset + 0xB1);
|
||||
UnknownC = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xB9);
|
||||
|
||||
Timestamp = DateTime.FromFileTimeUtc(EndianUtilities.ToInt64BigEndian(buffer, offset + 0xBD));
|
||||
}
|
||||
|
||||
////}
|
||||
//// throw new NotImplementedException();
|
||||
//// // Add all byte values for ?? bytes
|
||||
//// // Zero checksum bytes (0x08, 4)
|
||||
////{
|
||||
|
||||
////private static int CalcChecksum()
|
||||
}
|
||||
}
|
154
libhac.Nand/DiscUtils.Core/LogicalDiskManager/DatabaseRecord.cs
Normal file
154
libhac.Nand/DiscUtils.Core/LogicalDiskManager/DatabaseRecord.cs
Normal file
@ -0,0 +1,154 @@
|
||||
//
|
||||
// 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.LogicalDiskManager
|
||||
{
|
||||
internal abstract class DatabaseRecord
|
||||
{
|
||||
public uint Counter;
|
||||
public uint DataLength;
|
||||
public uint Flags;
|
||||
|
||||
public ulong Id;
|
||||
public uint Label;
|
||||
public string Name;
|
||||
public RecordType RecordType;
|
||||
public string Signature; // VBLK
|
||||
public uint Valid;
|
||||
|
||||
public static DatabaseRecord ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
DatabaseRecord result = null;
|
||||
|
||||
if (EndianUtilities.ToInt32BigEndian(buffer, offset + 0xC) != 0)
|
||||
{
|
||||
switch ((RecordType)(buffer[offset + 0x13] & 0xF))
|
||||
{
|
||||
case RecordType.Volume:
|
||||
result = new VolumeRecord();
|
||||
break;
|
||||
|
||||
case RecordType.Component:
|
||||
result = new ComponentRecord();
|
||||
break;
|
||||
|
||||
case RecordType.Extent:
|
||||
result = new ExtentRecord();
|
||||
break;
|
||||
|
||||
case RecordType.Disk:
|
||||
result = new DiskRecord();
|
||||
break;
|
||||
|
||||
case RecordType.DiskGroup:
|
||||
result = new DiskGroupRecord();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Unrecognized record type: " + buffer[offset + 0x13]);
|
||||
}
|
||||
|
||||
result.DoReadFrom(buffer, offset);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static ulong ReadVarULong(byte[] buffer, ref int offset)
|
||||
{
|
||||
int length = buffer[offset];
|
||||
|
||||
ulong result = 0;
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
result = (result << 8) | buffer[offset + i + 1];
|
||||
}
|
||||
|
||||
offset += length + 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static long ReadVarLong(byte[] buffer, ref int offset)
|
||||
{
|
||||
return (long)ReadVarULong(buffer, ref offset);
|
||||
}
|
||||
|
||||
protected static string ReadVarString(byte[] buffer, ref int offset)
|
||||
{
|
||||
int length = buffer[offset];
|
||||
|
||||
string result = EndianUtilities.BytesToString(buffer, offset + 1, length);
|
||||
offset += length + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static byte ReadByte(byte[] buffer, ref int offset)
|
||||
{
|
||||
return buffer[offset++];
|
||||
}
|
||||
|
||||
protected static uint ReadUInt(byte[] buffer, ref int offset)
|
||||
{
|
||||
offset += 4;
|
||||
return EndianUtilities.ToUInt32BigEndian(buffer, offset - 4);
|
||||
}
|
||||
|
||||
protected static long ReadLong(byte[] buffer, ref int offset)
|
||||
{
|
||||
offset += 8;
|
||||
return EndianUtilities.ToInt64BigEndian(buffer, offset - 8);
|
||||
}
|
||||
|
||||
protected static ulong ReadULong(byte[] buffer, ref int offset)
|
||||
{
|
||||
offset += 8;
|
||||
return EndianUtilities.ToUInt64BigEndian(buffer, offset - 8);
|
||||
}
|
||||
|
||||
protected static string ReadString(byte[] buffer, int len, ref int offset)
|
||||
{
|
||||
offset += len;
|
||||
return EndianUtilities.BytesToString(buffer, offset - len, len);
|
||||
}
|
||||
|
||||
protected static Guid ReadBinaryGuid(byte[] buffer, ref int offset)
|
||||
{
|
||||
offset += 16;
|
||||
return EndianUtilities.ToGuidBigEndian(buffer, offset - 16);
|
||||
}
|
||||
|
||||
protected virtual void DoReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 4);
|
||||
Label = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x04);
|
||||
Counter = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
|
||||
Valid = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x0C);
|
||||
Flags = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x10);
|
||||
RecordType = (RecordType)(Flags & 0xF);
|
||||
DataLength = EndianUtilities.ToUInt32BigEndian(buffer, 0x14);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.
|
||||
//
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
internal sealed class DiskGroupRecord : DatabaseRecord
|
||||
{
|
||||
public string GroupGuidString;
|
||||
public uint Unknown1;
|
||||
|
||||
protected override void DoReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
base.DoReadFrom(buffer, offset);
|
||||
|
||||
int pos = offset + 0x18;
|
||||
|
||||
Id = ReadVarULong(buffer, ref pos);
|
||||
Name = ReadVarString(buffer, ref pos);
|
||||
if ((Flags & 0xF0) == 0x40)
|
||||
{
|
||||
GroupGuidString = ReadBinaryGuid(buffer, ref pos).ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
GroupGuidString = ReadVarString(buffer, ref pos);
|
||||
}
|
||||
Unknown1 = ReadUInt(buffer, ref pos);
|
||||
}
|
||||
}
|
||||
}
|
47
libhac.Nand/DiscUtils.Core/LogicalDiskManager/DiskRecord.cs
Normal file
47
libhac.Nand/DiscUtils.Core/LogicalDiskManager/DiskRecord.cs
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// 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.LogicalDiskManager
|
||||
{
|
||||
internal sealed class DiskRecord : DatabaseRecord
|
||||
{
|
||||
public string DiskGuidString;
|
||||
|
||||
protected override void DoReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
base.DoReadFrom(buffer, offset);
|
||||
|
||||
int pos = offset + 0x18;
|
||||
|
||||
Id = ReadVarULong(buffer, ref pos);
|
||||
Name = ReadVarString(buffer, ref pos);
|
||||
if ((Flags & 0xF0) == 0x40)
|
||||
{
|
||||
DiskGuidString = ReadBinaryGuid(buffer, ref pos).ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
DiskGuidString = ReadVarString(buffer, ref pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
144
libhac.Nand/DiscUtils.Core/LogicalDiskManager/DynamicDisk.cs
Normal file
144
libhac.Nand/DiscUtils.Core/LogicalDiskManager/DynamicDisk.cs
Normal file
@ -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.IO;
|
||||
using DiscUtils.Partitions;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
internal class DynamicDisk : IDiagnosticTraceable
|
||||
{
|
||||
private readonly VirtualDisk _disk;
|
||||
private readonly PrivateHeader _header;
|
||||
|
||||
internal DynamicDisk(VirtualDisk disk)
|
||||
{
|
||||
_disk = disk;
|
||||
_header = GetPrivateHeader(_disk);
|
||||
|
||||
TocBlock toc = GetTableOfContents();
|
||||
|
||||
long dbStart = _header.ConfigurationStartLba * 512 + toc.Item1Start * 512;
|
||||
_disk.Content.Position = dbStart;
|
||||
Database = new Database(_disk.Content);
|
||||
}
|
||||
|
||||
public SparseStream Content
|
||||
{
|
||||
get { return _disk.Content; }
|
||||
}
|
||||
|
||||
public Database Database { get; }
|
||||
|
||||
public long DataOffset
|
||||
{
|
||||
get { return _header.DataStartLba; }
|
||||
}
|
||||
|
||||
public Guid GroupId
|
||||
{
|
||||
get { return string.IsNullOrEmpty(_header.DiskGroupId) ? Guid.Empty : new Guid(_header.DiskGroupId); }
|
||||
}
|
||||
|
||||
public Guid Id
|
||||
{
|
||||
get { return new Guid(_header.DiskId); }
|
||||
}
|
||||
|
||||
public void Dump(TextWriter writer, string linePrefix)
|
||||
{
|
||||
writer.WriteLine(linePrefix + "DISK (" + _header.DiskId + ")");
|
||||
writer.WriteLine(linePrefix + " Metadata Version: " + ((_header.Version >> 16) & 0xFFFF) + "." +
|
||||
(_header.Version & 0xFFFF));
|
||||
writer.WriteLine(linePrefix + " Timestamp: " + _header.Timestamp);
|
||||
writer.WriteLine(linePrefix + " Disk Id: " + _header.DiskId);
|
||||
writer.WriteLine(linePrefix + " Host Id: " + _header.HostId);
|
||||
writer.WriteLine(linePrefix + " Disk Group Id: " + _header.DiskGroupId);
|
||||
writer.WriteLine(linePrefix + " Disk Group Name: " + _header.DiskGroupName);
|
||||
writer.WriteLine(linePrefix + " Data Start: " + _header.DataStartLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Data Size: " + _header.DataSizeLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Configuration Start: " + _header.ConfigurationStartLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Configuration Size: " + _header.ConfigurationSizeLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " TOC Size: " + _header.TocSizeLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Next TOC: " + _header.NextTocLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Number of Configs: " + _header.NumberOfConfigs);
|
||||
writer.WriteLine(linePrefix + " Config Size: " + _header.ConfigurationSizeLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Number of Logs: " + _header.NumberOfLogs);
|
||||
writer.WriteLine(linePrefix + " Log Size: " + _header.LogSizeLba + " (Sectors)");
|
||||
}
|
||||
|
||||
internal static PrivateHeader GetPrivateHeader(VirtualDisk disk)
|
||||
{
|
||||
if (disk.IsPartitioned)
|
||||
{
|
||||
long headerPos = 0;
|
||||
PartitionTable pt = disk.Partitions;
|
||||
if (pt is BiosPartitionTable)
|
||||
{
|
||||
headerPos = 0xc00;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (PartitionInfo part in pt.Partitions)
|
||||
{
|
||||
if (part.GuidType == GuidPartitionTypes.WindowsLdmMetadata)
|
||||
{
|
||||
headerPos = part.LastSector * Sizes.Sector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (headerPos != 0)
|
||||
{
|
||||
disk.Content.Position = headerPos;
|
||||
byte[] buffer = new byte[Sizes.Sector];
|
||||
disk.Content.Read(buffer, 0, buffer.Length);
|
||||
|
||||
PrivateHeader hdr = new PrivateHeader();
|
||||
hdr.ReadFrom(buffer, 0);
|
||||
return hdr;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TocBlock GetTableOfContents()
|
||||
{
|
||||
byte[] buffer = new byte[_header.TocSizeLba * 512];
|
||||
_disk.Content.Position = _header.ConfigurationStartLba * 512 + 1 * _header.TocSizeLba * 512;
|
||||
|
||||
_disk.Content.Read(buffer, 0, buffer.Length);
|
||||
TocBlock tocBlock = new TocBlock();
|
||||
tocBlock.ReadFrom(buffer, 0);
|
||||
|
||||
if (tocBlock.Signature == "TOCBLOCK")
|
||||
{
|
||||
return tocBlock;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,322 @@
|
||||
//
|
||||
// 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;
|
||||
using DiscUtils.Partitions;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
internal class DynamicDiskGroup : IDiagnosticTraceable
|
||||
{
|
||||
private readonly Database _database;
|
||||
private readonly Dictionary<Guid, DynamicDisk> _disks;
|
||||
private readonly DiskGroupRecord _record;
|
||||
|
||||
internal DynamicDiskGroup(VirtualDisk disk)
|
||||
{
|
||||
_disks = new Dictionary<Guid, DynamicDisk>();
|
||||
|
||||
DynamicDisk dynDisk = new DynamicDisk(disk);
|
||||
_database = dynDisk.Database;
|
||||
_disks.Add(dynDisk.Id, dynDisk);
|
||||
_record = dynDisk.Database.GetDiskGroup(dynDisk.GroupId);
|
||||
}
|
||||
|
||||
#region IDiagnosticTraceable Members
|
||||
|
||||
public void Dump(TextWriter writer, string linePrefix)
|
||||
{
|
||||
writer.WriteLine(linePrefix + "DISK GROUP (" + _record.Name + ")");
|
||||
writer.WriteLine(linePrefix + " Name: " + _record.Name);
|
||||
writer.WriteLine(linePrefix + " Flags: 0x" +
|
||||
(_record.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
|
||||
writer.WriteLine(linePrefix + " Database Id: " + _record.Id);
|
||||
writer.WriteLine(linePrefix + " Guid: " + _record.GroupGuidString);
|
||||
writer.WriteLine();
|
||||
|
||||
writer.WriteLine(linePrefix + " DISKS");
|
||||
foreach (DiskRecord disk in _database.Disks)
|
||||
{
|
||||
writer.WriteLine(linePrefix + " DISK (" + disk.Name + ")");
|
||||
writer.WriteLine(linePrefix + " Name: " + disk.Name);
|
||||
writer.WriteLine(linePrefix + " Flags: 0x" +
|
||||
(disk.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
|
||||
writer.WriteLine(linePrefix + " Database Id: " + disk.Id);
|
||||
writer.WriteLine(linePrefix + " Guid: " + disk.DiskGuidString);
|
||||
|
||||
DynamicDisk dynDisk;
|
||||
if (_disks.TryGetValue(new Guid(disk.DiskGuidString), out dynDisk))
|
||||
{
|
||||
writer.WriteLine(linePrefix + " PRIVATE HEADER");
|
||||
dynDisk.Dump(writer, linePrefix + " ");
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteLine(linePrefix + " VOLUMES");
|
||||
foreach (VolumeRecord vol in _database.Volumes)
|
||||
{
|
||||
writer.WriteLine(linePrefix + " VOLUME (" + vol.Name + ")");
|
||||
writer.WriteLine(linePrefix + " Name: " + vol.Name);
|
||||
writer.WriteLine(linePrefix + " BIOS Type: " +
|
||||
vol.BiosType.ToString("X2", CultureInfo.InvariantCulture) + " [" +
|
||||
BiosPartitionTypes.ToString(vol.BiosType) + "]");
|
||||
writer.WriteLine(linePrefix + " Flags: 0x" +
|
||||
(vol.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
|
||||
writer.WriteLine(linePrefix + " Database Id: " + vol.Id);
|
||||
writer.WriteLine(linePrefix + " Guid: " + vol.VolumeGuid);
|
||||
writer.WriteLine(linePrefix + " State: " + vol.ActiveString);
|
||||
writer.WriteLine(linePrefix + " Drive Hint: " + vol.MountHint);
|
||||
writer.WriteLine(linePrefix + " Num Components: " + vol.ComponentCount);
|
||||
writer.WriteLine(linePrefix + " Link Id: " + vol.PartitionComponentLink);
|
||||
|
||||
writer.WriteLine(linePrefix + " COMPONENTS");
|
||||
foreach (ComponentRecord cmpnt in _database.GetVolumeComponents(vol.Id))
|
||||
{
|
||||
writer.WriteLine(linePrefix + " COMPONENT (" + cmpnt.Name + ")");
|
||||
writer.WriteLine(linePrefix + " Name: " + cmpnt.Name);
|
||||
writer.WriteLine(linePrefix + " Flags: 0x" +
|
||||
(cmpnt.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
|
||||
writer.WriteLine(linePrefix + " Database Id: " + cmpnt.Id);
|
||||
writer.WriteLine(linePrefix + " State: " + cmpnt.StatusString);
|
||||
writer.WriteLine(linePrefix + " Mode: " + cmpnt.MergeType);
|
||||
writer.WriteLine(linePrefix + " Num Extents: " + cmpnt.NumExtents);
|
||||
writer.WriteLine(linePrefix + " Link Id: " + cmpnt.LinkId);
|
||||
writer.WriteLine(linePrefix + " Stripe Size: " + cmpnt.StripeSizeSectors + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Stripe Stride: " + cmpnt.StripeStride);
|
||||
|
||||
writer.WriteLine(linePrefix + " EXTENTS");
|
||||
foreach (ExtentRecord extent in _database.GetComponentExtents(cmpnt.Id))
|
||||
{
|
||||
writer.WriteLine(linePrefix + " EXTENT (" + extent.Name + ")");
|
||||
writer.WriteLine(linePrefix + " Name: " + extent.Name);
|
||||
writer.WriteLine(linePrefix + " Flags: 0x" +
|
||||
(extent.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
|
||||
writer.WriteLine(linePrefix + " Database Id: " + extent.Id);
|
||||
writer.WriteLine(linePrefix + " Disk Offset: " + extent.DiskOffsetLba +
|
||||
" (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Volume Offset: " + extent.OffsetInVolumeLba +
|
||||
" (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Size: " + extent.SizeLba + " (Sectors)");
|
||||
writer.WriteLine(linePrefix + " Component Id: " + extent.ComponentId);
|
||||
writer.WriteLine(linePrefix + " Disk Id: " + extent.DiskId);
|
||||
writer.WriteLine(linePrefix + " Link Id: " + extent.PartitionComponentLink);
|
||||
writer.WriteLine(linePrefix + " Interleave Order: " + extent.InterleaveOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Add(VirtualDisk disk)
|
||||
{
|
||||
DynamicDisk dynDisk = new DynamicDisk(disk);
|
||||
_disks.Add(dynDisk.Id, dynDisk);
|
||||
}
|
||||
|
||||
internal DynamicVolume[] GetVolumes()
|
||||
{
|
||||
List<DynamicVolume> vols = new List<DynamicVolume>();
|
||||
foreach (VolumeRecord record in _database.GetVolumes())
|
||||
{
|
||||
vols.Add(new DynamicVolume(this, record.VolumeGuid));
|
||||
}
|
||||
|
||||
return vols.ToArray();
|
||||
}
|
||||
|
||||
internal VolumeRecord GetVolume(Guid volume)
|
||||
{
|
||||
return _database.GetVolume(volume);
|
||||
}
|
||||
|
||||
internal LogicalVolumeStatus GetVolumeStatus(ulong volumeId)
|
||||
{
|
||||
return GetVolumeStatus(_database.GetVolume(volumeId));
|
||||
}
|
||||
|
||||
internal SparseStream OpenVolume(ulong volumeId)
|
||||
{
|
||||
return OpenVolume(_database.GetVolume(volumeId));
|
||||
}
|
||||
|
||||
private static int CompareExtentOffsets(ExtentRecord x, ExtentRecord y)
|
||||
{
|
||||
if (x.OffsetInVolumeLba > y.OffsetInVolumeLba)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (x.OffsetInVolumeLba < y.OffsetInVolumeLba)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int CompareExtentInterleaveOrder(ExtentRecord x, ExtentRecord y)
|
||||
{
|
||||
if (x.InterleaveOrder > y.InterleaveOrder)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (x.InterleaveOrder < y.InterleaveOrder)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static LogicalVolumeStatus WorstOf(LogicalVolumeStatus x, LogicalVolumeStatus y)
|
||||
{
|
||||
return (LogicalVolumeStatus)Math.Max((int)x, (int)y);
|
||||
}
|
||||
|
||||
private LogicalVolumeStatus GetVolumeStatus(VolumeRecord volume)
|
||||
{
|
||||
int numFailed = 0;
|
||||
ulong numOK = 0;
|
||||
LogicalVolumeStatus worst = LogicalVolumeStatus.Healthy;
|
||||
foreach (ComponentRecord cmpnt in _database.GetVolumeComponents(volume.Id))
|
||||
{
|
||||
LogicalVolumeStatus cmpntStatus = GetComponentStatus(cmpnt);
|
||||
worst = WorstOf(worst, cmpntStatus);
|
||||
if (cmpntStatus == LogicalVolumeStatus.Failed)
|
||||
{
|
||||
numFailed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
numOK++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numOK < 1)
|
||||
{
|
||||
return LogicalVolumeStatus.Failed;
|
||||
}
|
||||
if (numOK == volume.ComponentCount)
|
||||
{
|
||||
return worst;
|
||||
}
|
||||
return LogicalVolumeStatus.FailedRedundancy;
|
||||
}
|
||||
|
||||
private LogicalVolumeStatus GetComponentStatus(ComponentRecord cmpnt)
|
||||
{
|
||||
// NOTE: no support for RAID, so either valid or failed...
|
||||
LogicalVolumeStatus status = LogicalVolumeStatus.Healthy;
|
||||
|
||||
foreach (ExtentRecord extent in _database.GetComponentExtents(cmpnt.Id))
|
||||
{
|
||||
DiskRecord disk = _database.GetDisk(extent.DiskId);
|
||||
if (!_disks.ContainsKey(new Guid(disk.DiskGuidString)))
|
||||
{
|
||||
status = LogicalVolumeStatus.Failed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private SparseStream OpenExtent(ExtentRecord extent)
|
||||
{
|
||||
DiskRecord disk = _database.GetDisk(extent.DiskId);
|
||||
|
||||
DynamicDisk diskObj = _disks[new Guid(disk.DiskGuidString)];
|
||||
|
||||
return new SubStream(diskObj.Content, Ownership.None,
|
||||
(diskObj.DataOffset + extent.DiskOffsetLba) * Sizes.Sector, extent.SizeLba * Sizes.Sector);
|
||||
}
|
||||
|
||||
private SparseStream OpenComponent(ComponentRecord component)
|
||||
{
|
||||
if (component.MergeType == ExtentMergeType.Concatenated)
|
||||
{
|
||||
List<ExtentRecord> extents = new List<ExtentRecord>(_database.GetComponentExtents(component.Id));
|
||||
extents.Sort(CompareExtentOffsets);
|
||||
|
||||
// Sanity Check...
|
||||
long pos = 0;
|
||||
foreach (ExtentRecord extent in extents)
|
||||
{
|
||||
if (extent.OffsetInVolumeLba != pos)
|
||||
{
|
||||
throw new IOException("Volume extents are non-contiguous");
|
||||
}
|
||||
|
||||
pos += extent.SizeLba;
|
||||
}
|
||||
|
||||
List<SparseStream> streams = new List<SparseStream>();
|
||||
foreach (ExtentRecord extent in extents)
|
||||
{
|
||||
streams.Add(OpenExtent(extent));
|
||||
}
|
||||
|
||||
return new ConcatStream(Ownership.Dispose, streams.ToArray());
|
||||
}
|
||||
if (component.MergeType == ExtentMergeType.Interleaved)
|
||||
{
|
||||
List<ExtentRecord> extents = new List<ExtentRecord>(_database.GetComponentExtents(component.Id));
|
||||
extents.Sort(CompareExtentInterleaveOrder);
|
||||
|
||||
List<SparseStream> streams = new List<SparseStream>();
|
||||
foreach (ExtentRecord extent in extents)
|
||||
{
|
||||
streams.Add(OpenExtent(extent));
|
||||
}
|
||||
|
||||
return new StripedStream(component.StripeSizeSectors * Sizes.Sector, Ownership.Dispose, streams.ToArray());
|
||||
}
|
||||
throw new NotImplementedException("Unknown component mode: " + component.MergeType);
|
||||
}
|
||||
|
||||
private SparseStream OpenVolume(VolumeRecord volume)
|
||||
{
|
||||
List<SparseStream> cmpntStreams = new List<SparseStream>();
|
||||
foreach (ComponentRecord component in _database.GetVolumeComponents(volume.Id))
|
||||
{
|
||||
if (GetComponentStatus(component) == LogicalVolumeStatus.Healthy)
|
||||
{
|
||||
cmpntStreams.Add(OpenComponent(component));
|
||||
}
|
||||
}
|
||||
|
||||
if (cmpntStreams.Count < 1)
|
||||
{
|
||||
throw new IOException("Volume with no associated or healthy components");
|
||||
}
|
||||
if (cmpntStreams.Count == 1)
|
||||
{
|
||||
return cmpntStreams[0];
|
||||
}
|
||||
return new MirrorStream(Ownership.Dispose, cmpntStreams.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Collections.Generic;
|
||||
using System.IO;
|
||||
using DiscUtils.Partitions;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that understands Windows LDM structures, mapping physical volumes to logical volumes.
|
||||
/// </summary>
|
||||
public class DynamicDiskManager : IDiagnosticTraceable
|
||||
{
|
||||
private readonly Dictionary<string, DynamicDiskGroup> _groups;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DynamicDiskManager class.
|
||||
/// </summary>
|
||||
/// <param name="disks">The initial set of disks to manage.</param>
|
||||
public DynamicDiskManager(params VirtualDisk[] disks)
|
||||
{
|
||||
_groups = new Dictionary<string, DynamicDiskGroup>();
|
||||
|
||||
foreach (VirtualDisk disk in disks)
|
||||
{
|
||||
Add(disk);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a diagnostic report about the state of the disk manager.
|
||||
/// </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>
|
||||
public void Dump(TextWriter writer, string linePrefix)
|
||||
{
|
||||
writer.WriteLine(linePrefix + "DISK GROUPS");
|
||||
foreach (DynamicDiskGroup group in _groups.Values)
|
||||
{
|
||||
group.Dump(writer, linePrefix + " ");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a physical volume contains LDM data.
|
||||
/// </summary>
|
||||
/// <param name="volumeInfo">The volume to inspect.</param>
|
||||
/// <returns><c>true</c> if the physical volume contains LDM data, else <c>false</c>.</returns>
|
||||
public static bool HandlesPhysicalVolume(PhysicalVolumeInfo volumeInfo)
|
||||
{
|
||||
PartitionInfo pi = volumeInfo.Partition;
|
||||
if (pi != null)
|
||||
{
|
||||
return IsLdmPartition(pi);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a disk is 'dynamic' (i.e. contains LDM volumes).
|
||||
/// </summary>
|
||||
/// <param name="disk">The disk to inspect.</param>
|
||||
/// <returns><c>true</c> if the disk contains LDM volumes, else <c>false</c>.</returns>
|
||||
public static bool IsDynamicDisk(VirtualDisk disk)
|
||||
{
|
||||
if (disk.IsPartitioned)
|
||||
{
|
||||
foreach (PartitionInfo partition in disk.Partitions.Partitions)
|
||||
{
|
||||
if (IsLdmPartition(partition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new disk to be managed.
|
||||
/// </summary>
|
||||
/// <param name="disk">The disk to manage.</param>
|
||||
public void Add(VirtualDisk disk)
|
||||
{
|
||||
PrivateHeader header = DynamicDisk.GetPrivateHeader(disk);
|
||||
|
||||
DynamicDiskGroup group;
|
||||
if (_groups.TryGetValue(header.DiskGroupId, out group))
|
||||
{
|
||||
group.Add(disk);
|
||||
}
|
||||
else
|
||||
{
|
||||
group = new DynamicDiskGroup(disk);
|
||||
_groups.Add(header.DiskGroupId, group);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logical volumes held across the set of managed disks.
|
||||
/// </summary>
|
||||
/// <returns>An array of logical volumes.</returns>
|
||||
public LogicalVolumeInfo[] GetLogicalVolumes()
|
||||
{
|
||||
List<LogicalVolumeInfo> result = new List<LogicalVolumeInfo>();
|
||||
foreach (DynamicDiskGroup group in _groups.Values)
|
||||
{
|
||||
foreach (DynamicVolume volume in group.GetVolumes())
|
||||
{
|
||||
LogicalVolumeInfo lvi = new LogicalVolumeInfo(
|
||||
volume.Identity,
|
||||
null,
|
||||
volume.Open,
|
||||
volume.Length,
|
||||
volume.BiosType,
|
||||
volume.Status);
|
||||
result.Add(lvi);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static bool IsLdmPartition(PartitionInfo partition)
|
||||
{
|
||||
return partition.BiosType == BiosPartitionTypes.WindowsDynamicVolume
|
||||
|| partition.GuidType == GuidPartitionTypes.WindowsLdmMetadata
|
||||
|| partition.GuidType == GuidPartitionTypes.WindowsLdmData;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 System.Collections.Generic;
|
||||
using DiscUtils.Internal;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
[LogicalVolumeFactory]
|
||||
internal class DynamicDiskManagerFactory : LogicalVolumeFactory
|
||||
{
|
||||
public override bool HandlesPhysicalVolume(PhysicalVolumeInfo volume)
|
||||
{
|
||||
return DynamicDiskManager.HandlesPhysicalVolume(volume);
|
||||
}
|
||||
|
||||
public override void MapDisks(IEnumerable<VirtualDisk> disks, Dictionary<string, LogicalVolumeInfo> result)
|
||||
{
|
||||
DynamicDiskManager mgr = new DynamicDiskManager();
|
||||
|
||||
foreach (VirtualDisk disk in disks)
|
||||
{
|
||||
if (DynamicDiskManager.IsDynamicDisk(disk))
|
||||
{
|
||||
mgr.Add(disk);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (LogicalVolumeInfo vol in mgr.GetLogicalVolumes())
|
||||
{
|
||||
result.Add(vol.Identity, vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.IO;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
internal class DynamicVolume
|
||||
{
|
||||
private readonly DynamicDiskGroup _group;
|
||||
|
||||
internal DynamicVolume(DynamicDiskGroup group, Guid volumeId)
|
||||
{
|
||||
_group = group;
|
||||
Identity = volumeId;
|
||||
}
|
||||
|
||||
public byte BiosType
|
||||
{
|
||||
get { return Record.BiosType; }
|
||||
}
|
||||
|
||||
public Guid Identity { get; }
|
||||
|
||||
public long Length
|
||||
{
|
||||
get { return Record.Size * Sizes.Sector; }
|
||||
}
|
||||
|
||||
private VolumeRecord Record
|
||||
{
|
||||
get { return _group.GetVolume(Identity); }
|
||||
}
|
||||
|
||||
public LogicalVolumeStatus Status
|
||||
{
|
||||
get { return _group.GetVolumeStatus(Record.Id); }
|
||||
}
|
||||
|
||||
public SparseStream Open()
|
||||
{
|
||||
if (Status == LogicalVolumeStatus.Failed)
|
||||
{
|
||||
throw new IOException("Attempt to open 'failed' volume");
|
||||
}
|
||||
return _group.OpenVolume(Record.Id);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
//
|
||||
// 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.LogicalDiskManager
|
||||
{
|
||||
internal enum ExtentMergeType : byte
|
||||
{
|
||||
None = 0,
|
||||
Interleaved = 1,
|
||||
Concatenated = 2
|
||||
}
|
||||
}
|
@ -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.LogicalDiskManager
|
||||
{
|
||||
internal sealed class ExtentRecord : DatabaseRecord
|
||||
{
|
||||
public ulong ComponentId;
|
||||
public ulong DiskId;
|
||||
public long DiskOffsetLba;
|
||||
public ulong InterleaveOrder;
|
||||
public long OffsetInVolumeLba;
|
||||
public uint PartitionComponentLink;
|
||||
public long SizeLba;
|
||||
public uint Unknown1;
|
||||
public uint Unknown2;
|
||||
|
||||
protected override void DoReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
base.DoReadFrom(buffer, offset);
|
||||
|
||||
int pos = offset + 0x18;
|
||||
|
||||
Id = ReadVarULong(buffer, ref pos);
|
||||
Name = ReadVarString(buffer, ref pos);
|
||||
Unknown1 = ReadUInt(buffer, ref pos);
|
||||
Unknown2 = ReadUInt(buffer, ref pos);
|
||||
PartitionComponentLink = ReadUInt(buffer, ref pos);
|
||||
DiskOffsetLba = ReadLong(buffer, ref pos);
|
||||
OffsetInVolumeLba = ReadLong(buffer, ref pos);
|
||||
SizeLba = ReadVarLong(buffer, ref pos);
|
||||
ComponentId = ReadVarULong(buffer, ref pos);
|
||||
DiskId = ReadVarULong(buffer, ref pos);
|
||||
|
||||
if ((Flags & 0x0800) != 0)
|
||||
{
|
||||
InterleaveOrder = ReadVarULong(buffer, ref pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
internal class PrivateHeader
|
||||
{
|
||||
public uint Checksum; // 00 00 2f 96
|
||||
public long ConfigSizeLba;
|
||||
public long ConfigurationSizeLba; // 08 00
|
||||
public long ConfigurationStartLba; // 03 FF F8 00
|
||||
public long DataSizeLba; // 03 FF F7 C1
|
||||
public long DataStartLba; // 3F
|
||||
public string DiskGroupId; // GUID string
|
||||
public string DiskGroupName; // MAX_COMPUTER_NAME_LENGTH?
|
||||
public string DiskId; // GUID string
|
||||
public string HostId; // GUID string
|
||||
public long LogSizeLba;
|
||||
public long NextTocLba;
|
||||
public long NumberOfConfigs;
|
||||
public long NumberOfLogs;
|
||||
public string Signature; // PRIVHEAD
|
||||
public DateTime Timestamp;
|
||||
public long TocSizeLba;
|
||||
public long Unknown2; // Active TOC? 00 .. 00 01
|
||||
public long Unknown3; // 00 .. 07 ff // 1 sector less than 2MB
|
||||
public long Unknown4; // 00 .. 07 40
|
||||
public uint Unknown5; // Sector Size?
|
||||
public uint Version; // 2.12
|
||||
|
||||
public void ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 8);
|
||||
Checksum = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
|
||||
Version = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x0C);
|
||||
Timestamp = DateTime.FromFileTimeUtc(EndianUtilities.ToInt64BigEndian(buffer, offset + 0x10));
|
||||
Unknown2 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x18);
|
||||
Unknown3 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x20);
|
||||
Unknown4 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x28);
|
||||
DiskId = EndianUtilities.BytesToString(buffer, offset + 0x30, 0x40).Trim('\0');
|
||||
HostId = EndianUtilities.BytesToString(buffer, offset + 0x70, 0x40).Trim('\0');
|
||||
DiskGroupId = EndianUtilities.BytesToString(buffer, offset + 0xB0, 0x40).Trim('\0');
|
||||
DiskGroupName = EndianUtilities.BytesToString(buffer, offset + 0xF0, 31).Trim('\0');
|
||||
Unknown5 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x10F);
|
||||
DataStartLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x11B);
|
||||
DataSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x123);
|
||||
ConfigurationStartLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x12B);
|
||||
ConfigurationSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x133);
|
||||
TocSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x13B);
|
||||
NextTocLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x143);
|
||||
|
||||
// These two may be reversed
|
||||
NumberOfConfigs = EndianUtilities.ToInt32BigEndian(buffer, offset + 0x14B);
|
||||
NumberOfLogs = EndianUtilities.ToInt32BigEndian(buffer, offset + 0x14F);
|
||||
|
||||
ConfigSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x153);
|
||||
LogSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x15B);
|
||||
}
|
||||
|
||||
////}
|
||||
//// throw new NotImplementedException();
|
||||
//// // Add all byte values for 512 bytes
|
||||
//// // Zero checksum bytes (0x08, 4)
|
||||
////{
|
||||
|
||||
////private static int CalcChecksum()
|
||||
}
|
||||
}
|
34
libhac.Nand/DiscUtils.Core/LogicalDiskManager/RecordType.cs
Normal file
34
libhac.Nand/DiscUtils.Core/LogicalDiskManager/RecordType.cs
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// 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.LogicalDiskManager
|
||||
{
|
||||
internal enum RecordType : byte
|
||||
{
|
||||
None = 0,
|
||||
Volume = 1,
|
||||
Component = 2,
|
||||
Extent = 3,
|
||||
Disk = 4,
|
||||
DiskGroup = 5
|
||||
}
|
||||
}
|
72
libhac.Nand/DiscUtils.Core/LogicalDiskManager/TocBlock.cs
Normal file
72
libhac.Nand/DiscUtils.Core/LogicalDiskManager/TocBlock.cs
Normal file
@ -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 DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.LogicalDiskManager
|
||||
{
|
||||
internal class TocBlock
|
||||
{
|
||||
public uint Checksum; // 00 00 08 B6
|
||||
public long Item1Size; // Unit?
|
||||
public long Item1Start; // Sector Offset from ConfigurationStart
|
||||
public string Item1Str; // 'config', length 10
|
||||
public long Item2Size; // Unit?
|
||||
public long Item2Start; // Sector Offset from ConfigurationStart
|
||||
public string Item2Str; // 'log', length 10
|
||||
public long SequenceNumber; // 00 .. 01
|
||||
public string Signature; // TOCBLOCK
|
||||
public long Unknown1; // 0
|
||||
public long Unknown2; // 00
|
||||
public uint Unknown3; // 00 06 00 01 (may be two values?)
|
||||
public uint Unknown4; // 00 00 00 00
|
||||
public uint Unknown5; // 00 06 00 01 (may be two values?)
|
||||
public uint Unknown6; // 00 00 00 00
|
||||
|
||||
public void ReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 8);
|
||||
Checksum = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
|
||||
SequenceNumber = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x0C);
|
||||
Unknown1 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x14);
|
||||
Unknown2 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x1C);
|
||||
Item1Str = EndianUtilities.BytesToString(buffer, offset + 0x24, 10).Trim('\0');
|
||||
Item1Start = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x2E);
|
||||
Item1Size = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x36);
|
||||
Unknown3 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x3E);
|
||||
Unknown4 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x42);
|
||||
Item2Str = EndianUtilities.BytesToString(buffer, offset + 0x46, 10).Trim('\0');
|
||||
Item2Start = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x50);
|
||||
Item2Size = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x58);
|
||||
Unknown5 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x60);
|
||||
Unknown6 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x64);
|
||||
}
|
||||
|
||||
////}
|
||||
//// throw new NotImplementedException();
|
||||
//// // Add all byte values for ?? bytes
|
||||
//// // Zero checksum bytes (0x08, 4)
|
||||
////{
|
||||
|
||||
////private static int CalcChecksum()
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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.LogicalDiskManager
|
||||
{
|
||||
internal sealed class VolumeRecord : DatabaseRecord
|
||||
{
|
||||
public string ActiveString;
|
||||
public byte BiosType;
|
||||
public ulong ComponentCount;
|
||||
public ulong DupCount; // ??Seen once after adding 'foreign disk', from broken mirror (identical links(P/V/C))
|
||||
public string GenString;
|
||||
public string MountHint;
|
||||
public string NumberString; // 8000000000000000 sometimes...
|
||||
public uint PartitionComponentLink;
|
||||
public long Size;
|
||||
public ulong Unknown1; // Zero
|
||||
public uint Unknown2; // Zero
|
||||
public ulong UnknownA; // Zero
|
||||
public ulong UnknownB; // 00 .. 03
|
||||
public uint UnknownC; // 00 00 00 11
|
||||
public uint UnknownD; // Zero
|
||||
public Guid VolumeGuid;
|
||||
|
||||
protected override void DoReadFrom(byte[] buffer, int offset)
|
||||
{
|
||||
base.DoReadFrom(buffer, offset);
|
||||
|
||||
int pos = offset + 0x18;
|
||||
|
||||
Id = ReadVarULong(buffer, ref pos);
|
||||
Name = ReadVarString(buffer, ref pos);
|
||||
GenString = ReadVarString(buffer, ref pos);
|
||||
NumberString = ReadVarString(buffer, ref pos);
|
||||
ActiveString = ReadString(buffer, 6, ref pos);
|
||||
UnknownA = ReadVarULong(buffer, ref pos);
|
||||
UnknownB = ReadULong(buffer, ref pos);
|
||||
DupCount = ReadVarULong(buffer, ref pos);
|
||||
UnknownC = ReadUInt(buffer, ref pos);
|
||||
ComponentCount = ReadVarULong(buffer, ref pos);
|
||||
UnknownD = ReadUInt(buffer, ref pos);
|
||||
PartitionComponentLink = ReadUInt(buffer, ref pos);
|
||||
Unknown1 = ReadULong(buffer, ref pos);
|
||||
Size = ReadVarLong(buffer, ref pos);
|
||||
Unknown2 = ReadUInt(buffer, ref pos);
|
||||
BiosType = ReadByte(buffer, ref pos);
|
||||
VolumeGuid = EndianUtilities.ToGuidBigEndian(buffer, pos);
|
||||
pos += 16;
|
||||
|
||||
if ((Flags & 0x0200) != 0)
|
||||
{
|
||||
MountHint = ReadVarString(buffer, ref pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
libhac.Nand/DiscUtils.Core/LogicalVolumeInfo.cs
Normal file
120
libhac.Nand/DiscUtils.Core/LogicalVolumeInfo.cs
Normal file
@ -0,0 +1,120 @@
|
||||
//
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Information about a logical disk volume, which may be backed by one or more physical volumes.
|
||||
/// </summary>
|
||||
public sealed class LogicalVolumeInfo : VolumeInfo
|
||||
{
|
||||
private Guid _guid;
|
||||
private readonly SparseStreamOpenDelegate _opener;
|
||||
private readonly PhysicalVolumeInfo _physicalVol;
|
||||
|
||||
internal LogicalVolumeInfo(Guid guid, PhysicalVolumeInfo physicalVolume, SparseStreamOpenDelegate opener,
|
||||
long length, byte biosType, LogicalVolumeStatus status)
|
||||
{
|
||||
_guid = guid;
|
||||
_physicalVol = physicalVolume;
|
||||
_opener = opener;
|
||||
Length = length;
|
||||
BiosType = biosType;
|
||||
Status = status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the disk geometry of the underlying storage medium (as used in BIOS calls), may be null.
|
||||
/// </summary>
|
||||
public override Geometry BiosGeometry
|
||||
{
|
||||
get { return _physicalVol == null ? Geometry.Null : _physicalVol.BiosGeometry; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the one-byte BIOS type for this volume, which indicates the content.
|
||||
/// </summary>
|
||||
public override byte BiosType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The stable identity for this logical volume.
|
||||
/// </summary>
|
||||
/// <remarks>The stability of the identity depends the disk structure.
|
||||
/// In some cases the identity may include a simple index, when no other information
|
||||
/// is available. Best practice is to add disks to the Volume Manager in a stable
|
||||
/// order, if the stability of this identity is paramount.</remarks>
|
||||
public override string Identity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_guid != Guid.Empty)
|
||||
{
|
||||
return "VLG" + _guid.ToString("B");
|
||||
}
|
||||
return "VLP:" + _physicalVol.Identity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the volume (in bytes).
|
||||
/// </summary>
|
||||
public override long Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the disk geometry of the underlying storage medium, if any (may be Geometry.Null).
|
||||
/// </summary>
|
||||
public override Geometry PhysicalGeometry
|
||||
{
|
||||
get { return _physicalVol == null ? Geometry.Null : _physicalVol.PhysicalGeometry; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset of this volume in the underlying storage medium, if any (may be Zero).
|
||||
/// </summary>
|
||||
public override long PhysicalStartSector
|
||||
{
|
||||
get { return _physicalVol == null ? 0 : _physicalVol.PhysicalStartSector; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the logical volume, indicating volume health.
|
||||
/// </summary>
|
||||
public LogicalVolumeStatus Status { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying physical volume info
|
||||
/// </summary>
|
||||
public PhysicalVolumeInfo PhysicalVolume { get { return _physicalVol; } }
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream with access to the content of the logical volume.
|
||||
/// </summary>
|
||||
/// <returns>The volume's content as a stream.</returns>
|
||||
public override SparseStream Open()
|
||||
{
|
||||
return _opener();
|
||||
}
|
||||
}
|
||||
}
|
23
libhac.Nand/DiscUtils.Core/LogicalVolumeStatus.cs
Normal file
23
libhac.Nand/DiscUtils.Core/LogicalVolumeStatus.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of the health status of a logical volume.
|
||||
/// </summary>
|
||||
public enum LogicalVolumeStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The volume is healthy and fully functional.
|
||||
/// </summary>
|
||||
Healthy = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The volume is completely accessible, but at degraded redundancy.
|
||||
/// </summary>
|
||||
FailedRedundancy = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The volume is wholey, or partly, inaccessible.
|
||||
/// </summary>
|
||||
Failed = 2
|
||||
}
|
||||
}
|
804
libhac.Nand/DiscUtils.Core/NativeFileSystem.cs
Normal file
804
libhac.Nand/DiscUtils.Core/NativeFileSystem.cs
Normal file
@ -0,0 +1,804 @@
|
||||
//
|
||||
// DiscUtils Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Original NativeFileSystem contributed by bsobel:
|
||||
// http://discutils.codeplex.com/workitem/5190
|
||||
//
|
||||
// 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.Streams;
|
||||
|
||||
namespace DiscUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an implementation for OS-mounted file systems.
|
||||
/// </summary>
|
||||
public class NativeFileSystem : DiscFileSystem
|
||||
{
|
||||
private readonly bool _readOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the NativeFileSystem class.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The 'root' directory of the new instance.</param>
|
||||
/// <param name="readOnly">Only permit 'read' activities.</param>
|
||||
public NativeFileSystem(string basePath, bool readOnly)
|
||||
{
|
||||
BasePath = basePath;
|
||||
if (!BasePath.EndsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
BasePath += @"\";
|
||||
}
|
||||
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base path used to create the file system.
|
||||
/// </summary>
|
||||
public string BasePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the file system is read-only or read-write.
|
||||
/// </summary>
|
||||
/// <returns>true if the file system is read-write.</returns>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return !_readOnly; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a friendly description of the file system type.
|
||||
/// </summary>
|
||||
public override string FriendlyName
|
||||
{
|
||||
get { return "Native"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file system is thread-safe.
|
||||
/// </summary>
|
||||
/// <remarks>The Native File System is thread safe.</remarks>
|
||||
public override bool IsThreadSafe
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root directory of the file system.
|
||||
/// </summary>
|
||||
public override DiscDirectoryInfo Root
|
||||
{
|
||||
get { return new DiscDirectoryInfo(this, string.Empty); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the volume label.
|
||||
/// </summary>
|
||||
public override string VolumeLabel
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
/// <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 override void CopyFile(string sourceFile, string destinationFile)
|
||||
{
|
||||
CopyFile(sourceFile, destinationFile, true);
|
||||
}
|
||||
|
||||
/// <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 override void CopyFile(string sourceFile, string destinationFile, bool overwrite)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (sourceFile.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sourceFile = sourceFile.Substring(1);
|
||||
}
|
||||
|
||||
if (destinationFile.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
destinationFile = destinationFile.Substring(1);
|
||||
}
|
||||
|
||||
File.Copy(Path.Combine(BasePath, sourceFile), Path.Combine(BasePath, destinationFile), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the new directory.</param>
|
||||
public override void CreateDirectory(string path)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(BasePath, path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory to delete.</param>
|
||||
public override void DeleteDirectory(string path)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
Directory.Delete(Path.Combine(BasePath, 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 override 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 override void DeleteFile(string path)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
File.Delete(Path.Combine(BasePath, path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the directory exists.</returns>
|
||||
public override bool DirectoryExists(string path)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
return Directory.Exists(Path.Combine(BasePath, path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a file exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns>true if the file exists.</returns>
|
||||
public override bool FileExists(string path)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
return File.Exists(Path.Combine(BasePath, 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 override 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 override 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 override 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 override string[] GetDirectories(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return CleanItems(Directory.GetDirectories(Path.Combine(BasePath, path), searchPattern, searchOption));
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 override 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 override 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 override string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return CleanItems(Directory.GetFiles(Path.Combine(BasePath, path), searchPattern, searchOption));
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 override string[] GetFileSystemEntries(string path)
|
||||
{
|
||||
return GetFileSystemEntries(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 override string[] GetFileSystemEntries(string path, string searchPattern)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return CleanItems(Directory.GetFileSystemEntries(Path.Combine(BasePath, path), searchPattern));
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves a directory.
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectoryName">The directory to move.</param>
|
||||
/// <param name="destinationDirectoryName">The target directory name.</param>
|
||||
public override void MoveDirectory(string sourceDirectoryName, string destinationDirectoryName)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (sourceDirectoryName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sourceDirectoryName = sourceDirectoryName.Substring(1);
|
||||
}
|
||||
|
||||
if (destinationDirectoryName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
destinationDirectoryName = destinationDirectoryName.Substring(1);
|
||||
}
|
||||
|
||||
Directory.Move(Path.Combine(BasePath, sourceDirectoryName),
|
||||
Path.Combine(BasePath, destinationDirectoryName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves a file.
|
||||
/// </summary>
|
||||
/// <param name="sourceName">The file to move.</param>
|
||||
/// <param name="destinationName">The target file name.</param>
|
||||
public override 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 override void MoveFile(string sourceName, string destinationName, bool overwrite)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (destinationName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
destinationName = destinationName.Substring(1);
|
||||
}
|
||||
|
||||
if (FileExists(Path.Combine(BasePath, destinationName)))
|
||||
{
|
||||
if (overwrite)
|
||||
{
|
||||
DeleteFile(Path.Combine(BasePath, destinationName));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("File already exists");
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sourceName = sourceName.Substring(1);
|
||||
}
|
||||
|
||||
File.Move(Path.Combine(BasePath, sourceName), Path.Combine(BasePath, destinationName));
|
||||
}
|
||||
|
||||
/// <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 override 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 override SparseStream OpenFile(string path, FileMode mode, FileAccess access)
|
||||
{
|
||||
if (_readOnly && access != FileAccess.Read)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
FileShare fileShare = FileShare.None;
|
||||
if (access == FileAccess.Read)
|
||||
{
|
||||
fileShare = FileShare.Read;
|
||||
}
|
||||
|
||||
var locator = new LocalFileLocator(BasePath);
|
||||
return SparseStream.FromStream(locator.Open(path, mode, access, fileShare),
|
||||
Ownership.Dispose);
|
||||
}
|
||||
|
||||
/// <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 override FileAttributes GetAttributes(string path)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
return File.GetAttributes(Path.Combine(BasePath, 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 override void SetAttributes(string path, FileAttributes newValue)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
File.SetAttributes(Path.Combine(BasePath, path), 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 override 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 override 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 override DateTime GetCreationTimeUtc(string path)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
return File.GetCreationTimeUtc(Path.Combine(BasePath, 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 override void SetCreationTimeUtc(string path, DateTime newTime)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
File.SetCreationTimeUtc(Path.Combine(BasePath, path), 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 override 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 override 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 override DateTime GetLastAccessTimeUtc(string path)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
return File.GetLastAccessTimeUtc(Path.Combine(BasePath, 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 override void SetLastAccessTimeUtc(string path, DateTime newTime)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
File.SetLastAccessTimeUtc(Path.Combine(BasePath, path), 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 override 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 override 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 override DateTime GetLastWriteTimeUtc(string path)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
return File.GetLastWriteTimeUtc(Path.Combine(BasePath, 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 override void SetLastWriteTimeUtc(string path, DateTime newTime)
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
File.SetLastWriteTimeUtc(Path.Combine(BasePath, path), 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 override long GetFileLength(string path)
|
||||
{
|
||||
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
return new FileInfo(Path.Combine(BasePath, path)).Length;
|
||||
}
|
||||
|
||||
/// <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 override 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 override 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 override DiscFileSystemInfo GetFileSystemInfo(string path)
|
||||
{
|
||||
return new DiscFileSystemInfo(this, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Size of the Filesystem in bytes
|
||||
/// </summary>
|
||||
public override long Size
|
||||
{
|
||||
get
|
||||
{
|
||||
DriveInfo info = new DriveInfo(BasePath);
|
||||
return info.TotalSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used space of the Filesystem in bytes
|
||||
/// </summary>
|
||||
public override long UsedSpace
|
||||
{
|
||||
get { return Size - AvailableSpace; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Available space of the Filesystem in bytes
|
||||
/// </summary>
|
||||
public override long AvailableSpace
|
||||
{
|
||||
get
|
||||
{
|
||||
DriveInfo info = new DriveInfo(BasePath);
|
||||
return info.AvailableFreeSpace;
|
||||
}
|
||||
}
|
||||
|
||||
private string[] CleanItems(string[] dirtyItems)
|
||||
{
|
||||
string[] cleanList = new string[dirtyItems.Length];
|
||||
for (int x = 0; x < dirtyItems.Length; x++)
|
||||
{
|
||||
cleanList[x] = dirtyItems[x].Substring(BasePath.Length - 1);
|
||||
}
|
||||
|
||||
return cleanList;
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Collections.Generic;
|
||||
using System.IO;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Partitions
|
||||
{
|
||||
internal class BiosExtendedPartitionTable
|
||||
{
|
||||
private readonly Stream _disk;
|
||||
private readonly uint _firstSector;
|
||||
|
||||
public BiosExtendedPartitionTable(Stream disk, uint firstSector)
|
||||
{
|
||||
_disk = disk;
|
||||
_firstSector = firstSector;
|
||||
}
|
||||
|
||||
public BiosPartitionRecord[] GetPartitions()
|
||||
{
|
||||
List<BiosPartitionRecord> result = new List<BiosPartitionRecord>();
|
||||
|
||||
uint partPos = _firstSector;
|
||||
while (partPos != 0)
|
||||
{
|
||||
_disk.Position = (long)partPos * Sizes.Sector;
|
||||
byte[] sector = StreamUtilities.ReadExact(_disk, Sizes.Sector);
|
||||
if (sector[510] != 0x55 || sector[511] != 0xAA)
|
||||
{
|
||||
throw new IOException("Invalid extended partition sector");
|
||||
}
|
||||
|
||||
uint nextPartPos = 0;
|
||||
for (int offset = 0x1BE; offset <= 0x1EE; offset += 0x10)
|
||||
{
|
||||
BiosPartitionRecord thisPart = new BiosPartitionRecord(sector, offset, partPos, -1);
|
||||
|
||||
if (thisPart.StartCylinder != 0 || thisPart.StartHead != 0 || thisPart.StartSector != 0 ||
|
||||
(thisPart.LBAStart != 0 && thisPart.LBALength != 0))
|
||||
{
|
||||
if (thisPart.PartitionType != 0x05 && thisPart.PartitionType != 0x0F)
|
||||
{
|
||||
result.Add(thisPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextPartPos = _firstSector + thisPart.LBAStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partPos = nextPartPos;
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the disk ranges containing partition table data.
|
||||
/// </summary>
|
||||
/// <returns>Set of stream extents, indicated as byte offset from the start of the disk.</returns>
|
||||
public IEnumerable<StreamExtent> GetMetadataDiskExtents()
|
||||
{
|
||||
List<StreamExtent> extents = new List<StreamExtent>();
|
||||
|
||||
uint partPos = _firstSector;
|
||||
while (partPos != 0)
|
||||
{
|
||||
extents.Add(new StreamExtent((long)partPos * Sizes.Sector, Sizes.Sector));
|
||||
|
||||
_disk.Position = (long)partPos * Sizes.Sector;
|
||||
byte[] sector = StreamUtilities.ReadExact(_disk, Sizes.Sector);
|
||||
if (sector[510] != 0x55 || sector[511] != 0xAA)
|
||||
{
|
||||
throw new IOException("Invalid extended partition sector");
|
||||
}
|
||||
|
||||
uint nextPartPos = 0;
|
||||
for (int offset = 0x1BE; offset <= 0x1EE; offset += 0x10)
|
||||
{
|
||||
BiosPartitionRecord thisPart = new BiosPartitionRecord(sector, offset, partPos, -1);
|
||||
|
||||
if (thisPart.StartCylinder != 0 || thisPart.StartHead != 0 || thisPart.StartSector != 0)
|
||||
{
|
||||
if (thisPart.PartitionType == 0x05 || thisPart.PartitionType == 0x0F)
|
||||
{
|
||||
nextPartPos = _firstSector + thisPart.LBAStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partPos = nextPartPos;
|
||||
}
|
||||
|
||||
return extents;
|
||||
}
|
||||
}
|
||||
}
|
136
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionInfo.cs
Normal file
136
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionInfo.cs
Normal file
@ -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 DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Partitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to partition records in a BIOS (MBR) partition table.
|
||||
/// </summary>
|
||||
public sealed class BiosPartitionInfo : PartitionInfo
|
||||
{
|
||||
private readonly BiosPartitionRecord _record;
|
||||
private readonly BiosPartitionTable _table;
|
||||
|
||||
internal BiosPartitionInfo(BiosPartitionTable table, BiosPartitionRecord record)
|
||||
{
|
||||
_table = table;
|
||||
_record = record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the partition.
|
||||
/// </summary>
|
||||
public override byte BiosType
|
||||
{
|
||||
get { return _record.PartitionType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end (inclusive) of the partition as a CHS address.
|
||||
/// </summary>
|
||||
public ChsAddress End
|
||||
{
|
||||
get { return new ChsAddress(_record.EndCylinder, _record.EndHead, _record.EndSector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first sector of the partion (relative to start of disk) as a Logical Block Address.
|
||||
/// </summary>
|
||||
public override long FirstSector
|
||||
{
|
||||
get { return _record.LBAStartAbsolute; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns <see cref="System.Guid"/>.Empty.
|
||||
/// </summary>
|
||||
public override Guid GuidType
|
||||
{
|
||||
get { return Guid.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this partition is active (bootable).
|
||||
/// </summary>
|
||||
public bool IsActive
|
||||
{
|
||||
get { return _record.Status != 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the partition is a primary (rather than extended) partition.
|
||||
/// </summary>
|
||||
public bool IsPrimary
|
||||
{
|
||||
get { return PrimaryIndex >= 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last sector of the partion (relative to start of disk) as a Logical Block Address (inclusive).
|
||||
/// </summary>
|
||||
public override long LastSector
|
||||
{
|
||||
get { return _record.LBAStartAbsolute + _record.LBALength - 1; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the partition in the primary partition table, or <c>-1</c> if not a primary partition.
|
||||
/// </summary>
|
||||
public int PrimaryIndex
|
||||
{
|
||||
get { return _record.Index; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start of the partition as a CHS address.
|
||||
/// </summary>
|
||||
public ChsAddress Start
|
||||
{
|
||||
get { return new ChsAddress(_record.StartCylinder, _record.StartHead, _record.StartSector); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the partition as a string.
|
||||
/// </summary>
|
||||
public override string TypeAsString
|
||||
{
|
||||
get { return _record.FriendlyPartitionType; }
|
||||
}
|
||||
|
||||
internal override PhysicalVolumeType VolumeType
|
||||
{
|
||||
get { return PhysicalVolumeType.BiosPartition; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream to access the content of the partition.
|
||||
/// </summary>
|
||||
/// <returns>The new stream.</returns>
|
||||
public override SparseStream Open()
|
||||
{
|
||||
return _table.Open(_record);
|
||||
}
|
||||
}
|
||||
}
|
107
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionRecord.cs
Normal file
107
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionRecord.cs
Normal file
@ -0,0 +1,107 @@
|
||||
//
|
||||
// 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.Partitions
|
||||
{
|
||||
internal class BiosPartitionRecord : IComparable<BiosPartitionRecord>
|
||||
{
|
||||
private readonly uint _lbaOffset;
|
||||
|
||||
public BiosPartitionRecord() {}
|
||||
|
||||
public BiosPartitionRecord(byte[] data, int offset, uint lbaOffset, int index)
|
||||
{
|
||||
_lbaOffset = lbaOffset;
|
||||
|
||||
Status = data[offset];
|
||||
StartHead = data[offset + 1];
|
||||
StartSector = (byte)(data[offset + 2] & 0x3F);
|
||||
StartCylinder = (ushort)(data[offset + 3] | ((data[offset + 2] & 0xC0) << 2));
|
||||
PartitionType = data[offset + 4];
|
||||
EndHead = data[offset + 5];
|
||||
EndSector = (byte)(data[offset + 6] & 0x3F);
|
||||
EndCylinder = (ushort)(data[offset + 7] | ((data[offset + 6] & 0xC0) << 2));
|
||||
LBAStart = EndianUtilities.ToUInt32LittleEndian(data, offset + 8);
|
||||
LBALength = EndianUtilities.ToUInt32LittleEndian(data, offset + 12);
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public ushort EndCylinder { get; set; }
|
||||
|
||||
public byte EndHead { get; set; }
|
||||
|
||||
public byte EndSector { get; set; }
|
||||
|
||||
public string FriendlyPartitionType
|
||||
{
|
||||
get { return BiosPartitionTypes.ToString(PartitionType); }
|
||||
}
|
||||
|
||||
public int Index { get; }
|
||||
|
||||
public bool IsValid
|
||||
{
|
||||
get { return EndHead != 0 || EndSector != 0 || EndCylinder != 0 || LBALength != 0; }
|
||||
}
|
||||
|
||||
public uint LBALength { get; set; }
|
||||
|
||||
public uint LBAStart { get; set; }
|
||||
|
||||
public uint LBAStartAbsolute
|
||||
{
|
||||
get { return LBAStart + _lbaOffset; }
|
||||
}
|
||||
|
||||
public byte PartitionType { get; set; }
|
||||
|
||||
public ushort StartCylinder { get; set; }
|
||||
|
||||
public byte StartHead { get; set; }
|
||||
|
||||
public byte StartSector { get; set; }
|
||||
|
||||
public byte Status { get; set; }
|
||||
|
||||
public int CompareTo(BiosPartitionRecord other)
|
||||
{
|
||||
return LBAStartAbsolute.CompareTo(other.LBAStartAbsolute);
|
||||
}
|
||||
|
||||
internal void WriteTo(byte[] buffer, int offset)
|
||||
{
|
||||
buffer[offset] = Status;
|
||||
buffer[offset + 1] = StartHead;
|
||||
buffer[offset + 2] = (byte)((StartSector & 0x3F) | ((StartCylinder >> 2) & 0xC0));
|
||||
buffer[offset + 3] = (byte)StartCylinder;
|
||||
buffer[offset + 4] = PartitionType;
|
||||
buffer[offset + 5] = EndHead;
|
||||
buffer[offset + 6] = (byte)((EndSector & 0x3F) | ((EndCylinder >> 2) & 0xC0));
|
||||
buffer[offset + 7] = (byte)EndCylinder;
|
||||
EndianUtilities.WriteBytesLittleEndian(LBAStart, buffer, offset + 8);
|
||||
EndianUtilities.WriteBytesLittleEndian(LBALength, buffer, offset + 12);
|
||||
}
|
||||
}
|
||||
}
|
734
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionTable.cs
Normal file
734
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionTable.cs
Normal file
@ -0,0 +1,734 @@
|
||||
//
|
||||
// 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.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using DiscUtils.Internal;
|
||||
using DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Partitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a BIOS (MBR) Partition Table.
|
||||
/// </summary>
|
||||
public sealed class BiosPartitionTable : PartitionTable
|
||||
{
|
||||
private Stream _diskData;
|
||||
private Geometry _diskGeometry;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BiosPartitionTable class.
|
||||
/// </summary>
|
||||
/// <param name="disk">The disk containing the partition table.</param>
|
||||
public BiosPartitionTable(VirtualDisk disk)
|
||||
{
|
||||
Init(disk.Content, disk.BiosGeometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BiosPartitionTable class.
|
||||
/// </summary>
|
||||
/// <param name="disk">The stream containing the disk data.</param>
|
||||
/// <param name="diskGeometry">The geometry of the disk.</param>
|
||||
public BiosPartitionTable(Stream disk, Geometry diskGeometry)
|
||||
{
|
||||
Init(disk, diskGeometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of the partitions for storing Operating System file-systems.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<BiosPartitionInfo> BiosUserPartitions
|
||||
{
|
||||
get
|
||||
{
|
||||
List<BiosPartitionInfo> result = new List<BiosPartitionInfo>();
|
||||
foreach (BiosPartitionRecord r in GetAllRecords())
|
||||
{
|
||||
if (r.IsValid)
|
||||
{
|
||||
result.Add(new BiosPartitionInfo(this, r));
|
||||
}
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<BiosPartitionInfo>(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID that uniquely identifies this disk, if supported (else returns <c>null</c>).
|
||||
/// </summary>
|
||||
public override Guid DiskGuid
|
||||
{
|
||||
get { return Guid.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of the partitions for storing Operating System file-systems.
|
||||
/// </summary>
|
||||
public override ReadOnlyCollection<PartitionInfo> Partitions
|
||||
{
|
||||
get
|
||||
{
|
||||
List<PartitionInfo> result = new List<PartitionInfo>();
|
||||
foreach (BiosPartitionRecord r in GetAllRecords())
|
||||
{
|
||||
if (r.IsValid)
|
||||
{
|
||||
result.Add(new BiosPartitionInfo(this, r));
|
||||
}
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<PartitionInfo>(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a best guess at the geometry of a disk.
|
||||
/// </summary>
|
||||
/// <param name="disk">String containing the disk image to detect the geometry from.</param>
|
||||
/// <returns>The detected geometry.</returns>
|
||||
public static Geometry DetectGeometry(Stream disk)
|
||||
{
|
||||
if (disk.Length >= Sizes.Sector)
|
||||
{
|
||||
disk.Position = 0;
|
||||
byte[] bootSector = StreamUtilities.ReadExact(disk, Sizes.Sector);
|
||||
if (bootSector[510] == 0x55 && bootSector[511] == 0xAA)
|
||||
{
|
||||
byte maxHead = 0;
|
||||
byte maxSector = 0;
|
||||
foreach (BiosPartitionRecord record in ReadPrimaryRecords(bootSector))
|
||||
{
|
||||
maxHead = Math.Max(maxHead, record.EndHead);
|
||||
maxSector = Math.Max(maxSector, record.EndSector);
|
||||
}
|
||||
|
||||
if (maxHead > 0 && maxSector > 0)
|
||||
{
|
||||
int cylSize = (maxHead + 1) * maxSector * 512;
|
||||
return new Geometry((int)MathUtilities.Ceil(disk.Length, cylSize), maxHead + 1, maxSector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Geometry.FromCapacity(disk.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a stream contains a valid partition table.
|
||||
/// </summary>
|
||||
/// <param name="disk">The stream to inspect.</param>
|
||||
/// <returns><c>true</c> if the partition table is valid, else <c>false</c>.</returns>
|
||||
public static bool IsValid(Stream disk)
|
||||
{
|
||||
if (disk.Length < Sizes.Sector)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
disk.Position = 0;
|
||||
byte[] bootSector = StreamUtilities.ReadExact(disk, Sizes.Sector);
|
||||
|
||||
// Check for the 'bootable sector' marker
|
||||
if (bootSector[510] != 0x55 || bootSector[511] != 0xAA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<StreamExtent> knownPartitions = new List<StreamExtent>();
|
||||
foreach (BiosPartitionRecord record in ReadPrimaryRecords(bootSector))
|
||||
{
|
||||
// If the partition extends beyond the end of the disk, this is probably an invalid partition table
|
||||
if (record.LBALength != 0xFFFFFFFF &&
|
||||
(record.LBAStart + (long)record.LBALength) * Sizes.Sector > disk.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (record.LBALength > 0)
|
||||
{
|
||||
StreamExtent[] thisPartitionExtents = { new StreamExtent(record.LBAStart, record.LBALength) };
|
||||
|
||||
// If the partition intersects another partition, this is probably an invalid partition table
|
||||
foreach (StreamExtent overlap in StreamExtent.Intersect(knownPartitions, thisPartitionExtents))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
knownPartitions = new List<StreamExtent>(StreamExtent.Union(knownPartitions, thisPartitionExtents));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new partition table on a disk.
|
||||
/// </summary>
|
||||
/// <param name="disk">The disk to initialize.</param>
|
||||
/// <returns>An object to access the newly created partition table.</returns>
|
||||
public static BiosPartitionTable Initialize(VirtualDisk disk)
|
||||
{
|
||||
return Initialize(disk.Content, disk.BiosGeometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new partition table on a disk containing a single partition.
|
||||
/// </summary>
|
||||
/// <param name="disk">The disk to initialize.</param>
|
||||
/// <param name="type">The partition type for the single partition.</param>
|
||||
/// <returns>An object to access the newly created partition table.</returns>
|
||||
public static BiosPartitionTable Initialize(VirtualDisk disk, WellKnownPartitionType type)
|
||||
{
|
||||
BiosPartitionTable table = Initialize(disk.Content, disk.BiosGeometry);
|
||||
table.Create(type, true);
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new partition table on a disk.
|
||||
/// </summary>
|
||||
/// <param name="disk">The stream containing the disk data.</param>
|
||||
/// <param name="diskGeometry">The geometry of the disk.</param>
|
||||
/// <returns>An object to access the newly created partition table.</returns>
|
||||
public static BiosPartitionTable Initialize(Stream disk, Geometry diskGeometry)
|
||||
{
|
||||
Stream data = disk;
|
||||
|
||||
byte[] bootSector;
|
||||
if (data.Length >= Sizes.Sector)
|
||||
{
|
||||
data.Position = 0;
|
||||
bootSector = StreamUtilities.ReadExact(data, Sizes.Sector);
|
||||
}
|
||||
else
|
||||
{
|
||||
bootSector = new byte[Sizes.Sector];
|
||||
}
|
||||
|
||||
// Wipe all four 16-byte partition table entries
|
||||
Array.Clear(bootSector, 0x01BE, 16 * 4);
|
||||
|
||||
// Marker bytes
|
||||
bootSector[510] = 0x55;
|
||||
bootSector[511] = 0xAA;
|
||||
|
||||
data.Position = 0;
|
||||
data.Write(bootSector, 0, bootSector.Length);
|
||||
|
||||
return new BiosPartitionTable(disk, diskGeometry);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
Geometry allocationGeometry = new Geometry(_diskData.Length, _diskGeometry.HeadsPerCylinder,
|
||||
_diskGeometry.SectorsPerTrack, _diskGeometry.BytesPerSector);
|
||||
|
||||
ChsAddress start = new ChsAddress(0, 1, 1);
|
||||
ChsAddress last = allocationGeometry.LastSector;
|
||||
|
||||
long startLba = allocationGeometry.ToLogicalBlockAddress(start);
|
||||
long lastLba = allocationGeometry.ToLogicalBlockAddress(last);
|
||||
|
||||
return CreatePrimaryByCylinder(0, allocationGeometry.Cylinders - 1,
|
||||
ConvertType(type, (lastLba - startLba) * Sizes.Sector), active);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new primary 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)
|
||||
{
|
||||
int cylinderCapacity = _diskGeometry.SectorsPerTrack * _diskGeometry.HeadsPerCylinder *
|
||||
_diskGeometry.BytesPerSector;
|
||||
int numCylinders = (int)(size / cylinderCapacity);
|
||||
|
||||
int startCylinder = FindCylinderGap(numCylinders);
|
||||
|
||||
return CreatePrimaryByCylinder(startCylinder, startCylinder + numCylinders - 1, ConvertType(type, size),
|
||||
active);
|
||||
}
|
||||
|
||||
/// <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 bytes).</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)
|
||||
{
|
||||
Geometry allocationGeometry = new Geometry(_diskData.Length, _diskGeometry.HeadsPerCylinder,
|
||||
_diskGeometry.SectorsPerTrack, _diskGeometry.BytesPerSector);
|
||||
|
||||
ChsAddress start = new ChsAddress(0, 1, 1);
|
||||
|
||||
long startLba = MathUtilities.RoundUp(allocationGeometry.ToLogicalBlockAddress(start),
|
||||
alignment / _diskGeometry.BytesPerSector);
|
||||
long lastLba = MathUtilities.RoundDown(_diskData.Length / _diskGeometry.BytesPerSector,
|
||||
alignment / _diskGeometry.BytesPerSector);
|
||||
|
||||
return CreatePrimaryBySector(startLba, lastLba - 1,
|
||||
ConvertType(type, (lastLba - startLba) * _diskGeometry.BytesPerSector), active);
|
||||
}
|
||||
|
||||
/// <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 bytes).</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)
|
||||
{
|
||||
if (size < _diskGeometry.BytesPerSector)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size), size, "size must be at least one sector");
|
||||
}
|
||||
|
||||
if (alignment % _diskGeometry.BytesPerSector != 0)
|
||||
{
|
||||
throw new ArgumentException("Alignment is not a multiple of the sector size");
|
||||
}
|
||||
|
||||
if (size % alignment != 0)
|
||||
{
|
||||
throw new ArgumentException("Size is not a multiple of the alignment");
|
||||
}
|
||||
|
||||
long sectorLength = size / _diskGeometry.BytesPerSector;
|
||||
long start = FindGap(size / _diskGeometry.BytesPerSector, alignment / _diskGeometry.BytesPerSector);
|
||||
|
||||
return CreatePrimaryBySector(start, start + sectorLength - 1,
|
||||
ConvertType(type, sectorLength * Sizes.Sector), active);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a partition at a given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the partition.</param>
|
||||
public override void Delete(int index)
|
||||
{
|
||||
WriteRecord(index, new BiosPartitionRecord());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Primary Partition that occupies whole cylinders, for best compatibility.
|
||||
/// </summary>
|
||||
/// <param name="first">The first cylinder to include in the partition (inclusive).</param>
|
||||
/// <param name="last">The last cylinder to include in the partition (inclusive).</param>
|
||||
/// <param name="type">The BIOS (MBR) type of the new partition.</param>
|
||||
/// <param name="markActive">Whether to mark the partition active (bootable).</param>
|
||||
/// <returns>The index of the new partition.</returns>
|
||||
/// <remarks>If the cylinder 0 is given, the first track will not be used, to reserve space
|
||||
/// for the meta-data at the start of the disk.</remarks>
|
||||
public int CreatePrimaryByCylinder(int first, int last, byte type, bool markActive)
|
||||
{
|
||||
if (first < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(first), first, "First cylinder must be Zero or greater");
|
||||
}
|
||||
|
||||
if (last <= first)
|
||||
{
|
||||
throw new ArgumentException("Last cylinder must be greater than first");
|
||||
}
|
||||
|
||||
long lbaStart = first == 0
|
||||
? _diskGeometry.ToLogicalBlockAddress(0, 1, 1)
|
||||
: _diskGeometry.ToLogicalBlockAddress(first, 0, 1);
|
||||
long lbaLast = _diskGeometry.ToLogicalBlockAddress(last, _diskGeometry.HeadsPerCylinder - 1,
|
||||
_diskGeometry.SectorsPerTrack);
|
||||
|
||||
return CreatePrimaryBySector(lbaStart, lbaLast, type, markActive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Primary Partition, specified by Logical Block Addresses.
|
||||
/// </summary>
|
||||
/// <param name="first">The LBA address of the first sector (inclusive).</param>
|
||||
/// <param name="last">The LBA address of the last sector (inclusive).</param>
|
||||
/// <param name="type">The BIOS (MBR) type of the new partition.</param>
|
||||
/// <param name="markActive">Whether to mark the partition active (bootable).</param>
|
||||
/// <returns>The index of the new partition.</returns>
|
||||
public int CreatePrimaryBySector(long first, long last, byte type, bool markActive)
|
||||
{
|
||||
if (first >= last)
|
||||
{
|
||||
throw new ArgumentException("The first sector in a partition must be before the last");
|
||||
}
|
||||
|
||||
if ((last + 1) * _diskGeometry.BytesPerSector > _diskData.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(last), last,
|
||||
"The last sector extends beyond the end of the disk");
|
||||
}
|
||||
|
||||
BiosPartitionRecord[] existing = GetPrimaryRecords();
|
||||
|
||||
BiosPartitionRecord newRecord = new BiosPartitionRecord();
|
||||
ChsAddress startAddr = _diskGeometry.ToChsAddress(first);
|
||||
ChsAddress endAddr = _diskGeometry.ToChsAddress(last);
|
||||
|
||||
// Because C/H/S addresses can max out at lower values than the LBA values,
|
||||
// the special tuple (1023, 254, 63) is used.
|
||||
if (startAddr.Cylinder > 1023)
|
||||
{
|
||||
startAddr = new ChsAddress(1023, 254, 63);
|
||||
}
|
||||
|
||||
if (endAddr.Cylinder > 1023)
|
||||
{
|
||||
endAddr = new ChsAddress(1023, 254, 63);
|
||||
}
|
||||
|
||||
newRecord.StartCylinder = (ushort)startAddr.Cylinder;
|
||||
newRecord.StartHead = (byte)startAddr.Head;
|
||||
newRecord.StartSector = (byte)startAddr.Sector;
|
||||
newRecord.EndCylinder = (ushort)endAddr.Cylinder;
|
||||
newRecord.EndHead = (byte)endAddr.Head;
|
||||
newRecord.EndSector = (byte)endAddr.Sector;
|
||||
newRecord.LBAStart = (uint)first;
|
||||
newRecord.LBALength = (uint)(last - first + 1);
|
||||
newRecord.PartitionType = type;
|
||||
newRecord.Status = (byte)(markActive ? 0x80 : 0x00);
|
||||
|
||||
// First check for overlap with existing partition...
|
||||
foreach (BiosPartitionRecord r in existing)
|
||||
{
|
||||
if (Utilities.RangesOverlap((uint)first, (uint)last + 1, r.LBAStartAbsolute,
|
||||
r.LBAStartAbsolute + r.LBALength))
|
||||
{
|
||||
throw new IOException("New partition overlaps with existing partition");
|
||||
}
|
||||
}
|
||||
|
||||
// Now look for empty partition
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (!existing[i].IsValid)
|
||||
{
|
||||
WriteRecord(i, newRecord);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("No primary partition slots available");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the active partition.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the primary partition to mark bootable, or <c>-1</c> for none.</param>
|
||||
/// <remarks>The supplied index is the index within the primary partition, see <c>PrimaryIndex</c> on <c>BiosPartitionInfo</c>.</remarks>
|
||||
public void SetActivePartition(int index)
|
||||
{
|
||||
List<BiosPartitionRecord> records = new List<BiosPartitionRecord>(GetPrimaryRecords());
|
||||
|
||||
for (int i = 0; i < records.Count; ++i)
|
||||
{
|
||||
records[i].Status = i == index ? (byte)0x80 : (byte)0x00;
|
||||
WriteRecord(i, records[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the disk ranges containing partition table metadata.
|
||||
/// </summary>
|
||||
/// <returns>Set of stream extents, indicated as byte offset from the start of the disk.</returns>
|
||||
public IEnumerable<StreamExtent> GetMetadataDiskExtents()
|
||||
{
|
||||
List<StreamExtent> extents = new List<StreamExtent>();
|
||||
|
||||
extents.Add(new StreamExtent(0, Sizes.Sector));
|
||||
|
||||
foreach (BiosPartitionRecord primaryRecord in GetPrimaryRecords())
|
||||
{
|
||||
if (primaryRecord.IsValid)
|
||||
{
|
||||
if (IsExtendedPartition(primaryRecord))
|
||||
{
|
||||
extents.AddRange(
|
||||
new BiosExtendedPartitionTable(_diskData, primaryRecord.LBAStart).GetMetadataDiskExtents());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extents;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the CHS fields in partition records to reflect a new BIOS geometry.
|
||||
/// </summary>
|
||||
/// <param name="geometry">The disk's new BIOS geometry.</param>
|
||||
/// <remarks>The partitions are not relocated to a cylinder boundary, just the CHS fields are updated on the
|
||||
/// assumption the LBA fields are definitive.</remarks>
|
||||
public void UpdateBiosGeometry(Geometry geometry)
|
||||
{
|
||||
_diskData.Position = 0;
|
||||
byte[] bootSector = StreamUtilities.ReadExact(_diskData, Sizes.Sector);
|
||||
|
||||
BiosPartitionRecord[] records = ReadPrimaryRecords(bootSector);
|
||||
for (int i = 0; i < records.Length; ++i)
|
||||
{
|
||||
BiosPartitionRecord record = records[i];
|
||||
if (record.IsValid)
|
||||
{
|
||||
ChsAddress newStartAddress = geometry.ToChsAddress(record.LBAStartAbsolute);
|
||||
if (newStartAddress.Cylinder > 1023)
|
||||
{
|
||||
newStartAddress = new ChsAddress(1023, geometry.HeadsPerCylinder - 1, geometry.SectorsPerTrack);
|
||||
}
|
||||
|
||||
ChsAddress newEndAddress = geometry.ToChsAddress(record.LBAStartAbsolute + record.LBALength - 1);
|
||||
if (newEndAddress.Cylinder > 1023)
|
||||
{
|
||||
newEndAddress = new ChsAddress(1023, geometry.HeadsPerCylinder - 1, geometry.SectorsPerTrack);
|
||||
}
|
||||
|
||||
record.StartCylinder = (ushort)newStartAddress.Cylinder;
|
||||
record.StartHead = (byte)newStartAddress.Head;
|
||||
record.StartSector = (byte)newStartAddress.Sector;
|
||||
record.EndCylinder = (ushort)newEndAddress.Cylinder;
|
||||
record.EndHead = (byte)newEndAddress.Head;
|
||||
record.EndSector = (byte)newEndAddress.Sector;
|
||||
|
||||
WriteRecord(i, record);
|
||||
}
|
||||
}
|
||||
|
||||
_diskGeometry = geometry;
|
||||
}
|
||||
|
||||
internal SparseStream Open(BiosPartitionRecord record)
|
||||
{
|
||||
return new SubStream(_diskData, Ownership.None,
|
||||
record.LBAStartAbsolute * _diskGeometry.BytesPerSector,
|
||||
record.LBALength * _diskGeometry.BytesPerSector);
|
||||
}
|
||||
|
||||
private static BiosPartitionRecord[] ReadPrimaryRecords(byte[] bootSector)
|
||||
{
|
||||
BiosPartitionRecord[] records = new BiosPartitionRecord[4];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
records[i] = new BiosPartitionRecord(bootSector, 0x01BE + i * 0x10, 0, i);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
private static bool IsExtendedPartition(BiosPartitionRecord r)
|
||||
{
|
||||
return r.PartitionType == BiosPartitionTypes.Extended || r.PartitionType == BiosPartitionTypes.ExtendedLba;
|
||||
}
|
||||
|
||||
private static byte ConvertType(WellKnownPartitionType type, long size)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case WellKnownPartitionType.WindowsFat:
|
||||
if (size < 512 * Sizes.OneMiB)
|
||||
{
|
||||
return BiosPartitionTypes.Fat16;
|
||||
}
|
||||
if (size < 1023 * (long)254 * 63 * 512)
|
||||
{
|
||||
// Max BIOS size
|
||||
return BiosPartitionTypes.Fat32;
|
||||
}
|
||||
return BiosPartitionTypes.Fat32Lba;
|
||||
|
||||
case WellKnownPartitionType.WindowsNtfs:
|
||||
return BiosPartitionTypes.Ntfs;
|
||||
case WellKnownPartitionType.Linux:
|
||||
return BiosPartitionTypes.LinuxNative;
|
||||
case WellKnownPartitionType.LinuxSwap:
|
||||
return BiosPartitionTypes.LinuxSwap;
|
||||
case WellKnownPartitionType.LinuxLvm:
|
||||
return BiosPartitionTypes.LinuxLvm;
|
||||
default:
|
||||
throw new ArgumentException(
|
||||
string.Format(CultureInfo.InvariantCulture, "Unrecognized partition type: '{0}'", type),
|
||||
nameof(type));
|
||||
}
|
||||
}
|
||||
|
||||
private BiosPartitionRecord[] GetAllRecords()
|
||||
{
|
||||
List<BiosPartitionRecord> newList = new List<BiosPartitionRecord>();
|
||||
|
||||
foreach (BiosPartitionRecord primaryRecord in GetPrimaryRecords())
|
||||
{
|
||||
if (primaryRecord.IsValid)
|
||||
{
|
||||
if (IsExtendedPartition(primaryRecord))
|
||||
{
|
||||
newList.AddRange(GetExtendedRecords(primaryRecord));
|
||||
}
|
||||
else
|
||||
{
|
||||
newList.Add(primaryRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newList.ToArray();
|
||||
}
|
||||
|
||||
private BiosPartitionRecord[] GetPrimaryRecords()
|
||||
{
|
||||
_diskData.Position = 0;
|
||||
byte[] bootSector = StreamUtilities.ReadExact(_diskData, Sizes.Sector);
|
||||
|
||||
return ReadPrimaryRecords(bootSector);
|
||||
}
|
||||
|
||||
private BiosPartitionRecord[] GetExtendedRecords(BiosPartitionRecord r)
|
||||
{
|
||||
return new BiosExtendedPartitionTable(_diskData, r.LBAStart).GetPartitions();
|
||||
}
|
||||
|
||||
private void WriteRecord(int i, BiosPartitionRecord newRecord)
|
||||
{
|
||||
_diskData.Position = 0;
|
||||
byte[] bootSector = StreamUtilities.ReadExact(_diskData, Sizes.Sector);
|
||||
newRecord.WriteTo(bootSector, 0x01BE + i * 16);
|
||||
_diskData.Position = 0;
|
||||
_diskData.Write(bootSector, 0, bootSector.Length);
|
||||
}
|
||||
|
||||
private int FindCylinderGap(int numCylinders)
|
||||
{
|
||||
List<BiosPartitionRecord> list = Utilities.Filter<List<BiosPartitionRecord>, BiosPartitionRecord>(GetPrimaryRecords(),
|
||||
r => r.IsValid);
|
||||
list.Sort();
|
||||
|
||||
int startCylinder = 0;
|
||||
foreach (BiosPartitionRecord r in list)
|
||||
{
|
||||
int existingStart = r.StartCylinder;
|
||||
int existingEnd = r.EndCylinder;
|
||||
|
||||
// LBA can represent bigger disk locations than CHS, so assume the LBA to be definitive in the case where it
|
||||
// appears the CHS address has been truncated.
|
||||
if (r.LBAStart > _diskGeometry.ToLogicalBlockAddress(r.StartCylinder, r.StartHead, r.StartSector))
|
||||
{
|
||||
existingStart = _diskGeometry.ToChsAddress((int)r.LBAStart).Cylinder;
|
||||
}
|
||||
|
||||
if (r.LBAStart + r.LBALength >
|
||||
_diskGeometry.ToLogicalBlockAddress(r.EndCylinder, r.EndHead, r.EndSector))
|
||||
{
|
||||
existingEnd = _diskGeometry.ToChsAddress((int)(r.LBAStart + r.LBALength)).Cylinder;
|
||||
}
|
||||
|
||||
if (
|
||||
!Utilities.RangesOverlap(startCylinder, startCylinder + numCylinders - 1, existingStart, existingEnd))
|
||||
{
|
||||
break;
|
||||
}
|
||||
startCylinder = existingEnd + 1;
|
||||
}
|
||||
|
||||
return startCylinder;
|
||||
}
|
||||
|
||||
private long FindGap(long numSectors, long alignmentSectors)
|
||||
{
|
||||
List<BiosPartitionRecord> list = Utilities.Filter<List<BiosPartitionRecord>, BiosPartitionRecord>(GetPrimaryRecords(),
|
||||
r => r.IsValid);
|
||||
list.Sort();
|
||||
|
||||
long startSector = MathUtilities.RoundUp(_diskGeometry.ToLogicalBlockAddress(0, 1, 1), alignmentSectors);
|
||||
|
||||
int idx = 0;
|
||||
while (idx < list.Count)
|
||||
{
|
||||
BiosPartitionRecord entry = list[idx];
|
||||
while (idx < list.Count && startSector >= entry.LBAStartAbsolute + entry.LBALength)
|
||||
{
|
||||
idx++;
|
||||
entry = list[idx];
|
||||
}
|
||||
|
||||
if (Utilities.RangesOverlap(startSector, startSector + numSectors, entry.LBAStartAbsolute,
|
||||
entry.LBAStartAbsolute + entry.LBALength))
|
||||
{
|
||||
startSector = MathUtilities.RoundUp(entry.LBAStartAbsolute + entry.LBALength, alignmentSectors);
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (_diskGeometry.TotalSectorsLong - startSector < numSectors)
|
||||
{
|
||||
throw new IOException(string.Format(CultureInfo.InvariantCulture,
|
||||
"Unable to find free space of {0} sectors", numSectors));
|
||||
}
|
||||
|
||||
return startSector;
|
||||
}
|
||||
|
||||
private void Init(Stream disk, Geometry diskGeometry)
|
||||
{
|
||||
_diskData = disk;
|
||||
_diskGeometry = diskGeometry;
|
||||
|
||||
_diskData.Position = 0;
|
||||
byte[] bootSector = StreamUtilities.ReadExact(_diskData, Sizes.Sector);
|
||||
if (bootSector[510] != 0x55 || bootSector[511] != 0xAA)
|
||||
{
|
||||
throw new IOException("Invalid boot sector - no magic number 0xAA55");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
197
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionTypes.cs
Normal file
197
libhac.Nand/DiscUtils.Core/Partitions/BiosPartitionTypes.cs
Normal file
@ -0,0 +1,197 @@
|
||||
//
|
||||
// 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.Partitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convenient access to well-known BIOS (MBR) Partition Types.
|
||||
/// </summary>
|
||||
public static class BiosPartitionTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft FAT12 (fewer than 32,680 sectors in the volume).
|
||||
/// </summary>
|
||||
public const byte Fat12 = 0x01;
|
||||
|
||||
/// <summary>
|
||||
/// Microsoft FAT16 (32,680–65,535 sectors or 16 MB–33 MB).
|
||||
/// </summary>
|
||||
public const byte Fat16Small = 0x04;
|
||||
|
||||
/// <summary>
|
||||
/// Extended Partition (contains other partitions).
|
||||
/// </summary>
|
||||
public const byte Extended = 0x05;
|
||||
|
||||
/// <summary>
|
||||
/// Microsoft BIGDOS FAT16 (33 MB–4 GB).
|
||||
/// </summary>
|
||||
public const byte Fat16 = 0x06;
|
||||
|
||||
/// <summary>
|
||||
/// Installable File System (NTFS).
|
||||
/// </summary>
|
||||
public const byte Ntfs = 0x07;
|
||||
|
||||
/// <summary>
|
||||
/// Microsoft FAT32.
|
||||
/// </summary>
|
||||
public const byte Fat32 = 0x0B;
|
||||
|
||||
/// <summary>
|
||||
/// Microsoft FAT32, accessed using Int13h BIOS LBA extensions.
|
||||
/// </summary>
|
||||
public const byte Fat32Lba = 0x0C;
|
||||
|
||||
/// <summary>
|
||||
/// Microsoft BIGDOS FAT16, accessed using Int13h BIOS LBA extensions.
|
||||
/// </summary>
|
||||
public const byte Fat16Lba = 0x0E;
|
||||
|
||||
/// <summary>
|
||||
/// Extended Partition (contains other partitions), accessed using Int13h BIOS LBA extensions.
|
||||
/// </summary>
|
||||
public const byte ExtendedLba = 0x0F;
|
||||
|
||||
/// <summary>
|
||||
/// Windows Logical Disk Manager dynamic volume.
|
||||
/// </summary>
|
||||
public const byte WindowsDynamicVolume = 0x42;
|
||||
|
||||
/// <summary>
|
||||
/// Linux Swap.
|
||||
/// </summary>
|
||||
public const byte LinuxSwap = 0x82;
|
||||
|
||||
/// <summary>
|
||||
/// Linux Native (ext2 and friends).
|
||||
/// </summary>
|
||||
public const byte LinuxNative = 0x83;
|
||||
|
||||
/// <summary>
|
||||
/// Linux Logical Volume Manager (LVM).
|
||||
/// </summary>
|
||||
public const byte LinuxLvm = 0x8E;
|
||||
|
||||
/// <summary>
|
||||
/// GUID Partition Table (GPT) protective partition, fills entire disk.
|
||||
/// </summary>
|
||||
public const byte GptProtective = 0xEE;
|
||||
|
||||
/// <summary>
|
||||
/// EFI System partition on an MBR disk.
|
||||
/// </summary>
|
||||
public const byte EfiSystem = 0xEF;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a string representation of some known BIOS partition types.
|
||||
/// </summary>
|
||||
/// <param name="type">The partition type to represent as a string.</param>
|
||||
/// <returns>The string representation.</returns>
|
||||
public static string ToString(byte type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 0x00:
|
||||
return "Unused";
|
||||
case 0x01:
|
||||
return "FAT12";
|
||||
case 0x02:
|
||||
return "XENIX root";
|
||||
case 0x03:
|
||||
return "XENIX /usr";
|
||||
case 0x04:
|
||||
return "FAT16 (<32M)";
|
||||
case 0x05:
|
||||
return "Extended (non-LBA)";
|
||||
case 0x06:
|
||||
return "FAT16 (>32M)";
|
||||
case 0x07:
|
||||
return "IFS (NTFS or HPFS)";
|
||||
case 0x0B:
|
||||
return "FAT32 (non-LBA)";
|
||||
case 0x0C:
|
||||
return "FAT32 (LBA)";
|
||||
case 0x0E:
|
||||
return "FAT16 (LBA)";
|
||||
case 0x0F:
|
||||
return "Extended (LBA)";
|
||||
case 0x11:
|
||||
return "Hidden FAT12";
|
||||
case 0x12:
|
||||
return "Vendor Config/Recovery/Diagnostics";
|
||||
case 0x14:
|
||||
return "Hidden FAT16 (<32M)";
|
||||
case 0x16:
|
||||
return "Hidden FAT16 (>32M)";
|
||||
case 0x17:
|
||||
return "Hidden IFS (NTFS or HPFS)";
|
||||
case 0x1B:
|
||||
return "Hidden FAT32 (non-LBA)";
|
||||
case 0x1C:
|
||||
return "Hidden FAT32 (LBA)";
|
||||
case 0x1E:
|
||||
return "Hidden FAT16 (LBA)";
|
||||
case 0x27:
|
||||
return "Windows Recovery Environment";
|
||||
case 0x42:
|
||||
return "Windows Dynamic Volume";
|
||||
case 0x80:
|
||||
return "Minix v1.1 - v1.4a";
|
||||
case 0x81:
|
||||
return "Minix / Early Linux";
|
||||
case 0x82:
|
||||
return "Linux Swap";
|
||||
case 0x83:
|
||||
return "Linux Native";
|
||||
case 0x84:
|
||||
return "Hibernation";
|
||||
case 0x8E:
|
||||
return "Linux LVM";
|
||||
case 0xA0:
|
||||
return "Laptop Hibernation";
|
||||
case 0xA8:
|
||||
return "Mac OS-X";
|
||||
case 0xAB:
|
||||
return "Mac OS-X Boot";
|
||||
case 0xAF:
|
||||
return "Mac OS-X HFS";
|
||||
case 0xC0:
|
||||
return "NTFT";
|
||||
case 0xDE:
|
||||
return "Dell OEM";
|
||||
case 0xEE:
|
||||
return "GPT Protective";
|
||||
case 0xEF:
|
||||
return "EFI";
|
||||
case 0xFB:
|
||||
return "VMware File System";
|
||||
case 0xFC:
|
||||
return "VMware Swap";
|
||||
case 0xFE:
|
||||
return "IBM OEM";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
//
|
||||
// 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 DiscUtils.Streams;
|
||||
|
||||
namespace DiscUtils.Partitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a stream with the contents of a BIOS partitioned disk.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class assembles a disk image dynamically in memory. The
|
||||
/// constructed stream will read data from the partition content
|
||||
/// streams only when a client of this class tries to read from
|
||||
/// that partition.
|
||||
/// </remarks>
|
||||
public class BiosPartitionedDiskBuilder : StreamBuilder
|
||||
{
|
||||
private Geometry _biosGeometry;
|
||||
private readonly SparseMemoryStream _bootSectors;
|
||||
private readonly long _capacity;
|
||||
|
||||
private readonly Dictionary<int, BuilderExtent> _partitionContents;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BiosPartitionedDiskBuilder class.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The capacity of the disk (in bytes).</param>
|
||||
/// <param name="biosGeometry">The BIOS geometry of the disk.</param>
|
||||
public BiosPartitionedDiskBuilder(long capacity, Geometry biosGeometry)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_biosGeometry = biosGeometry;
|
||||
|
||||
_bootSectors = new SparseMemoryStream();
|
||||
_bootSectors.SetLength(capacity);
|
||||
PartitionTable = BiosPartitionTable.Initialize(_bootSectors, _biosGeometry);
|
||||
|
||||
_partitionContents = new Dictionary<int, BuilderExtent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BiosPartitionedDiskBuilder class.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The capacity of the disk (in bytes).</param>
|
||||
/// <param name="bootSectors">The boot sector(s) of the disk.</param>
|
||||
/// <param name="biosGeometry">The BIOS geometry of the disk.</param>
|
||||
[Obsolete("Use the variant that takes VirtualDisk, this method breaks for disks with extended partitions", false
|
||||
)]
|
||||
public BiosPartitionedDiskBuilder(long capacity, byte[] bootSectors, Geometry biosGeometry)
|
||||
{
|
||||
if (bootSectors == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bootSectors));
|
||||
}
|
||||
|
||||
_capacity = capacity;
|
||||
_biosGeometry = biosGeometry;
|
||||
|
||||
_bootSectors = new SparseMemoryStream();
|
||||
_bootSectors.SetLength(capacity);
|
||||
_bootSectors.Write(bootSectors, 0, bootSectors.Length);
|
||||
PartitionTable = new BiosPartitionTable(_bootSectors, biosGeometry);
|
||||
|
||||
_partitionContents = new Dictionary<int, BuilderExtent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BiosPartitionedDiskBuilder class by
|
||||
/// cloning the partition structure of a source disk.
|
||||
/// </summary>
|
||||
/// <param name="sourceDisk">The disk to clone.</param>
|
||||
public BiosPartitionedDiskBuilder(VirtualDisk sourceDisk)
|
||||
{
|
||||
if (sourceDisk == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceDisk));
|
||||
}
|
||||
|
||||
_capacity = sourceDisk.Capacity;
|
||||
_biosGeometry = sourceDisk.BiosGeometry;
|
||||
|
||||
_bootSectors = new SparseMemoryStream();
|
||||
_bootSectors.SetLength(_capacity);
|
||||
|
||||
foreach (StreamExtent extent in new BiosPartitionTable(sourceDisk).GetMetadataDiskExtents())
|
||||
{
|
||||
sourceDisk.Content.Position = extent.Start;
|
||||
byte[] buffer = StreamUtilities.ReadExact(sourceDisk.Content, (int)extent.Length);
|
||||
_bootSectors.Position = extent.Start;
|
||||
_bootSectors.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
PartitionTable = new BiosPartitionTable(_bootSectors, _biosGeometry);
|
||||
|
||||
_partitionContents = new Dictionary<int, BuilderExtent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the partition table in the disk.
|
||||
/// </summary>
|
||||
public BiosPartitionTable PartitionTable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets a stream representing the content of a partition in the partition table.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the partition.</param>
|
||||
/// <param name="stream">The stream with the contents of the partition.</param>
|
||||
public void SetPartitionContent(int index, SparseStream stream)
|
||||
{
|
||||
_partitionContents[index] = new BuilderSparseStreamExtent(PartitionTable[index].FirstSector * Sizes.Sector,
|
||||
stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the CHS fields in partition records to reflect a new BIOS geometry.
|
||||
/// </summary>
|
||||
/// <param name="geometry">The disk's new BIOS geometry.</param>
|
||||
/// <remarks>The partitions are not relocated to a cylinder boundary, just the CHS fields are updated on the
|
||||
/// assumption the LBA fields are definitive.</remarks>
|
||||
public void UpdateBiosGeometry(Geometry geometry)
|
||||
{
|
||||
PartitionTable.UpdateBiosGeometry(geometry);
|
||||
_biosGeometry = geometry;
|
||||
}
|
||||
|
||||
protected override List<BuilderExtent> FixExtents(out long totalLength)
|
||||
{
|
||||
totalLength = _capacity;
|
||||
|
||||
List<BuilderExtent> extents = new List<BuilderExtent>();
|
||||
|
||||
foreach (StreamExtent extent in PartitionTable.GetMetadataDiskExtents())
|
||||
{
|
||||
_bootSectors.Position = extent.Start;
|
||||
byte[] buffer = StreamUtilities.ReadExact(_bootSectors, (int)extent.Length);
|
||||
|
||||
extents.Add(new BuilderBufferExtent(extent.Start, buffer));
|
||||
}
|
||||
|
||||
extents.AddRange(_partitionContents.Values);
|
||||
return extents;
|
||||
}
|
||||
}
|
||||
}
|
@ -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.IO;
|
||||
|
||||
namespace DiscUtils.Partitions
|
||||
{
|
||||
[PartitionTableFactory]
|
||||
internal sealed class DefaultPartitionTableFactory : PartitionTableFactory
|
||||
{
|
||||
public override bool DetectIsPartitioned(Stream s)
|
||||
{
|
||||
return BiosPartitionTable.IsValid(s);
|
||||
}
|
||||
|
||||
public override PartitionTable DetectPartitionTable(VirtualDisk disk)
|
||||
{
|
||||
if (BiosPartitionTable.IsValid(disk.Content))
|
||||
{
|
||||
BiosPartitionTable table = new BiosPartitionTable(disk);
|
||||
if (table.Count == 1 && table[0].BiosType == BiosPartitionTypes.GptProtective)
|
||||
{
|
||||
return new GuidPartitionTable(disk);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user