mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-21 06:31:02 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// Copyright (c) 2008-2011, Kenneth Bell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION 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.Registry
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// An internal structure within registry files, bins are the major unit of allocation in a registry hive.
|
||||
/// </summary>
|
||||
/// <remarks>Bins are divided into multiple cells, that contain actual registry data.</remarks>
|
||||
internal sealed class Bin
|
||||
{
|
||||
private RegistryHive _hive;
|
||||
private Stream _fileStream;
|
||||
private long _streamPos;
|
||||
|
||||
private BinHeader _header;
|
||||
private byte[] _buffer;
|
||||
|
||||
private List<Range<int, int>> _freeCells;
|
||||
|
||||
public Bin(RegistryHive hive, Stream stream)
|
||||
{
|
||||
_hive = hive;
|
||||
_fileStream = stream;
|
||||
_streamPos = stream.Position;
|
||||
|
||||
stream.Position = _streamPos;
|
||||
byte[] buffer = Utilities.ReadFully(stream, 0x20);
|
||||
_header = new BinHeader();
|
||||
_header.ReadFrom(buffer, 0);
|
||||
|
||||
_fileStream.Position = _streamPos;
|
||||
_buffer = Utilities.ReadFully(_fileStream, _header.BinSize);
|
||||
|
||||
// Gather list of all free cells.
|
||||
_freeCells = new List<Range<int, int>>();
|
||||
int pos = 0x20;
|
||||
while (pos < _buffer.Length)
|
||||
{
|
||||
int size = Utilities.ToInt32LittleEndian(_buffer, pos);
|
||||
if (size > 0)
|
||||
{
|
||||
_freeCells.Add(new Range<int, int>(pos, size));
|
||||
}
|
||||
|
||||
pos += Math.Abs(size);
|
||||
}
|
||||
}
|
||||
|
||||
public Cell TryGetCell(int index)
|
||||
{
|
||||
int size = Utilities.ToInt32LittleEndian(_buffer, index - _header.FileOffset);
|
||||
if (size >= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Cell.Parse(_hive, index, _buffer, index + 4 - _header.FileOffset);
|
||||
}
|
||||
|
||||
public void FreeCell(int index)
|
||||
{
|
||||
int freeIndex = index - _header.FileOffset;
|
||||
|
||||
int len = Utilities.ToInt32LittleEndian(_buffer, freeIndex);
|
||||
if (len >= 0)
|
||||
{
|
||||
throw new ArgumentException("Attempt to free non-allocated cell");
|
||||
}
|
||||
|
||||
len = Math.Abs(len);
|
||||
|
||||
// If there's a free cell before this one, combine
|
||||
int i = 0;
|
||||
while (i < _freeCells.Count && _freeCells[i].Offset < freeIndex)
|
||||
{
|
||||
if (_freeCells[i].Offset + _freeCells[i].Count == freeIndex)
|
||||
{
|
||||
freeIndex = _freeCells[i].Offset;
|
||||
len += _freeCells[i].Count;
|
||||
_freeCells.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's a free cell after this one, combine
|
||||
if (i < _freeCells.Count && _freeCells[i].Offset == freeIndex + len)
|
||||
{
|
||||
len += _freeCells[i].Count;
|
||||
_freeCells.RemoveAt(i);
|
||||
}
|
||||
|
||||
// Record the new free cell
|
||||
_freeCells.Insert(i, new Range<int, int>(freeIndex, len));
|
||||
|
||||
// Free cells are indicated by length > 0
|
||||
Utilities.WriteBytesLittleEndian(len, _buffer, freeIndex);
|
||||
|
||||
_fileStream.Position = _streamPos + freeIndex;
|
||||
_fileStream.Write(_buffer, freeIndex, 4);
|
||||
}
|
||||
|
||||
public bool UpdateCell(Cell cell)
|
||||
{
|
||||
int index = cell.Index - _header.FileOffset;
|
||||
int allocSize = Math.Abs(Utilities.ToInt32LittleEndian(_buffer, index));
|
||||
|
||||
int newSize = cell.Size + 4;
|
||||
if (newSize > allocSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cell.WriteTo(_buffer, index + 4);
|
||||
|
||||
_fileStream.Position = _streamPos + index;
|
||||
_fileStream.Write(_buffer, index, newSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] ReadRawCellData(int cellIndex, int maxBytes)
|
||||
{
|
||||
int index = cellIndex - _header.FileOffset;
|
||||
int len = Math.Abs(Utilities.ToInt32LittleEndian(_buffer, index));
|
||||
byte[] result = new byte[Math.Min(len - 4, maxBytes)];
|
||||
Array.Copy(_buffer, index + 4, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal bool WriteRawCellData(int cellIndex, byte[] data, int offset, int count)
|
||||
{
|
||||
int index = cellIndex - _header.FileOffset;
|
||||
int allocSize = Math.Abs(Utilities.ToInt32LittleEndian(_buffer, index));
|
||||
|
||||
int newSize = count + 4;
|
||||
if (newSize > allocSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Array.Copy(data, offset, _buffer, index + 4, count);
|
||||
|
||||
_fileStream.Position = _streamPos + index;
|
||||
_fileStream.Write(_buffer, index, newSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal int AllocateCell(int size)
|
||||
{
|
||||
if (size < 8 || size % 8 != 0)
|
||||
{
|
||||
throw new ArgumentException("Invalid cell size");
|
||||
}
|
||||
|
||||
// Very inefficient algorithm - will lead to fragmentation
|
||||
for (int i = 0; i < _freeCells.Count; ++i)
|
||||
{
|
||||
int result = _freeCells[i].Offset + _header.FileOffset;
|
||||
if (_freeCells[i].Count > size)
|
||||
{
|
||||
// Record the newly allocated cell
|
||||
Utilities.WriteBytesLittleEndian(-size, _buffer, _freeCells[i].Offset);
|
||||
_fileStream.Position = _streamPos + _freeCells[i].Offset;
|
||||
_fileStream.Write(_buffer, _freeCells[i].Offset, 4);
|
||||
|
||||
// Keep the remainder of the free buffer as unallocated
|
||||
_freeCells[i] = new Range<int, int>(_freeCells[i].Offset + size, _freeCells[i].Count - size);
|
||||
Utilities.WriteBytesLittleEndian(_freeCells[i].Count, _buffer, _freeCells[i].Offset);
|
||||
_fileStream.Position = _streamPos + _freeCells[i].Offset;
|
||||
_fileStream.Write(_buffer, _freeCells[i].Offset, 4);
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (_freeCells[i].Count == size)
|
||||
{
|
||||
// Record the whole of the free buffer as a newly allocated cell
|
||||
Utilities.WriteBytesLittleEndian(-size, _buffer, _freeCells[i].Offset);
|
||||
_fileStream.Position = _streamPos + _freeCells[i].Offset;
|
||||
_fileStream.Write(_buffer, _freeCells[i].Offset, 4);
|
||||
|
||||
_freeCells.RemoveAt(i);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user