Initial commit - WPinternals 2.6

This commit is contained in:
Rene Lergner
2018-10-25 22:35:49 +02:00
commit 396ae57f05
483 changed files with 159677 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
// Common/CRC.cs
namespace SevenZip
{
class CRC
{
public static readonly uint[] Table;
static CRC()
{
Table = new uint[256];
const uint kPoly = 0xEDB88320;
for (uint i = 0; i < 256; i++)
{
uint r = i;
for (int j = 0; j < 8; j++)
if ((r & 1) != 0)
r = (r >> 1) ^ kPoly;
else
r >>= 1;
Table[i] = r;
}
}
uint _value = 0xFFFFFFFF;
public void Init() { _value = 0xFFFFFFFF; }
public void UpdateByte(byte b)
{
_value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8);
}
public void Update(byte[] data, uint offset, uint size)
{
for (uint i = 0; i < size; i++)
_value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8);
}
public uint GetDigest() { return _value ^ 0xFFFFFFFF; }
static uint CalculateDigest(byte[] data, uint offset, uint size)
{
CRC crc = new CRC();
// crc.Init();
crc.Update(data, offset, size);
return crc.GetDigest();
}
static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size)
{
return (CalculateDigest(data, offset, size) == digest);
}
}
}
+274
View File
@@ -0,0 +1,274 @@
// CommandLineParser.cs
using System;
using System.Collections;
namespace SevenZip.CommandLineParser
{
public enum SwitchType
{
Simple,
PostMinus,
LimitedPostString,
UnLimitedPostString,
PostChar
}
public class SwitchForm
{
public string IDString;
public SwitchType Type;
public bool Multi;
public int MinLen;
public int MaxLen;
public string PostCharSet;
public SwitchForm(string idString, SwitchType type, bool multi,
int minLen, int maxLen, string postCharSet)
{
IDString = idString;
Type = type;
Multi = multi;
MinLen = minLen;
MaxLen = maxLen;
PostCharSet = postCharSet;
}
public SwitchForm(string idString, SwitchType type, bool multi, int minLen):
this(idString, type, multi, minLen, 0, "")
{
}
public SwitchForm(string idString, SwitchType type, bool multi):
this(idString, type, multi, 0)
{
}
}
public class SwitchResult
{
public bool ThereIs;
public bool WithMinus;
public ArrayList PostStrings = new ArrayList();
public int PostCharIndex;
public SwitchResult()
{
ThereIs = false;
}
}
public class Parser
{
public ArrayList NonSwitchStrings = new ArrayList();
SwitchResult[] _switches;
public Parser(int numSwitches)
{
_switches = new SwitchResult[numSwitches];
for (int i = 0; i < numSwitches; i++)
_switches[i] = new SwitchResult();
}
bool ParseString(string srcString, SwitchForm[] switchForms)
{
int len = srcString.Length;
if (len == 0)
return false;
int pos = 0;
if (!IsItSwitchChar(srcString[pos]))
return false;
while (pos < len)
{
if (IsItSwitchChar(srcString[pos]))
pos++;
const int kNoLen = -1;
int matchedSwitchIndex = 0;
int maxLen = kNoLen;
for (int switchIndex = 0; switchIndex < _switches.Length; switchIndex++)
{
int switchLen = switchForms[switchIndex].IDString.Length;
if (switchLen <= maxLen || pos + switchLen > len)
continue;
if (String.Compare(switchForms[switchIndex].IDString, 0,
srcString, pos, switchLen, true) == 0)
{
matchedSwitchIndex = switchIndex;
maxLen = switchLen;
}
}
if (maxLen == kNoLen)
throw new Exception("maxLen == kNoLen");
SwitchResult matchedSwitch = _switches[matchedSwitchIndex];
SwitchForm switchForm = switchForms[matchedSwitchIndex];
if ((!switchForm.Multi) && matchedSwitch.ThereIs)
throw new Exception("switch must be single");
matchedSwitch.ThereIs = true;
pos += maxLen;
int tailSize = len - pos;
SwitchType type = switchForm.Type;
switch (type)
{
case SwitchType.PostMinus:
{
if (tailSize == 0)
matchedSwitch.WithMinus = false;
else
{
matchedSwitch.WithMinus = (srcString[pos] == kSwitchMinus);
if (matchedSwitch.WithMinus)
pos++;
}
break;
}
case SwitchType.PostChar:
{
if (tailSize < switchForm.MinLen)
throw new Exception("switch is not full");
string charSet = switchForm.PostCharSet;
const int kEmptyCharValue = -1;
if (tailSize == 0)
matchedSwitch.PostCharIndex = kEmptyCharValue;
else
{
int index = charSet.IndexOf(srcString[pos]);
if (index < 0)
matchedSwitch.PostCharIndex = kEmptyCharValue;
else
{
matchedSwitch.PostCharIndex = index;
pos++;
}
}
break;
}
case SwitchType.LimitedPostString:
case SwitchType.UnLimitedPostString:
{
int minLen = switchForm.MinLen;
if (tailSize < minLen)
throw new Exception("switch is not full");
if (type == SwitchType.UnLimitedPostString)
{
matchedSwitch.PostStrings.Add(srcString.Substring(pos));
return true;
}
String stringSwitch = srcString.Substring(pos, minLen);
pos += minLen;
for (int i = minLen; i < switchForm.MaxLen && pos < len; i++, pos++)
{
char c = srcString[pos];
if (IsItSwitchChar(c))
break;
stringSwitch += c;
}
matchedSwitch.PostStrings.Add(stringSwitch);
break;
}
}
}
return true;
}
public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings)
{
int numCommandStrings = commandStrings.Length;
bool stopSwitch = false;
for (int i = 0; i < numCommandStrings; i++)
{
string s = commandStrings[i];
if (stopSwitch)
NonSwitchStrings.Add(s);
else
if (s == kStopSwitchParsing)
stopSwitch = true;
else
if (!ParseString(s, switchForms))
NonSwitchStrings.Add(s);
}
}
public SwitchResult this[int index] { get { return _switches[index]; } }
public static int ParseCommand(CommandForm[] commandForms, string commandString,
out string postString)
{
for (int i = 0; i < commandForms.Length; i++)
{
string id = commandForms[i].IDString;
if (commandForms[i].PostStringMode)
{
if (commandString.IndexOf(id) == 0)
{
postString = commandString.Substring(id.Length);
return i;
}
}
else
if (commandString == id)
{
postString = "";
return i;
}
}
postString = "";
return -1;
}
static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms,
string commandString, ArrayList indices)
{
indices.Clear();
int numUsedChars = 0;
for (int i = 0; i < numForms; i++)
{
CommandSubCharsSet charsSet = forms[i];
int currentIndex = -1;
int len = charsSet.Chars.Length;
for (int j = 0; j < len; j++)
{
char c = charsSet.Chars[j];
int newIndex = commandString.IndexOf(c);
if (newIndex >= 0)
{
if (currentIndex >= 0)
return false;
if (commandString.IndexOf(c, newIndex + 1) >= 0)
return false;
currentIndex = j;
numUsedChars++;
}
}
if (currentIndex == -1 && !charsSet.EmptyAllowed)
return false;
indices.Add(currentIndex);
}
return (numUsedChars == commandString.Length);
}
const char kSwitchID1 = '-';
const char kSwitchID2 = '/';
const char kSwitchMinus = '-';
const string kStopSwitchParsing = "--";
static bool IsItSwitchChar(char c)
{
return (c == kSwitchID1 || c == kSwitchID2);
}
}
public class CommandForm
{
public string IDString = "";
public bool PostStringMode = false;
public CommandForm(string idString, bool postStringMode)
{
IDString = idString;
PostStringMode = postStringMode;
}
}
class CommandSubCharsSet
{
public string Chars = "";
public bool EmptyAllowed = false;
}
}
+72
View File
@@ -0,0 +1,72 @@
// InBuffer.cs
namespace SevenZip.Buffer
{
public class InBuffer
{
byte[] m_Buffer;
uint m_Pos;
uint m_Limit;
uint m_BufferSize;
System.IO.Stream m_Stream;
bool m_StreamWasExhausted;
ulong m_ProcessedSize;
public InBuffer(uint bufferSize)
{
m_Buffer = new byte[bufferSize];
m_BufferSize = bufferSize;
}
public void Init(System.IO.Stream stream)
{
m_Stream = stream;
m_ProcessedSize = 0;
m_Limit = 0;
m_Pos = 0;
m_StreamWasExhausted = false;
}
public bool ReadBlock()
{
if (m_StreamWasExhausted)
return false;
m_ProcessedSize += m_Pos;
int aNumProcessedBytes = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize);
m_Pos = 0;
m_Limit = (uint)aNumProcessedBytes;
m_StreamWasExhausted = (aNumProcessedBytes == 0);
return (!m_StreamWasExhausted);
}
public void ReleaseStream()
{
// m_Stream.Close();
m_Stream = null;
}
public bool ReadByte(byte b) // check it
{
if (m_Pos >= m_Limit)
if (!ReadBlock())
return false;
b = m_Buffer[m_Pos++];
return true;
}
public byte ReadByte()
{
// return (byte)m_Stream.ReadByte();
if (m_Pos >= m_Limit)
if (!ReadBlock())
return 0xFF;
return m_Buffer[m_Pos++];
}
public ulong GetProcessedSize()
{
return m_ProcessedSize + m_Pos;
}
}
}
+47
View File
@@ -0,0 +1,47 @@
// OutBuffer.cs
namespace SevenZip.Buffer
{
public class OutBuffer
{
byte[] m_Buffer;
uint m_Pos;
uint m_BufferSize;
System.IO.Stream m_Stream;
ulong m_ProcessedSize;
public OutBuffer(uint bufferSize)
{
m_Buffer = new byte[bufferSize];
m_BufferSize = bufferSize;
}
public void SetStream(System.IO.Stream stream) { m_Stream = stream; }
public void FlushStream() { m_Stream.Flush(); }
public void CloseStream() { m_Stream.Close(); }
public void ReleaseStream() { m_Stream = null; }
public void Init()
{
m_ProcessedSize = 0;
m_Pos = 0;
}
public void WriteByte(byte b)
{
m_Buffer[m_Pos++] = b;
if (m_Pos >= m_BufferSize)
FlushData();
}
public void FlushData()
{
if (m_Pos == 0)
return;
m_Stream.Write(m_Buffer, 0, (int)m_Pos);
m_Pos = 0;
}
public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; }
}
}
+24
View File
@@ -0,0 +1,24 @@
// IMatchFinder.cs
using System;
namespace SevenZip.Compression.LZ
{
interface IInWindowStream
{
void SetStream(System.IO.Stream inStream);
void Init();
void ReleaseStream();
Byte GetIndexByte(Int32 index);
UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit);
UInt32 GetNumAvailableBytes();
}
interface IMatchFinder : IInWindowStream
{
void Create(UInt32 historySize, UInt32 keepAddBufferBefore,
UInt32 matchMaxLen, UInt32 keepAddBufferAfter);
UInt32 GetMatches(UInt32[] distances);
void Skip(UInt32 num);
}
}
+367
View File
@@ -0,0 +1,367 @@
// LzBinTree.cs
using System;
namespace SevenZip.Compression.LZ
{
public class BinTree : InWindow, IMatchFinder
{
UInt32 _cyclicBufferPos;
UInt32 _cyclicBufferSize = 0;
UInt32 _matchMaxLen;
UInt32[] _son;
UInt32[] _hash;
UInt32 _cutValue = 0xFF;
UInt32 _hashMask;
UInt32 _hashSizeSum = 0;
bool HASH_ARRAY = true;
const UInt32 kHash2Size = 1 << 10;
const UInt32 kHash3Size = 1 << 16;
const UInt32 kBT2HashSize = 1 << 16;
const UInt32 kStartMaxLen = 1;
const UInt32 kHash3Offset = kHash2Size;
const UInt32 kEmptyHashValue = 0;
const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1;
UInt32 kNumHashDirectBytes = 0;
UInt32 kMinMatchCheck = 4;
UInt32 kFixHashSize = kHash2Size + kHash3Size;
public void SetType(int numHashBytes)
{
HASH_ARRAY = (numHashBytes > 2);
if (HASH_ARRAY)
{
kNumHashDirectBytes = 0;
kMinMatchCheck = 4;
kFixHashSize = kHash2Size + kHash3Size;
}
else
{
kNumHashDirectBytes = 2;
kMinMatchCheck = 2 + 1;
kFixHashSize = 0;
}
}
public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); }
public new void ReleaseStream() { base.ReleaseStream(); }
public new void Init()
{
base.Init();
for (UInt32 i = 0; i < _hashSizeSum; i++)
_hash[i] = kEmptyHashValue;
_cyclicBufferPos = 0;
ReduceOffsets(-1);
}
public new void MovePos()
{
if (++_cyclicBufferPos >= _cyclicBufferSize)
_cyclicBufferPos = 0;
base.MovePos();
if (_pos == kMaxValForNormalize)
Normalize();
}
public new Byte GetIndexByte(Int32 index) { return base.GetIndexByte(index); }
public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit)
{ return base.GetMatchLen(index, distance, limit); }
public new UInt32 GetNumAvailableBytes() { return base.GetNumAvailableBytes(); }
public void Create(UInt32 historySize, UInt32 keepAddBufferBefore,
UInt32 matchMaxLen, UInt32 keepAddBufferAfter)
{
if (historySize > kMaxValForNormalize - 256)
throw new Exception();
_cutValue = 16 + (matchMaxLen >> 1);
UInt32 windowReservSize = (historySize + keepAddBufferBefore +
matchMaxLen + keepAddBufferAfter) / 2 + 256;
base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize);
_matchMaxLen = matchMaxLen;
UInt32 cyclicBufferSize = historySize + 1;
if (_cyclicBufferSize != cyclicBufferSize)
_son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2];
UInt32 hs = kBT2HashSize;
if (HASH_ARRAY)
{
hs = historySize - 1;
hs |= (hs >> 1);
hs |= (hs >> 2);
hs |= (hs >> 4);
hs |= (hs >> 8);
hs >>= 1;
hs |= 0xFFFF;
if (hs > (1 << 24))
hs >>= 1;
_hashMask = hs;
hs++;
hs += kFixHashSize;
}
if (hs != _hashSizeSum)
_hash = new UInt32[_hashSizeSum = hs];
}
public UInt32 GetMatches(UInt32[] distances)
{
UInt32 lenLimit;
if (_pos + _matchMaxLen <= _streamPos)
lenLimit = _matchMaxLen;
else
{
lenLimit = _streamPos - _pos;
if (lenLimit < kMinMatchCheck)
{
MovePos();
return 0;
}
}
UInt32 offset = 0;
UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
UInt32 cur = _bufferOffset + _pos;
UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize;
UInt32 hashValue, hash2Value = 0, hash3Value = 0;
if (HASH_ARRAY)
{
UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
hash2Value = temp & (kHash2Size - 1);
temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
hash3Value = temp & (kHash3Size - 1);
hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
}
else
hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);
UInt32 curMatch = _hash[kFixHashSize + hashValue];
if (HASH_ARRAY)
{
UInt32 curMatch2 = _hash[hash2Value];
UInt32 curMatch3 = _hash[kHash3Offset + hash3Value];
_hash[hash2Value] = _pos;
_hash[kHash3Offset + hash3Value] = _pos;
if (curMatch2 > matchMinPos)
if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
{
distances[offset++] = maxLen = 2;
distances[offset++] = _pos - curMatch2 - 1;
}
if (curMatch3 > matchMinPos)
if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur])
{
if (curMatch3 == curMatch2)
offset -= 2;
distances[offset++] = maxLen = 3;
distances[offset++] = _pos - curMatch3 - 1;
curMatch2 = curMatch3;
}
if (offset != 0 && curMatch2 == curMatch)
{
offset -= 2;
maxLen = kStartMaxLen;
}
}
_hash[kFixHashSize + hashValue] = _pos;
UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
UInt32 ptr1 = (_cyclicBufferPos << 1);
UInt32 len0, len1;
len0 = len1 = kNumHashDirectBytes;
if (kNumHashDirectBytes != 0)
{
if (curMatch > matchMinPos)
{
if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] !=
_bufferBase[cur + kNumHashDirectBytes])
{
distances[offset++] = maxLen = kNumHashDirectBytes;
distances[offset++] = _pos - curMatch - 1;
}
}
}
UInt32 count = _cutValue;
while(true)
{
if(curMatch <= matchMinPos || count-- == 0)
{
_son[ptr0] = _son[ptr1] = kEmptyHashValue;
break;
}
UInt32 delta = _pos - curMatch;
UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
(_cyclicBufferPos - delta) :
(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
UInt32 pby1 = _bufferOffset + curMatch;
UInt32 len = Math.Min(len0, len1);
if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
{
while(++len != lenLimit)
if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
break;
if (maxLen < len)
{
distances[offset++] = maxLen = len;
distances[offset++] = delta - 1;
if (len == lenLimit)
{
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos + 1];
break;
}
}
}
if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
{
_son[ptr1] = curMatch;
ptr1 = cyclicPos + 1;
curMatch = _son[ptr1];
len1 = len;
}
else
{
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = _son[ptr0];
len0 = len;
}
}
MovePos();
return offset;
}
public void Skip(UInt32 num)
{
do
{
UInt32 lenLimit;
if (_pos + _matchMaxLen <= _streamPos)
lenLimit = _matchMaxLen;
else
{
lenLimit = _streamPos - _pos;
if (lenLimit < kMinMatchCheck)
{
MovePos();
continue;
}
}
UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
UInt32 cur = _bufferOffset + _pos;
UInt32 hashValue;
if (HASH_ARRAY)
{
UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
UInt32 hash2Value = temp & (kHash2Size - 1);
_hash[hash2Value] = _pos;
temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
UInt32 hash3Value = temp & (kHash3Size - 1);
_hash[kHash3Offset + hash3Value] = _pos;
hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
}
else
hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);
UInt32 curMatch = _hash[kFixHashSize + hashValue];
_hash[kFixHashSize + hashValue] = _pos;
UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
UInt32 ptr1 = (_cyclicBufferPos << 1);
UInt32 len0, len1;
len0 = len1 = kNumHashDirectBytes;
UInt32 count = _cutValue;
while (true)
{
if (curMatch <= matchMinPos || count-- == 0)
{
_son[ptr0] = _son[ptr1] = kEmptyHashValue;
break;
}
UInt32 delta = _pos - curMatch;
UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
(_cyclicBufferPos - delta) :
(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
UInt32 pby1 = _bufferOffset + curMatch;
UInt32 len = Math.Min(len0, len1);
if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
{
while (++len != lenLimit)
if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
break;
if (len == lenLimit)
{
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos + 1];
break;
}
}
if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
{
_son[ptr1] = curMatch;
ptr1 = cyclicPos + 1;
curMatch = _son[ptr1];
len1 = len;
}
else
{
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = _son[ptr0];
len0 = len;
}
}
MovePos();
}
while (--num != 0);
}
void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue)
{
for (UInt32 i = 0; i < numItems; i++)
{
UInt32 value = items[i];
if (value <= subValue)
value = kEmptyHashValue;
else
value -= subValue;
items[i] = value;
}
}
void Normalize()
{
UInt32 subValue = _pos - _cyclicBufferSize;
NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
NormalizeLinks(_hash, _hashSizeSum, subValue);
ReduceOffsets((Int32)subValue);
}
public void SetCutValue(UInt32 cutValue) { _cutValue = cutValue; }
}
}
+132
View File
@@ -0,0 +1,132 @@
// LzInWindow.cs
using System;
namespace SevenZip.Compression.LZ
{
public class InWindow
{
public Byte[] _bufferBase = null; // pointer to buffer with data
System.IO.Stream _stream;
UInt32 _posLimit; // offset (from _buffer) of first byte when new block reading must be done
bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream
UInt32 _pointerToLastSafePosition;
public UInt32 _bufferOffset;
public UInt32 _blockSize; // Size of Allocated memory block
public UInt32 _pos; // offset (from _buffer) of curent byte
UInt32 _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos
UInt32 _keepSizeAfter; // how many BYTEs must be kept buffer after _pos
public UInt32 _streamPos; // offset (from _buffer) of first not read byte from Stream
public void MoveBlock()
{
UInt32 offset = (UInt32)(_bufferOffset) + _pos - _keepSizeBefore;
// we need one additional byte, since MovePos moves on 1 byte.
if (offset > 0)
offset--;
UInt32 numBytes = (UInt32)(_bufferOffset) + _streamPos - offset;
// check negative offset ????
for (UInt32 i = 0; i < numBytes; i++)
_bufferBase[i] = _bufferBase[offset + i];
_bufferOffset -= offset;
}
public virtual void ReadBlock()
{
if (_streamEndWasReached)
return;
while (true)
{
int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos);
if (size == 0)
return;
int numReadBytes = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size);
if (numReadBytes == 0)
{
_posLimit = _streamPos;
UInt32 pointerToPostion = _bufferOffset + _posLimit;
if (pointerToPostion > _pointerToLastSafePosition)
_posLimit = (UInt32)(_pointerToLastSafePosition - _bufferOffset);
_streamEndWasReached = true;
return;
}
_streamPos += (UInt32)numReadBytes;
if (_streamPos >= _pos + _keepSizeAfter)
_posLimit = _streamPos - _keepSizeAfter;
}
}
void Free() { _bufferBase = null; }
public void Create(UInt32 keepSizeBefore, UInt32 keepSizeAfter, UInt32 keepSizeReserv)
{
_keepSizeBefore = keepSizeBefore;
_keepSizeAfter = keepSizeAfter;
UInt32 blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv;
if (_bufferBase == null || _blockSize != blockSize)
{
Free();
_blockSize = blockSize;
_bufferBase = new Byte[_blockSize];
}
_pointerToLastSafePosition = _blockSize - keepSizeAfter;
}
public void SetStream(System.IO.Stream stream) { _stream = stream; }
public void ReleaseStream() { _stream = null; }
public void Init()
{
_bufferOffset = 0;
_pos = 0;
_streamPos = 0;
_streamEndWasReached = false;
ReadBlock();
}
public void MovePos()
{
_pos++;
if (_pos > _posLimit)
{
UInt32 pointerToPostion = _bufferOffset + _pos;
if (pointerToPostion > _pointerToLastSafePosition)
MoveBlock();
ReadBlock();
}
}
public Byte GetIndexByte(Int32 index) { return _bufferBase[_bufferOffset + _pos + index]; }
// index + limit have not to exceed _keepSizeAfter;
public UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit)
{
if (_streamEndWasReached)
if ((_pos + index) + limit > _streamPos)
limit = _streamPos - (UInt32)(_pos + index);
distance++;
// Byte *pby = _buffer + (size_t)_pos + index;
UInt32 pby = _bufferOffset + _pos + (UInt32)index;
UInt32 i;
for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++);
return i;
}
public UInt32 GetNumAvailableBytes() { return _streamPos - _pos; }
public void ReduceOffsets(Int32 subValue)
{
_bufferOffset += (UInt32)subValue;
_posLimit -= (UInt32)subValue;
_pos -= (UInt32)subValue;
_streamPos -= (UInt32)subValue;
}
}
}
+110
View File
@@ -0,0 +1,110 @@
// LzOutWindow.cs
namespace SevenZip.Compression.LZ
{
public class OutWindow
{
byte[] _buffer = null;
uint _pos;
uint _windowSize = 0;
uint _streamPos;
System.IO.Stream _stream;
public uint TrainSize = 0;
public void Create(uint windowSize)
{
if (_windowSize != windowSize)
{
// System.GC.Collect();
_buffer = new byte[windowSize];
}
_windowSize = windowSize;
_pos = 0;
_streamPos = 0;
}
public void Init(System.IO.Stream stream, bool solid)
{
ReleaseStream();
_stream = stream;
if (!solid)
{
_streamPos = 0;
_pos = 0;
TrainSize = 0;
}
}
public bool Train(System.IO.Stream stream)
{
long len = stream.Length;
uint size = (len < _windowSize) ? (uint)len : _windowSize;
TrainSize = size;
stream.Position = len - size;
_streamPos = _pos = 0;
while (size > 0)
{
uint curSize = _windowSize - _pos;
if (size < curSize)
curSize = size;
int numReadBytes = stream.Read(_buffer, (int)_pos, (int)curSize);
if (numReadBytes == 0)
return false;
size -= (uint)numReadBytes;
_pos += (uint)numReadBytes;
_streamPos += (uint)numReadBytes;
if (_pos == _windowSize)
_streamPos = _pos = 0;
}
return true;
}
public void ReleaseStream()
{
Flush();
_stream = null;
}
public void Flush()
{
uint size = _pos - _streamPos;
if (size == 0)
return;
_stream.Write(_buffer, (int)_streamPos, (int)size);
if (_pos >= _windowSize)
_pos = 0;
_streamPos = _pos;
}
public void CopyBlock(uint distance, uint len)
{
uint pos = _pos - distance - 1;
if (pos >= _windowSize)
pos += _windowSize;
for (; len > 0; len--)
{
if (pos >= _windowSize)
pos = 0;
_buffer[_pos++] = _buffer[pos++];
if (_pos >= _windowSize)
Flush();
}
}
public void PutByte(byte b)
{
_buffer[_pos++] = b;
if (_pos >= _windowSize)
Flush();
}
public byte GetByte(uint distance)
{
uint pos = _pos - distance - 1;
if (pos >= _windowSize)
pos += _windowSize;
return _buffer[pos];
}
}
}
+76
View File
@@ -0,0 +1,76 @@
// LzmaBase.cs
namespace SevenZip.Compression.LZMA
{
internal abstract class Base
{
public const uint kNumRepDistances = 4;
public const uint kNumStates = 12;
// static byte []kLiteralNextStates = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5};
// static byte []kMatchNextStates = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10};
// static byte []kRepNextStates = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11};
// static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11};
public struct State
{
public uint Index;
public void Init() { Index = 0; }
public void UpdateChar()
{
if (Index < 4) Index = 0;
else if (Index < 10) Index -= 3;
else Index -= 6;
}
public void UpdateMatch() { Index = (uint)(Index < 7 ? 7 : 10); }
public void UpdateRep() { Index = (uint)(Index < 7 ? 8 : 11); }
public void UpdateShortRep() { Index = (uint)(Index < 7 ? 9 : 11); }
public bool IsCharState() { return Index < 7; }
}
public const int kNumPosSlotBits = 6;
public const int kDicLogSizeMin = 0;
// public const int kDicLogSizeMax = 30;
// public const uint kDistTableSizeMax = kDicLogSizeMax * 2;
public const int kNumLenToPosStatesBits = 2; // it's for speed optimization
public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits;
public const uint kMatchMinLen = 2;
public static uint GetLenToPosState(uint len)
{
len -= kMatchMinLen;
if (len < kNumLenToPosStates)
return len;
return (uint)(kNumLenToPosStates - 1);
}
public const int kNumAlignBits = 4;
public const uint kAlignTableSize = 1 << kNumAlignBits;
public const uint kAlignMask = (kAlignTableSize - 1);
public const uint kStartPosModelIndex = 4;
public const uint kEndPosModelIndex = 14;
public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex;
public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2);
public const uint kNumLitPosStatesBitsEncodingMax = 4;
public const uint kNumLitContextBitsMax = 8;
public const int kNumPosStatesBitsMax = 4;
public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax);
public const int kNumPosStatesBitsEncodingMax = 4;
public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax);
public const int kNumLowLenBits = 3;
public const int kNumMidLenBits = 3;
public const int kNumHighLenBits = 8;
public const uint kNumLowLenSymbols = 1 << kNumLowLenBits;
public const uint kNumMidLenSymbols = 1 << kNumMidLenBits;
public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols +
(1 << kNumHighLenBits);
public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1;
}
}
+398
View File
@@ -0,0 +1,398 @@
// LzmaDecoder.cs
using System;
namespace SevenZip.Compression.LZMA
{
using RangeCoder;
public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream
{
class LenDecoder
{
BitDecoder m_Choice = new BitDecoder();
BitDecoder m_Choice2 = new BitDecoder();
BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits);
uint m_NumPosStates = 0;
public void Create(uint numPosStates)
{
for (uint posState = m_NumPosStates; posState < numPosStates; posState++)
{
m_LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits);
m_MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits);
}
m_NumPosStates = numPosStates;
}
public void Init()
{
m_Choice.Init();
for (uint posState = 0; posState < m_NumPosStates; posState++)
{
m_LowCoder[posState].Init();
m_MidCoder[posState].Init();
}
m_Choice2.Init();
m_HighCoder.Init();
}
public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState)
{
if (m_Choice.Decode(rangeDecoder) == 0)
return m_LowCoder[posState].Decode(rangeDecoder);
else
{
uint symbol = Base.kNumLowLenSymbols;
if (m_Choice2.Decode(rangeDecoder) == 0)
symbol += m_MidCoder[posState].Decode(rangeDecoder);
else
{
symbol += Base.kNumMidLenSymbols;
symbol += m_HighCoder.Decode(rangeDecoder);
}
return symbol;
}
}
}
class LiteralDecoder
{
struct Decoder2
{
BitDecoder[] m_Decoders;
public void Create() { m_Decoders = new BitDecoder[0x300]; }
public void Init() { for (int i = 0; i < 0x300; i++) m_Decoders[i].Init(); }
public byte DecodeNormal(RangeCoder.Decoder rangeDecoder)
{
uint symbol = 1;
do
symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder);
while (symbol < 0x100);
return (byte)symbol;
}
public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte)
{
uint symbol = 1;
do
{
uint matchBit = (uint)(matchByte >> 7) & 1;
matchByte <<= 1;
uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder);
symbol = (symbol << 1) | bit;
if (matchBit != bit)
{
while (symbol < 0x100)
symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder);
break;
}
}
while (symbol < 0x100);
return (byte)symbol;
}
}
Decoder2[] m_Coders;
int m_NumPrevBits;
int m_NumPosBits;
uint m_PosMask;
public void Create(int numPosBits, int numPrevBits)
{
if (m_Coders != null && m_NumPrevBits == numPrevBits &&
m_NumPosBits == numPosBits)
return;
m_NumPosBits = numPosBits;
m_PosMask = ((uint)1 << numPosBits) - 1;
m_NumPrevBits = numPrevBits;
uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
m_Coders = new Decoder2[numStates];
for (uint i = 0; i < numStates; i++)
m_Coders[i].Create();
}
public void Init()
{
uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
for (uint i = 0; i < numStates; i++)
m_Coders[i].Init();
}
uint GetState(uint pos, byte prevByte)
{ return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); }
public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte)
{ return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); }
public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte)
{ return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); }
};
LZ.OutWindow m_OutWindow = new LZ.OutWindow();
RangeCoder.Decoder m_RangeDecoder = new RangeCoder.Decoder();
BitDecoder[] m_IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
BitDecoder[] m_IsRepDecoders = new BitDecoder[Base.kNumStates];
BitDecoder[] m_IsRepG0Decoders = new BitDecoder[Base.kNumStates];
BitDecoder[] m_IsRepG1Decoders = new BitDecoder[Base.kNumStates];
BitDecoder[] m_IsRepG2Decoders = new BitDecoder[Base.kNumStates];
BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates];
BitDecoder[] m_PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex];
BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits);
LenDecoder m_LenDecoder = new LenDecoder();
LenDecoder m_RepLenDecoder = new LenDecoder();
LiteralDecoder m_LiteralDecoder = new LiteralDecoder();
uint m_DictionarySize;
uint m_DictionarySizeCheck;
uint m_PosStateMask;
public Decoder()
{
m_DictionarySize = 0xFFFFFFFF;
for (int i = 0; i < Base.kNumLenToPosStates; i++)
m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits);
}
void SetDictionarySize(uint dictionarySize)
{
if (m_DictionarySize != dictionarySize)
{
m_DictionarySize = dictionarySize;
m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1);
uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12));
m_OutWindow.Create(blockSize);
}
}
void SetLiteralProperties(int lp, int lc)
{
if (lp > 8)
throw new InvalidParamException();
if (lc > 8)
throw new InvalidParamException();
m_LiteralDecoder.Create(lp, lc);
}
void SetPosBitsProperties(int pb)
{
if (pb > Base.kNumPosStatesBitsMax)
throw new InvalidParamException();
uint numPosStates = (uint)1 << pb;
m_LenDecoder.Create(numPosStates);
m_RepLenDecoder.Create(numPosStates);
m_PosStateMask = numPosStates - 1;
}
bool _solid = false;
void Init(System.IO.Stream inStream, System.IO.Stream outStream)
{
m_RangeDecoder.Init(inStream);
m_OutWindow.Init(outStream, _solid);
uint i;
for (i = 0; i < Base.kNumStates; i++)
{
for (uint j = 0; j <= m_PosStateMask; j++)
{
uint index = (i << Base.kNumPosStatesBitsMax) + j;
m_IsMatchDecoders[index].Init();
m_IsRep0LongDecoders[index].Init();
}
m_IsRepDecoders[i].Init();
m_IsRepG0Decoders[i].Init();
m_IsRepG1Decoders[i].Init();
m_IsRepG2Decoders[i].Init();
}
m_LiteralDecoder.Init();
for (i = 0; i < Base.kNumLenToPosStates; i++)
m_PosSlotDecoder[i].Init();
// m_PosSpecDecoder.Init();
for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++)
m_PosDecoders[i].Init();
m_LenDecoder.Init();
m_RepLenDecoder.Init();
m_PosAlignDecoder.Init();
}
public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress)
{
Init(inStream, outStream);
Base.State state = new Base.State();
state.Init();
uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;
UInt64 nowPos64 = 0;
UInt64 outSize64 = (UInt64)outSize;
if (nowPos64 < outSize64)
{
if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0)
throw new DataErrorException();
state.UpdateChar();
byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0);
m_OutWindow.PutByte(b);
nowPos64++;
}
while (nowPos64 < outSize64)
{
// UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64);
// while(nowPos64 < next)
{
uint posState = (uint)nowPos64 & m_PosStateMask;
if (m_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
{
byte b;
byte prevByte = m_OutWindow.GetByte(0);
if (!state.IsCharState())
b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder,
(uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0));
else
b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte);
m_OutWindow.PutByte(b);
state.UpdateChar();
nowPos64++;
}
else
{
uint len;
if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1)
{
if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0)
{
if (m_IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
{
state.UpdateShortRep();
m_OutWindow.PutByte(m_OutWindow.GetByte(rep0));
nowPos64++;
continue;
}
}
else
{
UInt32 distance;
if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0)
{
distance = rep1;
}
else
{
if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0)
distance = rep2;
else
{
distance = rep3;
rep3 = rep2;
}
rep2 = rep1;
}
rep1 = rep0;
rep0 = distance;
}
len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen;
state.UpdateRep();
}
else
{
rep3 = rep2;
rep2 = rep1;
rep1 = rep0;
len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
state.UpdateMatch();
uint posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder);
if (posSlot >= Base.kStartPosModelIndex)
{
int numDirectBits = (int)((posSlot >> 1) - 1);
rep0 = ((2 | (posSlot & 1)) << numDirectBits);
if (posSlot < Base.kEndPosModelIndex)
rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders,
rep0 - posSlot - 1, m_RangeDecoder, numDirectBits);
else
{
rep0 += (m_RangeDecoder.DecodeDirectBits(
numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits);
rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
}
}
else
rep0 = posSlot;
}
if (rep0 >= m_OutWindow.TrainSize + nowPos64 || rep0 >= m_DictionarySizeCheck)
{
if (rep0 == 0xFFFFFFFF)
break;
throw new DataErrorException();
}
m_OutWindow.CopyBlock(rep0, len);
nowPos64 += len;
}
}
}
m_OutWindow.Flush();
m_OutWindow.ReleaseStream();
m_RangeDecoder.ReleaseStream();
}
public void SetDecoderProperties(byte[] properties)
{
if (properties.Length < 5)
throw new InvalidParamException();
int lc = properties[0] % 9;
int remainder = properties[0] / 9;
int lp = remainder % 5;
int pb = remainder / 5;
if (pb > Base.kNumPosStatesBitsMax)
throw new InvalidParamException();
UInt32 dictionarySize = 0;
for (int i = 0; i < 4; i++)
dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8);
SetDictionarySize(dictionarySize);
SetLiteralProperties(lp, lc);
SetPosBitsProperties(pb);
}
public bool Train(System.IO.Stream stream)
{
_solid = true;
return m_OutWindow.Train(stream);
}
/*
public override bool CanRead { get { return true; }}
public override bool CanWrite { get { return true; }}
public override bool CanSeek { get { return true; }}
public override long Length { get { return 0; }}
public override long Position
{
get { return 0; }
set { }
}
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{
return 0;
}
public override void Write(byte[] buffer, int offset, int count)
{
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
return 0;
}
public override void SetLength(long value) {}
*/
}
}
File diff suppressed because it is too large Load Diff
+234
View File
@@ -0,0 +1,234 @@
using System;
namespace SevenZip.Compression.RangeCoder
{
class Encoder
{
public const uint kTopValue = (1 << 24);
System.IO.Stream Stream;
public UInt64 Low;
public uint Range;
uint _cacheSize;
byte _cache;
long StartPosition;
public void SetStream(System.IO.Stream stream)
{
Stream = stream;
}
public void ReleaseStream()
{
Stream = null;
}
public void Init()
{
StartPosition = Stream.Position;
Low = 0;
Range = 0xFFFFFFFF;
_cacheSize = 1;
_cache = 0;
}
public void FlushData()
{
for (int i = 0; i < 5; i++)
ShiftLow();
}
public void FlushStream()
{
Stream.Flush();
}
public void CloseStream()
{
Stream.Close();
}
public void Encode(uint start, uint size, uint total)
{
Low += start * (Range /= total);
Range *= size;
while (Range < kTopValue)
{
Range <<= 8;
ShiftLow();
}
}
public void ShiftLow()
{
if ((uint)Low < (uint)0xFF000000 || (uint)(Low >> 32) == 1)
{
byte temp = _cache;
do
{
Stream.WriteByte((byte)(temp + (Low >> 32)));
temp = 0xFF;
}
while (--_cacheSize != 0);
_cache = (byte)(((uint)Low) >> 24);
}
_cacheSize++;
Low = ((uint)Low) << 8;
}
public void EncodeDirectBits(uint v, int numTotalBits)
{
for (int i = numTotalBits - 1; i >= 0; i--)
{
Range >>= 1;
if (((v >> i) & 1) == 1)
Low += Range;
if (Range < kTopValue)
{
Range <<= 8;
ShiftLow();
}
}
}
public void EncodeBit(uint size0, int numTotalBits, uint symbol)
{
uint newBound = (Range >> numTotalBits) * size0;
if (symbol == 0)
Range = newBound;
else
{
Low += newBound;
Range -= newBound;
}
while (Range < kTopValue)
{
Range <<= 8;
ShiftLow();
}
}
public long GetProcessedSizeAdd()
{
return _cacheSize +
Stream.Position - StartPosition + 4;
// (long)Stream.GetProcessedSize();
}
}
class Decoder
{
public const uint kTopValue = (1 << 24);
public uint Range;
public uint Code;
// public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16);
public System.IO.Stream Stream;
public void Init(System.IO.Stream stream)
{
// Stream.Init(stream);
Stream = stream;
Code = 0;
Range = 0xFFFFFFFF;
for (int i = 0; i < 5; i++)
Code = (Code << 8) | (byte)Stream.ReadByte();
}
public void ReleaseStream()
{
// Stream.ReleaseStream();
Stream = null;
}
public void CloseStream()
{
Stream.Close();
}
public void Normalize()
{
while (Range < kTopValue)
{
Code = (Code << 8) | (byte)Stream.ReadByte();
Range <<= 8;
}
}
public void Normalize2()
{
if (Range < kTopValue)
{
Code = (Code << 8) | (byte)Stream.ReadByte();
Range <<= 8;
}
}
public uint GetThreshold(uint total)
{
return Code / (Range /= total);
}
public void Decode(uint start, uint size, uint total)
{
Code -= start * Range;
Range *= size;
Normalize();
}
public uint DecodeDirectBits(int numTotalBits)
{
uint range = Range;
uint code = Code;
uint result = 0;
for (int i = numTotalBits; i > 0; i--)
{
range >>= 1;
/*
result <<= 1;
if (code >= range)
{
code -= range;
result |= 1;
}
*/
uint t = (code - range) >> 31;
code -= range & (t - 1);
result = (result << 1) | (1 - t);
if (range < kTopValue)
{
code = (code << 8) | (byte)Stream.ReadByte();
range <<= 8;
}
}
Range = range;
Code = code;
return result;
}
public uint DecodeBit(uint size0, int numTotalBits)
{
uint newBound = (Range >> numTotalBits) * size0;
uint symbol;
if (Code < newBound)
{
symbol = 0;
Range = newBound;
}
else
{
symbol = 1;
Code -= newBound;
Range -= newBound;
}
Normalize();
return symbol;
}
// ulong GetProcessedSize() {return Stream.GetProcessedSize(); }
}
}
+117
View File
@@ -0,0 +1,117 @@
using System;
namespace SevenZip.Compression.RangeCoder
{
struct BitEncoder
{
public const int kNumBitModelTotalBits = 11;
public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
const int kNumMoveBits = 5;
const int kNumMoveReducingBits = 2;
public const int kNumBitPriceShiftBits = 6;
uint Prob;
public void Init() { Prob = kBitModelTotal >> 1; }
public void UpdateModel(uint symbol)
{
if (symbol == 0)
Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
else
Prob -= (Prob) >> kNumMoveBits;
}
public void Encode(Encoder encoder, uint symbol)
{
// encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol);
// UpdateModel(symbol);
uint newBound = (encoder.Range >> kNumBitModelTotalBits) * Prob;
if (symbol == 0)
{
encoder.Range = newBound;
Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
}
else
{
encoder.Low += newBound;
encoder.Range -= newBound;
Prob -= (Prob) >> kNumMoveBits;
}
if (encoder.Range < Encoder.kTopValue)
{
encoder.Range <<= 8;
encoder.ShiftLow();
}
}
private static UInt32[] ProbPrices = new UInt32[kBitModelTotal >> kNumMoveReducingBits];
static BitEncoder()
{
const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
for (int i = kNumBits - 1; i >= 0; i--)
{
UInt32 start = (UInt32)1 << (kNumBits - i - 1);
UInt32 end = (UInt32)1 << (kNumBits - i);
for (UInt32 j = start; j < end; j++)
ProbPrices[j] = ((UInt32)i << kNumBitPriceShiftBits) +
(((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1));
}
}
public uint GetPrice(uint symbol)
{
return ProbPrices[(((Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits];
}
public uint GetPrice0() { return ProbPrices[Prob >> kNumMoveReducingBits]; }
public uint GetPrice1() { return ProbPrices[(kBitModelTotal - Prob) >> kNumMoveReducingBits]; }
}
struct BitDecoder
{
public const int kNumBitModelTotalBits = 11;
public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
const int kNumMoveBits = 5;
uint Prob;
public void UpdateModel(int numMoveBits, uint symbol)
{
if (symbol == 0)
Prob += (kBitModelTotal - Prob) >> numMoveBits;
else
Prob -= (Prob) >> numMoveBits;
}
public void Init() { Prob = kBitModelTotal >> 1; }
public uint Decode(RangeCoder.Decoder rangeDecoder)
{
uint newBound = (uint)(rangeDecoder.Range >> kNumBitModelTotalBits) * (uint)Prob;
if (rangeDecoder.Code < newBound)
{
rangeDecoder.Range = newBound;
Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
if (rangeDecoder.Range < Decoder.kTopValue)
{
rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
rangeDecoder.Range <<= 8;
}
return 0;
}
else
{
rangeDecoder.Range -= newBound;
rangeDecoder.Code -= newBound;
Prob -= (Prob) >> kNumMoveBits;
if (rangeDecoder.Range < Decoder.kTopValue)
{
rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
rangeDecoder.Range <<= 8;
}
return 1;
}
}
}
}
@@ -0,0 +1,157 @@
using System;
namespace SevenZip.Compression.RangeCoder
{
struct BitTreeEncoder
{
BitEncoder[] Models;
int NumBitLevels;
public BitTreeEncoder(int numBitLevels)
{
NumBitLevels = numBitLevels;
Models = new BitEncoder[1 << numBitLevels];
}
public void Init()
{
for (uint i = 1; i < (1 << NumBitLevels); i++)
Models[i].Init();
}
public void Encode(Encoder rangeEncoder, UInt32 symbol)
{
UInt32 m = 1;
for (int bitIndex = NumBitLevels; bitIndex > 0; )
{
bitIndex--;
UInt32 bit = (symbol >> bitIndex) & 1;
Models[m].Encode(rangeEncoder, bit);
m = (m << 1) | bit;
}
}
public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol)
{
UInt32 m = 1;
for (UInt32 i = 0; i < NumBitLevels; i++)
{
UInt32 bit = symbol & 1;
Models[m].Encode(rangeEncoder, bit);
m = (m << 1) | bit;
symbol >>= 1;
}
}
public UInt32 GetPrice(UInt32 symbol)
{
UInt32 price = 0;
UInt32 m = 1;
for (int bitIndex = NumBitLevels; bitIndex > 0; )
{
bitIndex--;
UInt32 bit = (symbol >> bitIndex) & 1;
price += Models[m].GetPrice(bit);
m = (m << 1) + bit;
}
return price;
}
public UInt32 ReverseGetPrice(UInt32 symbol)
{
UInt32 price = 0;
UInt32 m = 1;
for (int i = NumBitLevels; i > 0; i--)
{
UInt32 bit = symbol & 1;
symbol >>= 1;
price += Models[m].GetPrice(bit);
m = (m << 1) | bit;
}
return price;
}
public static UInt32 ReverseGetPrice(BitEncoder[] Models, UInt32 startIndex,
int NumBitLevels, UInt32 symbol)
{
UInt32 price = 0;
UInt32 m = 1;
for (int i = NumBitLevels; i > 0; i--)
{
UInt32 bit = symbol & 1;
symbol >>= 1;
price += Models[startIndex + m].GetPrice(bit);
m = (m << 1) | bit;
}
return price;
}
public static void ReverseEncode(BitEncoder[] Models, UInt32 startIndex,
Encoder rangeEncoder, int NumBitLevels, UInt32 symbol)
{
UInt32 m = 1;
for (int i = 0; i < NumBitLevels; i++)
{
UInt32 bit = symbol & 1;
Models[startIndex + m].Encode(rangeEncoder, bit);
m = (m << 1) | bit;
symbol >>= 1;
}
}
}
struct BitTreeDecoder
{
BitDecoder[] Models;
int NumBitLevels;
public BitTreeDecoder(int numBitLevels)
{
NumBitLevels = numBitLevels;
Models = new BitDecoder[1 << numBitLevels];
}
public void Init()
{
for (uint i = 1; i < (1 << NumBitLevels); i++)
Models[i].Init();
}
public uint Decode(RangeCoder.Decoder rangeDecoder)
{
uint m = 1;
for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--)
m = (m << 1) + Models[m].Decode(rangeDecoder);
return m - ((uint)1 << NumBitLevels);
}
public uint ReverseDecode(RangeCoder.Decoder rangeDecoder)
{
uint m = 1;
uint symbol = 0;
for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
{
uint bit = Models[m].Decode(rangeDecoder);
m <<= 1;
m += bit;
symbol |= (bit << bitIndex);
}
return symbol;
}
public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex,
RangeCoder.Decoder rangeDecoder, int NumBitLevels)
{
uint m = 1;
uint symbol = 0;
for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
{
uint bit = Models[startIndex + m].Decode(rangeDecoder);
m <<= 1;
m += bit;
symbol |= (bit << bitIndex);
}
return symbol;
}
}
}
+157
View File
@@ -0,0 +1,157 @@
// ICoder.h
using System;
namespace SevenZip
{
/// <summary>
/// The exception that is thrown when an error in input stream occurs during decoding.
/// </summary>
class DataErrorException : ApplicationException
{
public DataErrorException(): base("Data Error") { }
}
/// <summary>
/// The exception that is thrown when the value of an argument is outside the allowable range.
/// </summary>
class InvalidParamException : ApplicationException
{
public InvalidParamException(): base("Invalid Parameter") { }
}
public interface ICodeProgress
{
/// <summary>
/// Callback progress.
/// </summary>
/// <param name="inSize">
/// input size. -1 if unknown.
/// </param>
/// <param name="outSize">
/// output size. -1 if unknown.
/// </param>
void SetProgress(Int64 inSize, Int64 outSize);
};
public interface ICoder
{
/// <summary>
/// Codes streams.
/// </summary>
/// <param name="inStream">
/// input Stream.
/// </param>
/// <param name="outStream">
/// output Stream.
/// </param>
/// <param name="inSize">
/// input Size. -1 if unknown.
/// </param>
/// <param name="outSize">
/// output Size. -1 if unknown.
/// </param>
/// <param name="progress">
/// callback progress reference.
/// </param>
/// <exception cref="SevenZip.DataErrorException">
/// if input stream is not valid
/// </exception>
void Code(System.IO.Stream inStream, System.IO.Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress);
};
/*
public interface ICoder2
{
void Code(ISequentialInStream []inStreams,
const UInt64 []inSizes,
ISequentialOutStream []outStreams,
UInt64 []outSizes,
ICodeProgress progress);
};
*/
/// <summary>
/// Provides the fields that represent properties idenitifiers for compressing.
/// </summary>
public enum CoderPropID
{
/// <summary>
/// Specifies default property.
/// </summary>
DefaultProp = 0,
/// <summary>
/// Specifies size of dictionary.
/// </summary>
DictionarySize,
/// <summary>
/// Specifies size of memory for PPM*.
/// </summary>
UsedMemorySize,
/// <summary>
/// Specifies order for PPM methods.
/// </summary>
Order,
/// <summary>
/// Specifies Block Size.
/// </summary>
BlockSize,
/// <summary>
/// Specifies number of postion state bits for LZMA (0 <= x <= 4).
/// </summary>
PosStateBits,
/// <summary>
/// Specifies number of literal context bits for LZMA (0 <= x <= 8).
/// </summary>
LitContextBits,
/// <summary>
/// Specifies number of literal position bits for LZMA (0 <= x <= 4).
/// </summary>
LitPosBits,
/// <summary>
/// Specifies number of fast bytes for LZ*.
/// </summary>
NumFastBytes,
/// <summary>
/// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B".
/// </summary>
MatchFinder,
/// <summary>
/// Specifies the number of match finder cyckes.
/// </summary>
MatchFinderCycles,
/// <summary>
/// Specifies number of passes.
/// </summary>
NumPasses,
/// <summary>
/// Specifies number of algorithm.
/// </summary>
Algorithm,
/// <summary>
/// Specifies the number of threads.
/// </summary>
NumThreads,
/// <summary>
/// Specifies mode with end marker.
/// </summary>
EndMarker
};
public interface ISetCoderProperties
{
void SetCoderProperties(CoderPropID[] propIDs, object[] properties);
};
public interface IWriteCoderProperties
{
void WriteCoderProperties(System.IO.Stream outStream);
}
public interface ISetDecoderProperties
{
void SetDecoderProperties(byte[] properties);
}
}
+112
View File
@@ -0,0 +1,112 @@
<!--
Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
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.
-->
<Application x:Class="WPinternals.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Views\StartupWindow.xaml"
xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=PresentationFramework"
>
<Application.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<Trigger Property="ComponentModel:DesignerProperties.IsInDesignMode"
Value="true">
<Setter Property="Background"
Value="White" />
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="TopicExpanderTemplate" TargetType="{x:Type Expander}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="3" SnapsToDevicePixels="True">
<DockPanel>
<ToggleButton x:Name="HeaderSite" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" DockPanel.Dock="Top" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontStyle}" FontStretch="{TemplateBinding FontStretch}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" MinWidth="0" MinHeight="0" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
<ToggleButton.FocusVisualStyle>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="0" SnapsToDevicePixels="True" Stroke="Black" StrokeThickness="1" StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.FocusVisualStyle>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}">
<Grid Background="Transparent" SnapsToDevicePixels="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="19"/>
</Grid.ColumnDefinitions>
<Ellipse x:Name="circle" Fill="White" HorizontalAlignment="Center" Height="19" Stroke="#FF333333" VerticalAlignment="Center" Width="19" Grid.Column="1" />
<Path x:Name="arrow" Data="M1,1.5L4.5,5 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="False" Stroke="#FF333333" StrokeThickness="2" VerticalAlignment="Center" Grid.Column="1" />
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="Left" Margin="0,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center" TextBlock.FontSize="18" TextBlock.FontWeight="Bold" TextBlock.Foreground="#FF3753A6"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Data" TargetName="arrow" Value="M1,4.5L4.5,1 8,4.5"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" TargetName="circle" Value="#FF5593FF"/>
<Setter Property="Fill" TargetName="circle" Value="#FFF3F9FF"/>
<Setter Property="Stroke" TargetName="arrow" Value="Black"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Stroke" TargetName="circle" Value="#FF3C77DD"/>
<Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
<Setter Property="Fill" TargetName="circle" Value="#FFD9ECFF"/>
<Setter Property="Stroke" TargetName="arrow" Value="Black"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Stroke" TargetName="circle" Value="#FFBCBCBC"/>
<Setter Property="Fill" TargetName="circle" Value="#FFE6E6E6"/>
<Setter Property="Stroke" TargetName="arrow" Value="#FF707070"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<ContentPresenter x:Name="ExpandSite" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" DockPanel.Dock="Bottom" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Application.Resources>
</Application>
+94
View File
@@ -0,0 +1,94 @@
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
//
// 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.Linq;
using System.Windows;
using System.IO;
using System.Threading;
namespace WPinternals
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
internal static Action NavigateToGettingStarted;
internal static Action NavigateToUnlockBoot;
internal static PatchEngine PatchEngine;
internal static WPinternalsConfig Config;
internal static Mutex mutex = new Mutex(false, "Global\\WPinternalsRunning");
internal static DownloadsViewModel DownloadManager;
internal static bool InterruptBoot = false;
internal static bool IsPnPEventLogMissing = true;
public App()
: base()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
if (Environment.GetCommandLineArgs().Count() > 1)
CommandLine.OpenConsole();
if (!mutex.WaitOne(0, false))
{
if (Environment.GetCommandLineArgs().Count() > 1)
{
Console.WriteLine("Windows Phone Internals is already running");
CommandLine.CloseConsole();
}
else
MessageBox.Show("Windows Phone Internals is already running.", "Windows Phone Internals", MessageBoxButton.OK, MessageBoxImage.Exclamation);
Environment.Exit(0);
}
Registration.CheckExpiration();
string PatchDefintionsXml;
string PatchDefintionsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PatchDefintions.xml");
if (File.Exists(PatchDefintionsPath))
{
PatchDefintionsXml = File.ReadAllText(PatchDefintionsPath);
}
else
{
using (Stream stream = System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("WPinternals.PatchDefinitions.xml"))
{
using (StreamReader sr = new StreamReader(stream))
{
PatchDefintionsXml = sr.ReadToEnd();
}
}
}
PatchEngine = new PatchEngine(PatchDefintionsXml);
Config = WPinternalsConfig.ReadConfig();
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception)
{
LogFile.LogException(e.ExceptionObject as Exception);
}
}
}
}
+1744
View File
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils
{
internal class Block
{
public Block()
{
}
public long Position { get; set; }
public byte[] Data { get; set; }
public int Available { get; set; }
public bool Equals(Block other)
{
return Position == other.Position;
}
}
}
+138
View File
@@ -0,0 +1,138 @@
//
// 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
{
using System;
using System.Collections.Generic;
using System.Text;
internal class BlockCache<T>
where T : Block, new()
{
private int _blockSize;
private Dictionary<long, T> _blocks;
private LinkedList<T> _lru;
private List<T> _freeBlocks;
private int _blocksCreated;
private int _totalBlocks;
private int _freeBlockCount;
public BlockCache(int blockSize, int blockCount)
{
_blockSize = blockSize;
_totalBlocks = blockCount;
_blocks = new Dictionary<long, T>();
_lru = new LinkedList<T>();
_freeBlocks = new List<T>(_totalBlocks);
_freeBlockCount = _totalBlocks;
}
public int FreeBlockCount
{
get { return _freeBlockCount; }
}
public bool ContainsBlock(long position)
{
return _blocks.ContainsKey(position);
}
public bool TryGetBlock(long position, out T block)
{
if (_blocks.TryGetValue(position, out block))
{
_lru.Remove(block);
_lru.AddFirst(block);
return true;
}
return false;
}
public T GetBlock(long position)
{
T result;
if (TryGetBlock(position, out result))
{
return result;
}
result = GetFreeBlock();
result.Position = position;
result.Available = -1;
StoreBlock(result);
return result;
}
public void ReleaseBlock(long position)
{
T block;
if (_blocks.TryGetValue(position, out block))
{
_blocks.Remove(position);
_lru.Remove(block);
_freeBlocks.Add(block);
_freeBlockCount++;
}
}
private void StoreBlock(T block)
{
_blocks[block.Position] = block;
_lru.AddFirst(block);
}
private T GetFreeBlock()
{
T block;
if (_freeBlocks.Count > 0)
{
int idx = _freeBlocks.Count - 1;
block = _freeBlocks[idx];
_freeBlocks.RemoveAt(idx);
_freeBlockCount--;
}
else if (_blocksCreated < _totalBlocks)
{
block = new T();
block.Data = new byte[_blockSize];
_blocksCreated++;
_freeBlockCount--;
}
else
{
block = _lru.Last.Value;
_lru.RemoveLast();
_blocks.Remove(block.Position);
}
return block;
}
}
}
+77
View File
@@ -0,0 +1,77 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils
{
/// <summary>
/// Settings controlling BlockCache instances.
/// </summary>
public sealed class BlockCacheSettings
{
/// <summary>
/// Initializes a new instance of the BlockCacheSettings class.
/// </summary>
public BlockCacheSettings()
{
this.BlockSize = (int)(4 * Sizes.OneKiB);
this.ReadCacheSize = 4 * Sizes.OneMiB;
this.LargeReadSize = 64 * Sizes.OneKiB;
this.OptimumReadSize = (int)(64 * Sizes.OneKiB);
}
/// <summary>
/// Initializes a new instance of the BlockCacheSettings class.
/// </summary>
/// <param name="settings">The cache settings.</param>
internal BlockCacheSettings(BlockCacheSettings settings)
{
this.BlockSize = settings.BlockSize;
this.ReadCacheSize = settings.ReadCacheSize;
this.LargeReadSize = settings.LargeReadSize;
this.OptimumReadSize = settings.OptimumReadSize;
}
/// <summary>
/// Gets or sets the size (in bytes) of each cached block.
/// </summary>
public int BlockSize { get; set; }
/// <summary>
/// Gets or sets the size (in bytes) of the read cache.
/// </summary>
public long ReadCacheSize { get; set; }
/// <summary>
/// Gets or sets the maximum read size that will be cached.
/// </summary>
/// <remarks>Large reads are not cached, on the assumption they will not
/// be repeated. This setting controls what is considered 'large'.
/// Any read that is more than this many bytes will not be cached.</remarks>
public long LargeReadSize { get; set; }
/// <summary>
/// Gets or sets the optimum size of a read to the wrapped stream.
/// </summary>
/// <remarks>This value must be a multiple of BlockSize.</remarks>
public int OptimumReadSize { get; set; }
}
}
+79
View File
@@ -0,0 +1,79 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils
{
/// <summary>
/// Statistical information about the effectiveness of a BlockCache instance.
/// </summary>
public sealed class BlockCacheStatistics
{
/// <summary>
/// Gets the number of requested 'large' reads, as defined by the LargeReadSize setting.
/// </summary>
public long LargeReadsIn { get; internal set; }
/// <summary>
/// Gets the number of requested unaligned reads.
/// </summary>
/// <remarks>Unaligned reads are reads where the read doesn't start on a multiple of
/// the block size.</remarks>
public long UnalignedReadsIn { get; internal set; }
/// <summary>
/// Gets the total number of requested reads.
/// </summary>
public long TotalReadsIn { get; internal set; }
/// <summary>
/// Gets the total number of reads passed on by the cache.
/// </summary>
public long TotalReadsOut { get; internal set; }
/// <summary>
/// Gets the number of times a read request was serviced (in part or whole) from the cache.
/// </summary>
public long ReadCacheHits { get; internal set; }
/// <summary>
/// Gets the number of time a read request was serviced (in part or whole) from the wrapped stream.
/// </summary>
public long ReadCacheMisses { get; internal set; }
/// <summary>
/// Gets the number of requested unaligned writes.
/// </summary>
/// <remarks>Unaligned writes are writes where the write doesn't start on a multiple of
/// the block size.</remarks>
public long UnalignedWritesIn { get; internal set; }
/// <summary>
/// Gets the total number of requested writes.
/// </summary>
public long TotalWritesIn { get; internal set; }
/// <summary>
/// Gets the number of free blocks in the read cache.
/// </summary>
public int FreeReadBlocks { get; internal set; }
}
}
+472
View File
@@ -0,0 +1,472 @@
//
// 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
{
using System;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// A stream implementing a block-oriented read cache.
/// </summary>
public sealed class BlockCacheStream : SparseStream
{
private SparseStream _wrappedStream;
private Ownership _ownWrapped;
private BlockCacheSettings _settings;
private BlockCacheStatistics _stats;
private long _position;
private bool _atEof;
private BlockCache<Block> _cache;
private byte[] _readBuffer;
private int _blocksInReadBuffer;
/// <summary>
/// Initializes a new instance of the BlockCacheStream class.
/// </summary>
/// <param name="toWrap">The stream to wrap.</param>
/// <param name="ownership">Whether to assume ownership of <c>toWrap</c>.</param>
public BlockCacheStream(SparseStream toWrap, Ownership ownership)
: this(toWrap, ownership, new BlockCacheSettings())
{
}
/// <summary>
/// Initializes a new instance of the BlockCacheStream class.
/// </summary>
/// <param name="toWrap">The stream to wrap.</param>
/// <param name="ownership">Whether to assume ownership of <c>toWrap</c>.</param>
/// <param name="settings">The cache settings.</param>
public BlockCacheStream(SparseStream toWrap, Ownership ownership, BlockCacheSettings settings)
{
if (!toWrap.CanRead)
{
throw new ArgumentException("The wrapped stream does not support reading", "toWrap");
}
if (!toWrap.CanSeek)
{
throw new ArgumentException("The wrapped stream does not support seeking", "toWrap");
}
_wrappedStream = toWrap;
_ownWrapped = ownership;
_settings = new BlockCacheSettings(settings);
if (_settings.OptimumReadSize % _settings.BlockSize != 0)
{
throw new ArgumentException("Invalid settings, OptimumReadSize must be a multiple of BlockSize", "settings");
}
_readBuffer = new byte[_settings.OptimumReadSize];
_blocksInReadBuffer = _settings.OptimumReadSize / _settings.BlockSize;
int totalBlocks = (int)(_settings.ReadCacheSize / _settings.BlockSize);
_cache = new BlockCache<Block>(_settings.BlockSize, totalBlocks);
_stats = new BlockCacheStatistics();
_stats.FreeReadBlocks = totalBlocks;
}
/// <summary>
/// Gets the parts of the stream that are stored.
/// </summary>
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
public override IEnumerable<StreamExtent> Extents
{
get
{
CheckDisposed();
return _wrappedStream.Extents;
}
}
/// <summary>
/// Gets an indication as to whether the stream can be read.
/// </summary>
public override bool CanRead
{
get { return true; }
}
/// <summary>
/// Gets an indication as to whether the stream position can be changed.
/// </summary>
public override bool CanSeek
{
get { return true; }
}
/// <summary>
/// Gets an indication as to whether the stream can be written to.
/// </summary>
public override bool CanWrite
{
get { return _wrappedStream.CanWrite; }
}
/// <summary>
/// Gets the length of the stream.
/// </summary>
public override long Length
{
get
{
CheckDisposed();
return _wrappedStream.Length;
}
}
/// <summary>
/// Gets and sets the current stream position.
/// </summary>
public override long Position
{
get
{
CheckDisposed();
return _position;
}
set
{
CheckDisposed();
_position = value;
}
}
/// <summary>
/// Gets the performance statistics for this instance.
/// </summary>
public BlockCacheStatistics Statistics
{
get
{
_stats.FreeReadBlocks = _cache.FreeBlockCount;
return _stats;
}
}
/// <summary>
/// Gets the parts of a stream that are stored, within a specified range.
/// </summary>
/// <param name="start">The offset of the first byte of interest.</param>
/// <param name="count">The number of bytes of interest.</param>
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
{
CheckDisposed();
return _wrappedStream.GetExtentsInRange(start, count);
}
/// <summary>
/// Reads data from the stream.
/// </summary>
/// <param name="buffer">The buffer to fill.</param>
/// <param name="offset">The buffer offset to start from.</param>
/// <param name="count">The number of bytes to read.</param>
/// <returns>The number of bytes read.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
CheckDisposed();
if (_position >= Length)
{
if (_atEof)
{
throw new IOException("Attempt to read beyond end of stream");
}
else
{
_atEof = true;
return 0;
}
}
_stats.TotalReadsIn++;
if (count > _settings.LargeReadSize)
{
_stats.LargeReadsIn++;
_stats.TotalReadsOut++;
_wrappedStream.Position = _position;
int numRead = _wrappedStream.Read(buffer, offset, count);
_position = _wrappedStream.Position;
if (_position >= Length)
{
_atEof = true;
}
return numRead;
}
int totalBytesRead = 0;
bool servicedFromCache = false;
bool servicedOutsideCache = false;
int blockSize = _settings.BlockSize;
long firstBlock = _position / blockSize;
int offsetInNextBlock = (int)(_position % blockSize);
long endBlock = Utilities.Ceil(Math.Min(_position + count, Length), blockSize);
int numBlocks = (int)(endBlock - firstBlock);
if (offsetInNextBlock != 0)
{
_stats.UnalignedReadsIn++;
}
int blocksRead = 0;
while (blocksRead < numBlocks)
{
Block block;
// Read from the cache as much as possible
while (blocksRead < numBlocks && _cache.TryGetBlock(firstBlock + blocksRead, out block))
{
int bytesToRead = Math.Min(count - totalBytesRead, block.Available - offsetInNextBlock);
Array.Copy(block.Data, offsetInNextBlock, buffer, offset + totalBytesRead, bytesToRead);
offsetInNextBlock = 0;
totalBytesRead += bytesToRead;
_position += bytesToRead;
blocksRead++;
servicedFromCache = true;
}
// Now handle a sequence of (one or more) blocks that are not cached
if (blocksRead < numBlocks && !_cache.ContainsBlock(firstBlock + blocksRead))
{
servicedOutsideCache = true;
// Figure out how many blocks to read from the wrapped stream
int blocksToRead = 0;
while (blocksRead + blocksToRead < numBlocks
&& blocksToRead < _blocksInReadBuffer
&& !_cache.ContainsBlock(firstBlock + blocksRead + blocksToRead))
{
++blocksToRead;
}
// Allow for the end of the stream not being block-aligned
long readPosition = (firstBlock + blocksRead) * (long)blockSize;
int bytesToRead = (int)Math.Min(blocksToRead * (long)blockSize, Length - readPosition);
// Do the read
_stats.TotalReadsOut++;
_wrappedStream.Position = readPosition;
int bytesRead = Utilities.ReadFully(_wrappedStream, _readBuffer, 0, bytesToRead);
if (bytesRead != bytesToRead)
{
throw new IOException("Short read before end of stream");
}
// Cache the read blocks
for (int i = 0; i < blocksToRead; ++i)
{
int copyBytes = Math.Min(blockSize, bytesToRead - (i * blockSize));
block = _cache.GetBlock(firstBlock + blocksRead + i);
Array.Copy(_readBuffer, i * blockSize, block.Data, 0, copyBytes);
block.Available = copyBytes;
if (copyBytes < blockSize)
{
Array.Clear(_readBuffer, (i * blockSize) + copyBytes, blockSize - copyBytes);
}
}
blocksRead += blocksToRead;
// Propogate the data onto the caller
int bytesToCopy = Math.Min(count - totalBytesRead, bytesRead - offsetInNextBlock);
Array.Copy(_readBuffer, offsetInNextBlock, buffer, offset + totalBytesRead, bytesToCopy);
totalBytesRead += bytesToCopy;
_position += bytesToCopy;
offsetInNextBlock = 0;
}
}
if (_position >= Length && totalBytesRead == 0)
{
_atEof = true;
}
if (servicedFromCache)
{
_stats.ReadCacheHits++;
}
if (servicedOutsideCache)
{
_stats.ReadCacheMisses++;
}
return totalBytesRead;
}
/// <summary>
/// Flushes the stream.
/// </summary>
public override void Flush()
{
CheckDisposed();
_wrappedStream.Flush();
}
/// <summary>
/// Moves the stream position.
/// </summary>
/// <param name="offset">The origin-relative location.</param>
/// <param name="origin">The base location.</param>
/// <returns>The new absolute stream position.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
CheckDisposed();
long effectiveOffset = offset;
if (origin == SeekOrigin.Current)
{
effectiveOffset += _position;
}
else if (origin == SeekOrigin.End)
{
effectiveOffset += Length;
}
_atEof = false;
if (effectiveOffset < 0)
{
throw new IOException("Attempt to move before beginning of disk");
}
else
{
_position = effectiveOffset;
return _position;
}
}
/// <summary>
/// Sets the length of the stream.
/// </summary>
/// <param name="value">The new length.</param>
public override void SetLength(long value)
{
CheckDisposed();
_wrappedStream.SetLength(value);
}
/// <summary>
/// Writes data to the stream at the current location.
/// </summary>
/// <param name="buffer">The data to write.</param>
/// <param name="offset">The first byte to write from buffer.</param>
/// <param name="count">The number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
{
CheckDisposed();
_stats.TotalWritesIn++;
int blockSize = _settings.BlockSize;
long firstBlock = _position / blockSize;
long endBlock = Utilities.Ceil(Math.Min(_position + count, Length), blockSize);
int numBlocks = (int)(endBlock - firstBlock);
try
{
_wrappedStream.Position = _position;
_wrappedStream.Write(buffer, offset, count);
}
catch
{
InvalidateBlocks(firstBlock, numBlocks);
throw;
}
int offsetInNextBlock = (int)(_position % blockSize);
if (offsetInNextBlock != 0)
{
_stats.UnalignedWritesIn++;
}
// For each block touched, if it's cached, update it
int bytesProcessed = 0;
for (int i = 0; i < numBlocks; ++i)
{
int bufferPos = offset + bytesProcessed;
int bytesThisBlock = Math.Min(count - bytesProcessed, blockSize - offsetInNextBlock);
Block block;
if (_cache.TryGetBlock(firstBlock + i, out block))
{
Array.Copy(buffer, bufferPos, block.Data, offsetInNextBlock, bytesThisBlock);
block.Available = Math.Max(block.Available, offsetInNextBlock + bytesThisBlock);
}
offsetInNextBlock = 0;
bytesProcessed += bytesThisBlock;
}
_position += count;
}
/// <summary>
/// Disposes of this instance, freeing up associated resources.
/// </summary>
/// <param name="disposing"><c>true</c> if invoked from <c>Dispose</c>, else <c>false</c>.</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_wrappedStream != null && _ownWrapped == Ownership.Dispose)
{
_wrappedStream.Dispose();
}
_wrappedStream = null;
}
base.Dispose(disposing);
}
private void CheckDisposed()
{
if (_wrappedStream == null)
{
throw new ObjectDisposedException("BlockCacheStream");
}
}
private void InvalidateBlocks(long firstBlock, int numBlocks)
{
for (long i = firstBlock; i < (firstBlock + numBlocks); ++i)
{
_cache.ReleaseBlock(i);
}
}
}
}
@@ -0,0 +1,55 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.BootConfig
{
/// <summary>
/// Enumeration of known application image types.
/// </summary>
public enum ApplicationImageType
{
/// <summary>
/// Unknown type.
/// </summary>
None = 0x0,
/// <summary>
/// Firmware application.
/// </summary>
Firmware = 0x1,
/// <summary>
/// Windows boot loader.
/// </summary>
WindowsBoot = 0x2,
/// <summary>
/// Legacy boot loader.
/// </summary>
LegacyLoader = 0x3,
/// <summary>
/// Real mode boot loader.
/// </summary>
RealMode = 0x4
}
}
+80
View File
@@ -0,0 +1,80 @@
//
// 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.BootConfig
{
/// <summary>
/// Enumeration of known application types.
/// </summary>
public enum ApplicationType
{
/// <summary>
/// Unknown type.
/// </summary>
None = 0,
/// <summary>
/// Firmware boot manager (e.g. UEFI).
/// </summary>
FirmwareBootManager = 1,
/// <summary>
/// Windows boot manager.
/// </summary>
BootManager = 2,
/// <summary>
/// Operating System Loader.
/// </summary>
OsLoader = 3,
/// <summary>
/// Resume loader.
/// </summary>
Resume = 4,
/// <summary>
/// Memory diagnostic application.
/// </summary>
MemoryDiagnostics = 5,
/// <summary>
/// Legacy NT loader application.
/// </summary>
NtLoader = 6,
/// <summary>
/// Windows setup application.
/// </summary>
SetupLoader = 7,
/// <summary>
/// Boot sector application.
/// </summary>
BootSector = 8,
/// <summary>
/// Startup application.
/// </summary>
Startup = 9
}
}
+75
View 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.
//
namespace DiscUtils.BootConfig
{
using System;
using System.Collections.Generic;
/// <summary>
/// Base class for BCD storage repositories.
/// </summary>
internal abstract class BaseStorage
{
/// <summary>
/// Tests if an element is present (i.e. has a value) on an object.
/// </summary>
/// <param name="obj">The object to inspect.</param>
/// <param name="element">The element to inspect.</param>
/// <returns><c>true</c> if present, else <c>false</c>.</returns>
public abstract bool HasValue(Guid obj, int element);
/// <summary>
/// Gets the value of a string element.
/// </summary>
/// <param name="obj">The object to inspect.</param>
/// <param name="element">The element to retrieve.</param>
/// <returns>The value as a string.</returns>
public abstract string GetString(Guid obj, int element);
public abstract byte[] GetBinary(Guid obj, int element);
public abstract void SetString(Guid obj, int element, string value);
public abstract void SetBinary(Guid obj, int element, byte[] value);
public abstract string[] GetMultiString(Guid obj, int element);
public abstract void SetMultiString(Guid obj, int element, string[] values);
public abstract IEnumerable<Guid> EnumerateObjects();
public abstract IEnumerable<int> EnumerateElements(Guid obj);
public abstract int GetObjectType(Guid obj);
public abstract bool ObjectExists(Guid obj);
public abstract Guid CreateObject(Guid obj, int type);
public abstract void CreateElement(Guid obj, int element);
public abstract void DeleteObject(Guid obj);
public abstract void DeleteElement(Guid obj, int element);
}
}
+365
View File
@@ -0,0 +1,365 @@
//
// 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.
//
//
// Symbolic names of BCD Objects taken from Geoff Chappell's website:
// http://www.geoffchappell.com/viewer.htm?doc=notes/windows/boot/bcd/objects.htm
//
//
namespace DiscUtils.BootConfig
{
using System;
using System.Collections.Generic;
/// <summary>
/// Represents a Boot Configuration Database object (application, device or inherited settings).
/// </summary>
public class BcdObject
{
/// <summary>
/// Well-known object for Emergency Management Services settings.
/// </summary>
public const string EmsSettingsGroupId = "{0CE4991B-E6B3-4B16-B23C-5E0D9250E5D9}";
/// <summary>
/// Well-known object for the Resume boot loader.
/// </summary>
public const string ResumeLoaderSettingsGroupId = "{1AFA9C49-16AB-4A5C-4A90-212802DA9460}";
/// <summary>
/// Alias for the Default boot entry.
/// </summary>
public const string DefaultBootEntryId = "{1CAE1EB7-A0DF-4D4D-9851-4860E34EF535}";
/// <summary>
/// Well-known object for Emergency Management Services settings.
/// </summary>
public const string DebuggerSettingsGroupId = "{4636856E-540F-4170-A130-A84776F4C654}";
/// <summary>
/// Well-known object for NTLDR application.
/// </summary>
public const string WindowsLegacyNtldrId = "{466F5A88-0AF2-4F76-9038-095B170DC21C}";
/// <summary>
/// Well-known object for bad memory settings.
/// </summary>
public const string BadMemoryGroupId = "{5189B25C-5558-4BF2-BCA4-289B11BD29E2}";
/// <summary>
/// Well-known object for Boot Loader settings.
/// </summary>
public const string BootLoaderSettingsGroupId = "{6EFB52BF-1766-41DB-A6B3-0EE5EFF72BD7}";
/// <summary>
/// Well-known object for EFI setup.
/// </summary>
public const string WindowsSetupEfiId = "{7254A080-1510-4E85-AC0F-E7FB3D444736}";
/// <summary>
/// Well-known object for Global settings.
/// </summary>
public const string GlobalSettingsGroupId = "{7EA2E1AC-2E61-4728-AAA3-896D9D0A9F0E}";
/// <summary>
/// Well-known object for Windows Boot Manager.
/// </summary>
public const string WindowsBootManagerId = "{9DEA862C-5CDD-4E70-ACC1-F32B344D4795}";
/// <summary>
/// Well-known object for PCAT Template.
/// </summary>
public const string WindowsOsTargetTemplatePcatId = "{A1943BBC-EA85-487C-97C7-C9EDE908A38A}";
/// <summary>
/// Well-known object for Firmware Boot Manager.
/// </summary>
public const string FirmwareBootManagerId = "{A5A30FA2-3D06-4E9F-B5F4-A01DF9D1FCBA}";
/// <summary>
/// Well-known object for Windows Setup RAMDISK options.
/// </summary>
public const string WindowsSetupRamdiskOptionsId = "{AE5534E0-A924-466C-B836-758539A3EE3A}";
/// <summary>
/// Well-known object for EFI template.
/// </summary>
public const string WindowsOsTargetTemplateEfiId = "{B012B84D-C47C-4ED5-B722-C0C42163E569}";
/// <summary>
/// Well-known object for Windows memory tester application.
/// </summary>
public const string WindowsMemoryTesterId = "{B2721D73-1DB4-4C62-BF78-C548A880142D}";
/// <summary>
/// Well-known object for Windows PCAT setup.
/// </summary>
public const string WindowsSetupPcatId = "{CBD971BF-B7B8-4885-951A-FA03044F5D71}";
/// <summary>
/// Alias for the current boot entry.
/// </summary>
public const string CurrentBootEntryId = "{FA926493-6F1C-4193-A414-58F0B2456D1E}";
private static Dictionary<string, Guid> s_nameToGuid;
private static Dictionary<Guid, string> s_guidToName;
private BaseStorage _storage;
private Guid _id;
private int _type;
static BcdObject()
{
s_nameToGuid = new Dictionary<string, Guid>();
s_guidToName = new Dictionary<Guid, string>();
AddMapping("{emssettings}", EmsSettingsGroupId);
AddMapping("{resumeloadersettings}", ResumeLoaderSettingsGroupId);
AddMapping("{default}", DefaultBootEntryId);
AddMapping("{dbgsettings}", DebuggerSettingsGroupId);
AddMapping("{legacy}", WindowsLegacyNtldrId);
AddMapping("{ntldr}", WindowsLegacyNtldrId);
AddMapping("{badmemory}", BadMemoryGroupId);
AddMapping("{bootloadersettings}", BootLoaderSettingsGroupId);
AddMapping("{globalsettings}", GlobalSettingsGroupId);
AddMapping("{bootmgr}", WindowsBootManagerId);
AddMapping("{fwbootmgr}", FirmwareBootManagerId);
AddMapping("{ramdiskoptions}", WindowsSetupRamdiskOptionsId);
AddMapping("{memdiag}", WindowsMemoryTesterId);
AddMapping("{current}", CurrentBootEntryId);
}
internal BcdObject(BaseStorage store, Guid id)
{
_storage = store;
_id = id;
_type = _storage.GetObjectType(id);
}
/// <summary>
/// Gets the identity of this object.
/// </summary>
public Guid Identity
{
get { return _id; }
}
/// <summary>
/// Gets the friendly name for this object, if known.
/// </summary>
public string FriendlyName
{
get
{
string name;
if (s_guidToName.TryGetValue(_id, out name))
{
return name;
}
return _id.ToString("B");
}
}
/// <summary>
/// Gets the object type for this object.
/// </summary>
public ObjectType ObjectType
{
get { return (ObjectType)((_type >> 28) & 0xF); }
}
/// <summary>
/// Gets the image type for this application.
/// </summary>
public ApplicationImageType ApplicationImageType
{
get { return IsApplication ? (ApplicationImageType)((_type & 0x00F00000) >> 20) : 0; }
}
/// <summary>
/// Gets the application type for this application.
/// </summary>
public ApplicationType ApplicationType
{
get { return IsApplication ? (ApplicationType)(_type & 0xFFFFF) : 0; }
}
/// <summary>
/// Gets the elements in this object.
/// </summary>
public IEnumerable<Element> Elements
{
get
{
foreach (var el in _storage.EnumerateElements(_id))
{
yield return new Element(_storage, _id, ApplicationType, el);
}
}
}
private bool IsApplication
{
get { return ObjectType == ObjectType.Application; }
}
/// <summary>
/// Indicates if the settings in this object are inheritable by another object.
/// </summary>
/// <param name="type">The type of the object to test for inheritability.</param>
/// <returns><c>true</c> if the settings can be inherited, else <c>false</c>.</returns>
public bool IsInheritableBy(ObjectType type)
{
if (type == ObjectType.Inherit)
{
throw new ArgumentException("Can not test inheritability by inherit objects", "type");
}
if (ObjectType != ObjectType.Inherit)
{
return false;
}
InheritType setting = (InheritType)((_type & 0x00F00000) >> 20);
return setting == InheritType.AnyObject
|| (setting == InheritType.ApplicationObjects && type == ObjectType.Application)
|| (setting == InheritType.DeviceObjects && type == ObjectType.Device);
}
/// <summary>
/// Indicates if this object has a specific element.
/// </summary>
/// <param name="id">The identity of the element to look for.</param>
/// <returns><c>true</c> if present, else <c>false</c>.</returns>
public bool HasElement(int id)
{
return _storage.HasValue(_id, id);
}
/// <summary>
/// Indicates if this object has a specific element.
/// </summary>
/// <param name="id">The identity of the element to look for.</param>
/// <returns><c>true</c> if present, else <c>false</c>.</returns>
public bool HasElement(WellKnownElement id)
{
return HasElement((int)id);
}
/// <summary>
/// Gets a specific element in this object.
/// </summary>
/// <param name="id">The identity of the element to look for.</param>
/// <returns>The element object.</returns>
public Element GetElement(int id)
{
if (HasElement(id))
{
return new Element(_storage, _id, ApplicationType, id);
}
return null;
}
/// <summary>
/// Gets a specific element in this object.
/// </summary>
/// <param name="id">The identity of the element to look for.</param>
/// <returns>The element object.</returns>
public Element GetElement(WellKnownElement id)
{
return GetElement((int)id);
}
/// <summary>
/// Adds an element in this object.
/// </summary>
/// <param name="id">The identity of the element to add.</param>
/// <param name="initialValue">The initial value of the element.</param>
/// <returns>The element object.</returns>
public Element AddElement(int id, ElementValue initialValue)
{
_storage.CreateElement(_id, id);
Element el = new Element(_storage, _id, ApplicationType, id);
el.Value = initialValue;
return el;
}
/// <summary>
/// Adds an element in this object.
/// </summary>
/// <param name="id">The identity of the element to add.</param>
/// <param name="initialValue">The initial value of the element.</param>
/// <returns>The element object.</returns>
public Element AddElement(WellKnownElement id, ElementValue initialValue)
{
return AddElement((int)id, initialValue);
}
/// <summary>
/// Removes a specific element.
/// </summary>
/// <param name="id">The element to remove.</param>
public void RemoveElement(int id)
{
_storage.DeleteElement(_id, id);
}
/// <summary>
/// Removes a specific element.
/// </summary>
/// <param name="id">The element to remove.</param>
public void RemoveElement(WellKnownElement id)
{
RemoveElement((int)id);
}
/// <summary>
/// Returns the object identity as a GUID string.
/// </summary>
/// <returns>A string representation, with surrounding curly braces.</returns>
public override string ToString()
{
return _id.ToString("B");
}
internal static int MakeApplicationType(ApplicationImageType imageType, ApplicationType appType)
{
return 0x10000000 | (((int)imageType << 20) & 0x00F00000) | ((int)appType & 0x0000FFFF);
}
internal static int MakeInheritType(InheritType inheritType)
{
return 0x20000000 | (((int)inheritType << 20) & 0x00F00000);
}
private static void AddMapping(string name, string id)
{
Guid guid = new Guid(id);
s_nameToGuid[name] = guid;
s_guidToName[guid] = name;
}
}
}
@@ -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.
//
namespace DiscUtils.BootConfig
{
internal class BooleanElementValue : ElementValue
{
private bool _value;
public BooleanElementValue(byte[] value)
{
_value = value[0] != 0;
}
public BooleanElementValue(bool value)
{
_value = value;
}
public override ElementFormat Format
{
get { return ElementFormat.Boolean; }
}
public override string ToString()
{
return _value ? "True" : "False";
}
internal byte[] GetBytes()
{
return new byte[] { (_value ? (byte)1 : (byte)0) };
}
}
}
@@ -0,0 +1,58 @@
//
// 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.BootConfig
{
using System;
using System.Text;
internal class DeviceAndPathRecord : DeviceRecord
{
private DeviceRecord _container;
private string _path;
public override int Size
{
get { throw new NotImplementedException(); }
}
public override void GetBytes(byte[] data, int offset)
{
throw new NotImplementedException();
}
public override string ToString()
{
return _container.ToString() + ":" + _path;
}
protected override void DoParse(byte[] data, int offset)
{
base.DoParse(data, offset);
_container = DeviceRecord.Parse(data, offset + 0x34);
int pathStart = 0x34 + _container.Size;
_path = Encoding.Unicode.GetString(data, offset + pathStart, Length - pathStart);
}
}
}
+114
View File
@@ -0,0 +1,114 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.BootConfig
{
using System;
using System.Globalization;
internal class DeviceElementValue : ElementValue
{
private Guid _parentObject;
private DeviceRecord _record;
public DeviceElementValue()
{
_parentObject = Guid.Empty;
PartitionRecord record = new PartitionRecord();
record.Type = 5;
_record = record;
}
public DeviceElementValue(Guid parentObject, PhysicalVolumeInfo pvi)
{
_parentObject = parentObject;
PartitionRecord record = new PartitionRecord();
record.Type = 6;
if (pvi.VolumeType == PhysicalVolumeType.BiosPartition)
{
record.PartitionType = 1;
record.DiskIdentity = new byte[4];
Utilities.WriteBytesLittleEndian(pvi.DiskSignature, record.DiskIdentity, 0);
record.PartitionIdentity = new byte[8];
Utilities.WriteBytesLittleEndian(pvi.PhysicalStartSector * 512, record.PartitionIdentity, 0);
}
else if (pvi.VolumeType == PhysicalVolumeType.GptPartition)
{
record.PartitionType = 0;
record.DiskIdentity = new byte[16];
Utilities.WriteBytesLittleEndian(pvi.DiskIdentity, record.DiskIdentity, 0);
record.PartitionIdentity = new byte[16];
Utilities.WriteBytesLittleEndian(pvi.PartitionIdentity, record.PartitionIdentity, 0);
}
else
{
throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Unknown how to convert volume type {0} to a Device element", pvi.VolumeType));
}
_record = record;
}
public DeviceElementValue(byte[] value)
{
_parentObject = Utilities.ToGuidLittleEndian(value, 0x00);
_record = DeviceRecord.Parse(value, 0x10);
}
public override Guid ParentObject
{
get { return _parentObject; }
}
public override ElementFormat Format
{
get { return ElementFormat.Device; }
}
public override string ToString()
{
if (_parentObject != Guid.Empty)
{
return _parentObject.ToString() + ":" + _record.ToString();
}
else if (_record != null)
{
return _record.ToString();
}
else
{
return "<unknown>";
}
}
internal byte[] GetBytes()
{
byte[] buffer = new byte[_record.Size + 0x10];
Utilities.WriteBytesLittleEndian(_parentObject, buffer, 0);
_record.GetBytes(buffer, 0x10);
return buffer;
}
}
}
+84
View File
@@ -0,0 +1,84 @@
//
// 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.BootConfig
{
using System;
using System.IO;
internal abstract class DeviceRecord
{
public int Type { get; set; }
public int Length { get; set; }
public abstract int Size { get; }
public static DeviceRecord Parse(byte[] data, int offset)
{
int type = Utilities.ToInt32LittleEndian(data, offset);
int length = Utilities.ToInt32LittleEndian(data, offset + 0x8);
if (offset + length > data.Length)
{
throw new InvalidDataException("Device record is truncated");
}
DeviceRecord newRecord = null;
switch (type)
{
case 0:
newRecord = new DeviceAndPathRecord();
break;
case 5: // Logical 'boot' device
case 6: // Disk partition
newRecord = new PartitionRecord();
break;
case 8: // custom:nnnnnn
break;
default:
throw new NotImplementedException("Unknown device type: " + type);
}
if (newRecord != null)
{
newRecord.DoParse(data, offset);
}
return newRecord;
}
public abstract void GetBytes(byte[] data, int offset);
protected virtual void DoParse(byte[] data, int offset)
{
Type = Utilities.ToInt32LittleEndian(data, offset);
Length = Utilities.ToInt32LittleEndian(data, offset + 0x8);
}
protected void WriteHeader(byte[] data, int offset)
{
Length = Size;
Utilities.WriteBytesLittleEndian(Type, data, offset);
Utilities.WriteBytesLittleEndian(Size, data, offset + 0x8);
}
}
}
@@ -0,0 +1,162 @@
//
// 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.BootConfig
{
using System;
using System.Collections.Generic;
using System.Globalization;
using DiscUtils.Registry;
internal class DiscUtilsRegistryStorage : BaseStorage
{
private const string ElementsPathTemplate = @"Objects\{0}\Elements";
private const string ElementPathTemplate = @"Objects\{0}\Elements\{1:X8}";
private const string ObjectTypePathTemplate = @"Objects\{0}\Description";
private const string ObjectsPath = @"Objects";
private RegistryKey _rootKey;
public DiscUtilsRegistryStorage(RegistryKey key)
{
_rootKey = key;
}
public override string GetString(Guid obj, int element)
{
return GetValue(obj, element) as string;
}
public override void SetString(Guid obj, int element, string value)
{
SetValue(obj, element, value);
}
public override byte[] GetBinary(Guid obj, int element)
{
return GetValue(obj, element) as byte[];
}
public override void SetBinary(Guid obj, int element, byte[] value)
{
SetValue(obj, element, value);
}
public override string[] GetMultiString(Guid obj, int element)
{
return GetValue(obj, element) as string[];
}
public override void SetMultiString(Guid obj, int element, string[] values)
{
SetValue(obj, element, values);
}
public override IEnumerable<Guid> EnumerateObjects()
{
RegistryKey parentKey = _rootKey.OpenSubKey("Objects");
foreach (var key in parentKey.GetSubKeyNames())
{
yield return new Guid(key);
}
}
public override IEnumerable<int> EnumerateElements(Guid obj)
{
string path = string.Format(CultureInfo.InvariantCulture, ElementsPathTemplate, obj.ToString("B"));
RegistryKey parentKey = _rootKey.OpenSubKey(path);
foreach (var key in parentKey.GetSubKeyNames())
{
yield return int.Parse(key, NumberStyles.HexNumber);
}
}
public override int GetObjectType(Guid obj)
{
string path = string.Format(CultureInfo.InvariantCulture, ObjectTypePathTemplate, obj.ToString("B"));
RegistryKey descKey = _rootKey.OpenSubKey(path);
object val = descKey.GetValue("Type");
return (int)val;
}
public override bool HasValue(Guid obj, int element)
{
string path = string.Format(CultureInfo.InvariantCulture, ElementPathTemplate, obj.ToString("B"), element);
return _rootKey.OpenSubKey(path) != null;
}
public override bool ObjectExists(Guid obj)
{
string path = string.Format(CultureInfo.InvariantCulture, ObjectTypePathTemplate, obj.ToString("B"));
return _rootKey.OpenSubKey(path) != null;
}
public override Guid CreateObject(Guid obj, int type)
{
Guid realObj = (obj == Guid.Empty) ? Guid.NewGuid() : obj;
string path = string.Format(CultureInfo.InvariantCulture, ObjectTypePathTemplate, realObj.ToString("B"));
RegistryKey key = _rootKey.CreateSubKey(path);
key.SetValue("Type", type, RegistryValueType.Dword);
return realObj;
}
public override void CreateElement(Guid obj, int element)
{
string path = string.Format(CultureInfo.InvariantCulture, ElementPathTemplate, obj.ToString("B"), element);
_rootKey.CreateSubKey(path);
}
public override void DeleteObject(Guid obj)
{
string path = string.Format(CultureInfo.InvariantCulture, ObjectTypePathTemplate, obj.ToString("B"));
_rootKey.DeleteSubKeyTree(path);
}
public override void DeleteElement(Guid obj, int element)
{
string path = string.Format(CultureInfo.InvariantCulture, ElementPathTemplate, obj.ToString("B"), element);
_rootKey.DeleteSubKeyTree(path);
}
private object GetValue(Guid obj, int element)
{
string path = string.Format(CultureInfo.InvariantCulture, ElementPathTemplate, obj.ToString("B"), element);
RegistryKey key = _rootKey.OpenSubKey(path);
return key.GetValue("Element");
}
private void SetValue(Guid obj, int element, object value)
{
string path = string.Format(CultureInfo.InvariantCulture, ElementPathTemplate, obj.ToString("B"), element);
RegistryKey key = _rootKey.OpenSubKey(path);
key.SetValue("Element", value);
}
}
}
+390
View File
@@ -0,0 +1,390 @@
//
// 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.
//
//
// Symbolic names of BCD Elements taken from Geoff Chappell's website:
// http://www.geoffchappell.com/viewer.htm?doc=notes/windows/boot/bcd/elements.htm
//
//
namespace DiscUtils.BootConfig
{
using System;
using System.Globalization;
/// <summary>
/// Represents an element in a Boot Configuration Database object.
/// </summary>
public class Element
{
private BaseStorage _storage;
private Guid _obj;
private ApplicationType _appType;
private int _identifier;
private ElementValue _value;
internal Element(BaseStorage storage, Guid obj, ApplicationType appType, int identifier)
{
_storage = storage;
_obj = obj;
_appType = appType;
_identifier = identifier;
}
/// <summary>
/// Gets the friendly name of the element, if any.
/// </summary>
public string FriendlyName
{
get
{
return "{" + IdentifierToName(_appType, _identifier) + "}";
}
}
/// <summary>
/// Gets the class of the element.
/// </summary>
public ElementClass Class
{
get { return (ElementClass)((_identifier >> 28) & 0xF); }
}
/// <summary>
/// Gets the element's format.
/// </summary>
public ElementFormat Format
{
get { return (ElementFormat)((_identifier >> 24) & 0xF); }
}
/// <summary>
/// Gets or sets the element's value.
/// </summary>
public ElementValue Value
{
get
{
if (_value == null)
{
_value = LoadValue();
}
return _value;
}
set
{
if (Format != value.Format)
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Attempt to assign {1} value to {0} format element", Format, value.Format));
}
_value = value;
WriteValue();
}
}
/// <summary>
/// Gets the element's id as a hex string.
/// </summary>
/// <returns>A hex string.</returns>
public override string ToString()
{
return _identifier.ToString("X8", CultureInfo.InvariantCulture);
}
private static string IdentifierToName(ApplicationType appType, int identifier)
{
ElementClass idClass = GetClass(identifier);
if (idClass == ElementClass.Library)
{
switch (identifier)
{
case 0x11000001: return "device";
case 0x12000002: return "path";
case 0x12000004: return "description";
case 0x12000005: return "locale";
case 0x14000006: return "inherit";
case 0x15000007: return "truncatememory";
case 0x14000008: return "recoverysequence";
case 0x16000009: return "recoveryenabled";
case 0x1700000A: return "badmemorylist";
case 0x1600000B: return "badmemoryaccess";
case 0x1500000C: return "firstmegabytepolicy";
case 0x16000010: return "bootdebug";
case 0x15000011: return "debugtype";
case 0x15000012: return "debugaddress";
case 0x15000013: return "debugport";
case 0x15000014: return "baudrate";
case 0x15000015: return "channel";
case 0x12000016: return "targetname";
case 0x16000017: return "noumex";
case 0x15000018: return "debugstart";
case 0x16000020: return "bootems";
case 0x15000022: return "emsport";
case 0x15000023: return "emsbaudrate";
case 0x12000030: return "loadoptions";
case 0x16000040: return "advancedoptions";
case 0x16000041: return "optionsedit";
case 0x15000042: return "keyringaddress";
case 0x16000046: return "graphicsmodedisabled";
case 0x15000047: return "configaccesspolicy";
case 0x16000048: return "nointegritychecks";
case 0x16000049: return "testsigning";
case 0x16000050: return "extendedinput";
case 0x15000051: return "initialconsoleinput";
}
}
else if (idClass == ElementClass.Application)
{
switch (appType)
{
case ApplicationType.FirmwareBootManager:
case ApplicationType.BootManager:
switch (identifier)
{
case 0x24000001: return "displayorder";
case 0x24000002: return "bootsequence";
case 0x23000003: return "default";
case 0x25000004: return "timeout";
case 0x26000005: return "resume";
case 0x23000006: return "resumeobject";
case 0x24000010: return "toolsdisplayorder";
case 0x26000020: return "displaybootmenu";
case 0x26000021: return "noerrordisplay";
case 0x21000022: return "bcddevice";
case 0x22000023: return "bcdfilepath";
case 0x27000030: return "customactions";
}
break;
case ApplicationType.OsLoader:
switch (identifier)
{
case 0x21000001: return "osdevice";
case 0x22000002: return "systemroot";
case 0x23000003: return "resumeobject";
case 0x26000010: return "detecthal";
case 0x22000011: return "kernel";
case 0x22000012: return "hal";
case 0x22000013: return "dbgtransport";
case 0x25000020: return "nx";
case 0x25000021: return "pae";
case 0x26000022: return "winpe";
case 0x26000024: return "nocrashautoreboot";
case 0x26000025: return "lastknowngood";
case 0x26000026: return "oslnointegritychecks";
case 0x26000027: return "osltestsigning";
case 0x26000030: return "nolowmem";
case 0x25000031: return "removememory";
case 0x25000032: return "increaseuserva";
case 0x25000033: return "perfmem";
case 0x26000040: return "vga";
case 0x26000041: return "quietboot";
case 0x26000042: return "novesa";
case 0x25000050: return "clustermodeaddressing";
case 0x26000051: return "usephysicaldestination";
case 0x25000052: return "restrictapiccluster";
case 0x26000060: return "onecpu";
case 0x25000061: return "numproc";
case 0x26000062: return "maxproc";
case 0x25000063: return "configflags";
case 0x26000070: return "usefirmwarepcisettings";
case 0x25000071: return "msi";
case 0x25000072: return "pciexpress";
case 0x25000080: return "safeboot";
case 0x26000081: return "safebootalternateshell";
case 0x26000090: return "bootlog";
case 0x26000091: return "sos";
case 0x260000A0: return "debug";
case 0x260000A1: return "halbreakpoint";
case 0x260000B0: return "ems";
case 0x250000C0: return "forcefailure";
case 0x250000C1: return "driverloadfailurepolicy";
case 0x250000E0: return "bootstatuspolicy";
}
break;
case ApplicationType.Resume:
switch (identifier)
{
case 0x21000001: return "filedevice";
case 0x22000002: return "filepath";
case 0x26000003: return "customsettings";
case 0x26000004: return "pae";
case 0x21000005: return "associatedosdevice";
case 0x26000006: return "debugoptionenabled";
}
break;
case ApplicationType.MemoryDiagnostics:
switch (identifier)
{
case 0x25000001: return "passcount";
case 0x25000002: return "testmix";
case 0x25000003: return "failurecount";
case 0x25000004: return "testtofail";
}
break;
case ApplicationType.NtLoader:
case ApplicationType.SetupLoader:
switch (identifier)
{
case 0x22000001: return "bpbstring";
}
break;
case ApplicationType.Startup:
switch (identifier)
{
case 0x26000001: return "pxesoftreboot";
case 0x22000002: return "applicationname";
}
break;
}
}
else if (idClass == ElementClass.Device)
{
switch (identifier)
{
case 0x35000001: return "ramdiskimageoffset";
case 0x35000002: return "ramdisktftpclientport";
case 0x31000003: return "ramdisksdidevice";
case 0x32000004: return "ramdisksdipath";
case 0x35000005: return "ramdiskimagelength";
case 0x36000006: return "exportascd";
case 0x35000007: return "ramdisktftpblocksize";
}
}
else if (idClass == ElementClass.Hidden)
{
switch (identifier)
{
case 0x45000001: return "devicetype";
case 0x42000002: return "apprelativepath";
case 0x42000003: return "ramdiskdevicerelativepath";
case 0x46000004: return "omitosloaderelements";
case 0x46000010: return "recoveryos";
}
}
return identifier.ToString("X8", CultureInfo.InvariantCulture);
}
private static ElementClass GetClass(int identifier)
{
return (ElementClass)((identifier >> 28) & 0xF);
}
private ElementValue LoadValue()
{
switch (Format)
{
case ElementFormat.Boolean:
return new BooleanElementValue(_storage.GetBinary(_obj, _identifier));
case ElementFormat.Device:
return new DeviceElementValue(_storage.GetBinary(_obj, _identifier));
case ElementFormat.Guid:
return new GuidElementValue(_storage.GetString(_obj, _identifier));
case ElementFormat.GuidList:
return new GuidListElementValue(_storage.GetMultiString(_obj, _identifier));
case ElementFormat.Integer:
return new IntegerElementValue(_storage.GetBinary(_obj, _identifier));
case ElementFormat.IntegerList:
return new IntegerListElementValue(_storage.GetBinary(_obj, _identifier));
case ElementFormat.String:
return new StringElementValue(_storage.GetString(_obj, _identifier));
default:
throw new NotImplementedException("Unknown element format: " + Format);
}
}
private void WriteValue()
{
switch (_value.Format)
{
case ElementFormat.Boolean:
_storage.SetBinary(_obj, _identifier, ((BooleanElementValue)_value).GetBytes());
break;
case ElementFormat.Device:
_storage.SetBinary(_obj, _identifier, ((DeviceElementValue)_value).GetBytes());
break;
case ElementFormat.GuidList:
_storage.SetMultiString(_obj, _identifier, ((GuidListElementValue)_value).GetGuidStrings());
break;
case ElementFormat.Integer:
_storage.SetBinary(_obj, _identifier, ((IntegerElementValue)_value).GetBytes());
break;
case ElementFormat.IntegerList:
_storage.SetBinary(_obj, _identifier, ((IntegerListElementValue)_value).GetBytes());
break;
case ElementFormat.Guid:
case ElementFormat.String:
_storage.SetString(_obj, _identifier, _value.ToString());
break;
default:
throw new NotImplementedException("Unknown element format: " + Format);
}
}
}
}
+55
View File
@@ -0,0 +1,55 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.BootConfig
{
/// <summary>
/// The known classes of element.
/// </summary>
public enum ElementClass
{
/// <summary>
/// Unknown class.
/// </summary>
None = 0,
/// <summary>
/// Common setting.
/// </summary>
Library = 1,
/// <summary>
/// An application setting.
/// </summary>
Application = 2,
/// <summary>
/// A device (or partition) setting.
/// </summary>
Device = 3,
/// <summary>
/// A hidden setting.
/// </summary>
Hidden = 4
}
}
+70
View 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.
//
namespace DiscUtils.BootConfig
{
/// <summary>
/// The known formats used to store BCD values.
/// </summary>
public enum ElementFormat
{
/// <summary>
/// Unknown format.
/// </summary>
None = 0,
/// <summary>
/// A block device, or partition.
/// </summary>
Device = 1,
/// <summary>
/// A unicode string.
/// </summary>
String = 2,
/// <summary>
/// A Globally Unique Identifier (GUID).
/// </summary>
Guid = 3,
/// <summary>
/// A GUID list.
/// </summary>
GuidList = 4,
/// <summary>
/// An integer.
/// </summary>
Integer = 5,
/// <summary>
/// A boolean.
/// </summary>
Boolean = 6,
/// <summary>
/// An integer list.
/// </summary>
IntegerList = 7
}
}
+138
View File
@@ -0,0 +1,138 @@
//
// 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.BootConfig
{
using System;
using System.Globalization;
/// <summary>
/// The value of an element.
/// </summary>
public abstract class ElementValue
{
/// <summary>
/// Gets the format of the value.
/// </summary>
public abstract ElementFormat Format { get; }
/// <summary>
/// Gets the parent object (only for Device values).
/// </summary>
public virtual Guid ParentObject
{
get { return Guid.Empty; }
}
/// <summary>
/// Gets a value representing a device (aka partition).
/// </summary>
/// <param name="parentObject">Object containing detailed information about the device.</param>
/// <param name="physicalVolume">The volume to represent.</param>
/// <returns>The value as an object.</returns>
public static ElementValue ForDevice(Guid parentObject, PhysicalVolumeInfo physicalVolume)
{
return new DeviceElementValue(parentObject, physicalVolume);
}
/// <summary>
/// Gets a value representing the logical boot device.
/// </summary>
/// <returns>The boot pseudo-device as an object.</returns>
public static ElementValue ForBootDevice()
{
return new DeviceElementValue();
}
/// <summary>
/// Gets a value representing a string value.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>The value as an object.</returns>
public static ElementValue ForString(string value)
{
return new StringElementValue(value);
}
/// <summary>
/// Gets a value representing an integer value.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>The value as an object.</returns>
public static ElementValue ForInteger(long value)
{
return new IntegerElementValue((ulong)value);
}
/// <summary>
/// Gets a value representing an integer list value.
/// </summary>
/// <param name="values">The value to convert.</param>
/// <returns>The value as an object.</returns>
public static ElementValue ForIntegerList(long[] values)
{
ulong[] ulValues = new ulong[values.Length];
for (int i = 0; i < values.Length; ++i)
{
ulValues[i] = (ulong)values[i];
}
return new IntegerListElementValue(ulValues);
}
/// <summary>
/// Gets a value representing a boolean value.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>The value as an object.</returns>
public static ElementValue ForBoolean(bool value)
{
return new BooleanElementValue(value);
}
/// <summary>
/// Gets a value representing a GUID value.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>The value as an object.</returns>
public static ElementValue ForGuid(Guid value)
{
return new GuidElementValue(value.ToString("B"));
}
/// <summary>
/// Gets a value representing a GUID list value.
/// </summary>
/// <param name="values">The value to convert.</param>
/// <returns>The value as an object.</returns>
public static ElementValue ForGuidList(Guid[] values)
{
string[] strValues = new string[values.Length];
for (int i = 0; i < values.Length; ++i)
{
strValues[i] = values[i].ToString("B");
}
return new GuidListElementValue(strValues);
}
}
}
+49
View 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.
//
namespace DiscUtils.BootConfig
{
internal class GuidElementValue : ElementValue
{
private string _value;
public GuidElementValue(string value)
{
_value = value;
}
public override ElementFormat Format
{
get { return ElementFormat.Guid; }
}
public override string ToString()
{
if (string.IsNullOrEmpty(_value))
{
return "<none>";
}
return _value;
}
}
}
@@ -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.BootConfig
{
internal class GuidListElementValue : ElementValue
{
private string[] _values;
public GuidListElementValue(string[] values)
{
_values = values;
}
public override ElementFormat Format
{
get { return ElementFormat.GuidList; }
}
public override string ToString()
{
if (_values == null || _values.Length == 0)
{
return "<none>";
}
string result = _values[0];
for (int i = 1; i < _values.Length; ++i)
{
result += "," + _values[i];
}
return result;
}
internal string[] GetGuidStrings()
{
return _values;
}
}
}
+54
View 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.
//
namespace DiscUtils.BootConfig
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Indicates the type of objects that can inherit from an object.
/// </summary>
public enum InheritType
{
/// <summary>
/// Undefined value.
/// </summary>
None = 0x0,
/// <summary>
/// Any type of object may inherit from this object.
/// </summary>
AnyObject = 0x1,
/// <summary>
/// Only Application objects may inherit from this object.
/// </summary>
ApplicationObjects = 0x2,
/// <summary>
/// Only Device objects may inherit from this object.
/// </summary>
DeviceObjects = 0x3
}
}
@@ -0,0 +1,63 @@
//
// 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.BootConfig
{
using System;
using System.Globalization;
internal class IntegerElementValue : ElementValue
{
private ulong _value;
public IntegerElementValue(byte[] value)
{
// Actual bytes stored may be less than 8
byte[] buffer = new byte[8];
Array.Copy(value, buffer, value.Length);
_value = Utilities.ToUInt64LittleEndian(buffer, 0);
}
public IntegerElementValue(ulong value)
{
_value = value;
}
public override ElementFormat Format
{
get { return ElementFormat.Integer; }
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}", _value);
}
internal byte[] GetBytes()
{
byte[] bytes = new byte[8];
Utilities.WriteBytesLittleEndian(_value, bytes, 0);
return bytes;
}
}
}
@@ -0,0 +1,82 @@
//
// 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.BootConfig
{
using System.Globalization;
internal class IntegerListElementValue : ElementValue
{
private ulong[] _values;
public IntegerListElementValue(byte[] value)
{
_values = new ulong[value.Length / 8];
for (int i = 0; i < _values.Length; ++i)
{
_values[i] = Utilities.ToUInt64LittleEndian(value, i * 8);
}
}
public IntegerListElementValue(ulong[] values)
{
_values = values;
}
public override ElementFormat Format
{
get { return ElementFormat.IntegerList; }
}
public override string ToString()
{
if (_values == null || _values.Length == 0)
{
return "<none>";
}
string result = string.Empty;
for (int i = 0; i < _values.Length; ++i)
{
if (i != 0)
{
result += " ";
}
result += _values[i].ToString("X16", CultureInfo.InvariantCulture);
}
return result;
}
internal byte[] GetBytes()
{
byte[] bytes = new byte[_values.Length * 8];
for (int i = 0; i < _values.Length; ++i)
{
Utilities.WriteBytesLittleEndian(_values[i], bytes, i * 8);
}
return bytes;
}
}
}
+50
View 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.BootConfig
{
/// <summary>
/// Enumeration of known object types.
/// </summary>
public enum ObjectType
{
/// <summary>
/// An unknown type.
/// </summary>
None = 0x0,
/// <summary>
/// An application object.
/// </summary>
Application = 0x1,
/// <summary>
/// Inheritable common settings.
/// </summary>
Inherit = 0x2,
/// <summary>
/// Device (or partition) object.
/// </summary>
Device = 0x3
}
}
+149
View 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.
//
namespace DiscUtils.BootConfig
{
using System;
using System.Globalization;
internal class PartitionRecord : DeviceRecord
{
public int PartitionType { get; set; }
public byte[] DiskIdentity { get; set; }
public byte[] PartitionIdentity { get; set; }
public override int Size
{
get { return 0x48; }
}
public override void GetBytes(byte[] data, int offset)
{
WriteHeader(data, offset);
if (Type == 5)
{
Array.Clear(data, offset + 0x10, 0x38);
}
else if (Type == 6)
{
Utilities.WriteBytesLittleEndian(PartitionType, data, offset + 0x24);
if (PartitionType == 1)
{
Array.Copy(DiskIdentity, 0, data, offset + 0x28, 4);
Array.Copy(PartitionIdentity, 0, data, offset + 0x10, 8);
}
else if (PartitionType == 0)
{
Array.Copy(DiskIdentity, 0, data, offset + 0x28, 16);
Array.Copy(PartitionIdentity, 0, data, offset + 0x10, 16);
}
else
{
throw new NotImplementedException("Unknown partition type: " + PartitionType);
}
}
else
{
throw new NotImplementedException("Unknown device type: " + Type);
}
}
public override string ToString()
{
if (Type == 5)
{
return "<boot device>";
}
else if (Type == 6)
{
if (PartitionType == 1)
{
return string.Format(
CultureInfo.InvariantCulture,
"(disk:{0:X2}{1:X2}{2:X2}{3:X2} part-offset:{4})",
DiskIdentity[0],
DiskIdentity[1],
DiskIdentity[2],
DiskIdentity[3],
Utilities.ToUInt64LittleEndian(PartitionIdentity, 0));
}
else
{
Guid diskGuid = Utilities.ToGuidLittleEndian(DiskIdentity, 0);
Guid partitionGuid = Utilities.ToGuidLittleEndian(PartitionIdentity, 0);
return string.Format(CultureInfo.InvariantCulture, "(disk:{0} partition:{1})", diskGuid, partitionGuid);
}
}
else if (Type == 8)
{
return "custom:<unknown>";
}
else
{
return "<unknown>";
}
}
protected override void DoParse(byte[] data, int offset)
{
base.DoParse(data, offset);
if (Type == 5)
{
// Nothing to do - just empty...
}
else if (Type == 6)
{
PartitionType = Utilities.ToInt32LittleEndian(data, offset + 0x24);
if (PartitionType == 1)
{
// BIOS disk
DiskIdentity = new byte[4];
Array.Copy(data, offset + 0x28, DiskIdentity, 0, 4);
PartitionIdentity = new byte[8];
Array.Copy(data, offset + 0x10, PartitionIdentity, 0, 8);
}
else if (PartitionType == 0)
{
// GPT disk
DiskIdentity = new byte[16];
Array.Copy(data, offset + 0x28, DiskIdentity, 0, 16);
PartitionIdentity = new byte[16];
Array.Copy(data, offset + 0x10, PartitionIdentity, 0, 16);
}
else
{
throw new NotImplementedException("Unknown partition type: " + PartitionType);
}
}
else
{
throw new NotImplementedException("Unknown device type: " + Type);
}
}
}
}
+168
View File
@@ -0,0 +1,168 @@
//
// 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.BootConfig
{
using System;
using System.Collections.Generic;
using DiscUtils.Registry;
/// <summary>
/// Represents a Boot Configuration Database store (i.e. a BCD file).
/// </summary>
public class Store
{
private BaseStorage _store;
/// <summary>
/// Initializes a new instance of the Store class.
/// </summary>
/// <param name="key">The registry key that is the root of the configuration database.</param>
public Store(RegistryKey key)
{
_store = new DiscUtilsRegistryStorage(key);
}
/// <summary>
/// Gets the objects within the store.
/// </summary>
public IEnumerable<BcdObject> Objects
{
get
{
foreach (var obj in _store.EnumerateObjects())
{
yield return new BcdObject(_store, obj);
}
}
}
/// <summary>
/// Initializes a new Boot Configuration Database.
/// </summary>
/// <param name="root">The registry key at the root of the database.</param>
/// <returns>The BCD store.</returns>
public static Store Initialize(RegistryKey root)
{
RegistryKey descKey = root.CreateSubKey("Description");
descKey.SetValue("KeyName", "BCD00000001");
root.CreateSubKey("Objects");
return new Store(root);
}
/// <summary>
/// Gets an object from the store.
/// </summary>
/// <param name="id">The identity of the object.</param>
/// <returns>The requested object, or <c>null</c>.</returns>
public BcdObject GetObject(Guid id)
{
if (_store.ObjectExists(id))
{
return new BcdObject(_store, id);
}
else
{
return null;
}
}
/// <summary>
/// Creates a specific object.
/// </summary>
/// <param name="id">The identity of the object to create.</param>
/// <param name="type">The object's type.</param>
/// <returns>The object representing the new application.</returns>
public BcdObject CreateObject(Guid id, int type)
{
_store.CreateObject(id, type);
return new BcdObject(_store, id);
}
/// <summary>
/// Creates an application object.
/// </summary>
/// <param name="imageType">The image type of the application.</param>
/// <param name="appType">The application's type.</param>
/// <returns>The object representing the new application.</returns>
public BcdObject CreateApplication(ApplicationImageType imageType, ApplicationType appType)
{
Guid obj = _store.CreateObject(Guid.NewGuid(), BcdObject.MakeApplicationType(imageType, appType));
return new BcdObject(_store, obj);
}
/// <summary>
/// Creates an application object.
/// </summary>
/// <param name="id">The identity of the object to create.</param>
/// <param name="imageType">The image type of the application.</param>
/// <param name="appType">The application's type.</param>
/// <returns>The object representing the new application.</returns>
public BcdObject CreateApplication(Guid id, ApplicationImageType imageType, ApplicationType appType)
{
Guid obj = _store.CreateObject(id, BcdObject.MakeApplicationType(imageType, appType));
return new BcdObject(_store, obj);
}
/// <summary>
/// Creates an 'inherit' object that contains common settings.
/// </summary>
/// <param name="inheritType">The type of object the settings apply to.</param>
/// <returns>The object representing the new settings.</returns>
public BcdObject CreateInherit(InheritType inheritType)
{
Guid obj = _store.CreateObject(Guid.NewGuid(), BcdObject.MakeInheritType(inheritType));
return new BcdObject(_store, obj);
}
/// <summary>
/// Creates an 'inherit' object that contains common settings.
/// </summary>
/// <param name="id">The identity of the object to create.</param>
/// <param name="inheritType">The type of object the settings apply to.</param>
/// <returns>The object representing the new settings.</returns>
public BcdObject CreateInherit(Guid id, InheritType inheritType)
{
Guid obj = _store.CreateObject(id, BcdObject.MakeInheritType(inheritType));
return new BcdObject(_store, obj);
}
/// <summary>
/// Creates a new device object, representing a partition.
/// </summary>
/// <returns>The object representing the new device.</returns>
public BcdObject CreateDevice()
{
Guid obj = _store.CreateObject(Guid.NewGuid(), 0x30000000);
return new BcdObject(_store, obj);
}
/// <summary>
/// Removes an object.
/// </summary>
/// <param name="id">The identity of the object to remove.</param>
public void RemoveObject(Guid id)
{
_store.DeleteObject(id);
}
}
}
@@ -0,0 +1,44 @@
//
// 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.BootConfig
{
internal class StringElementValue : ElementValue
{
private string _value;
public StringElementValue(string value)
{
_value = value;
}
public override ElementFormat Format
{
get { return ElementFormat.String; }
}
public override string ToString()
{
return _value;
}
}
}
+618
View File
@@ -0,0 +1,618 @@
//
// 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.
//
//
// Symbolic names of BCD Elements taken from Geoff Chappell's website:
// http://www.geoffchappell.com/viewer.htm?doc=notes/windows/boot/bcd/elements.htm
//
//
namespace DiscUtils.BootConfig
{
/// <summary>
/// Enumeration of known BCD elements.
/// </summary>
public enum WellKnownElement : int
{
/// <summary>
/// Not specified.
/// </summary>
None = 0,
/// <summary>
/// Device containing the application.
/// </summary>
LibraryApplicationDevice = 0x11000001,
/// <summary>
/// Path to the application.
/// </summary>
LibraryApplicationPath = 0x12000002,
/// <summary>
/// Description of the object.
/// </summary>
LibraryDescription = 0x12000004,
/// <summary>
/// Preferred locale of the object.
/// </summary>
LibraryPreferredLocale = 0x12000005,
/// <summary>
/// Objects containing elements inherited by the object.
/// </summary>
LibraryInheritedObjects = 0x14000006,
/// <summary>
/// Upper bound on physical addresses used by Windows.
/// </summary>
LibraryTruncatePhysicalMemory = 0x15000007,
/// <summary>
/// List of objects, indicating recovery sequence.
/// </summary>
LibraryRecoverySequence = 0x14000008,
/// <summary>
/// Enables auto recovery.
/// </summary>
LibraryAutoRecoveryEnabled = 0x16000009,
/// <summary>
/// List of bad memory regions.
/// </summary>
LibraryBadMemoryList = 0x1700000A,
/// <summary>
/// Allow use of bad memory regions.
/// </summary>
LibraryAllowBadMemoryAccess = 0x1600000B,
/// <summary>
/// Policy on use of first mega-byte of physical RAM.
/// </summary>
/// <remarks>0 = UseNone, 1 = UseAll, 2 = UsePrivate.</remarks>
LibraryFirstMegaBytePolicy = 0x1500000C,
/// <summary>
/// Debugger enabled.
/// </summary>
LibraryDebuggerEnabled = 0x16000010,
/// <summary>
/// Debugger type.
/// </summary>
/// <remarks>0 = Serial, 1 = 1394, 2 = USB.</remarks>
LibraryDebuggerType = 0x15000011,
/// <summary>
/// Debugger serial port address.
/// </summary>
LibraryDebuggerSerialAddress = 0x15000012,
/// <summary>
/// Debugger serial port.
/// </summary>
LibraryDebuggerSerialPort = 0x15000013,
/// <summary>
/// Debugger serial port baud rate.
/// </summary>
LibraryDebuggerSerialBaudRate = 0x15000014,
/// <summary>
/// Debugger 1394 channel.
/// </summary>
LibraryDebugger1394Channel = 0x15000015,
/// <summary>
/// Debugger USB target name.
/// </summary>
LibraryDebuggerUsbTargetName = 0x12000016,
/// <summary>
/// Debugger ignores user mode exceptions.
/// </summary>
LibraryDebuggerIgnoreUserModeExceptions = 0x16000017,
/// <summary>
/// Debugger start policy.
/// </summary>
/// <remarks>0 = Active, 1 = AutoEnable, 2 = Disable.</remarks>
LibraryDebuggerStartPolicy = 0x15000018,
/// <summary>
/// Emergency Management System enabled.
/// </summary>
LibraryEmergencyManagementSystemEnabled = 0x16000020,
/// <summary>
/// Emergency Management System serial port.
/// </summary>
LibraryEmergencyManagementSystemPort = 0x15000022,
/// <summary>
/// Emergency Management System baud rate.
/// </summary>
LibraryEmergencyManagementSystemBaudRate = 0x15000023,
/// <summary>
/// Load options.
/// </summary>
LibraryLoadOptions = 0x12000030,
/// <summary>
/// Displays advanced options.
/// </summary>
LibraryDisplayAdvancedOptions = 0x16000040,
/// <summary>
/// Displays UI to edit advanced options.
/// </summary>
LibraryDisplayOptionsEdit = 0x16000041,
/// <summary>
/// FVE (Full Volume Encryption - aka BitLocker?) KeyRing address.
/// </summary>
LibraryFveKeyRingAddress = 0x16000042,
/// <summary>
/// Device to contain Boot Status Log.
/// </summary>
LibraryBootStatusLogDevice = 0x11000043,
/// <summary>
/// Path to Boot Status Log.
/// </summary>
LibraryBootStatusLogFile = 0x12000044,
/// <summary>
/// Whether to append to the existing Boot Status Log.
/// </summary>
LibraryBootStatusLogAppend = 0x12000045,
/// <summary>
/// Disables graphics mode.
/// </summary>
LibraryGraphicsModeDisabled = 0x16000046,
/// <summary>
/// Configure access policy.
/// </summary>
/// <remarks>0 = default, 1 = DisallowMmConfig.</remarks>
LibraryConfigAccessPolicy = 0x15000047,
/// <summary>
/// Disables integrity checks.
/// </summary>
LibraryDisableIntegrityChecks = 0x16000048,
/// <summary>
/// Allows pre-release signatures (test signing).
/// </summary>
LibraryAllowPrereleaseSignatures = 0x16000049,
/// <summary>
/// Console extended input.
/// </summary>
LibraryConsoleExtendedInput = 0x16000050,
/// <summary>
/// Initial console input.
/// </summary>
LibraryInitialConsoleInput = 0x15000051,
/// <summary>
/// Application display order.
/// </summary>
BootMgrDisplayOrder = 0x24000001,
/// <summary>
/// Application boot sequence.
/// </summary>
BootMgrBootSequence = 0x24000002,
/// <summary>
/// Default application.
/// </summary>
BootMgrDefaultObject = 0x23000003,
/// <summary>
/// User input timeout.
/// </summary>
BootMgrTimeout = 0x25000004,
/// <summary>
/// Attempt to resume from hibernated state.
/// </summary>
BootMgrAttemptResume = 0x26000005,
/// <summary>
/// The resume application.
/// </summary>
BootMgrResumeObject = 0x23000006,
/// <summary>
/// The tools display order.
/// </summary>
BootMgrToolsDisplayOrder = 0x24000010,
/// <summary>
/// Displays the boot menu.
/// </summary>
BootMgrDisplayBootMenu = 0x26000020,
/// <summary>
/// No error display.
/// </summary>
BootMgrNoErrorDisplay = 0x26000021,
/// <summary>
/// The BCD device.
/// </summary>
BootMgrBcdDevice = 0x21000022,
/// <summary>
/// The BCD file path.
/// </summary>
BootMgrBcdFilePath = 0x22000023,
/// <summary>
/// The custom actions list.
/// </summary>
BootMgrCustomActionsList = 0x27000030,
/// <summary>
/// Device containing the Operating System.
/// </summary>
OsLoaderOsDevice = 0x21000001,
/// <summary>
/// System root on the OS device.
/// </summary>
OsLoaderSystemRoot = 0x22000002,
/// <summary>
/// The resume application associated with this OS.
/// </summary>
OsLoaderAssociatedResumeObject = 0x23000003,
/// <summary>
/// Auto-detect the correct kernel &amp; HAL.
/// </summary>
OsLoaderDetectKernelAndHal = 0x26000010,
/// <summary>
/// The filename of the kernel.
/// </summary>
OsLoaderKernelPath = 0x22000011,
/// <summary>
/// The filename of the HAL.
/// </summary>
OsLoaderHalPath = 0x22000012,
/// <summary>
/// The debug transport path.
/// </summary>
OsLoaderDebugTransportPath = 0x22000013,
/// <summary>
/// NX (No-Execute) policy.
/// </summary>
/// <remarks>0 = OptIn, 1 = OptOut, 2 = AlwaysOff, 3 = AlwaysOn.</remarks>
OsLoaderNxPolicy = 0x25000020,
/// <summary>
/// PAE policy.
/// </summary>
/// <remarks>0 = default, 1 = ForceEnable, 2 = ForceDisable.</remarks>
OsLoaderPaePolicy = 0x25000021,
/// <summary>
/// WinPE mode.
/// </summary>
OsLoaderWinPeMode = 0x26000022,
/// <summary>
/// Disable automatic reboot on OS crash.
/// </summary>
OsLoaderDisableCrashAutoReboot = 0x26000024,
/// <summary>
/// Use the last known good settings.
/// </summary>
OsLoaderUseLastGoodSettings = 0x26000025,
/// <summary>
/// Disable integrity checks.
/// </summary>
OsLoaderDisableIntegrityChecks = 0x26000026,
/// <summary>
/// Allows pre-release signatures (test signing).
/// </summary>
OsLoaderAllowPrereleaseSignatures = 0x26000027,
/// <summary>
/// Loads all executables above 4GB boundary.
/// </summary>
OsLoaderNoLowMemory = 0x26000030,
/// <summary>
/// Excludes a given amount of memory from use by Windows.
/// </summary>
OsLoaderRemoveMemory = 0x25000031,
/// <summary>
/// Increases the User Mode virtual address space.
/// </summary>
OsLoaderIncreaseUserVa = 0x25000032,
/// <summary>
/// Size of buffer (in MB) for perfomance data logging.
/// </summary>
OsLoaderPerformanceDataMemory = 0x25000033,
/// <summary>
/// Uses the VGA display driver.
/// </summary>
OsLoaderUseVgaDriver = 0x26000040,
/// <summary>
/// Quiet boot.
/// </summary>
OsLoaderDisableBootDisplay = 0x26000041,
/// <summary>
/// Disables use of the VESA BIOS.
/// </summary>
OsLoaderDisableVesaBios = 0x26000042,
/// <summary>
/// Maximum processors in a single APIC cluster.
/// </summary>
OsLoaderClusterModeAddressing = 0x25000050,
/// <summary>
/// Forces the physical APIC to be used.
/// </summary>
OsLoaderUsePhysicalDestination = 0x26000051,
/// <summary>
/// The largest APIC cluster number the system can use.
/// </summary>
OsLoaderRestrictApicCluster = 0x25000052,
/// <summary>
/// Forces only the boot processor to be used.
/// </summary>
OsLoaderUseBootProcessorOnly = 0x26000060,
/// <summary>
/// The number of processors to be used.
/// </summary>
OsLoaderNumberOfProcessors = 0x25000061,
/// <summary>
/// Use maximum number of processors.
/// </summary>
OsLoaderForceMaxProcessors = 0x26000062,
/// <summary>
/// Processor specific configuration flags.
/// </summary>
OsLoaderProcessorConfigurationFlags = 0x25000063,
/// <summary>
/// Uses BIOS-configured PCI resources.
/// </summary>
OsLoaderUseFirmwarePciSettings = 0x26000070,
/// <summary>
/// Message Signalled Interrupt setting.
/// </summary>
OsLoaderMsiPolicy = 0x25000071,
/// <summary>
/// PCE Express Policy.
/// </summary>
OsLoaderPciExpressPolicy = 0x25000072,
/// <summary>
/// The safe boot option.
/// </summary>
/// <remarks>0 = Minimal, 1 = Network, 2 = DsRepair.</remarks>
OsLoaderSafeBoot = 0x25000080,
/// <summary>
/// Loads the configured alternate shell during a safe boot.
/// </summary>
OsLoaderSafeBootAlternateShell = 0x26000081,
/// <summary>
/// Enables boot log.
/// </summary>
OsLoaderBootLogInitialization = 0x26000090,
/// <summary>
/// Displays diagnostic information during boot.
/// </summary>
OsLoaderVerboseObjectLoadMode = 0x26000091,
/// <summary>
/// Enables the kernel debugger.
/// </summary>
OsLoaderKernelDebuggerEnabled = 0x260000A0,
/// <summary>
/// Causes the kernal to halt early during boot.
/// </summary>
OsLoaderDebuggerHalBreakpoint = 0x260000A1,
/// <summary>
/// Enables Windows Emergency Management System.
/// </summary>
OsLoaderEmsEnabled = 0x260000B0,
/// <summary>
/// Forces a failure on boot.
/// </summary>
OsLoaderForceFailure = 0x250000C0,
/// <summary>
/// The OS failure policy.
/// </summary>
OsLoaderDriverLoadFailurePolicy = 0x250000C1,
/// <summary>
/// The OS boot status policy.
/// </summary>
OsLoaderBootStatusPolicy = 0x250000E0,
/// <summary>
/// The device containing the hibernation file.
/// </summary>
ResumeHiberFileDevice = 0x21000001,
/// <summary>
/// The path to the hibernation file.
/// </summary>
ResumeHiberFilePath = 0x22000002,
/// <summary>
/// Allows resume loader to use custom settings.
/// </summary>
ResumeUseCustomSettings = 0x26000003,
/// <summary>
/// PAE settings for resume application.
/// </summary>
ResumePaeMode = 0x26000004,
/// <summary>
/// An MS-DOS device with containing resume application.
/// </summary>
ResumeAssociatedDosDevice = 0x21000005,
/// <summary>
/// Enables debug option.
/// </summary>
ResumeDebugOptionEnabled = 0x26000006,
/// <summary>
/// The number of iterations to run.
/// </summary>
MemDiagPassCount = 0x25000001,
/// <summary>
/// The test mix.
/// </summary>
MemDiagTestMix = 0x25000002,
/// <summary>
/// The failure count.
/// </summary>
MemDiagFailureCount = 0x25000003,
/// <summary>
/// The tests to fail.
/// </summary>
MemDiagTestToFail = 0x25000004,
/// <summary>
/// BPB string.
/// </summary>
LoaderBpbString = 0x22000001,
/// <summary>
/// Causes a soft PXE reboot.
/// </summary>
StartupPxeSoftReboot = 0x26000001,
/// <summary>
/// PXE application name.
/// </summary>
StartupPxeApplicationName = 0x22000002,
/// <summary>
/// Offset of the RAM disk image.
/// </summary>
DeviceRamDiskImageOffset = 0x35000001,
/// <summary>
/// Client port for TFTP.
/// </summary>
DeviceRamDiskTftpClientPort = 0x35000002,
/// <summary>
/// Device containing the SDI file.
/// </summary>
DeviceRamDiskSdiDevice = 0x31000003,
/// <summary>
/// Path to the SDI file.
/// </summary>
DeviceRamDiskSdiPath = 0x32000004,
/// <summary>
/// Length of the RAM disk image.
/// </summary>
DeviceRamDiskRamDiskImageLength = 0x35000005,
/// <summary>
/// Exports the image as a CD.
/// </summary>
DeviceRamDiskExportAsCd = 0x36000006,
/// <summary>
/// The TFTP transfer block size.
/// </summary>
DeviceRamDiskTftpBlockSize = 0x35000007,
/// <summary>
/// The device type.
/// </summary>
SetupDeviceType = 0x45000001,
/// <summary>
/// The application relative path.
/// </summary>
SetupAppRelativePath = 0x42000002,
/// <summary>
/// The device relative path.
/// </summary>
SetupRamDiskDeviceRelativePath = 0x42000003,
/// <summary>
/// Omit OS loader elements.
/// </summary>
SetupOmitOsLoaderElements = 0x46000004,
/// <summary>
/// Recovery OS flag.
/// </summary>
SetupRecoveryOs = 0x46000010,
}
}
+120
View 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.
//
namespace DiscUtils
{
using System;
using System.Collections.Generic;
/// <summary>
/// Abstract base class for implementations of IBuffer.
/// </summary>
public abstract class Buffer : MarshalByRefObject, IBuffer
{
/// <summary>
/// Gets a value indicating whether this buffer can be read.
/// </summary>
public abstract bool CanRead { get; }
/// <summary>
/// Gets a value indicating whether this buffer can be modified.
/// </summary>
public abstract bool CanWrite { get; }
/// <summary>
/// Gets the current capacity of the buffer, in bytes.
/// </summary>
public abstract long Capacity { get; }
/// <summary>
/// Gets the parts of the stream that are stored.
/// </summary>
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
public virtual IEnumerable<StreamExtent> Extents
{
get
{
return GetExtentsInRange(0, Capacity);
}
}
/// <summary>
/// Reads from the buffer into a byte array.
/// </summary>
/// <param name="pos">The offset within the buffer to start reading.</param>
/// <param name="buffer">The destination byte array.</param>
/// <param name="offset">The start offset within the destination buffer.</param>
/// <param name="count">The number of bytes to read.</param>
/// <returns>The actual number of bytes read.</returns>
public abstract int Read(long pos, byte[] buffer, int offset, int count);
/// <summary>
/// Writes a byte array into the buffer.
/// </summary>
/// <param name="pos">The start offset within the buffer.</param>
/// <param name="buffer">The source byte array.</param>
/// <param name="offset">The start offset within the source byte array.</param>
/// <param name="count">The number of bytes to write.</param>
public abstract void Write(long pos, byte[] buffer, int offset, int count);
/// <summary>
/// Clears bytes from the buffer.
/// </summary>
/// <param name="pos">The start offset within the buffer.</param>
/// <param name="count">The number of bytes to clear.</param>
/// <remarks>
/// <para>Logically equivalent to writing <c>count</c> null/zero bytes to the buffer, some
/// implementations determine that some (or all) of the range indicated is not actually
/// stored. There is no direct, automatic, correspondence to clearing bytes and them
/// not being represented as an 'extent' - for example, the implementation of the underlying
/// stream may not permit fine-grained extent storage.</para>
/// <para>It is always safe to call this method to 'zero-out' a section of a buffer, regardless of
/// the underlying buffer implementation.</para>
/// </remarks>
public virtual void Clear(long pos, int count)
{
Write(pos, new byte[count], 0, count);
}
/// <summary>
/// Flushes all data to the underlying storage.
/// </summary>
/// <remarks>The default behaviour, implemented by this class, is to take no action.</remarks>
public virtual void Flush()
{
}
/// <summary>
/// Sets the capacity of the buffer, truncating if appropriate.
/// </summary>
/// <param name="value">The desired capacity of the buffer.</param>
public abstract void SetCapacity(long value);
/// <summary>
/// Gets the parts of a buffer that are stored, within a specified range.
/// </summary>
/// <param name="start">The offset of the first byte of interest.</param>
/// <param name="count">The number of bytes of interest.</param>
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
public abstract IEnumerable<StreamExtent> GetExtentsInRange(long start, long count);
}
}
+218
View File
@@ -0,0 +1,218 @@
//
// 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
{
using System.Collections.Generic;
using System.IO;
/// <summary>
/// Converts a Buffer into a Stream.
/// </summary>
public class BufferStream : SparseStream
{
private IBuffer _buffer;
private FileAccess _access;
private long _position;
/// <summary>
/// Initializes a new instance of the BufferStream class.
/// </summary>
/// <param name="buffer">The buffer to use.</param>
/// <param name="access">The access permitted to clients.</param>
public BufferStream(IBuffer buffer, FileAccess access)
{
_buffer = buffer;
_access = access;
}
/// <summary>
/// Gets an indication of whether read access is permitted.
/// </summary>
public override bool CanRead
{
get { return _access != FileAccess.Write; }
}
/// <summary>
/// Gets an indication of whether seeking is permitted.
/// </summary>
public override bool CanSeek
{
get { return true; }
}
/// <summary>
/// Gets an indication of whether write access is permitted.
/// </summary>
public override bool CanWrite
{
get { return _access != FileAccess.Read; }
}
/// <summary>
/// Gets the length of the stream (the capacity of the underlying buffer).
/// </summary>
public override long Length
{
get { return _buffer.Capacity; }
}
/// <summary>
/// Gets and sets the current position within the stream.
/// </summary>
public override long Position
{
get { return _position; }
set { _position = value; }
}
/// <summary>
/// Gets the stored extents within the sparse stream.
/// </summary>
public override IEnumerable<StreamExtent> Extents
{
get { return _buffer.Extents; }
}
/// <summary>
/// Flushes all data to the underlying storage.
/// </summary>
public override void Flush()
{
}
/// <summary>
/// Reads a number of bytes from the stream.
/// </summary>
/// <param name="buffer">The destination buffer.</param>
/// <param name="offset">The start offset within the destination buffer.</param>
/// <param name="count">The number of bytes to read.</param>
/// <returns>The number of bytes read.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (!CanRead)
{
throw new IOException("Attempt to read from write-only stream");
}
Utilities.AssertBufferParameters(buffer, offset, count);
int numRead = _buffer.Read(_position, buffer, offset, count);
_position += numRead;
return numRead;
}
/// <summary>
/// Changes the current stream position.
/// </summary>
/// <param name="offset">The origin-relative stream position.</param>
/// <param name="origin">The origin for the stream position.</param>
/// <returns>The new stream position.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
long effectiveOffset = offset;
if (origin == SeekOrigin.Current)
{
effectiveOffset += _position;
}
else if (origin == SeekOrigin.End)
{
effectiveOffset += _buffer.Capacity;
}
if (effectiveOffset < 0)
{
throw new IOException("Attempt to move before beginning of disk");
}
else
{
_position = effectiveOffset;
return _position;
}
}
/// <summary>
/// Sets the length of the stream (the underlying buffer's capacity).
/// </summary>
/// <param name="value">The new length of the stream.</param>
public override void SetLength(long value)
{
_buffer.SetCapacity(value);
}
/// <summary>
/// Writes a buffer to the stream.
/// </summary>
/// <param name="buffer">The buffer to write.</param>
/// <param name="offset">The starting offset within buffer.</param>
/// <param name="count">The number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
{
if (!CanWrite)
{
throw new IOException("Attempt to write to read-only stream");
}
Utilities.AssertBufferParameters(buffer, offset, count);
_buffer.Write(_position, buffer, offset, count);
_position += count;
}
/// <summary>
/// Clears bytes from the stream.
/// </summary>
/// <param name="count">The number of bytes (from the current position) to clear.</param>
/// <remarks>
/// <para>Logically equivalent to writing <c>count</c> null/zero bytes to the stream, some
/// implementations determine that some (or all) of the range indicated is not actually
/// stored. There is no direct, automatic, correspondence to clearing bytes and them
/// not being represented as an 'extent' - for example, the implementation of the underlying
/// stream may not permit fine-grained extent storage.</para>
/// <para>It is always safe to call this method to 'zero-out' a section of a stream, regardless of
/// the underlying stream implementation.</para>
/// </remarks>
public override void Clear(int count)
{
if (!CanWrite)
{
throw new IOException("Attempt to erase bytes in a read-only stream");
}
_buffer.Clear(_position, count);
_position += count;
}
/// <summary>
/// Gets the parts of a stream that are stored, within a specified range.
/// </summary>
/// <param name="start">The offset of the first byte of interest.</param>
/// <param name="count">The number of bytes of interest.</param>
/// <returns>An enumeration of stream extents, indicating stored bytes.</returns>
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
{
return _buffer.GetExtentsInRange(start, count);
}
}
}
+77
View File
@@ -0,0 +1,77 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils
{
using System;
internal class BuilderBufferExtent : BuilderExtent
{
private bool _fixedBuffer;
private byte[] _buffer;
public BuilderBufferExtent(long start, long length)
: base(start, length)
{
}
public BuilderBufferExtent(long start, byte[] buffer)
: base(start, buffer.Length)
{
_fixedBuffer = true;
_buffer = buffer;
}
public override void Dispose()
{
}
internal override void PrepareForRead()
{
if (!_fixedBuffer)
{
_buffer = GetBuffer();
}
}
internal override int Read(long diskOffset, byte[] block, int offset, int count)
{
int startOffset = (int)(diskOffset - Start);
int numBytes = (int)Math.Min(Length - startOffset, count);
Array.Copy(_buffer, startOffset, block, offset, numBytes);
return numBytes;
}
internal override void DisposeReadState()
{
if (!_fixedBuffer)
{
_buffer = null;
}
}
protected virtual byte[] GetBuffer()
{
throw new NotSupportedException("Derived class should implement");
}
}
}
+69
View File
@@ -0,0 +1,69 @@
//
// 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
{
using System;
using System.Collections.Generic;
internal abstract class BuilderExtent : IDisposable
{
private long _start;
private long _length;
public BuilderExtent(long start, long length)
{
_start = start;
_length = length;
}
/// <summary>
/// Gets the parts of the stream that are stored.
/// </summary>
/// <remarks>This may be an empty enumeration if all bytes are zero.</remarks>
public virtual IEnumerable<StreamExtent> StreamExtents
{
get
{
return new StreamExtent[] { new StreamExtent(Start, Length) };
}
}
internal long Start
{
get { return _start; }
}
internal long Length
{
get { return _length; }
}
public abstract void Dispose();
internal abstract void PrepareForRead();
internal abstract int Read(long diskOffset, byte[] block, int offset, int count);
internal abstract void DisposeReadState();
}
}
+74
View File
@@ -0,0 +1,74 @@
//
// 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
{
using System;
using System.Collections.Generic;
using System.Text;
internal class BuilderSparseStreamExtent : BuilderExtent
{
private SparseStream _stream;
private Ownership _ownership;
public BuilderSparseStreamExtent(long start, SparseStream stream)
: this(start, stream, Ownership.None)
{
}
public BuilderSparseStreamExtent(long start, SparseStream stream, Ownership ownership)
: base(start, stream.Length)
{
_stream = stream;
_ownership = ownership;
}
public override IEnumerable<StreamExtent> StreamExtents
{
get { return StreamExtent.Offset(_stream.Extents, Start); }
}
public override void Dispose()
{
if (_stream != null && _ownership == Ownership.Dispose)
{
_stream.Dispose();
_stream = null;
}
}
internal override void PrepareForRead()
{
}
internal override int Read(long diskOffset, byte[] block, int offset, int count)
{
_stream.Position = diskOffset - Start;
return _stream.Read(block, offset, count);
}
internal override void DisposeReadState()
{
}
}
}
+327
View File
@@ -0,0 +1,327 @@
//
// 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
{
using System;
using System.Collections.Generic;
using System.IO;
internal class BuiltStream : SparseStream
{
private Stream _baseStream;
private long _length;
private List<BuilderExtent> _extents;
private BuilderExtent _currentExtent;
private long _position;
public BuiltStream(long length, List<BuilderExtent> extents)
{
_baseStream = new ZeroStream(length);
_length = length;
_extents = extents;
// Make sure the extents are sorted, so binary searches will work.
_extents.Sort(new ExtentStartComparer());
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return _length; }
}
public override long Position
{
get { return _position; }
set { _position = value; }
}
public override IEnumerable<StreamExtent> Extents
{
get
{
foreach (var extent in _extents)
{
foreach (var streamExtent in extent.StreamExtents)
{
yield return streamExtent;
}
}
}
}
public override void Flush()
{
return;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (_position >= _length)
{
return 0;
}
int totalRead = 0;
while (totalRead < count && _position < _length)
{
// If current region is outside the area of interest, clean it up
if (_currentExtent != null && (_position < _currentExtent.Start || _position >= _currentExtent.Start + _currentExtent.Length))
{
_currentExtent.DisposeReadState();
_currentExtent = null;
}
// If we need to find a new region, look for it
if (_currentExtent == null)
{
using (SearchExtent searchExtent = new SearchExtent(_position))
{
int idx = _extents.BinarySearch(searchExtent, new ExtentRangeComparer());
if (idx >= 0)
{
BuilderExtent extent = _extents[idx];
extent.PrepareForRead();
_currentExtent = extent;
}
}
}
int numRead = 0;
// If the block is outside any known extent, defer to base stream.
if (_currentExtent == null)
{
_baseStream.Position = _position;
BuilderExtent nextExtent = FindNext(_position);
if (nextExtent != null)
{
numRead = _baseStream.Read(buffer, offset + totalRead, (int)Math.Min(count - totalRead, nextExtent.Start - _position));
}
else
{
numRead = _baseStream.Read(buffer, offset + totalRead, count - totalRead);
}
}
else
{
numRead = _currentExtent.Read(_position, buffer, offset + totalRead, count - totalRead);
}
_position += numRead;
totalRead += numRead;
}
return totalRead;
}
public override long Seek(long offset, SeekOrigin origin)
{
long newPos = offset;
if (origin == SeekOrigin.Current)
{
newPos += _position;
}
else if (origin == SeekOrigin.End)
{
newPos += _length;
}
_position = newPos;
return newPos;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_currentExtent != null)
{
_currentExtent.DisposeReadState();
_currentExtent = null;
}
if (_baseStream != null)
{
_baseStream.Dispose();
_baseStream = null;
}
}
}
finally
{
base.Dispose(disposing);
}
}
private BuilderExtent FindNext(long pos)
{
int min = 0;
int max = _extents.Count - 1;
if (_extents.Count == 0 || (_extents[_extents.Count - 1].Start + _extents[_extents.Count - 1].Length) <= pos)
{
return null;
}
while (true)
{
if (min >= max)
{
return _extents[min];
}
int mid = (max + min) / 2;
if (_extents[mid].Start < pos)
{
min = mid + 1;
}
else if (_extents[mid].Start > pos)
{
max = mid;
}
else
{
return _extents[mid];
}
}
}
private class SearchExtent : BuilderExtent
{
public SearchExtent(long pos)
: base(pos, 1)
{
}
public override void Dispose()
{
}
internal override void PrepareForRead()
{
// Not valid to use this 'dummy' extent for actual construction
throw new NotSupportedException();
}
internal override int Read(long diskOffset, byte[] block, int offset, int count)
{
// Not valid to use this 'dummy' extent for actual construction
throw new NotSupportedException();
}
internal override void DisposeReadState()
{
// Not valid to use this 'dummy' extent for actual construction
throw new NotSupportedException();
}
}
private class ExtentRangeComparer : IComparer<BuilderExtent>
{
public int Compare(BuilderExtent x, BuilderExtent y)
{
if (x == null)
{
throw new ArgumentNullException("x");
}
if (y == null)
{
throw new ArgumentNullException("y");
}
if (x.Start + x.Length <= y.Start)
{
// x < y, with no intersection
return -1;
}
else if (x.Start >= y.Start + y.Length)
{
// x > y, with no intersection
return 1;
}
// x intersects y
return 0;
}
}
private class ExtentStartComparer : IComparer<BuilderExtent>
{
public int Compare(BuilderExtent x, BuilderExtent y)
{
if (x == null)
{
throw new ArgumentNullException("x");
}
if (y == null)
{
throw new ArgumentNullException("y");
}
long val = x.Start - y.Start;
if (val < 0)
{
return -1;
}
else if (val > 0)
{
return 1;
}
else
{
return 0;
}
}
}
}
}
+112
View File
@@ -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.
//
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);
private int _cylinder;
private int _head;
private int _sector;
/// <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 { return _cylinder; }
}
/// <summary>
/// Gets the head (zero-based).
/// </summary>
public int Head
{
get { return _head; }
}
/// <summary>
/// Gets the sector number (one-based).
/// </summary>
public int Sector
{
get { return _sector; }
}
/// <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 + ")";
}
}
}
+128
View File
@@ -0,0 +1,128 @@
//
// 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
{
using System;
using System.Collections.Generic;
using System.Text;
/// <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,
}
/// <summary>
/// Class that identifies the role of each cluster in a file system.
/// </summary>
public sealed class ClusterMap
{
private ClusterRoles[] _clusterToRole;
private object[] _clusterToFileId;
private 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;
}
else
{
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];
}
else
{
return new string[0];
}
}
}
}
+93
View 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.
//
namespace DiscUtils.Compression
{
using System;
/// <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("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Offset outside of array bounds", "offset");
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentException("Array index out of bounds", "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;
}
}
}
}
+144
View 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.
//
//
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
//
namespace DiscUtils.Compression
{
using System.IO;
internal class BZip2BlockDecoder
{
private InverseBurrowsWheeler _inverseBurrowsWheeler;
private uint _crc;
public BZip2BlockDecoder(int blockSize)
{
_inverseBurrowsWheeler = new InverseBurrowsWheeler(blockSize);
}
public uint Crc
{
get { return _crc; }
}
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,135 @@
//
// 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.
//
namespace DiscUtils.Compression
{
using System.IO;
/// <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 BitStream _bitstream;
private byte[] _selectors;
private HuffmanTree[] _trees;
private HuffmanTree _activeTree;
private int _symbolsToNextSelector;
private int _nextSelector;
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;
}
}
}
+349
View File
@@ -0,0 +1,349 @@
//
// 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.
//
namespace DiscUtils.Compression
{
using System;
using System.IO;
/// <summary>
/// Implementation of a BZip2 decoder.
/// </summary>
public sealed class BZip2DecoderStream : Stream
{
private long _position;
private Stream _compressedStream;
private Ownership _ownsCompressed;
private BitStream _bitstream;
private BZip2RleStream _rleStream;
private BZip2BlockDecoder _blockDecoder;
private Crc32 _calcBlockCrc;
private byte[] _blockBuffer;
private uint _blockCrc;
private uint _compoundCrc;
private uint _calcCompoundCrc;
private bool _eof;
/// <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("buffer");
}
if (buffer.Length < offset + count)
{
throw new ArgumentException("Buffer smaller than declared");
}
if (offset < 0)
{
throw new ArgumentException("Offset less than zero", "offset");
}
if (count < 0)
{
throw new ArgumentException("Count less than zero", "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;
}
else if (marker == 0x177245385090)
{
_compoundCrc = ReadUint();
return 0;
}
else
{
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
View 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.
//
namespace DiscUtils.Compression
{
using System;
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;
}
}
}
+161
View File
@@ -0,0 +1,161 @@
//
// 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.
//
namespace DiscUtils.Compression
{
using System;
using System.IO;
internal class BZip2RleStream : Stream
{
private long _position;
private byte[] _blockBuffer;
private int _blockOffset;
private int _blockRemaining;
private int _numSame;
private byte _lastByte;
private int _runBytesOutstanding;
public BZip2RleStream()
{
}
public bool AtEof
{
get { return _runBytesOutstanding == 0 && _blockRemaining == 0; }
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { return _position; }
set { throw new NotSupportedException(); }
}
public void Reset(byte[] buffer, int offset, int count)
{
_position = 0;
_blockBuffer = buffer;
_blockOffset = offset;
_blockRemaining = count;
_numSame = -1;
_lastByte = 0;
_runBytesOutstanding = 0;
}
public override void Flush()
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
int numRead = 0;
while (numRead < count && _runBytesOutstanding > 0)
{
int runCount = Math.Min(_runBytesOutstanding, count);
for (int i = 0; i < runCount; ++i)
{
buffer[offset + numRead] = _lastByte;
}
_runBytesOutstanding -= runCount;
numRead += runCount;
}
while (numRead < count && _blockRemaining > 0)
{
byte b = _blockBuffer[_blockOffset];
++_blockOffset;
--_blockRemaining;
if (_numSame == 4)
{
int runCount = Math.Min(b, count - numRead);
for (int i = 0; i < runCount; ++i)
{
buffer[offset + numRead] = _lastByte;
numRead++;
}
_runBytesOutstanding = b - runCount;
_numSame = 0;
}
else
{
if (b != _lastByte || _numSame <= 0)
{
_lastByte = b;
_numSame = 0;
}
buffer[offset + numRead] = b;
numRead++;
_numSame++;
}
}
_position += numRead;
return numRead;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}
@@ -0,0 +1,95 @@
//
// 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
{
using System.IO;
/// <summary>
/// Converts a byte stream into a bit stream.
/// </summary>
internal class BigEndianBitStream : BitStream
{
private Stream _byteStream;
private uint _buffer;
private int _bufferAvailable;
private 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 (uint)((_buffer >> _bufferAvailable) & mask);
}
public override uint Peek(int count)
{
EnsureBufferFilled();
uint mask = (uint)((1 << count) - 1);
return (uint)((_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 = (uint)((uint)(_buffer << 16) | (uint)(_readBuffer[0] << 8) | (uint)_readBuffer[1]);
_bufferAvailable += 16;
}
}
}
}
+60
View 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);
}
}
+62
View File
@@ -0,0 +1,62 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.Compression
{
/// <summary>
/// Base class for block compression algorithms.
/// </summary>
public abstract class BlockCompressor
{
/// <summary>
/// Gets or sets the block size parameter to the algorithm.
/// </summary>
/// <remarks>
/// Some algorithms may use this to control both compression and decompression, others may
/// only use it to control compression. Some may ignore it entirely.
/// </remarks>
public int BlockSize { get; set; }
/// <summary>
/// Compresses some data.
/// </summary>
/// <param name="source">The uncompressed input.</param>
/// <param name="sourceOffset">Offset of the input data in <c>source</c>.</param>
/// <param name="sourceLength">The amount of uncompressed data.</param>
/// <param name="compressed">The destination for the output compressed data.</param>
/// <param name="compressedOffset">Offset for the output data in <c>compressed</c>.</param>
/// <param name="compressedLength">The maximum size of the compressed data on input, and the actual size on output.</param>
/// <returns>Indication of success, or indication the data could not compress into the requested space.</returns>
public abstract CompressionResult Compress(byte[] source, int sourceOffset, int sourceLength, byte[] compressed, int compressedOffset, ref int compressedLength);
/// <summary>
/// Decompresses some data.
/// </summary>
/// <param name="source">The compressed input.</param>
/// <param name="sourceOffset">Offset of the input data in <c>source</c>.</param>
/// <param name="sourceLength">The amount of compressed data.</param>
/// <param name="decompressed">The destination for the output decompressed data.</param>
/// <param name="decompressedOffset">Offset for the output data in <c>decompressed</c>.</param>
/// <returns>The amount of decompressed data.</returns>
public abstract int Decompress(byte[] source, int sourceOffset, int sourceLength, byte[] decompressed, int decompressedOffset);
}
}
@@ -0,0 +1,50 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.Compression
{
/// <summary>
/// Possible results of attempting to compress data.
/// </summary>
/// <remarks>
/// A compression routine <i>may</i> return <c>Compressed</c>, even if the data
/// was 'all zeros' or increased in size. The <c>AllZeros</c> and <c>Incompressible</c>
/// values are for algorithms that include special detection for these cases.
/// </remarks>
public enum CompressionResult
{
/// <summary>
/// The data compressed succesfully.
/// </summary>
Compressed,
/// <summary>
/// The data was all-zero's.
/// </summary>
AllZeros,
/// <summary>
/// The data was incompressible (could not fit into destination buffer).
/// </summary>
Incompressible
}
}
@@ -0,0 +1,70 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.Compression
{
using System;
using System.Globalization;
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);
}
}
+109
View File
@@ -0,0 +1,109 @@
//
// 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 int _numBits; // Max bits per symbol
private int _numSymbols; // Max symbols
private uint[] _lengths;
private uint[] _buffer;
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
{
return _lengths;
}
}
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.
//
namespace DiscUtils.Compression
{
using System;
internal sealed class InverseBurrowsWheeler : DataBlockTransform
{
private int[] _pointers;
private int[] _nextPos;
public InverseBurrowsWheeler(int bufferSize)
{
_pointers = new int[bufferSize];
_nextPos = new int[256];
}
public int OriginalIndex { get; set; }
protected override bool BuffersMustNotOverlap
{
get { return true; }
}
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;
}
}
}
+70
View 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.
//
namespace DiscUtils.Compression
{
internal class MoveToFront
{
private 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;
}
}
}
+237
View File
@@ -0,0 +1,237 @@
//
// 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
{
using System;
using System.IO;
using System.IO.Compression;
/// <summary>
/// Implementation of the Zlib compression algorithm.
/// </summary>
/// <remarks>Only decompression is currently implemented.</remarks>
public class ZlibStream : Stream
{
private Stream _stream;
private CompressionMode _mode;
private DeflateStream _deflateStream;
private Adler32 _adler32;
/// <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 = Utilities.ReadFully(stream, 2);
ushort header = Utilities.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];
Utilities.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>
public override void Close()
{
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 = Utilities.ReadFully(_stream, 4);
if (Utilities.ToInt32BigEndian(footerBuffer, 0) != _adler32.Value)
{
throw new InvalidDataException("Corrupt decompressed data detected");
}
}
_deflateStream.Close();
}
else
{
_deflateStream.Close();
byte[] footerBuffer = new byte[4];
Utilities.WriteBytesBigEndian(_adler32.Value, footerBuffer, 0);
_stream.Write(footerBuffer, 0, 4);
}
}
/// <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("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Offset outside of array bounds", "offset");
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentException("Array index out of bounds", "count");
}
}
}
}
+289
View File
@@ -0,0 +1,289 @@
//
// 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
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
/// <summary>
/// The concatenation of multiple streams (read-only, for now).
/// </summary>
internal class ConcatStream : SparseStream
{
private SparseStream[] _streams;
private Ownership _ownsStreams;
private bool _canWrite;
private long _position;
public ConcatStream(Ownership ownsStreams, params SparseStream[] streams)
{
_ownsStreams = ownsStreams;
_streams = streams;
// Only allow writes if all streams can be written
_canWrite = true;
foreach (var stream in streams)
{
if (!stream.CanWrite)
{
_canWrite = false;
}
}
}
public override bool CanRead
{
get
{
CheckDisposed();
return true;
}
}
public override bool CanSeek
{
get
{
CheckDisposed();
return true;
}
}
public override bool CanWrite
{
get
{
CheckDisposed();
return _canWrite;
}
}
public override long Length
{
get
{
CheckDisposed();
long length = 0;
for (int i = 0; i < _streams.Length; ++i)
{
length += _streams[i].Length;
}
return length;
}
}
public override long Position
{
get
{
CheckDisposed();
return _position;
}
set
{
CheckDisposed();
_position = value;
}
}
public override IEnumerable<StreamExtent> Extents
{
get
{
CheckDisposed();
List<StreamExtent> extents = new List<StreamExtent>();
long pos = 0;
for (int i = 0; i < _streams.Length; ++i)
{
foreach (StreamExtent extent in _streams[i].Extents)
{
extents.Add(new StreamExtent(extent.Start + pos, extent.Length));
}
pos += _streams[i].Length;
}
return extents;
}
}
public override void Flush()
{
CheckDisposed();
for (int i = 0; i < _streams.Length; ++i)
{
_streams[i].Flush();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
CheckDisposed();
int totalRead = 0;
int numRead = 0;
do
{
long activeStreamStartPos;
int activeStream = GetActiveStream(out activeStreamStartPos);
_streams[activeStream].Position = _position - activeStreamStartPos;
numRead = _streams[activeStream].Read(buffer, offset + totalRead, count - totalRead);
totalRead += numRead;
_position += numRead;
}
while (numRead != 0);
return totalRead;
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
CheckDisposed();
long effectiveOffset = offset;
if (origin == SeekOrigin.Current)
{
effectiveOffset += _position;
}
else if (origin == SeekOrigin.End)
{
effectiveOffset += Length;
}
if (effectiveOffset < 0)
{
throw new IOException("Attempt to move before beginning of disk");
}
else
{
Position = effectiveOffset;
return Position;
}
}
public override void SetLength(long value)
{
CheckDisposed();
long lastStreamOffset;
int lastStream = GetStream(Length, out lastStreamOffset);
if (value < lastStreamOffset)
{
throw new IOException(string.Format(CultureInfo.InvariantCulture, "Unable to reduce stream length to less than {0}", lastStreamOffset));
}
_streams[lastStream].SetLength(value - lastStreamOffset);
}
public override void Write(byte[] buffer, int offset, int count)
{
CheckDisposed();
int totalWritten = 0;
while (totalWritten != count)
{
// Offset of the stream = streamOffset
long streamOffset;
int streamIdx = GetActiveStream(out streamOffset);
// Offset within the stream = streamPos
long streamPos = _position - streamOffset;
_streams[streamIdx].Position = streamPos;
// Write (limited to the stream's length), except for final stream - that may be
// extendable
int numToWrite;
if (streamIdx == _streams.Length - 1)
{
numToWrite = count - totalWritten;
}
else
{
numToWrite = (int)Math.Min(count - totalWritten, _streams[streamIdx].Length - streamPos);
}
_streams[streamIdx].Write(buffer, offset + totalWritten, numToWrite);
totalWritten += numToWrite;
_position += numToWrite;
}
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing && _ownsStreams == Ownership.Dispose && _streams != null)
{
foreach (var stream in _streams)
{
stream.Dispose();
}
_streams = null;
}
}
finally
{
base.Dispose(disposing);
}
}
private int GetActiveStream(out long startPos)
{
return GetStream(_position, out startPos);
}
private int GetStream(long targetPos, out long streamStartPos)
{
// Find the stream that _position is within
streamStartPos = 0;
int focusStream = 0;
while (focusStream < _streams.Length - 1 && streamStartPos + _streams[focusStream].Length <= targetPos)
{
streamStartPos += _streams[focusStream].Length;
focusStream++;
}
return focusStream;
}
private void CheckDisposed()
{
if (_streams == null)
{
throw new ObjectDisposedException("ConcatStream");
}
}
}
}
+43
View 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 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
View 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 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
}
}
+96
View File
@@ -0,0 +1,96 @@
//
// 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>
/// 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;
}
}
}
+100
View File
@@ -0,0 +1,100 @@
//
// 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>
/// 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;
}
}
}
+186
View File
@@ -0,0 +1,186 @@
//
// 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
{
using System.IO;
/// <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<string, DiscDirectoryInfo>(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<string, DiscDirectoryInfo>(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<string, DiscFileInfo>(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<string, DiscFileInfo>(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<string, DiscFileSystemInfo>(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<string, DiscFileSystemInfo>(FileSystem.GetFileSystemEntries(Path, pattern), (p) => new DiscFileSystemInfo(FileSystem, p));
}
}
}
+205
View File
@@ -0,0 +1,205 @@
//
// 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
{
using System.IO;
/// <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 a value indicating whether the file exists.
/// </summary>
public override bool Exists
{
get { return FileSystem.FileExists(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 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);
}
}
}
+92
View File
@@ -0,0 +1,92 @@
//
// 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
{
using System;
using System.IO;
internal sealed class DiscFileLocator : FileLocator
{
private DiscFileSystem _fileSystem;
private string _basePath;
public DiscFileLocator(DiscFileSystem fileSystem, string basePath)
{
_fileSystem = fileSystem;
_basePath = basePath;
}
public override bool Exists(string fileName)
{
return _fileSystem.FileExists(Utilities.CombinePaths(_basePath, fileName));
}
public override Stream Open(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 Object.ReferenceEquals(otherDiscLocator._fileSystem, _fileSystem);
}
public override string ResolveRelativePath(string path)
{
return Utilities.ResolveRelativePath(_basePath, path);
}
}
}
+499
View File
@@ -0,0 +1,499 @@
//
// 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
{
using System;
using System.IO;
/// <summary>
/// Provides the base class for all file systems.
/// </summary>
public abstract class DiscFileSystem : MarshalByRefObject, IFileSystem, IDisposable
{
private DiscFileSystemOptions _options;
/// <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 { return _options; }
}
/// <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;
}
#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
}
}
+76
View File
@@ -0,0 +1,76 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils
{
using System;
using System.IO;
/// <summary>
/// Flags for the amount of detail to include in a report.
/// </summary>
[Flags]
public enum ReportLevels
{
/// <summary>
/// Report no information.
/// </summary>
None = 0x00,
/// <summary>
/// Report informational level items.
/// </summary>
Information = 0x01,
/// <summary>
/// Report warning level items.
/// </summary>
Warnings = 0x02,
/// <summary>
/// Report error level items.
/// </summary>
Errors = 0x04,
/// <summary>
/// Report all items.
/// </summary>
All = 0x07
}
/// <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);
}
}
+226
View File
@@ -0,0 +1,226 @@
//
// 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
{
using System;
using System.IO;
/// <summary>
/// Provides the base class for both <see cref="DiscFileInfo"/> and <see cref="DiscDirectoryInfo"/> objects.
/// </summary>
public class DiscFileSystemInfo
{
private DiscFileSystem _fileSystem;
private string _path;
internal DiscFileSystemInfo(DiscFileSystem fileSystem, string path)
{
if (path == null)
{
throw new ArgumentNullException("path");
}
_fileSystem = fileSystem;
_path = path.Trim('\\');
}
/// <summary>
/// Gets the file system the referenced file or directory exists on.
/// </summary>
public DiscFileSystem FileSystem
{
get { return _fileSystem; }
}
/// <summary>
/// Gets the name of the file or directory.
/// </summary>
public virtual string Name
{
get { return Utilities.GetFileFromPath(_path); }
}
/// <summary>
/// Gets the full path of the file or directory.
/// </summary>
public virtual string FullName
{
get { return _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 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 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 a value indicating whether the file system object exists.
/// </summary>
public virtual bool Exists
{
get { return FileSystem.Exists(_path); }
}
/// <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 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 path to the referenced file.
/// </summary>
protected string Path
{
get { return _path; }
}
/// <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 && DiscFileSystem.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();
}
}
}
+56
View 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.
//
namespace DiscUtils
{
using System;
/// <summary>
/// Common file system options.
/// </summary>
/// <remarks>Not all options are honoured by all file systems.</remarks>
public class DiscFileSystemOptions
{
private Random _rng;
/// <summary>
/// Initializes a new instance of the DiscFileSystemOptions class.
/// </summary>
/// <remarks>You shouldn't normally create a new instance. File systems will provide
/// an instance of the correct derived type.</remarks>
public 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 { return _rng; }
set { _rng = value; }
}
}
}
+144
View 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.
//
namespace DiscUtils
{
using System;
using System.Collections.Generic;
using System.Globalization;
/// <summary>
/// Base class for all disk image builders.
/// </summary>
public abstract class DiskImageBuilder
{
private static Dictionary<string, VirtualDiskFactory> s_typeMap;
private SparseStream _content;
private Geometry _geometry;
private Geometry _biosGeometry;
private GenericDiskAdapterType _adaptorType;
/// <summary>
/// Gets or sets the content for this disk, implying the size of the disk.
/// </summary>
public SparseStream Content
{
get { return _content; }
set { _content = value; }
}
/// <summary>
/// Gets or sets the geometry of this disk, will be implied from the content stream if not set.
/// </summary>
public Geometry Geometry
{
get { return _geometry; }
set { _geometry = value; }
}
/// <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 { return _biosGeometry; }
set { _biosGeometry = value; }
}
/// <summary>
/// Gets or sets the adapter type for created virtual disk, for file formats that encode this information.
/// </summary>
public virtual GenericDiskAdapterType GenericAdapterType
{
get { return _adaptorType; }
set { _adaptorType = value; }
}
/// <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 (s_typeMap == null)
{
InitializeMaps();
}
return s_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), "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 (var type in typeof(VirtualDisk).Assembly.GetTypes())
{
VirtualDiskFactoryAttribute attr = (VirtualDiskFactoryAttribute)Attribute.GetCustomAttribute(type, typeof(VirtualDiskFactoryAttribute), false);
if (attr != null)
{
VirtualDiskFactory factory = (VirtualDiskFactory)Activator.CreateInstance(type);
typeMap.Add(attr.Type, factory);
}
}
s_typeMap = typeMap;
}
}
}
+56
View 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.
//
namespace DiscUtils
{
/// <summary>
/// Describes a particular file that is a constituent part of a virtual disk.
/// </summary>
public sealed class DiskImageFileSpecification
{
private string _name;
private StreamBuilder _builder;
internal DiskImageFileSpecification(string name, StreamBuilder builder)
{
_name = name;
_builder = builder;
}
/// <summary>
/// Gets name of the file.
/// </summary>
public string Name
{
get { return _name; }
}
/// <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();
}
}
}
+94
View 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.Fat
{
using System;
using System.IO;
internal sealed class ClusterReader
{
private Stream _stream;
private int _firstDataSector;
private int _sectorsPerCluster;
private int _bytesPerSector;
/// <summary>
/// Pre-calculated value because of number of uses of this externally.
/// </summary>
private int _clusterSize;
public ClusterReader(Stream stream, int firstDataSector, int sectorsPerCluster, int bytesPerSector)
{
_stream = stream;
_firstDataSector = firstDataSector;
_sectorsPerCluster = sectorsPerCluster;
_bytesPerSector = bytesPerSector;
_clusterSize = _sectorsPerCluster * _bytesPerSector;
}
public int ClusterSize
{
get { return _clusterSize; }
}
public void ReadCluster(uint cluster, byte[] buffer, int offset)
{
if (offset + ClusterSize > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset", "buffer is too small - cluster would overflow buffer");
}
uint firstSector = (uint)(((cluster - 2) * _sectorsPerCluster) + _firstDataSector);
_stream.Position = firstSector * _bytesPerSector;
if (Utilities.ReadFully(_stream, buffer, offset, _clusterSize) != _clusterSize)
{
throw new IOException("Failed to read cluster " + cluster);
}
}
internal void WriteCluster(uint cluster, byte[] buffer, int offset)
{
if (offset + ClusterSize > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset", "buffer is too small - cluster would overflow buffer");
}
uint firstSector = (uint)(((cluster - 2) * _sectorsPerCluster) + _firstDataSector);
_stream.Position = firstSector * _bytesPerSector;
_stream.Write(buffer, offset, _clusterSize);
}
internal void WipeCluster(uint cluster)
{
uint firstSector = (uint)(((cluster - 2) * _sectorsPerCluster) + _firstDataSector);
_stream.Position = firstSector * _bytesPerSector;
_stream.Write(new byte[_clusterSize], 0, _clusterSize);
}
}
}
+475
View File
@@ -0,0 +1,475 @@
//
// 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.Fat
{
using System;
using System.Collections.Generic;
using System.IO;
internal delegate void FirstClusterChangedDelegate(uint cluster);
internal class ClusterStream : Stream
{
private FileAccess _access;
private ClusterReader _reader;
private FileAllocationTable _fat;
private uint _length;
private List<uint> _knownClusters;
private long _position;
private uint _currentCluster = 0;
private byte[] _clusterBuffer;
private bool _atEOF;
internal ClusterStream(FatFileSystem fileSystem, FileAccess access, uint firstCluster, uint length)
{
_access = access;
_reader = fileSystem.ClusterReader;
_fat = fileSystem.Fat;
_length = length;
_knownClusters = new List<uint>();
if (firstCluster != 0)
{
_knownClusters.Add(firstCluster);
}
else
{
_knownClusters.Add(FatBuffer.EndOfChain);
}
if (_length == uint.MaxValue)
{
_length = DetectLength();
}
_currentCluster = uint.MaxValue;
_clusterBuffer = new byte[_reader.ClusterSize];
}
public event FirstClusterChangedDelegate FirstClusterChanged;
public override bool CanRead
{
get { return _access == FileAccess.Read || _access == FileAccess.ReadWrite; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return _access == FileAccess.ReadWrite || _access == FileAccess.Write; }
}
public override long Length
{
get { return _length; }
}
public override long Position
{
get
{
return _position;
}
set
{
if (value >= 0)
{
_position = value;
_atEOF = false;
}
else
{
throw new ArgumentOutOfRangeException("value", "Attempt to move before beginning of stream");
}
}
}
public override void Flush()
{
return;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (!CanRead)
{
throw new IOException("Attempt to read from file not opened for read");
}
if (_position > _length)
{
throw new IOException("Attempt to read beyond end of file");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", "Attempt to read negative number of bytes");
}
int target = count;
if (_length - _position < count)
{
target = (int)(_length - _position);
}
if (!TryLoadCurrentCluster())
{
if ((_position == _length || _position == DetectLength()) && !_atEOF)
{
_atEOF = true;
return 0;
}
else
{
throw new IOException("Attempt to read beyond known clusters");
}
}
int numRead = 0;
while (numRead < target)
{
int clusterOffset = (int)(_position % _reader.ClusterSize);
int toCopy = Math.Min(_reader.ClusterSize - clusterOffset, target - numRead);
Array.Copy(_clusterBuffer, clusterOffset, buffer, offset + numRead, toCopy);
// Remember how many we've read in total
numRead += toCopy;
// Increment the position
_position += toCopy;
// Abort if we've hit the end of the file
if (!TryLoadCurrentCluster())
{
break;
}
}
if (numRead == 0)
{
_atEOF = true;
}
return numRead;
}
public override long Seek(long offset, SeekOrigin origin)
{
long newPos = offset;
if (origin == SeekOrigin.Current)
{
newPos += _position;
}
else if (origin == SeekOrigin.End)
{
newPos += Length;
}
_position = newPos;
_atEOF = false;
return newPos;
}
public override void SetLength(long value)
{
long desiredNumClusters = (value + _reader.ClusterSize - 1) / _reader.ClusterSize;
long actualNumClusters = (_length + _reader.ClusterSize - 1) / _reader.ClusterSize;
if (desiredNumClusters < actualNumClusters)
{
uint cluster;
if (!TryGetClusterByPosition(value, out cluster))
{
throw new IOException("Internal state corrupt - unable to find cluster");
}
uint firstToFree = _fat.GetNext(cluster);
_fat.SetEndOfChain(cluster);
_fat.FreeChain(firstToFree);
while (_knownClusters.Count > desiredNumClusters)
{
_knownClusters.RemoveAt(_knownClusters.Count - 1);
}
_knownClusters.Add(FatBuffer.EndOfChain);
if (desiredNumClusters == 0)
{
FireFirstClusterAllocated(0);
}
}
else if (desiredNumClusters > actualNumClusters)
{
uint cluster;
while (!TryGetClusterByPosition(value, out cluster))
{
cluster = ExtendChain();
_reader.WipeCluster(cluster);
}
}
if (_length != value)
{
_length = (uint)value;
if (_position > _length)
{
_position = _length;
}
}
}
public override void Write(byte[] buffer, int offset, int count)
{
int bytesRemaining = count;
if (!CanWrite)
{
throw new IOException("Attempting to write to file not opened for writing");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", count, "Attempting to write negative number of bytes");
}
if (offset > buffer.Length || (offset + count) > buffer.Length)
{
throw new ArgumentException("Attempt to write bytes outside of the buffer");
}
// TODO: Free space check...
try
{
while (bytesRemaining > 0)
{
// Extend the stream until it encompasses _position
uint cluster;
while (!TryGetClusterByPosition(_position, out cluster))
{
cluster = ExtendChain();
_reader.WipeCluster(cluster);
}
// Fill this cluster with as much data as we can (WriteToCluster preserves existing cluster
// data, if necessary)
int numWritten = WriteToCluster(cluster, (int)(_position % _reader.ClusterSize), buffer, offset, bytesRemaining);
offset += numWritten;
bytesRemaining -= numWritten;
_position += numWritten;
}
_length = (uint)Math.Max(_length, _position);
}
finally
{
_fat.Flush();
}
_atEOF = false;
}
/// <summary>
/// Writes up to the next cluster boundary, making sure to preserve existing data in the cluster
/// that falls outside of the updated range.
/// </summary>
/// <param name="cluster">The cluster to write to.</param>
/// <param name="pos">The file position of the write (within the cluster).</param>
/// <param name="buffer">The buffer with the new data.</param>
/// <param name="offset">Offset into buffer of the first byte to write.</param>
/// <param name="count">The maximum number of bytes to write.</param>
/// <returns>The number of bytes written - either count, or the number that fit up to
/// the cluster boundary.</returns>
private int WriteToCluster(uint cluster, int pos, byte[] buffer, int offset, int count)
{
if (pos == 0 && count >= _reader.ClusterSize)
{
_currentCluster = cluster;
Array.Copy(buffer, offset, _clusterBuffer, 0, _reader.ClusterSize);
WriteCurrentCluster();
return _reader.ClusterSize;
}
else
{
// Partial cluster, so need to read existing cluster data first
LoadCluster(cluster);
int copyLength = Math.Min(count, (int)(_reader.ClusterSize - (pos % _reader.ClusterSize)));
Array.Copy(buffer, offset, _clusterBuffer, pos, copyLength);
WriteCurrentCluster();
return copyLength;
}
}
/// <summary>
/// Adds a new cluster to the end of the existing chain, by allocating a free cluster.
/// </summary>
/// <returns>The cluster allocated.</returns>
/// <remarks>This method does not initialize the data in the cluster, the caller should
/// perform a write to ensure the cluster data is in known state.</remarks>
private uint ExtendChain()
{
// Sanity check - make sure the final known cluster is the EOC marker
if (!_fat.IsEndOfChain(_knownClusters[_knownClusters.Count - 1]))
{
throw new IOException("Corrupt file system: final cluster isn't End-of-Chain");
}
uint cluster;
if (!_fat.TryGetFreeCluster(out cluster))
{
throw new IOException("Out of disk space");
}
_fat.SetEndOfChain(cluster);
if (_knownClusters.Count == 1)
{
FireFirstClusterAllocated(cluster);
}
else
{
_fat.SetNext(_knownClusters[_knownClusters.Count - 2], cluster);
}
_knownClusters[_knownClusters.Count - 1] = cluster;
_knownClusters.Add(_fat.GetNext(cluster));
return cluster;
}
private void FireFirstClusterAllocated(uint cluster)
{
if (FirstClusterChanged != null)
{
FirstClusterChanged(cluster);
}
}
private bool TryLoadCurrentCluster()
{
return TryLoadClusterByPosition(_position);
}
private bool TryLoadClusterByPosition(long pos)
{
uint cluster;
if (!TryGetClusterByPosition(pos, out cluster))
{
return false;
}
// Read the cluster, it's different to the one currently loaded
if (cluster != _currentCluster)
{
_reader.ReadCluster(cluster, _clusterBuffer, 0);
_currentCluster = cluster;
}
return true;
}
private void LoadCluster(uint cluster)
{
// Read the cluster, it's different to the one currently loaded
if (cluster != _currentCluster)
{
_reader.ReadCluster(cluster, _clusterBuffer, 0);
_currentCluster = cluster;
}
}
private void WriteCurrentCluster()
{
_reader.WriteCluster(_currentCluster, _clusterBuffer, 0);
}
private bool TryGetClusterByPosition(long pos, out uint cluster)
{
int index = (int)(pos / _reader.ClusterSize);
if (_knownClusters.Count <= index)
{
if (!TryPopulateKnownClusters(index))
{
cluster = uint.MaxValue;
return false;
}
}
// Chain is shorter than the current stream position
if (_knownClusters.Count <= index)
{
cluster = uint.MaxValue;
return false;
}
cluster = _knownClusters[(int)index];
// This is the 'special' End-of-chain cluster identifer, so the stream position
// is greater than the actual file length.
if (_fat.IsEndOfChain(cluster))
{
return false;
}
return true;
}
private bool TryPopulateKnownClusters(int index)
{
uint lastKnown = _knownClusters[_knownClusters.Count - 1];
while (!_fat.IsEndOfChain(lastKnown) && _knownClusters.Count <= index)
{
lastKnown = _fat.GetNext(lastKnown);
_knownClusters.Add(lastKnown);
}
return _knownClusters.Count > index;
}
private uint DetectLength()
{
while (!_fat.IsEndOfChain(_knownClusters[_knownClusters.Count - 1]))
{
if (!TryPopulateKnownClusters(_knownClusters.Count))
{
throw new IOException("Corrupt file stream - unable to discover end of cluster chain");
}
}
return (uint)((long)(_knownClusters.Count - 1) * (long)_reader.ClusterSize);
}
}
}
+568
View File
@@ -0,0 +1,568 @@
//
// 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.Fat
{
using System;
using System.Collections.Generic;
using System.IO;
internal class Directory : IDisposable
{
private FatFileSystem _fileSystem;
private Directory _parent;
private long _parentId;
private Stream _dirStream;
private Dictionary<long, DirectoryEntry> _entries;
private List<long> _freeEntries;
private long _endOfEntries;
private DirectoryEntry _selfEntry;
private long _selfEntryLocation;
private DirectoryEntry _parentEntry;
private long _parentEntryLocation;
internal Dictionary<string, string> LongFileNames_ShortKey = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
internal Dictionary<string, string> LongFileNames_LongKey = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Initializes a new instance of the Directory class. Use this constructor to represent non-root directories.
/// </summary>
/// <param name="parent">The parent directory.</param>
/// <param name="parentId">The identity of the entry representing this directory in the parent.</param>
internal Directory(Directory parent, long parentId)
{
_fileSystem = parent._fileSystem;
_parent = parent;
_parentId = parentId;
DirectoryEntry dirEntry = ParentsChildEntry;
_dirStream = new ClusterStream(_fileSystem, FileAccess.ReadWrite, dirEntry.FirstCluster, uint.MaxValue);
LoadEntries();
}
/// <summary>
/// Initializes a new instance of the Directory class. Use this constructor to represent the root directory.
/// </summary>
/// <param name="fileSystem">The file system.</param>
/// <param name="dirStream">The stream containing the directory info.</param>
internal Directory(FatFileSystem fileSystem, Stream dirStream)
{
_fileSystem = fileSystem;
_dirStream = dirStream;
LoadEntries();
}
private static string GetLfnChunk(byte[] buffer)
{
// see http://home.teleport.com/~brainy/lfn.htm
// NOTE: we assume ordinals are ok here.
char[] chars = new char[13];
chars[0] = (char)(256 * buffer[2] + buffer[1]);
chars[1] = (char)(256 * buffer[4] + buffer[3]);
chars[2] = (char)(256 * buffer[6] + buffer[5]);
chars[3] = (char)(256 * buffer[8] + buffer[7]);
chars[4] = (char)(256 * buffer[10] + buffer[9]);
chars[5] = (char)(256 * buffer[15] + buffer[14]);
chars[6] = (char)(256 * buffer[17] + buffer[16]);
chars[7] = (char)(256 * buffer[19] + buffer[18]);
chars[8] = (char)(256 * buffer[21] + buffer[20]);
chars[9] = (char)(256 * buffer[23] + buffer[22]);
chars[10] = (char)(256 * buffer[25] + buffer[24]);
chars[11] = (char)(256 * buffer[29] + buffer[28]);
chars[12] = (char)(256 * buffer[31] + buffer[30]);
string chunk = new string(chars);
int zero = chunk.IndexOf('\0');
return zero >= 0 ? chunk.Substring(0, zero) : chunk;
}
public FatFileSystem FileSystem
{
get { return _fileSystem; }
}
public bool IsEmpty
{
get { return _entries.Count == 0; }
}
public DirectoryEntry[] Entries
{
get { return new List<DirectoryEntry>(_entries.Values).ToArray(); }
}
#region Convenient accessors for special entries
internal DirectoryEntry ParentsChildEntry
{
get
{
if (_parent == null)
{
return new DirectoryEntry(_fileSystem.FatOptions, FileName.ParentEntryName, FatAttributes.Directory, _fileSystem.FatVariant);
}
else
{
return _parent.GetEntry(_parentId);
}
}
set
{
if (_parent != null)
{
_parent.UpdateEntry(_parentId, value);
}
}
}
internal DirectoryEntry SelfEntry
{
get
{
if (_parent == null)
{
// If we're the root directory, simulate the parent entry with a dummy record
return new DirectoryEntry(_fileSystem.FatOptions, FileName.Null, FatAttributes.Directory, _fileSystem.FatVariant);
}
else
{
return _selfEntry;
}
}
set
{
if (_selfEntryLocation >= 0)
{
_dirStream.Position = _selfEntryLocation;
value.WriteTo(_dirStream);
_selfEntry = value;
}
}
}
internal DirectoryEntry ParentEntry
{
get
{
return _parentEntry;
}
set
{
if (_parentEntryLocation < 0)
{
throw new IOException("No parent entry on disk to update");
}
_dirStream.Position = _parentEntryLocation;
value.WriteTo(_dirStream);
_parentEntry = value;
}
}
#endregion
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public DirectoryEntry[] GetDirectories()
{
List<DirectoryEntry> dirs = new List<DirectoryEntry>(_entries.Count);
foreach (DirectoryEntry dirEntry in _entries.Values)
{
if ((dirEntry.Attributes & FatAttributes.Directory) != 0)
{
dirs.Add(dirEntry);
}
}
return dirs.ToArray();
}
public DirectoryEntry[] GetFiles()
{
List<DirectoryEntry> files = new List<DirectoryEntry>(_entries.Count);
foreach (DirectoryEntry dirEntry in _entries.Values)
{
if ((dirEntry.Attributes & (FatAttributes.Directory | FatAttributes.VolumeId)) == 0)
{
files.Add(dirEntry);
}
}
return files.ToArray();
}
public DirectoryEntry GetEntry(long id)
{
return (id < 0) ? null : _entries[id];
}
public Directory GetChildDirectory(FileName name)
{
long id = FindEntry(name);
if (id < 0)
{
return null;
}
else if ((_entries[id].Attributes & FatAttributes.Directory) == 0)
{
return null;
}
else
{
return _fileSystem.GetDirectory(this, id);
}
}
internal Directory CreateChildDirectory(FileName name)
{
long id = FindEntry(name);
if (id >= 0)
{
if ((_entries[id].Attributes & FatAttributes.Directory) == 0)
{
throw new IOException("A file exists with the same name");
}
else
{
return _fileSystem.GetDirectory(this, id);
}
}
else
{
try
{
uint firstCluster;
if (!_fileSystem.Fat.TryGetFreeCluster(out firstCluster))
{
throw new IOException("Failed to allocate first cluster for new directory");
}
_fileSystem.Fat.SetEndOfChain(firstCluster);
DirectoryEntry newEntry = new DirectoryEntry(_fileSystem.FatOptions, name, FatAttributes.Directory, _fileSystem.FatVariant);
newEntry.FirstCluster = firstCluster;
newEntry.CreationTime = _fileSystem.ConvertFromUtc(DateTime.UtcNow);
newEntry.LastWriteTime = newEntry.CreationTime;
id = AddEntry(newEntry);
PopulateNewChildDirectory(newEntry);
// Rather than just creating a new instance, pull it through the fileSystem cache
// to ensure the cache model is preserved.
return _fileSystem.GetDirectory(this, id);
}
finally
{
_fileSystem.Fat.Flush();
}
}
}
internal void AttachChildDirectory(FileName name, Directory newChild)
{
long id = FindEntry(name);
if (id >= 0)
{
throw new IOException("Directory entry already exists");
}
DirectoryEntry newEntry = new DirectoryEntry(newChild.ParentsChildEntry);
newEntry.Name = name;
AddEntry(newEntry);
DirectoryEntry newParentEntry = new DirectoryEntry(SelfEntry);
newParentEntry.Name = FileName.ParentEntryName;
newChild.ParentEntry = newParentEntry;
}
internal long FindVolumeId()
{
foreach (long id in _entries.Keys)
{
DirectoryEntry focus = _entries[id];
if ((focus.Attributes & FatAttributes.VolumeId) != 0)
{
return id;
}
}
return -1;
}
internal long FindEntry(FileName name)
{
foreach (long id in _entries.Keys)
{
DirectoryEntry focus = _entries[id];
if (focus.Name == name && (focus.Attributes & FatAttributes.VolumeId) == 0)
{
return id;
}
}
return -1;
}
internal SparseStream OpenFile(FileName name, FileMode mode, FileAccess fileAccess)
{
if (mode == FileMode.Append || mode == FileMode.Truncate)
{
throw new NotImplementedException();
}
long fileId = FindEntry(name);
bool exists = fileId != -1;
if (mode == FileMode.CreateNew && exists)
{
throw new IOException("File already exists");
}
else if (mode == FileMode.Open && !exists)
{
throw new FileNotFoundException("File not found", name.GetDisplayName(_fileSystem.FatOptions.FileNameEncoding));
}
else if ((mode == FileMode.Open || mode == FileMode.OpenOrCreate || mode == FileMode.Create) && exists)
{
SparseStream stream = new FatFileStream(_fileSystem, this, fileId, fileAccess);
if (mode == FileMode.Create)
{
stream.SetLength(0);
}
HandleAccessed(false);
return stream;
}
else if ((mode == FileMode.OpenOrCreate || mode == FileMode.CreateNew || mode == FileMode.Create) && !exists)
{
// Create new file
DirectoryEntry newEntry = new DirectoryEntry(_fileSystem.FatOptions, name, FatAttributes.Archive, _fileSystem.FatVariant);
newEntry.FirstCluster = 0; // i.e. Zero-length
newEntry.CreationTime = _fileSystem.ConvertFromUtc(DateTime.UtcNow);
newEntry.LastWriteTime = newEntry.CreationTime;
fileId = AddEntry(newEntry);
return new FatFileStream(_fileSystem, this, fileId, fileAccess);
}
else
{
// Should never get here...
throw new NotImplementedException();
}
}
internal long AddEntry(DirectoryEntry newEntry)
{
// Unlink an entry from the free list (or add to the end of the existing directory)
long pos;
if (_freeEntries.Count > 0)
{
pos = _freeEntries[0];
_freeEntries.RemoveAt(0);
}
else
{
pos = _endOfEntries;
_endOfEntries += 32;
}
// Put the new entry into it's slot
_dirStream.Position = pos;
newEntry.WriteTo(_dirStream);
// Update internal structures to reflect new entry (as if read from disk)
_entries.Add(pos, newEntry);
HandleAccessed(true);
return pos;
}
internal void DeleteEntry(long id, bool releaseContents)
{
if (id < 0)
{
throw new IOException("Attempt to delete unknown directory entry");
}
try
{
DirectoryEntry entry = _entries[id];
DirectoryEntry copy = new DirectoryEntry(entry);
copy.Name = entry.Name.Deleted();
_dirStream.Position = id;
copy.WriteTo(_dirStream);
if (releaseContents)
{
_fileSystem.Fat.FreeChain(entry.FirstCluster);
}
_entries.Remove(id);
_freeEntries.Add(id);
HandleAccessed(true);
}
finally
{
_fileSystem.Fat.Flush();
}
}
internal void UpdateEntry(long id, DirectoryEntry entry)
{
if (id < 0)
{
throw new IOException("Attempt to update unknown directory entry");
}
_dirStream.Position = id;
entry.WriteTo(_dirStream);
_entries[id] = entry;
}
private void LoadEntries()
{
_entries = new Dictionary<long, DirectoryEntry>();
_freeEntries = new List<long>();
_selfEntryLocation = -1;
_parentEntryLocation = -1;
string lfn = null; //+++
while (_dirStream.Position < _dirStream.Length)
{
long streamPos = _dirStream.Position;
DirectoryEntry entry = new DirectoryEntry(_fileSystem.FatOptions, _dirStream, _fileSystem.FatVariant);
if (entry.Attributes == (FatAttributes.ReadOnly | FatAttributes.Hidden | FatAttributes.System | FatAttributes.VolumeId))
{
// Long File Name entry
_dirStream.Position = streamPos; //+++
lfn = GetLfnChunk(Utilities.ReadFully(_dirStream, 32)) + lfn; //+++
}
else if (entry.Name.IsDeleted())
{
// E5 = Free Entry
_freeEntries.Add(streamPos);
lfn = null; //+++
}
else if (entry.Name == FileName.SelfEntryName)
{
_selfEntry = entry;
_selfEntryLocation = streamPos;
lfn = null; //+++
}
else if (entry.Name == FileName.ParentEntryName)
{
_parentEntry = entry;
_parentEntryLocation = streamPos;
lfn = null; //+++
}
else if (entry.Name.IsEndMarker())
{
// Free Entry, no more entries available
_endOfEntries = streamPos;
break;
}
else
{
if (lfn != null) //+++
{ //+++
LongFileNames_ShortKey.Add(entry.Name.GetDisplayName(_fileSystem.FatOptions.FileNameEncoding), lfn); //+++
LongFileNames_LongKey.Add(lfn, entry.Name.GetDisplayName(_fileSystem.FatOptions.FileNameEncoding)); //+++
} //+++
_entries.Add(streamPos, entry);
lfn = null; //+++
}
}
}
private void HandleAccessed(bool forWrite)
{
if (_fileSystem.CanWrite && _parent != null)
{
DateTime now = DateTime.Now;
DirectoryEntry entry = SelfEntry;
DateTime oldAccessTime = entry.LastAccessTime;
DateTime oldWriteTime = entry.LastWriteTime;
entry.LastAccessTime = now;
if (forWrite)
{
entry.LastWriteTime = now;
}
if (entry.LastAccessTime != oldAccessTime || entry.LastWriteTime != oldWriteTime)
{
SelfEntry = entry;
DirectoryEntry parentEntry = ParentsChildEntry;
parentEntry.LastAccessTime = entry.LastAccessTime;
parentEntry.LastWriteTime = entry.LastWriteTime;
ParentsChildEntry = parentEntry;
}
}
}
private void PopulateNewChildDirectory(DirectoryEntry newEntry)
{
// Populate new directory with initial (special) entries. First one is easy, just change the name!
using (ClusterStream stream = new ClusterStream(_fileSystem, FileAccess.Write, newEntry.FirstCluster, uint.MaxValue))
{
// First is the self-referencing entry...
DirectoryEntry selfEntry = new DirectoryEntry(newEntry);
selfEntry.Name = FileName.SelfEntryName;
selfEntry.WriteTo(stream);
// Second is a clone of our self entry (i.e. parent) - though dates are odd...
DirectoryEntry parentEntry = new DirectoryEntry(SelfEntry);
parentEntry.Name = FileName.ParentEntryName;
parentEntry.CreationTime = newEntry.CreationTime;
parentEntry.LastWriteTime = newEntry.LastWriteTime;
parentEntry.WriteTo(stream);
}
}
private void Dispose(bool disposing)
{
if (disposing)
{
_dirStream.Dispose();
}
}
}
}
+223
View File
@@ -0,0 +1,223 @@
//
// 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.Fat
{
using System;
using System.IO;
using System.Text;
internal class DirectoryEntry
{
private FatFileSystemOptions _options;
private FatType _fatVariant;
private FileName _name;
private byte _attr;
private byte _creationTimeTenth;
private ushort _creationTime;
private ushort _creationDate;
private ushort _lastAccessDate;
private ushort _firstClusterHi;
private ushort _lastWriteTime;
private ushort _lastWriteDate;
private ushort _firstClusterLo;
private uint _fileSize;
internal DirectoryEntry(FatFileSystemOptions options, Stream stream, FatType fatVariant)
{
_options = options;
_fatVariant = fatVariant;
byte[] buffer = Utilities.ReadFully(stream, 32);
Load(buffer, 0);
}
internal DirectoryEntry(FatFileSystemOptions options, FileName name, FatAttributes attrs, FatType fatVariant)
{
_options = options;
_fatVariant = fatVariant;
_name = name;
_attr = (byte)attrs;
}
internal DirectoryEntry(DirectoryEntry toCopy)
{
_options = toCopy._options;
_fatVariant = toCopy._fatVariant;
_name = toCopy._name;
_attr = toCopy._attr;
_creationTimeTenth = toCopy._creationTimeTenth;
_creationTime = toCopy._creationTime;
_creationDate = toCopy._creationDate;
_lastAccessDate = toCopy._lastAccessDate;
_firstClusterHi = toCopy._firstClusterHi;
_lastWriteTime = toCopy._lastWriteTime;
_firstClusterLo = toCopy._firstClusterLo;
_fileSize = toCopy._fileSize;
}
public FileName Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public FatAttributes Attributes
{
get { return (FatAttributes)_attr; }
set { _attr = (byte)value; }
}
public DateTime CreationTime
{
get { return FileTimeToDateTime(_creationDate, _creationTime, _creationTimeTenth); }
set { DateTimeToFileTime(value, out _creationDate, out _creationTime, out _creationTimeTenth); }
}
public DateTime LastAccessTime
{
get { return FileTimeToDateTime(_lastAccessDate, 0, 0); }
set { DateTimeToFileTime(value, out _lastAccessDate); }
}
public DateTime LastWriteTime
{
get { return FileTimeToDateTime(_lastWriteDate, _lastWriteTime, 0); }
set { DateTimeToFileTime(value, out _lastWriteDate, out _lastWriteTime); }
}
public int FileSize
{
get { return (int)_fileSize; }
set { _fileSize = (uint)value; }
}
public uint FirstCluster
{
get
{
if (_fatVariant == FatType.Fat32)
{
return (uint)(_firstClusterHi << 16) | _firstClusterLo;
}
else
{
return _firstClusterLo;
}
}
set
{
if (_fatVariant == FatType.Fat32)
{
_firstClusterHi = (ushort)((value >> 16) & 0xFFFF);
}
_firstClusterLo = (ushort)(value & 0xFFFF);
}
}
internal void WriteTo(Stream stream)
{
byte[] buffer = new byte[32];
_name.GetBytes(buffer, 0);
buffer[11] = _attr;
buffer[13] = _creationTimeTenth;
Utilities.WriteBytesLittleEndian((ushort)_creationTime, buffer, 14);
Utilities.WriteBytesLittleEndian((ushort)_creationDate, buffer, 16);
Utilities.WriteBytesLittleEndian((ushort)_lastAccessDate, buffer, 18);
Utilities.WriteBytesLittleEndian((ushort)_firstClusterHi, buffer, 20);
Utilities.WriteBytesLittleEndian((ushort)_lastWriteTime, buffer, 22);
Utilities.WriteBytesLittleEndian((ushort)_lastWriteDate, buffer, 24);
Utilities.WriteBytesLittleEndian((ushort)_firstClusterLo, buffer, 26);
Utilities.WriteBytesLittleEndian((uint)_fileSize, buffer, 28);
stream.Write(buffer, 0, buffer.Length);
}
private static DateTime FileTimeToDateTime(ushort date, ushort time, byte tenths)
{
if (date == 0 || date == 0xFFFF)
{
// Return Epoch - this is an invalid date
return FatFileSystem.Epoch;
}
int year = 1980 + ((date & 0xFE00) >> 9);
int month = (date & 0x01E0) >> 5;
int day = date & 0x001F;
int hour = (time & 0xF800) >> 11;
int minute = (time & 0x07E0) >> 5;
int second = ((time & 0x001F) * 2) + (tenths / 100);
int millis = (tenths % 100) * 10;
return new DateTime(year, month, day, hour, minute, second, millis);
}
private static void DateTimeToFileTime(DateTime value, out ushort date)
{
byte tenths;
ushort time;
DateTimeToFileTime(value, out date, out time, out tenths);
}
private static void DateTimeToFileTime(DateTime value, out ushort date, out ushort time)
{
byte tenths;
DateTimeToFileTime(value, out date, out time, out tenths);
}
private static void DateTimeToFileTime(DateTime value, out ushort date, out ushort time, out byte tenths)
{
if (value.Year < 1980)
{
value = FatFileSystem.Epoch;
}
date = (ushort)((((value.Year - 1980) << 9) & 0xFE00) | ((value.Month << 5) & 0x01E0) | (value.Day & 0x001F));
time = (ushort)(((value.Hour << 11) & 0xF800) | ((value.Minute << 5) & 0x07E0) | ((value.Second / 2) & 0x001F));
tenths = (byte)(((value.Second % 2) * 100) + (value.Millisecond / 10));
}
private void Load(byte[] data, int offset)
{
_name = new FileName(data, offset);
_attr = data[offset + 11];
_creationTimeTenth = data[offset + 13];
_creationTime = Utilities.ToUInt16LittleEndian(data, offset + 14);
_creationDate = Utilities.ToUInt16LittleEndian(data, offset + 16);
_lastAccessDate = Utilities.ToUInt16LittleEndian(data, offset + 18);
_firstClusterHi = Utilities.ToUInt16LittleEndian(data, offset + 20);
_lastWriteTime = Utilities.ToUInt16LittleEndian(data, offset + 22);
_lastWriteDate = Utilities.ToUInt16LittleEndian(data, offset + 24);
_firstClusterLo = Utilities.ToUInt16LittleEndian(data, offset + 26);
_fileSize = Utilities.ToUInt32LittleEndian(data, offset + 28);
}
}
}
+37
View File
@@ -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.
//
namespace DiscUtils.Fat
{
using System;
[Flags]
internal enum FatAttributes : byte
{
ReadOnly = 0x01,
Hidden = 0x02,
System = 0x04,
VolumeId = 0x08,
Directory = 0x10,
Archive = 0x20,
}
}
+262
View File
@@ -0,0 +1,262 @@
//
// 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.Fat
{
using System;
using System.Collections.Generic;
using System.IO;
internal class FatBuffer
{
/// <summary>
/// The End-of-chain marker to WRITE (SetNext). Don't use this value to test for end of chain.
/// </summary>
/// <remarks>
/// The actual end-of-chain marker bits on disk vary by FAT type, and can end ...F8 through ...FF.
/// </remarks>
public const uint EndOfChain = 0xFFFFFFFF;
/// <summary>
/// The Bad-Cluster marker to WRITE (SetNext). Don't use this value to test for bad clusters.
/// </summary>
/// <remarks>
/// The actual bad-cluster marker bits on disk vary by FAT type.
/// </remarks>
public const uint BadCluster = 0xFFFFFFF7;
/// <summary>
/// The Free-Cluster marker to WRITE (SetNext). Don't use this value to test for free clusters.
/// </summary>
/// <remarks>
/// The actual free-cluster marker bits on disk vary by FAT type.
/// </remarks>
public const uint FreeCluster = 0;
private const uint DirtyRegionSize = 512;
private FatType _type;
private byte[] _buffer;
private uint _nextFreeCandidate;
private Dictionary<uint, uint> _dirtySectors;
public FatBuffer(FatType type, byte[] buffer)
{
_type = type;
_buffer = buffer;
_dirtySectors = new Dictionary<uint, uint>();
}
internal int NumEntries
{
get
{
switch (_type)
{
case FatType.Fat12:
return (_buffer.Length / 3) * 2;
case FatType.Fat16:
return _buffer.Length / 2;
default: // FAT32
return _buffer.Length / 4;
}
}
}
internal int Size
{
get { return _buffer.Length; }
}
internal bool IsFree(uint val)
{
return val == 0;
}
internal bool IsEndOfChain(uint val)
{
switch (_type)
{
case FatType.Fat12: return (val & 0x0FFF) >= 0x0FF8;
case FatType.Fat16: return (val & 0xFFFF) >= 0xFFF8;
case FatType.Fat32: return (val & 0x0FFFFFF8) >= 0x0FFFFFF8;
default: throw new ArgumentException("Unknown FAT type");
}
}
internal bool IsBadCluster(uint val)
{
switch (_type)
{
case FatType.Fat12: return (val & 0x0FFF) == 0x0FF7;
case FatType.Fat16: return (val & 0xFFFF) == 0xFFF7;
case FatType.Fat32: return (val & 0x0FFFFFF8) == 0x0FFFFFF7;
default: throw new ArgumentException("Unknown FAT type");
}
}
internal uint GetNext(uint cluster)
{
if (_type == FatType.Fat16)
{
return Utilities.ToUInt16LittleEndian(_buffer, (int)(cluster * 2));
}
else if (_type == FatType.Fat32)
{
return Utilities.ToUInt32LittleEndian(_buffer, (int)(cluster * 4)) & 0x0FFFFFFF;
}
else
{
// FAT12
if ((cluster & 1) != 0)
{
return (uint)((Utilities.ToUInt16LittleEndian(_buffer, (int)(cluster + (cluster / 2))) >> 4) & 0x0FFF);
}
else
{
return (uint)(Utilities.ToUInt16LittleEndian(_buffer, (int)(cluster + (cluster / 2))) & 0x0FFF);
}
}
}
internal void SetEndOfChain(uint cluster)
{
SetNext(cluster, EndOfChain);
}
internal void SetBadCluster(uint cluster)
{
SetNext(cluster, BadCluster);
}
internal void SetFree(uint cluster)
{
if (cluster < _nextFreeCandidate)
{
_nextFreeCandidate = cluster;
}
SetNext(cluster, FreeCluster);
}
internal void SetNext(uint cluster, uint next)
{
if (_type == FatType.Fat16)
{
MarkDirty(cluster * 2);
Utilities.WriteBytesLittleEndian((ushort)next, _buffer, (int)(cluster * 2));
}
else if (_type == FatType.Fat32)
{
MarkDirty(cluster * 4);
uint oldVal = Utilities.ToUInt32LittleEndian(_buffer, (int)(cluster * 4));
uint newVal = (oldVal & 0xF0000000) | (next & 0x0FFFFFFF);
Utilities.WriteBytesLittleEndian((uint)newVal, _buffer, (int)(cluster * 4));
}
else
{
uint offset = cluster + (cluster / 2);
MarkDirty(offset);
MarkDirty(offset + 1); // On alternate sector boundaries, cluster info crosses two sectors
ushort maskedOldVal;
if ((cluster & 1) != 0)
{
next = next << 4;
maskedOldVal = (ushort)(Utilities.ToUInt16LittleEndian(_buffer, (int)offset) & 0x000F);
}
else
{
next = next & 0x0FFF;
maskedOldVal = (ushort)(Utilities.ToUInt16LittleEndian(_buffer, (int)offset) & 0xF000);
}
ushort newVal = (ushort)(maskedOldVal | next);
Utilities.WriteBytesLittleEndian(newVal, _buffer, (int)offset);
}
}
internal bool TryGetFreeCluster(out uint cluster)
{
// Simple scan - don't hold a free list...
uint numEntries = (uint)NumEntries;
for (uint i = 0; i < numEntries; i++)
{
uint candidate = (i + _nextFreeCandidate) % numEntries;
if (IsFree(GetNext(candidate)))
{
cluster = candidate;
_nextFreeCandidate = candidate + 1;
return true;
}
}
cluster = 0;
return false;
}
internal void FreeChain(uint head)
{
foreach (uint cluster in GetChain(head))
{
SetFree(cluster);
}
}
internal List<uint> GetChain(uint head)
{
List<uint> result = new List<uint>();
if (head != 0)
{
uint focus = head;
while (!IsEndOfChain(focus))
{
result.Add(focus);
focus = GetNext(focus);
}
}
return result;
}
internal void MarkDirty(uint offset)
{
_dirtySectors[offset / DirtyRegionSize] = offset / DirtyRegionSize;
}
internal void WriteDirtyRegions(Stream stream, long position)
{
foreach (uint val in _dirtySectors.Values)
{
stream.Position = position + (val * DirtyRegionSize);
stream.Write(_buffer, (int)(val * DirtyRegionSize), (int)DirtyRegionSize);
}
}
internal void ClearDirtyRegions()
{
_dirtySectors.Clear();
}
}
}
+140
View File
@@ -0,0 +1,140 @@
//
// 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.Fat
{
using System;
using System.Collections.Generic;
using System.IO;
internal class FatFileStream : SparseStream
{
private Directory _dir;
private long _dirId;
private ClusterStream _stream;
private bool didWrite = false;
public FatFileStream(FatFileSystem fileSystem, Directory dir, long fileId, FileAccess access)
{
_dir = dir;
_dirId = fileId;
DirectoryEntry dirEntry = _dir.GetEntry(_dirId);
_stream = new ClusterStream(fileSystem, access, (uint)dirEntry.FirstCluster, (uint)dirEntry.FileSize);
_stream.FirstClusterChanged += FirstClusterAllocatedHandler;
}
public override long Position
{
get { return _stream.Position; }
set { _stream.Position = value; }
}
public override bool CanRead
{
get { return _stream.CanRead; }
}
public override bool CanSeek
{
get { return _stream.CanSeek; }
}
public override bool CanWrite
{
get { return _stream.CanWrite; }
}
public override long Length
{
get { return _stream.Length; }
}
public override IEnumerable<StreamExtent> Extents
{
get
{
return new StreamExtent[] { new StreamExtent(0, Length) };
}
}
public override void Close()
{
if (_dir.FileSystem.CanWrite)
{
try
{
DateTime now = _dir.FileSystem.ConvertFromUtc(DateTime.UtcNow);
DirectoryEntry dirEntry = _dir.GetEntry(_dirId);
dirEntry.LastAccessTime = now;
if (didWrite)
{
dirEntry.FileSize = (int)_stream.Length;
dirEntry.LastWriteTime = now;
}
_dir.UpdateEntry(_dirId, dirEntry);
}
finally
{
base.Close();
}
}
}
public override void SetLength(long value)
{
didWrite = true;
_stream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
didWrite = true;
_stream.Write(buffer, offset, count);
}
public override void Flush()
{
_stream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _stream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _stream.Seek(offset, origin);
}
private void FirstClusterAllocatedHandler(uint cluster)
{
DirectoryEntry dirEntry = _dir.GetEntry(_dirId);
dirEntry.FirstCluster = cluster;
_dir.UpdateEntry(_dirId, dirEntry);
}
}
}
File diff suppressed because it is too large Load Diff
+73
View 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.
//
namespace DiscUtils.Fat
{
using System;
using System.Text;
/// <summary>
/// FAT file system options.
/// </summary>
public sealed class FatFileSystemOptions : DiscFileSystemOptions
{
private Encoding _encoding;
internal FatFileSystemOptions()
{
FileNameEncoding = Encoding.GetEncoding(437);
}
internal FatFileSystemOptions(FileSystemParameters parameters)
{
if (parameters != null && parameters.FileNameEncoding != null)
{
FileNameEncoding = parameters.FileNameEncoding;
}
else
{
FileNameEncoding = Encoding.GetEncoding(437);
}
}
/// <summary>
/// Gets or sets the character encoding used for file names.
/// </summary>
public Encoding FileNameEncoding
{
get
{
return _encoding;
}
set
{
if (!value.IsSingleByte)
{
throw new ArgumentException(value.EncodingName + " is not a single byte encoding");
}
_encoding = value;
}
}
}
}
+50
View 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.Fat
{
/// <summary>
/// Enumeration of known FAT types.
/// </summary>
public enum FatType
{
/// <summary>
/// Represents no known FAT type.
/// </summary>
None = 0,
/// <summary>
/// Represents a 12-bit FAT.
/// </summary>
Fat12 = 12,
/// <summary>
/// Represents a 16-bit FAT.
/// </summary>
Fat16 = 16,
/// <summary>
/// Represents a 32-bit FAT.
/// </summary>
Fat32 = 32
}
}
+101
View File
@@ -0,0 +1,101 @@
//
// 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.Fat
{
using System;
using System.IO;
internal class FileAllocationTable
{
private Stream _stream;
private ushort _firstFatSector;
private byte _numFats;
private FatBuffer _buffer;
public FileAllocationTable(FatType type, Stream stream, ushort firstFatSector, uint fatSize, byte numFats, byte activeFat)
{
_stream = stream;
_firstFatSector = firstFatSector;
_numFats = numFats;
_stream.Position = (firstFatSector + (fatSize * activeFat)) * Utilities.SectorSize;
_buffer = new FatBuffer(type, Utilities.ReadFully(_stream, (int)(fatSize * Utilities.SectorSize)));
}
internal bool IsFree(uint val)
{
return _buffer.IsFree(val);
}
internal bool IsEndOfChain(uint val)
{
return _buffer.IsEndOfChain(val);
}
internal bool IsBadCluster(uint val)
{
return _buffer.IsBadCluster(val);
}
internal uint GetNext(uint cluster)
{
return _buffer.GetNext(cluster);
}
internal void SetEndOfChain(uint cluster)
{
_buffer.SetEndOfChain(cluster);
}
internal void SetBadCluster(uint cluster)
{
_buffer.SetBadCluster(cluster);
}
internal void SetNext(uint cluster, uint next)
{
_buffer.SetNext(cluster, next);
}
internal void Flush()
{
for (int i = 0; i < _numFats; ++i)
{
_buffer.WriteDirtyRegions(_stream, (_firstFatSector * Utilities.SectorSize) + (_buffer.Size * i));
}
_buffer.ClearDirtyRegions();
}
internal bool TryGetFreeCluster(out uint cluster)
{
return _buffer.TryGetFreeCluster(out cluster);
}
internal void FreeChain(uint head)
{
_buffer.FreeChain(head);
}
}
}
+212
View File
@@ -0,0 +1,212 @@
//
// 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.Fat
{
using System;
using System.Text;
internal sealed class FileName : IEquatable<FileName>
{
public static readonly FileName SelfEntryName = new FileName(new byte[] { 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, 0);
public static readonly FileName ParentEntryName = new FileName(new byte[] { 0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, 0);
public static readonly FileName Null = new FileName(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0);
private const byte SpaceByte = 0x20;
private static readonly byte[] InvalidBytes = new byte[] { 0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C };
private byte[] _raw;
public FileName(byte[] data, int offset)
{
_raw = new byte[11];
Array.Copy(data, offset, _raw, 0, 11);
}
public FileName(string name, Encoding encoding)
{
_raw = new byte[11];
byte[] bytes = encoding.GetBytes(name.ToUpperInvariant());
int nameIdx = 0;
int rawIdx = 0;
while (nameIdx < bytes.Length && bytes[nameIdx] != '.' && rawIdx < _raw.Length)
{
byte b = bytes[nameIdx++];
if (b < 0x20 || Contains(InvalidBytes, b))
{
throw new ArgumentException("Invalid character in file name '" + (char)b + "'", "name");
}
_raw[rawIdx++] = b;
}
if (rawIdx > 8)
{
throw new ArgumentException("File name too long '" + name + "'", "name");
}
else if (rawIdx == 0)
{
throw new ArgumentException("File name too short '" + name + "'", "name");
}
while (rawIdx < 8)
{
_raw[rawIdx++] = SpaceByte;
}
if (nameIdx < bytes.Length && bytes[nameIdx] == '.')
{
++nameIdx;
}
while (nameIdx < bytes.Length && rawIdx < _raw.Length)
{
byte b = bytes[nameIdx++];
if (b < 0x20 || Contains(InvalidBytes, b))
{
throw new ArgumentException("Invalid character in file extension '" + (char)b + "'", "name");
}
_raw[rawIdx++] = b;
}
while (rawIdx < 11)
{
_raw[rawIdx++] = SpaceByte;
}
if (nameIdx != bytes.Length)
{
throw new ArgumentException("File extension too long '" + name + "'", "name");
}
}
public static FileName FromPath(string path, Encoding encoding)
{
return new FileName(Utilities.GetFileFromPath(path), encoding);
}
public static bool operator ==(FileName a, FileName b)
{
return CompareRawNames(a, b) == 0;
}
public static bool operator !=(FileName a, FileName b)
{
return CompareRawNames(a, b) != 0;
}
public string GetDisplayName(Encoding encoding)
{
return GetSearchName(encoding).TrimEnd('.');
}
public string GetSearchName(Encoding encoding)
{
return encoding.GetString(_raw, 0, 8).TrimEnd() + "." + encoding.GetString(_raw, 8, 3).TrimEnd();
}
public string GetRawName(Encoding encoding)
{
return encoding.GetString(_raw, 0, 11).TrimEnd();
}
public FileName Deleted()
{
byte[] data = new byte[11];
Array.Copy(_raw, data, 11);
data[0] = 0xE5;
return new FileName(data, 0);
}
public bool IsDeleted()
{
return _raw[0] == 0xE5;
}
public bool IsEndMarker()
{
return _raw[0] == 0x00;
}
public void GetBytes(byte[] data, int offset)
{
Array.Copy(_raw, 0, data, offset, 11);
}
public override bool Equals(object other)
{
return Equals(other as FileName);
}
public bool Equals(FileName other)
{
if (other == null)
{
return false;
}
return CompareRawNames(this, other) == 0;
}
public override int GetHashCode()
{
int val = 0x1A8D3C4E;
for (int i = 0; i < 11; ++i)
{
val = (val << 2) ^ _raw[i];
}
return val;
}
private static int CompareRawNames(FileName a, FileName b)
{
for (int i = 0; i < 11; ++i)
{
if (a._raw[i] != b._raw[i])
{
return (int)a._raw[i] - (int)b._raw[i];
}
}
return 0;
}
private static bool Contains(byte[] array, byte val)
{
foreach (byte b in array)
{
if (b == val)
{
return true;
}
}
return false;
}
}
}
+46
View File
@@ -0,0 +1,46 @@
//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.Fat
{
using System.IO;
using DiscUtils.Vfs;
[VfsFileSystemFactory]
internal class FileSystemFactory : VfsFileSystemFactory
{
public override DiscUtils.FileSystemInfo[] Detect(Stream stream, VolumeInfo volume)
{
if (FatFileSystem.Detect(stream))
{
return new DiscUtils.FileSystemInfo[] { new VfsFileSystemInfo("FAT", "Microsoft FAT", Open) };
}
return new DiscUtils.FileSystemInfo[0];
}
private DiscFileSystem Open(Stream stream, VolumeInfo volumeInfo, FileSystemParameters parameters)
{
return new FatFileSystem(stream, Ownership.None, parameters);
}
}
}
+61
View 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.
//
namespace DiscUtils
{
using System;
using System.IO;
internal abstract class FileLocator
{
public abstract bool Exists(string fileName);
public abstract Stream Open(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
View 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.
//
namespace DiscUtils
{
using System.IO;
/// <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 the name of the file system.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets a one-line description of the file system.
/// </summary>
public abstract string Description { 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;
}
}
}
+58
View File
@@ -0,0 +1,58 @@
//
// 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
{
using System;
using System.Text;
/// <summary>
/// Converts a time to/from UTC.
/// </summary>
/// <param name="time">The time to convert.</param>
/// <param name="toUtc"><c>true</c> to convert FAT time to UTC, <c>false</c> to convert UTC to FAT time.</param>
/// <returns>The converted time.</returns>
public delegate DateTime TimeConverter(DateTime time, bool toUtc);
/// <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; }
}
}
+45
View 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
}
}

Some files were not shown because too many files have changed in this diff Show More