ntdll: Implement RtlDecompressFragment.
Based on a patch by Michael Müller.
This commit is contained in:
parent
cf7a118a9e
commit
e3503799d9
|
@ -513,7 +513,7 @@
|
|||
@ stdcall RtlDecodePointer(ptr)
|
||||
# @ stub RtlDecodeSystemPointer
|
||||
@ stdcall RtlDecompressBuffer(long ptr long ptr long ptr)
|
||||
@ stub RtlDecompressFragment
|
||||
@ stdcall RtlDecompressFragment(long ptr long ptr long long ptr ptr)
|
||||
@ stub RtlDefaultNpAcl
|
||||
@ stub RtlDelete
|
||||
@ stdcall RtlDeleteAce(ptr long)
|
||||
|
|
216
dlls/ntdll/rtl.c
216
dlls/ntdll/rtl.c
|
@ -1309,17 +1309,221 @@ NTSTATUS WINAPI RtlCompressBuffer(USHORT format, PUCHAR uncompressed, ULONG unco
|
|||
}
|
||||
}
|
||||
|
||||
/* decompress a single LZNT1 chunk */
|
||||
static UCHAR *lznt1_decompress_chunk(UCHAR *dst, ULONG dst_size, UCHAR *src, ULONG src_size)
|
||||
{
|
||||
UCHAR *src_cur = src, *src_end = src + src_size;
|
||||
UCHAR *dst_cur = dst, *dst_end = dst + dst_size;
|
||||
ULONG displacement_bits, length_bits;
|
||||
ULONG code_displacement, code_length;
|
||||
WORD flags, code;
|
||||
|
||||
while (src_cur < src_end && dst_cur < dst_end)
|
||||
{
|
||||
flags = 0x8000 | *src_cur++;
|
||||
while ((flags & 0xff00) && src_cur < src_end)
|
||||
{
|
||||
if (flags & 1)
|
||||
{
|
||||
/* backwards reference */
|
||||
if (src_cur + sizeof(WORD) > src_end)
|
||||
return NULL;
|
||||
|
||||
code = *(WORD *)src_cur;
|
||||
src_cur += sizeof(WORD);
|
||||
|
||||
/* find length / displacement bits */
|
||||
for (displacement_bits = 12; displacement_bits > 4; displacement_bits--)
|
||||
if ((1 << (displacement_bits - 1)) < dst_cur - dst) break;
|
||||
|
||||
length_bits = 16 - displacement_bits;
|
||||
code_length = (code & ((1 << length_bits) - 1)) + 3;
|
||||
code_displacement = (code >> length_bits) + 1;
|
||||
|
||||
if (dst_cur < dst + code_displacement)
|
||||
return NULL;
|
||||
|
||||
/* copy bytes of chunk - we can't use memcpy() since source and dest can
|
||||
* be overlapping, and the same bytes can be repeated over and over again */
|
||||
while (code_length--)
|
||||
{
|
||||
if (dst_cur >= dst_end) return dst_cur;
|
||||
*dst_cur = *(dst_cur - code_displacement);
|
||||
dst_cur++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* uncompressed data */
|
||||
if (dst_cur >= dst_end) return dst_cur;
|
||||
*dst_cur++ = *src_cur++;
|
||||
}
|
||||
flags >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return dst_cur;
|
||||
}
|
||||
|
||||
/* decompress data encoded with LZNT1 */
|
||||
static NTSTATUS lznt1_decompress(UCHAR *dst, ULONG dst_size, UCHAR *src, ULONG src_size,
|
||||
ULONG offset, ULONG *final_size, UCHAR *workspace)
|
||||
{
|
||||
UCHAR *src_cur = src, *src_end = src + src_size;
|
||||
UCHAR *dst_cur = dst, *dst_end = dst + dst_size;
|
||||
ULONG chunk_size, block_size;
|
||||
WORD chunk_header;
|
||||
UCHAR *ptr;
|
||||
|
||||
if (src_cur + sizeof(WORD) > src_end)
|
||||
return STATUS_BAD_COMPRESSION_BUFFER;
|
||||
|
||||
/* skip over chunks with a distance >= 0x1000 to the destination offset */
|
||||
while (offset >= 0x1000 && src_cur + sizeof(WORD) <= src_end)
|
||||
{
|
||||
chunk_header = *(WORD *)src_cur;
|
||||
src_cur += sizeof(WORD);
|
||||
if (!chunk_header) goto out;
|
||||
chunk_size = (chunk_header & 0xfff) + 1;
|
||||
|
||||
if (src_cur + chunk_size > src_end)
|
||||
return STATUS_BAD_COMPRESSION_BUFFER;
|
||||
|
||||
src_cur += chunk_size;
|
||||
offset -= 0x1000;
|
||||
}
|
||||
|
||||
/* handle partially included chunk */
|
||||
if (offset && src_cur + sizeof(WORD) <= src_end)
|
||||
{
|
||||
chunk_header = *(WORD *)src_cur;
|
||||
src_cur += sizeof(WORD);
|
||||
if (!chunk_header) goto out;
|
||||
chunk_size = (chunk_header & 0xfff) + 1;
|
||||
|
||||
if (src_cur + chunk_size > src_end)
|
||||
return STATUS_BAD_COMPRESSION_BUFFER;
|
||||
|
||||
if (dst_cur >= dst_end)
|
||||
goto out;
|
||||
|
||||
if (chunk_header & 0x8000)
|
||||
{
|
||||
/* compressed chunk */
|
||||
if (!workspace) return STATUS_ACCESS_VIOLATION;
|
||||
ptr = lznt1_decompress_chunk(workspace, 0x1000, src_cur, chunk_size);
|
||||
if (!ptr) return STATUS_BAD_COMPRESSION_BUFFER;
|
||||
if (ptr - workspace > offset)
|
||||
{
|
||||
block_size = min((ptr - workspace) - offset, dst_end - dst_cur);
|
||||
memcpy(dst_cur, workspace + offset, block_size);
|
||||
dst_cur += block_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* uncompressed chunk */
|
||||
if (chunk_size > offset)
|
||||
{
|
||||
block_size = min(chunk_size - offset, dst_end - dst_cur);
|
||||
memcpy(dst_cur, src_cur + offset, block_size);
|
||||
dst_cur += block_size;
|
||||
}
|
||||
}
|
||||
|
||||
src_cur += chunk_size;
|
||||
}
|
||||
|
||||
/* handle remaining chunks */
|
||||
while (src_cur + sizeof(WORD) <= src_end)
|
||||
{
|
||||
chunk_header = *(WORD *)src_cur;
|
||||
src_cur += sizeof(WORD);
|
||||
if (!chunk_header) goto out;
|
||||
chunk_size = (chunk_header & 0xfff) + 1;
|
||||
|
||||
if (src_cur + chunk_size > src_end)
|
||||
return STATUS_BAD_COMPRESSION_BUFFER;
|
||||
|
||||
/* fill space with padding when the previous chunk was decompressed
|
||||
* to less than 4096 bytes. no padding is needed for the last chunk
|
||||
* or when the next chunk is truncated */
|
||||
block_size = ((dst_cur - dst) + offset) & 0xfff;
|
||||
if (block_size)
|
||||
{
|
||||
block_size = 0x1000 - block_size;
|
||||
if (dst_cur + block_size >= dst_end)
|
||||
goto out;
|
||||
memset(dst_cur, 0, block_size);
|
||||
dst_cur += block_size;
|
||||
}
|
||||
|
||||
if (dst_cur >= dst_end)
|
||||
goto out;
|
||||
|
||||
if (chunk_header & 0x8000)
|
||||
{
|
||||
/* compressed chunk */
|
||||
dst_cur = lznt1_decompress_chunk(dst_cur, dst_end - dst_cur, src_cur, chunk_size);
|
||||
if (!dst_cur) return STATUS_BAD_COMPRESSION_BUFFER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* uncompressed chunk */
|
||||
block_size = min(chunk_size, dst_end - dst_cur);
|
||||
memcpy(dst_cur, src_cur, block_size);
|
||||
dst_cur += block_size;
|
||||
}
|
||||
|
||||
src_cur += chunk_size;
|
||||
}
|
||||
|
||||
out:
|
||||
if (final_size)
|
||||
*final_size = dst_cur - dst;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* RtlDecompressFragment [NTDLL.@]
|
||||
*/
|
||||
NTSTATUS RtlDecompressFragment(USHORT format, PUCHAR uncompressed, ULONG uncompressed_size,
|
||||
PUCHAR compressed, ULONG compressed_size, ULONG offset,
|
||||
PULONG final_size, PVOID workspace)
|
||||
{
|
||||
TRACE("0x%04x, %p, %u, %p, %u, %u, %p, %p\n", format, uncompressed,
|
||||
uncompressed_size, compressed, compressed_size, offset, final_size, workspace);
|
||||
|
||||
switch (format & ~COMPRESSION_ENGINE_MAXIMUM)
|
||||
{
|
||||
case COMPRESSION_FORMAT_LZNT1:
|
||||
return lznt1_decompress(uncompressed, uncompressed_size, compressed,
|
||||
compressed_size, offset, final_size, workspace);
|
||||
|
||||
case COMPRESSION_FORMAT_NONE:
|
||||
case COMPRESSION_FORMAT_DEFAULT:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
default:
|
||||
FIXME("format %u not implemented\n", format);
|
||||
return STATUS_UNSUPPORTED_COMPRESSION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* RtlDecompressBuffer [NTDLL.@]
|
||||
*/
|
||||
NTSTATUS WINAPI RtlDecompressBuffer(USHORT CompressionFormat, PUCHAR UncompressedBuffer,
|
||||
ULONG UncompressedBufferSize, PUCHAR CompressedBuffer,
|
||||
ULONG CompressedBufferSize, PULONG FinalUncompressedSize)
|
||||
NTSTATUS WINAPI RtlDecompressBuffer(USHORT format, PUCHAR uncompressed, ULONG uncompressed_size,
|
||||
PUCHAR compressed, ULONG compressed_size, PULONG final_size)
|
||||
{
|
||||
FIXME("0x%04x, %p, %u, %p, %u, %p :stub\n", CompressionFormat, UncompressedBuffer, UncompressedBufferSize,
|
||||
CompressedBuffer, CompressedBufferSize, FinalUncompressedSize);
|
||||
TRACE("0x%04x, %p, %u, %p, %u, %p\n", format, uncompressed,
|
||||
uncompressed_size, compressed, compressed_size, final_size);
|
||||
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
return RtlDecompressFragment(format, uncompressed, uncompressed_size,
|
||||
compressed, compressed_size, 0, final_size, NULL);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
|
|
@ -1663,11 +1663,8 @@ static void test_RtlCompressBuffer(void)
|
|||
memset(buf2, 0x11, sizeof(buf2));
|
||||
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf2, sizeof(buf2),
|
||||
buf1, buf_size, &final_size);
|
||||
todo_wine
|
||||
ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
|
||||
todo_wine
|
||||
ok(final_size == sizeof(test_buffer), "got wrong final_size %u\n", final_size);
|
||||
todo_wine
|
||||
ok(!memcmp(buf2, test_buffer, sizeof(test_buffer)), "got wrong decoded data\n");
|
||||
ok(buf2[sizeof(test_buffer)] == 0x11, "too many bytes written\n");
|
||||
|
||||
|
|
|
@ -977,7 +977,7 @@
|
|||
@ stub RtlCustomCPToUnicodeN
|
||||
@ stdcall RtlDecompressBuffer(long ptr long ptr long ptr) ntdll.RtlDecompressBuffer
|
||||
@ stub RtlDecompressChunks
|
||||
@ stub RtlDecompressFragment
|
||||
@ stdcall RtlDecompressFragment(long ptr long ptr long long ptr ptr) ntdll.RtlDecompressFragment
|
||||
@ stub RtlDelete
|
||||
@ stdcall RtlDeleteAce(ptr long) ntdll.RtlDeleteAce
|
||||
@ stdcall RtlDeleteAtomFromAtomTable(ptr long) ntdll.RtlDeleteAtomFromAtomTable
|
||||
|
|
Loading…
Reference in New Issue