Added patch to implement RtlDecompressBuffer.

This commit is contained in:
Sebastian Lackner 2014-10-30 18:46:55 +01:00
parent 9c9639f3ae
commit 621ece3c6f
8 changed files with 1245 additions and 1 deletions

View File

@ -35,10 +35,11 @@ Wine. All those differences are also documented on the
Included bugfixes and improvements
==================================
**Bugfixes and features included in the next upcoming release [4]:**
**Bugfixes and features included in the next upcoming release [5]:**
* Cinema 4D needs NotifyIpInterfaceChange ([Wine Bug #34573](http://bugs.winehq.org/show_bug.cgi?id=34573))
* D3DCompileShader should filter specific warning messages ([Wine Bug #33770](http://bugs.winehq.org/show_bug.cgi?id=33770))
* Support for RtlDecompressBuffer ([Wine Bug #37449](http://bugs.winehq.org/show_bug.cgi?id=37449))
* Support for pasting HTML from Unix applications ([Wine Bug #7372](http://bugs.winehq.org/show_bug.cgi?id=7372))
* Tumblebugs 2 requires DXTn software encoding support ([Wine Bug #29586](http://bugs.winehq.org/show_bug.cgi?id=29586))

1
debian/changelog vendored
View File

@ -4,6 +4,7 @@ wine-compholio (1.7.30) UNRELEASED; urgency=low
* Added patch to filter specific warning messages for D3DCompileShader.
* Added patch to implement iphlpapi stub functions.
* Added patch to implement support for pasting HTML from native Unix applications.
* Added patch to implement RtlDecompressBuffer.
* Removed patch to avoid Clang compiler warning because of unused Vtable (accepted upstream).
* Removed patch for additional ATL thunks (accepted upstream).
-- Sebastian Lackner <sebastian@fds-team.de> Mon, 20 Oct 2014 19:53:47 +0200

View File

@ -57,6 +57,7 @@ PATCHLIST := \
ntdll-Fix_Free.ok \
ntdll-Heap_FreeLists.ok \
ntdll-Junction_Points.ok \
ntdll-LZNT1_Compression.ok \
ntdll-NtQuerySection.ok \
ntdll-Pipe_SpecialCharacters.ok \
ntdll-WRITECOPY.ok \
@ -884,6 +885,27 @@ ntdll-Junction_Points.ok: ntdll-Fix_Free.ok
echo '+ { "ntdll-Junction_Points", "Erich E. Hoover", "Support for junction points/reparse points." },'; \
) > ntdll-Junction_Points.ok
# Patchset ntdll-LZNT1_Compression
# |
# | Included patches:
# | * Implement Rtl[Decompress|Compress]Buffer and RtlGetCompressionWorkSpaceSize. [by Sebastian Lackner / Michael Müller]
# |
# | This patchset fixes the following Wine bugs:
# | * [#37449] Support for RtlDecompressBuffer
# |
# | Modified files:
# | * dlls/ntdll/ntdll.spec, dlls/ntdll/rtl.c, dlls/ntdll/tests/rtl.c, include/winnt.h
# |
.INTERMEDIATE: ntdll-LZNT1_Compression.ok
ntdll-LZNT1_Compression.ok:
$(call APPLY_FILE,ntdll-LZNT1_Compression/0001-ntdll-Implement-semi-stub-for-RtlGetCompressionWorkS.patch)
$(call APPLY_FILE,ntdll-LZNT1_Compression/0002-ntdll-Implement-semi-stub-for-RtlCompressBuffer.patch)
$(call APPLY_FILE,ntdll-LZNT1_Compression/0003-ntdll-Implement-LZNT1-algorithm-for-RtlDecompressBuf.patch)
$(call APPLY_FILE,ntdll-LZNT1_Compression/0004-ntdll-tests-Add-tests-for-Rtl-Decompress-Compress-Bu.patch)
@( \
echo '+ { "ntdll-LZNT1_Compression", "Sebastian Lackner / Michael Müller", "Implement Rtl[Decompress|Compress]Buffer and RtlGetCompressionWorkSpaceSize." },'; \
) > ntdll-LZNT1_Compression.ok
# Patchset ntdll-NtQuerySection
# |
# | Included patches:

View File

@ -0,0 +1,69 @@
From 8f2dbbc47749542a135c5410c94097d8b14e0a84 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Thu, 30 Oct 2014 17:19:42 +0100
Subject: ntdll: Implement semi-stub for RtlGetCompressionWorkSpaceSize.
---
dlls/ntdll/rtl.c | 26 ++++++++++++++++++++------
include/winnt.h | 6 ++++++
2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c
index 8f6f386..884a14a 100644
--- a/dlls/ntdll/rtl.c
+++ b/dlls/ntdll/rtl.c
@@ -1215,14 +1215,28 @@ PSLIST_ENTRY WINAPI RtlInterlockedPushListSList(PSLIST_HEADER list, PSLIST_ENTRY
/******************************************************************************
* RtlGetCompressionWorkSpaceSize [NTDLL.@]
*/
-NTSTATUS WINAPI RtlGetCompressionWorkSpaceSize(USHORT CompressionFormatAndEngine,
- PULONG CompressBufferWorkSpaceSize,
- PULONG CompressFragmentWorkSpaceSize)
+NTSTATUS WINAPI RtlGetCompressionWorkSpaceSize(USHORT format, PULONG compress_workspace,
+ PULONG decompress_workspace)
{
- FIXME("0x%04x, %p, %p: stub!\n", CompressionFormatAndEngine, CompressBufferWorkSpaceSize,
- CompressFragmentWorkSpaceSize);
+ FIXME("0x%04x, %p, %p: semi-stub\n", format, compress_workspace, decompress_workspace);
- return STATUS_NOT_IMPLEMENTED;
+ switch (format & ~COMPRESSION_ENGINE_MAXIMUM)
+ {
+ case COMPRESSION_FORMAT_LZNT1:
+ if (compress_workspace)
+ *compress_workspace = 16; /* FIXME */
+ if (decompress_workspace)
+ *decompress_workspace = 0x1000;
+ return STATUS_SUCCESS;
+
+ case COMPRESSION_FORMAT_NONE:
+ case COMPRESSION_FORMAT_DEFAULT:
+ return STATUS_INVALID_PARAMETER;
+
+ default:
+ FIXME("format %d not implemented\n", format);
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
}
/******************************************************************************
diff --git a/include/winnt.h b/include/winnt.h
index 709a93f..401e3ad 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -4722,6 +4722,12 @@ typedef struct _QUOTA_LIMITS_EX {
#define FILE_256_BYTE_ALIGNMENT 0x000000ff
#define FILE_512_BYTE_ALIGNMENT 0x000001ff
+#define COMPRESSION_FORMAT_NONE 0
+#define COMPRESSION_FORMAT_DEFAULT 1
+#define COMPRESSION_FORMAT_LZNT1 2
+#define COMPRESSION_ENGINE_STANDARD 0
+#define COMPRESSION_ENGINE_MAXIMUM 256
+
#define MAILSLOT_NO_MESSAGE ((DWORD)-1)
#define MAILSLOT_WAIT_FOREVER ((DWORD)-1)
--
2.1.2

View File

@ -0,0 +1,90 @@
From e487c879736038053aac64aa728ba35c78888e71 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Thu, 30 Oct 2014 17:24:26 +0100
Subject: ntdll: Implement semi-stub for RtlCompressBuffer.
---
dlls/ntdll/rtl.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 54 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c
index 884a14a..b790910 100644
--- a/dlls/ntdll/rtl.c
+++ b/dlls/ntdll/rtl.c
@@ -1239,19 +1239,65 @@ NTSTATUS WINAPI RtlGetCompressionWorkSpaceSize(USHORT format, PULONG compress_wo
}
}
+/* compress data using LZNT1, currently only a stub */
+static NTSTATUS lznt1_compress(UCHAR *src, ULONG src_size, UCHAR *dst, ULONG dst_size,
+ ULONG chunk_size, ULONG *final_size, UCHAR *workspace)
+{
+ UCHAR *src_cur, *src_end, *dst_cur, *dst_end;
+ ULONG block_size;
+
+ src_cur = src;
+ src_end = src + src_size;
+ dst_cur = dst;
+ dst_end = dst + dst_size;
+
+ while (src_cur < src_end)
+ {
+ /* determine size of current chunk */
+ block_size = min(0x1000, src_end - src_cur);
+ if (dst_cur + sizeof(WORD) + block_size > dst_end)
+ return STATUS_BUFFER_TOO_SMALL;
+
+ /* write (uncompressed) chunk header */
+ *(WORD *)dst_cur = 0x3000 | (block_size - 1);
+ dst_cur += sizeof(WORD);
+
+ /* write chunk content */
+ memcpy(dst_cur, src_cur, block_size);
+ dst_cur += block_size;
+ src_cur += block_size;
+ }
+
+ if (final_size)
+ *final_size = dst_cur - dst;
+
+ return STATUS_SUCCESS;
+}
+
/******************************************************************************
* RtlCompressBuffer [NTDLL.@]
*/
-NTSTATUS WINAPI RtlCompressBuffer(USHORT CompressionFormatAndEngine, PUCHAR UncompressedBuffer,
- ULONG UncompressedBufferSize, PUCHAR CompressedBuffer,
- ULONG CompressedBufferSize, ULONG UncompressedChunkSize,
- PULONG FinalCompressedSize, PVOID WorkSpace)
+NTSTATUS WINAPI RtlCompressBuffer(USHORT format, PUCHAR uncompressed, ULONG uncompressed_size,
+ PUCHAR compressed, ULONG compressed_size, ULONG chunk_size,
+ PULONG final_size, PVOID workspace)
{
- FIXME("0x%04x, %p, %u, %p, %u, %u, %p, %p :stub\n", CompressionFormatAndEngine, UncompressedBuffer,
- UncompressedBufferSize, CompressedBuffer, CompressedBufferSize, UncompressedChunkSize,
- FinalCompressedSize, WorkSpace);
+ FIXME("0x%04x, %p, %u, %p, %u, %u, %p, %p :semi-stub\n", format, uncompressed,
+ uncompressed_size, compressed, compressed_size, chunk_size, final_size, workspace);
- return STATUS_NOT_IMPLEMENTED;
+ switch (format & ~COMPRESSION_ENGINE_MAXIMUM)
+ {
+ case COMPRESSION_FORMAT_LZNT1:
+ return lznt1_compress(uncompressed, uncompressed_size, compressed,
+ compressed_size, chunk_size, final_size, workspace);
+
+ case COMPRESSION_FORMAT_NONE:
+ case COMPRESSION_FORMAT_DEFAULT:
+ return STATUS_INVALID_PARAMETER;
+
+ default:
+ FIXME("format %d not implemented\n", format);
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
}
/******************************************************************************
--
2.1.2

View File

@ -0,0 +1,274 @@
From 20d7a57c882c47dea7060b3a0e397f8092fdeddc Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Thu, 30 Oct 2014 17:26:42 +0100
Subject: ntdll: Implement LZNT1 algorithm for RtlDecompressBuffer.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Based on a patch by Michael Müller.
---
dlls/ntdll/ntdll.spec | 2 +-
dlls/ntdll/rtl.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 223 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 5bac269..785d5ed 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -510,7 +510,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)
diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c
index b790910..c9715df 100644
--- a/dlls/ntdll/rtl.c
+++ b/dlls/ntdll/rtl.c
@@ -1300,17 +1300,232 @@ NTSTATUS WINAPI RtlCompressBuffer(USHORT format, PUCHAR uncompressed, ULONG unco
}
}
+/* decompress a single LZNT1 chunk */
+static PUCHAR lznt1_decompress_chunk(UCHAR *dst, ULONG dst_size, UCHAR *src, ULONG src_size)
+{
+ UCHAR *src_cur, *src_end, *dst_cur, *dst_end;
+ ULONG displacement_bits, length_bits;
+ ULONG code_displacement, code_length;
+ WORD flags, code;
+
+ src_cur = src;
+ src_end = src + src_size;
+ dst_cur = dst;
+ dst_end = dst + dst_size;
+
+ /* Partial decompression is no error on Windows. */
+ while (src_cur < src_end && dst_cur < dst_end)
+ {
+ /* read flags header */
+ flags = 0x8000 | *src_cur++;
+
+ /* parse following 8 entities, either uncompressed data or backwards reference */
+ 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;
+
+ /* ensure reference is valid */
+ if (dst_cur < dst + code_displacement)
+ return NULL;
+
+ /* copy bytes of chunk - we can't use memcpy()
+ * since source and dest can be overlapping */
+ 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_end, *dst_cur, *dst_end, *ptr;
+ ULONG chunk_size, block_size;
+ WORD chunk_header;
+
+ src_cur = src;
+ src_end = src + src_size;
+ dst_cur = dst;
+ dst_end = dst + dst_size;
+
+ if (src_cur + sizeof(WCHAR) > src_end)
+ return STATUS_BAD_COMPRESSION_BUFFER;
+
+ /* skip over chunks which have a big distance (>= 0x1000) to the destination offset */
+ while (offset >= 0x1000 && src_cur + sizeof(WCHAR) <= src_end)
+ {
+ /* read chunk header and extract size */
+ chunk_header = *(WORD *)src_cur;
+ src_cur += sizeof(WCHAR);
+ if (!chunk_header) goto out;
+ chunk_size = (chunk_header & 0xFFF) + 1;
+
+ /* ensure we have enough buffer to process chunk */
+ if (src_cur + chunk_size > src_end)
+ return STATUS_BAD_COMPRESSION_BUFFER;
+
+ src_cur += chunk_size;
+ offset -= 0x1000;
+ }
+
+ /* this chunk is can be included partially */
+ if (offset && src_cur + sizeof(WCHAR) <= src_end)
+ {
+ /* read chunk header and extract size */
+ chunk_header = *(WORD *)src_cur;
+ src_cur += sizeof(WCHAR);
+ if (!chunk_header) goto out;
+ chunk_size = (chunk_header & 0xFFF) + 1;
+
+ /* ensure we have enough buffer to process chunk */
+ 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;
+ }
+
+ while (src_cur + sizeof(WCHAR) <= src_end)
+ {
+ /* read chunk header and extract size */
+ chunk_header = *(WORD *)src_cur;
+ src_cur += sizeof(WCHAR);
+ if (!chunk_header) goto out;
+ chunk_size = (chunk_header & 0xFFF) + 1;
+
+ /* ensure we have enough buffer to process chunk */
+ if (src_cur + chunk_size > src_end)
+ return STATUS_BAD_COMPRESSION_BUFFER;
+
+ /* add padding if required */
+ 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;
+ }
+ else 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;
+
+}
+
/******************************************************************************
- * RtlDecompressBuffer [NTDLL.@]
+ * RtlDecompressFragment [NTDLL.@]
*/
-NTSTATUS WINAPI RtlDecompressBuffer(USHORT CompressionFormat, PUCHAR UncompressedBuffer,
- ULONG UncompressedBufferSize, PUCHAR CompressedBuffer,
- ULONG CompressedBufferSize, PULONG FinalUncompressedSize)
+NTSTATUS RtlDecompressFragment(USHORT format, PUCHAR uncompressed, ULONG uncompressed_size,
+ PUCHAR compressed, ULONG compressed_size, ULONG offset,
+ PULONG final_size, PVOID workspace)
{
- FIXME("0x%04x, %p, %u, %p, %u, %p :stub\n", CompressionFormat, UncompressedBuffer, UncompressedBufferSize,
- CompressedBuffer, CompressedBufferSize, FinalUncompressedSize);
- return STATUS_NOT_IMPLEMENTED;
+ TRACE("0x%04x, %p, %u, %p, %u, %u, %p, %p :stub\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 %d not implemented\n", format);
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+}
+
+
+/******************************************************************************
+ * RtlDecompressBuffer [NTDLL.@]
+ */
+NTSTATUS WINAPI RtlDecompressBuffer(USHORT format, PUCHAR uncompressed, ULONG uncompressed_size,
+ PUCHAR compressed, ULONG compressed_size, PULONG final_size)
+{
+ return RtlDecompressFragment(format, uncompressed, uncompressed_size,
+ compressed, compressed_size, 0, final_size, NULL);
}
/***********************************************************************
--
2.1.2

View File

@ -0,0 +1,783 @@
From ab32af3b920cce657cdb5b41bc04b6a77c9181cf Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Thu, 30 Oct 2014 17:41:11 +0100
Subject: ntdll/tests: Add tests for Rtl[Decompress|Compress]Buffer and
RtlGetCompressionWorkSpaceSize.
---
dlls/ntdll/tests/rtl.c | 740 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 740 insertions(+)
diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c
index e8eb04a..3ddb76a 100644
--- a/dlls/ntdll/tests/rtl.c
+++ b/dlls/ntdll/tests/rtl.c
@@ -92,6 +92,10 @@ static NTSTATUS (WINAPI *pRtlIpv4StringToAddressA)(PCSTR, BOOLEAN, PCSTR *, IN_
static NTSTATUS (WINAPI *pLdrAddRefDll)(ULONG, HMODULE);
static NTSTATUS (WINAPI *pLdrLockLoaderLock)(ULONG, ULONG*, ULONG_PTR*);
static NTSTATUS (WINAPI *pLdrUnlockLoaderLock)(ULONG, ULONG_PTR);
+static NTSTATUS (WINAPI *pRtlGetCompressionWorkSpaceSize)(USHORT, PULONG, PULONG);
+static NTSTATUS (WINAPI *pRtlDecompressBuffer)(USHORT, PUCHAR, ULONG, const UCHAR*, ULONG, PULONG);
+static NTSTATUS (WINAPI *pRtlDecompressFragment)(USHORT, PUCHAR, ULONG, const UCHAR*, ULONG, ULONG, PULONG, PVOID);
+static NTSTATUS (WINAPI *pRtlCompressBuffer)(USHORT, const UCHAR*, ULONG, PUCHAR, ULONG, ULONG, PULONG, PVOID);
static HMODULE hkernel32 = 0;
static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
@@ -139,6 +143,10 @@ static void InitFunctionPtrs(void)
pLdrAddRefDll = (void *)GetProcAddress(hntdll, "LdrAddRefDll");
pLdrLockLoaderLock = (void *)GetProcAddress(hntdll, "LdrLockLoaderLock");
pLdrUnlockLoaderLock = (void *)GetProcAddress(hntdll, "LdrUnlockLoaderLock");
+ pRtlGetCompressionWorkSpaceSize = (void *)GetProcAddress(hntdll, "RtlGetCompressionWorkSpaceSize");
+ pRtlDecompressBuffer = (void *)GetProcAddress(hntdll, "RtlDecompressBuffer");
+ pRtlDecompressFragment = (void *)GetProcAddress(hntdll, "RtlDecompressFragment");
+ pRtlCompressBuffer = (void *)GetProcAddress(hntdll, "RtlCompressBuffer");
}
hkernel32 = LoadLibraryA("kernel32.dll");
ok(hkernel32 != 0, "LoadLibrary failed\n");
@@ -1599,6 +1607,735 @@ static void test_LdrLockLoaderLock(void)
pLdrUnlockLoaderLock(0, magic);
}
+static void test_RtlGetCompressionWorkSpaceSize(void)
+{
+ ULONG compress_workspace, decompress_workspace;
+ NTSTATUS status;
+
+ if (!pRtlGetCompressionWorkSpaceSize)
+ {
+ win_skip("RtlGetCompressionWorkSpaceSize is not available\n");
+ return;
+ }
+
+ status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_NONE, &compress_workspace,
+ &decompress_workspace);
+ ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
+
+ status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_DEFAULT, &compress_workspace,
+ &decompress_workspace);
+ ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
+
+ status = pRtlGetCompressionWorkSpaceSize(0xFF, &compress_workspace, &decompress_workspace);
+ ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status);
+
+ compress_workspace = decompress_workspace = 0xdeadbeef;
+ status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1, &compress_workspace,
+ &decompress_workspace);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ ok(compress_workspace != 0, "got wrong compress_workspace %d\n", compress_workspace);
+ ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %d\n", decompress_workspace);
+
+ compress_workspace = decompress_workspace = 0xdeadbeef;
+ status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM,
+ &compress_workspace, &decompress_workspace);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ ok(compress_workspace != 0, "got wrong compress_workspace %d\n", compress_workspace);
+ ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %d\n", decompress_workspace);
+}
+
+/* helper for test_RtlDecompressBuffer, checks if a chunk is incomplete */
+static BOOL is_incomplete_chunk(const UCHAR *compressed, ULONG compressed_size, BOOL check_all)
+{
+ ULONG chunk_size;
+ if (compressed_size <= sizeof(WORD))
+ return TRUE;
+ while (compressed_size >= sizeof(WORD))
+ {
+ chunk_size = (*(WORD *)compressed & 0xFFF) + 1;
+ if (compressed_size < sizeof(WORD) + chunk_size)
+ return TRUE;
+ if (!check_all)
+ break;
+ compressed += sizeof(WORD) + chunk_size;
+ compressed_size -= sizeof(WORD) + chunk_size;
+ }
+ return FALSE;
+}
+
+#define DECOMPRESS_BROKEN_TRUNCATED 1
+#define DECOMPRESS_BROKEN_FRAGMENT0 2
+#define DECOMPRESS_BROKEN_FRAGMENT1 4
+#define DECOMPRESS_BROKEN_FRAGMENT4095 8
+
+static void test_RtlDecompressBuffer(void)
+{
+ static const UCHAR test_multiple_chunks[] = {0x03, 0x30, 'W', 'i', 'n', 'e',
+ 0x03, 0x30, 'W', 'i', 'n', 'e'};
+ static const struct
+ {
+ UCHAR compressed[32];
+ ULONG compressed_size;
+ NTSTATUS status;
+ UCHAR uncompressed[32];
+ ULONG uncompressed_size;
+ DWORD broken_flags;
+ }
+ test_lznt[] =
+ {
+ /* 4 byte uncompressed chunk */
+ {
+ {0x03, 0x30, 'W', 'i', 'n', 'e'},
+ 6,
+ STATUS_SUCCESS,
+ "Wine",
+ 4,
+ DECOMPRESS_BROKEN_FRAGMENT4095 |
+ DECOMPRESS_BROKEN_FRAGMENT1 |
+ DECOMPRESS_BROKEN_FRAGMENT0
+ },
+ /* 8 byte uncompressed chunk */
+ {
+ {0x07, 0x30, 'W', 'i', 'n', 'e', 'W', 'i', 'n', 'e'},
+ 10,
+ STATUS_SUCCESS,
+ "WineWine",
+ 8,
+ DECOMPRESS_BROKEN_FRAGMENT4095 |
+ DECOMPRESS_BROKEN_FRAGMENT1 |
+ DECOMPRESS_BROKEN_FRAGMENT0
+ },
+ /* 4 byte compressed chunk */
+ {
+ {0x04, 0xB0, 0x00, 'W', 'i', 'n', 'e'},
+ 7,
+ STATUS_SUCCESS,
+ "Wine",
+ 4
+ },
+ /* 8 byte compressed chunk */
+ {
+ {0x08, 0xB0, 0x00, 'W', 'i', 'n', 'e', 'W', 'i', 'n', 'e'},
+ 11,
+ STATUS_SUCCESS,
+ "WineWine",
+ 8
+ },
+ /* compressed chunk using backwards reference */
+ {
+ {0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x01, 0x30},
+ 9,
+ STATUS_SUCCESS,
+ "WineWine",
+ 8,
+ DECOMPRESS_BROKEN_TRUNCATED
+ },
+ /* compressed chunk using backwards reference with length > bytes_read */
+ {
+ {0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05, 0x30},
+ 9,
+ STATUS_SUCCESS,
+ "WineWineWine",
+ 12,
+ DECOMPRESS_BROKEN_TRUNCATED
+ },
+ /* same as above, but unused bits != 0 */
+ {
+ {0x06, 0xB0, 0x30, 'W', 'i', 'n', 'e', 0x01, 0x30},
+ 9,
+ STATUS_SUCCESS,
+ "WineWine",
+ 8,
+ DECOMPRESS_BROKEN_TRUNCATED
+ },
+ /* compressed chunk without backwards reference and unused bits != 0 */
+ {
+ {0x01, 0xB0, 0x02, 'W'},
+ 4,
+ STATUS_SUCCESS,
+ "W",
+ 1
+ },
+ /* termination sequence after first chunk */
+ {
+ {0x03, 0x30, 'W', 'i', 'n', 'e', 0x00, 0x00, 0x03, 0x30, 'W', 'i', 'n', 'e'},
+ 14,
+ STATUS_SUCCESS,
+ "Wine",
+ 4,
+ DECOMPRESS_BROKEN_FRAGMENT4095 |
+ DECOMPRESS_BROKEN_FRAGMENT1 |
+ DECOMPRESS_BROKEN_FRAGMENT0
+ },
+ /* compressed chunk using backwards reference with 4 bit offset, 12 bit length */
+ {
+ {0x14, 0xB0, 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 0x00, 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 0x01, 0x01, 0xF0},
+ 23,
+ STATUS_SUCCESS,
+ "ABCDEFGHIJKLMNOPABCD",
+ 20,
+ DECOMPRESS_BROKEN_TRUNCATED
+ },
+ /* compressed chunk using backwards reference with 5 bit offset, 11 bit length */
+ {
+ {0x15, 0xB0, 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 0x00, 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 0x02, 'A', 0x00, 0x78},
+ 24,
+ STATUS_SUCCESS,
+ "ABCDEFGHIJKLMNOPABCD",
+ 20,
+ DECOMPRESS_BROKEN_TRUNCATED
+ },
+ /* uncompressed chunk with invalid magic */
+ {
+ {0x03, 0x20, 'W', 'i', 'n', 'e'},
+ 6,
+ STATUS_SUCCESS,
+ "Wine",
+ 4,
+ DECOMPRESS_BROKEN_FRAGMENT4095 |
+ DECOMPRESS_BROKEN_FRAGMENT1 |
+ DECOMPRESS_BROKEN_FRAGMENT0
+ },
+ /* compressed chunk with invalid magic */
+ {
+ {0x04, 0xA0, 0x00, 'W', 'i', 'n', 'e'},
+ 7,
+ STATUS_SUCCESS,
+ "Wine",
+ 4
+ },
+ /* garbage byte after end of buffer */
+ {
+ {0x00, 0xB0, 0x02, 0x01},
+ 4,
+ STATUS_SUCCESS,
+ "",
+ 0
+ },
+ /* empty compressed chunk */
+ {
+ {0x00, 0xB0, 0x00},
+ 3,
+ STATUS_SUCCESS,
+ "",
+ 0
+ },
+ /* empty compressed chunk with unused bits != 0 */
+ {
+ {0x00, 0xB0, 0x01},
+ 3,
+ STATUS_SUCCESS,
+ "",
+ 0
+ },
+ /* empty input buffer */
+ {
+ {},
+ 0,
+ STATUS_BAD_COMPRESSION_BUFFER,
+ },
+ /* incomplete chunk header */
+ {
+ {0x01},
+ 1,
+ STATUS_BAD_COMPRESSION_BUFFER
+ },
+ /* incomplete chunk header */
+ {
+ {0x00, 0x30},
+ 2,
+ STATUS_BAD_COMPRESSION_BUFFER
+ },
+ /* compressed chunk with invalid backwards reference */
+ {
+ {0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05, 0x40},
+ 9,
+ STATUS_BAD_COMPRESSION_BUFFER
+ },
+ /* compressed chunk with incomplete backwards reference */
+ {
+ {0x05, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05},
+ 8,
+ STATUS_BAD_COMPRESSION_BUFFER
+ },
+ /* incomplete uncompressed chunk */
+ {
+ {0x07, 0x30, 'W', 'i', 'n', 'e'},
+ 6,
+ STATUS_BAD_COMPRESSION_BUFFER
+ },
+ /* incomplete compressed chunk */
+ {
+ {0x08, 0xB0, 0x00, 'W', 'i', 'n', 'e'},
+ 7,
+ STATUS_BAD_COMPRESSION_BUFFER
+ },
+ /* two compressed chunks, the second one incomplete */
+ {
+ {0x00, 0xB0, 0x02, 0x00, 0xB0},
+ 5,
+ STATUS_BAD_COMPRESSION_BUFFER,
+ }
+ };
+
+ static UCHAR buf[0x2000], workspace[0x1000];
+ NTSTATUS status, expected_status;
+ ULONG final_size;
+ int i;
+
+ if (!pRtlDecompressBuffer || !pRtlDecompressFragment)
+ {
+ win_skip("RtlDecompressBuffer or RtlDecompressFragment is not available\n");
+ return;
+ }
+
+ /* test compression format / engine */
+ final_size = 0xdeadbeef;
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_NONE, buf, sizeof(buf) - 1, test_lznt[0].compressed,
+ test_lznt[0].compressed_size, &final_size);
+ ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
+ ok(final_size == 0xdeadbeef, "got wrong final_size %d\n", final_size);
+
+ final_size = 0xdeadbeef;
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_DEFAULT, buf, sizeof(buf) - 1, test_lznt[0].compressed,
+ test_lznt[0].compressed_size, &final_size);
+ ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
+ ok(final_size == 0xdeadbeef, "got wrong final_size %d\n", final_size);
+
+ final_size = 0xdeadbeef;
+ status = pRtlDecompressBuffer(0xFF, buf, sizeof(buf) - 1, test_lznt[0].compressed,
+ test_lznt[0].compressed_size, &final_size);
+ ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status);
+ ok(final_size == 0xdeadbeef, "got wrong final_size %d\n", final_size);
+
+ /* regular tests for RtlDecompressBuffer */
+ for (i = 0; i < sizeof(test_lznt) / sizeof(test_lznt[0]); i++)
+ {
+ trace("Running test %d (compressed_size=%d, compressed_size=%d, status=%d)\n",
+ i, test_lznt[i].compressed_size, test_lznt[i].compressed_size, test_lznt[i].status);
+
+ /* test with very big buffer */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed,
+ test_lznt[i].compressed_size, &final_size);
+ ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status);
+ if (!status)
+ {
+ ok(final_size == test_lznt[i].uncompressed_size,
+ "%d: got wrong final_size %d\n", i, final_size);
+ ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size),
+ "%d: got wrong decoded data\n", i);
+ ok(buf[test_lznt[i].uncompressed_size] == 0x11,
+ "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size);
+ }
+
+ /* test that modifier for compression engine is ignored */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buf, sizeof(buf),
+ test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size);
+ ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status);
+ if (!status)
+ {
+ ok(final_size == test_lznt[i].uncompressed_size,
+ "%d: got wrong final_size %d\n", i, final_size);
+ ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size),
+ "%d: got wrong decoded data\n", i);
+ ok(buf[test_lznt[i].uncompressed_size] == 0x11,
+ "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size);
+ }
+
+ /* test with expected output size */
+ if (test_lznt[i].uncompressed_size > 0)
+ {
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size,
+ test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size);
+ ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status);
+ if (!status)
+ {
+ ok(final_size == test_lznt[i].uncompressed_size,
+ "%d: got wrong final_size %d\n", i, final_size);
+ ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size),
+ "%d: got wrong decoded data\n", i);
+ ok(buf[test_lznt[i].uncompressed_size] == 0x11,
+ "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size);
+ }
+ }
+
+ /* test with smaller output size */
+ if (test_lznt[i].uncompressed_size > 1)
+ {
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size - 1,
+ test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size);
+ ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER &&
+ (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_TRUNCATED)), "%d: got wrong status 0x%08x\n", i, status);
+ if (!status)
+ {
+ ok(final_size == test_lznt[i].uncompressed_size - 1,
+ "%d: got wrong final_size %d\n", i, final_size);
+ ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size - 1),
+ "%d: got wrong decoded data\n", i);
+ ok(buf[test_lznt[i].uncompressed_size - 1] == 0x11,
+ "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size - 1);
+ }
+ }
+
+ /* test with zero output size */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 0, test_lznt[i].compressed,
+ test_lznt[i].compressed_size, &final_size);
+ if (is_incomplete_chunk(test_lznt[i].compressed, test_lznt[i].compressed_size, FALSE))
+ {
+ ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status);
+ }
+ else
+ {
+ ok(status == STATUS_SUCCESS, "%d: got wrong status 0x%08x\n", i, status);
+ ok(final_size == 0, "%d: got wrong final_size %d\n", i, final_size);
+ ok(buf[0] == 0x11, "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size);
+ }
+
+ /* test RtlDecompressBuffer with offset = 0 */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed,
+ test_lznt[i].compressed_size, 0, &final_size, workspace);
+ ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER &&
+ (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT0)), "%d: got wrong status 0x%08x\n", i, status);
+ if (!status)
+ {
+ ok(final_size == test_lznt[i].uncompressed_size,
+ "%d: got wrong final_size %d\n", i, final_size);
+ ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size),
+ "%d: got wrong decoded data\n", i);
+ ok(buf[test_lznt[i].uncompressed_size] == 0x11,
+ "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size);
+ }
+
+ /* test RtlDecompressBuffer with offset = 1 */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed,
+ test_lznt[i].compressed_size, 1, &final_size, workspace);
+ ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER &&
+ (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT1)), "%d: got wrong status 0x%08x\n", i, status);
+ if (!status)
+ {
+ if (test_lznt[i].uncompressed_size == 0)
+ {
+ todo_wine
+ ok(final_size == 4095,
+ "%d: got wrong final size %d\n", i, final_size);
+ /* Buffer doesn't contain any useful value on Windows */
+ ok(buf[4095] == 0x11,
+ "%d: buf[4095] overwritten\n", i);
+ }
+ else
+ {
+ ok(final_size == test_lznt[i].uncompressed_size - 1,
+ "%d: got wrong final_size %d\n", i, final_size);
+ ok(!memcmp(buf, test_lznt[i].uncompressed + 1, test_lznt[i].uncompressed_size - 1),
+ "%d: got wrong decoded data\n", i);
+ ok(buf[test_lznt[i].uncompressed_size - 1] == 0x11,
+ "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size - 1);
+ }
+ }
+
+ /* test RtlDecompressBuffer with offset = 4095 */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed,
+ test_lznt[i].compressed_size, 4095, &final_size, workspace);
+ ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER &&
+ (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT4095)), "%d: got wrong status 0x%08x\n", i, status);
+ if (!status)
+ {
+ todo_wine
+ ok(final_size == 1,
+ "%d: got wrong final size %d\n", i, final_size);
+ todo_wine
+ ok(buf[0] == 0,
+ "%d: padding is not zero\n", i);
+ ok(buf[1] == 0x11,
+ "%d: buf[1] overwritten\n", i);
+ }
+
+ /* test RtlDecompressBuffer with offset = 4096 */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed,
+ test_lznt[i].compressed_size, 4096, &final_size, workspace);
+ expected_status = is_incomplete_chunk(test_lznt[i].compressed, test_lznt[i].compressed_size, TRUE) ?
+ test_lznt[i].status : STATUS_SUCCESS;
+ ok(status == expected_status, "%d: got wrong status 0x%08x, expected 0x%08x\n", i, status, expected_status);
+ if (!status)
+ {
+ ok(final_size == 0,
+ "%d: got wrong final size %d\n", i, final_size);
+ ok(buf[0] == 0x11,
+ "%d: buf[4096] overwritten\n", i);
+ }
+ }
+
+ /* test decoding of multiple chunks with pRtlDecompressBuffer */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks,
+ sizeof(test_multiple_chunks), &final_size);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4100, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n");
+ ok(buf[4] == 0 && buf[4095] == 0, "padding is not zero\n");
+ ok(!memcmp(buf + 4096, "Wine", 4), "got wrong decoded data at offset 4096\n");
+ ok(buf[4100] == 0x11, "buf[4100] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 4097, test_multiple_chunks,
+ sizeof(test_multiple_chunks), &final_size);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4097, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n");
+ ok(buf[4] == 0 && buf[4095] == 0, "padding is not zero\n");
+ ok(buf[4096], "got wrong decoded data at offset 4096\n");
+ ok(buf[4097] == 0x11, "buf[4097] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 4096, test_multiple_chunks,
+ sizeof(test_multiple_chunks), &final_size);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n");
+ ok(buf[4] == 0x11, "buf[4] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 4, test_multiple_chunks,
+ sizeof(test_multiple_chunks), &final_size);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n");
+ ok(buf[4] == 0x11, "buf[4] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 3, test_multiple_chunks,
+ sizeof(test_multiple_chunks), &final_size);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 3, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "Wine", 3), "got wrong decoded data at offset 0\n");
+ ok(buf[3] == 0x11, "buf[3] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 0, test_multiple_chunks,
+ sizeof(test_multiple_chunks), &final_size);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 0, "got wrong final_size %d\n", final_size);
+ ok(buf[0] == 0x11, "buf[0] overwritten\n");
+ }
+
+ /* test multiple chunks in combination with RtlDecompressBuffer and offset=1 */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks,
+ sizeof(test_multiple_chunks), 1, &final_size, workspace);
+ ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER),
+ "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4099, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n");
+ ok(buf[3] == 0 && buf[4094] == 0, "padding is not zero\n");
+ ok(!memcmp(buf + 4095, "Wine", 4), "got wrong decoded data at offset 4095\n");
+ ok(buf[4099] == 0x11, "buf[4099] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, 4096, test_multiple_chunks,
+ sizeof(test_multiple_chunks), 1, &final_size, workspace);
+ ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER),
+ "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4096, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n");
+ ok(buf[3] == 0 && buf[4094] == 0, "padding is not zero\n");
+ ok(buf[4095] == 'W', "got wrong decoded data at offset 4095\n");
+ ok(buf[4096] == 0x11, "buf[4096] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, 4095, test_multiple_chunks,
+ sizeof(test_multiple_chunks), 1, &final_size, workspace);
+ ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER),
+ "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 3, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n");
+ ok(buf[4] == 0x11, "buf[4] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, 3, test_multiple_chunks,
+ sizeof(test_multiple_chunks), 1, &final_size, workspace);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 3, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n");
+ ok(buf[3] == 0x11, "buf[3] overwritten\n");
+ }
+
+ /* test multiple chunks in combination with RtlDecompressBuffer and offset=4 */
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks,
+ sizeof(test_multiple_chunks), 4, &final_size, workspace);
+ ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER),
+ "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4096, "got wrong final_size %d\n", final_size);
+ ok(buf[0] == 0 && buf[4091] == 0, "padding is not zero\n");
+ ok(!memcmp(buf + 4092, "Wine", 4), "got wrong decoded data at offset 4092\n");
+ ok(buf[4096] == 0x11, "buf[4096] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks,
+ sizeof(test_multiple_chunks), 4095, &final_size, workspace);
+ ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER),
+ "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 5, "got wrong final_size %d\n", final_size);
+ ok(buf[0] == 0, "padding is not zero\n");
+ ok(!memcmp(buf + 1, "Wine", 4), "got wrong decoded data at offset 1\n");
+ ok(buf[5] == 0x11, "buf[5] overwritten\n");
+ }
+
+ final_size = 0xdeadbeef;
+ memset(buf, 0x11, sizeof(buf));
+ status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks,
+ sizeof(test_multiple_chunks), 4096, &final_size, workspace);
+ ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER),
+ "got wrong status 0x%08x\n", status);
+ if (!status)
+ {
+ ok(final_size == 4, "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n");
+ ok(buf[4] == 0x11, "buf[4] overwritten\n");
+ }
+
+}
+
+static void test_RtlCompressBuffer(void)
+{
+ ULONG compress_workspace, decompress_workspace;
+ static const UCHAR test_buffer[] = "WineWineWine";
+ static UCHAR buf1[0x1000], buf2[0x1000], *workspace;
+ ULONG final_size, buf_size;
+ NTSTATUS status;
+
+ if (!pRtlCompressBuffer || !pRtlGetCompressionWorkSpaceSize)
+ {
+ win_skip("RtlCompressBuffer or RtlGetCompressionWorkSpaceSize is not available\n");
+ return;
+ }
+
+ compress_workspace = decompress_workspace = 0xdeadbeef;
+ status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1, &compress_workspace,
+ &decompress_workspace);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ ok(compress_workspace != 0, "got wrong compress_workspace %d\n", compress_workspace);
+
+ workspace = HeapAlloc( GetProcessHeap(), 0, compress_workspace );
+ ok(workspace != NULL, "HeapAlloc failed %x\n", GetLastError());
+
+ /* test compression format / engine */
+ final_size = 0xdeadbeef;
+ status = pRtlCompressBuffer(COMPRESSION_FORMAT_NONE, test_buffer, sizeof(test_buffer),
+ buf1, sizeof(buf1) - 1, 4096, &final_size, workspace);
+ ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
+ ok(final_size == 0xdeadbeef, "got wrong final_size %d\n", final_size);
+
+ final_size = 0xdeadbeef;
+ status = pRtlCompressBuffer(COMPRESSION_FORMAT_DEFAULT, test_buffer, sizeof(test_buffer),
+ buf1, sizeof(buf1) - 1, 4096, &final_size, workspace);
+ ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
+ ok(final_size == 0xdeadbeef, "got wrong final_size %d\n", final_size);
+
+ final_size = 0xdeadbeef;
+ status = pRtlCompressBuffer(0xFF, test_buffer, sizeof(test_buffer),
+ buf1, sizeof(buf1) - 1, 4096, &final_size, workspace);
+ ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status);
+ ok(final_size == 0xdeadbeef, "got wrong final_size %d\n", final_size);
+
+ /* test compression */
+ final_size = 0xdeadbeef;
+ memset(buf1, 0x11, sizeof(buf1));
+ status = pRtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, test_buffer, sizeof(test_buffer),
+ buf1, sizeof(buf1), 4096, &final_size, workspace);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ ok((*(WORD *)buf1 & 0x7000) == 0x3000, "no chunk signature found %04x\n", *(WORD *)buf1);
+ buf_size = final_size;
+ todo_wine
+ ok(final_size < sizeof(test_buffer), "got wrong final_size %d\n", final_size);
+
+ /* test decompression */
+ final_size = 0xdeadbeef;
+ memset(buf2, 0x11, sizeof(buf2));
+ status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf2, sizeof(buf2),
+ buf1, buf_size, &final_size);
+ ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
+ ok(final_size == sizeof(test_buffer), "got wrong final_size %d\n", final_size);
+ ok(!memcmp(buf2, test_buffer, sizeof(test_buffer)), "got wrong decoded data\n");
+ ok(buf2[sizeof(test_buffer)] == 0x11, "buf[%d] overwritten\n", sizeof(test_buffer));
+
+ /* buffer too small */
+ final_size = 0xdeadbeef;
+ memset(buf1, 0x11, sizeof(buf1));
+ status = pRtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, test_buffer, sizeof(test_buffer),
+ buf1, 4, 4096, &final_size, workspace);
+ ok(status == STATUS_BUFFER_TOO_SMALL, "got wrong status 0x%08x\n", status);
+
+ HeapFree(GetProcessHeap(), 0, workspace);
+}
+
START_TEST(rtl)
{
InitFunctionPtrs();
@@ -1625,4 +2362,7 @@ START_TEST(rtl)
test_RtlIpv4StringToAddress();
test_LdrAddRefDll();
test_LdrLockLoaderLock();
+ test_RtlGetCompressionWorkSpaceSize();
+ test_RtlDecompressBuffer();
+ test_RtlCompressBuffer();
}
--
2.1.2

View File

@ -0,0 +1,4 @@
Author: Sebastian Lackner / Michael Müller
Subject: Implement Rtl[Decompress|Compress]Buffer and RtlGetCompressionWorkSpaceSize.
Revision: 1
Fixes: [37449] Support for RtlDecompressBuffer