diff --git a/README.md b/README.md index ca941a78..645f6a4f 100644 --- a/README.md +++ b/README.md @@ -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)) diff --git a/debian/changelog b/debian/changelog index e100160a..be932de4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 Mon, 20 Oct 2014 19:53:47 +0200 diff --git a/patches/Makefile b/patches/Makefile index cfb0b895..b2b0906d 100644 --- a/patches/Makefile +++ b/patches/Makefile @@ -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: diff --git a/patches/ntdll-LZNT1_Compression/0001-ntdll-Implement-semi-stub-for-RtlGetCompressionWorkS.patch b/patches/ntdll-LZNT1_Compression/0001-ntdll-Implement-semi-stub-for-RtlGetCompressionWorkS.patch new file mode 100644 index 00000000..60ea5f8b --- /dev/null +++ b/patches/ntdll-LZNT1_Compression/0001-ntdll-Implement-semi-stub-for-RtlGetCompressionWorkS.patch @@ -0,0 +1,69 @@ +From 8f2dbbc47749542a135c5410c94097d8b14e0a84 Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +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 + diff --git a/patches/ntdll-LZNT1_Compression/0002-ntdll-Implement-semi-stub-for-RtlCompressBuffer.patch b/patches/ntdll-LZNT1_Compression/0002-ntdll-Implement-semi-stub-for-RtlCompressBuffer.patch new file mode 100644 index 00000000..e46fcc22 --- /dev/null +++ b/patches/ntdll-LZNT1_Compression/0002-ntdll-Implement-semi-stub-for-RtlCompressBuffer.patch @@ -0,0 +1,90 @@ +From e487c879736038053aac64aa728ba35c78888e71 Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +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 + diff --git a/patches/ntdll-LZNT1_Compression/0003-ntdll-Implement-LZNT1-algorithm-for-RtlDecompressBuf.patch b/patches/ntdll-LZNT1_Compression/0003-ntdll-Implement-LZNT1-algorithm-for-RtlDecompressBuf.patch new file mode 100644 index 00000000..d3b26fd6 --- /dev/null +++ b/patches/ntdll-LZNT1_Compression/0003-ntdll-Implement-LZNT1-algorithm-for-RtlDecompressBuf.patch @@ -0,0 +1,274 @@ +From 20d7a57c882c47dea7060b3a0e397f8092fdeddc Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +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 + diff --git a/patches/ntdll-LZNT1_Compression/0004-ntdll-tests-Add-tests-for-Rtl-Decompress-Compress-Bu.patch b/patches/ntdll-LZNT1_Compression/0004-ntdll-tests-Add-tests-for-Rtl-Decompress-Compress-Bu.patch new file mode 100644 index 00000000..2607acc8 --- /dev/null +++ b/patches/ntdll-LZNT1_Compression/0004-ntdll-tests-Add-tests-for-Rtl-Decompress-Compress-Bu.patch @@ -0,0 +1,783 @@ +From ab32af3b920cce657cdb5b41bc04b6a77c9181cf Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +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 + diff --git a/patches/ntdll-LZNT1_Compression/definition b/patches/ntdll-LZNT1_Compression/definition new file mode 100644 index 00000000..10ed9a0b --- /dev/null +++ b/patches/ntdll-LZNT1_Compression/definition @@ -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