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=671…
==============================================================================
--- 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