Author: cwittich Date: Fri Apr 10 08:18:00 2015 New Revision: 67124
URL: http://svn.reactos.org/svn/reactos?rev=67124&view=rev Log: [RTL] import LZNT1 decompression from wine staging
Modified: trunk/reactos/lib/rtl/compress.c
Modified: trunk/reactos/lib/rtl/compress.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/compress.c?rev=6712... ============================================================================== --- trunk/reactos/lib/rtl/compress.c [iso-8859-1] (original) +++ trunk/reactos/lib/rtl/compress.c [iso-8859-1] Fri Apr 10 08:18:00 2015 @@ -4,6 +4,8 @@ * PURPOSE: Compression and decompression functions * FILE: lib/rtl/compress.c * PROGRAMER: Eric Kohl + Sebastian Lackner + Michael Müller */
/* INCLUDES *****************************************************************/ @@ -22,6 +24,198 @@
/* FUNCTIONS ****************************************************************/ + +/* Based on Wine Staging */ + +/* 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; + +}
static NTSTATUS @@ -111,23 +305,6 @@ return STATUS_NOT_IMPLEMENTED; }
- -/* - * @unimplemented - */ -NTSTATUS NTAPI -RtlDecompressBuffer(IN USHORT CompressionFormat, - OUT PUCHAR UncompressedBuffer, - IN ULONG UncompressedBufferSize, - IN PUCHAR CompressedBuffer, - IN ULONG CompressedBufferSize, - OUT PULONG FinalUncompressedSize) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - - /* * @unimplemented */ @@ -144,24 +321,52 @@ return STATUS_NOT_IMPLEMENTED; }
- -/* - * @unimplemented - */ -NTSTATUS NTAPI -RtlDecompressFragment(IN USHORT CompressionFormat, - OUT PUCHAR UncompressedFragment, - IN ULONG UncompressedFragmentSize, - IN PUCHAR CompressedBuffer, - IN ULONG CompressedBufferSize, - IN ULONG FragmentOffset, - OUT PULONG FinalUncompressedSize, - IN PVOID WorkSpace) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - +/* + * @implemented + */ +NTSTATUS NTAPI +RtlDecompressFragment(IN USHORT format, + OUT PUCHAR uncompressed, + IN ULONG uncompressed_size, + IN PUCHAR compressed, + IN ULONG compressed_size, + IN ULONG offset, + OUT PULONG final_size, + IN PVOID workspace) +{ + DPRINT("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: + DPRINT1("format %d not implemented\n", format); + return STATUS_UNSUPPORTED_COMPRESSION; + } +} + +/* + * @implemented + */ +NTSTATUS NTAPI +RtlDecompressBuffer(IN USHORT CompressionFormat, + OUT PUCHAR UncompressedBuffer, + IN ULONG UncompressedBufferSize, + IN PUCHAR CompressedBuffer, + IN ULONG CompressedBufferSize, + OUT PULONG FinalUncompressedSize) +{ + return RtlDecompressFragment(CompressionFormat, UncompressedBuffer, UncompressedBufferSize, + CompressedBuffer, CompressedBufferSize, 0, FinalUncompressedSize, NULL); +}
/* * @unimplemented