Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * PURPOSE: Compression and decompression functions * FILE: lib/rtl/compress.c * PROGRAMER: Eric Kohl Sebastian Lackner Michael Müller */
/* INCLUDES *****************************************************************/
#include <rtl.h>
#define NDEBUG#include <debug.h>
/* MACROS *******************************************************************/
#define COMPRESSION_FORMAT_MASK 0x00FF#define COMPRESSION_ENGINE_MASK 0xFF00
/* 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, *src_end = src + src_size; UCHAR *dst_cur = dst, *dst_end = dst + dst_size; ULONG chunk_size, block_size; WORD chunk_header; UCHAR *ptr;
if (src_cur + sizeof(WORD) > src_end) return STATUS_BAD_COMPRESSION_BUFFER;
/* skip over chunks which have a big distance (>= 0x1000) to the destination offset */ while (offset >= 0x1000 && src_cur + sizeof(WORD) <= src_end) { /* read chunk header and extract size */ chunk_header = *(WORD *)src_cur; src_cur += sizeof(WORD); 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(WORD) <= src_end) { /* read chunk header and extract size */ chunk_header = *(WORD *)src_cur; src_cur += sizeof(WORD); 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; }
/* handle remaining chunks */ while (src_cur + sizeof(WORD) <= src_end) { /* read chunk header and extract size */ chunk_header = *(WORD *)src_cur; src_cur += sizeof(WORD); if (!chunk_header) goto out; chunk_size = (chunk_header & 0xFFF) + 1;
if (src_cur + chunk_size > src_end) return STATUS_BAD_COMPRESSION_BUFFER;
/* 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; }
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 NTSTATUSRtlpCompressBufferLZNT1(UCHAR *src, ULONG src_size, UCHAR *dst, ULONG dst_size, ULONG chunk_size, ULONG *final_size, UCHAR *workspace){ UCHAR *src_cur = src, *src_end = src + src_size; UCHAR *dst_cur = dst, *dst_end = dst + dst_size; ULONG block_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;}
static NTSTATUSRtlpWorkSpaceSizeLZNT1(USHORT Engine, PULONG BufferAndWorkSpaceSize, PULONG FragmentWorkSpaceSize){ if (Engine == COMPRESSION_ENGINE_STANDARD) { *BufferAndWorkSpaceSize = 0x8010; *FragmentWorkSpaceSize = 0x1000; return(STATUS_SUCCESS); } else if (Engine == COMPRESSION_ENGINE_MAXIMUM) { *BufferAndWorkSpaceSize = 0x10; *FragmentWorkSpaceSize = 0x1000; return(STATUS_SUCCESS); }
return(STATUS_NOT_SUPPORTED);}
/* * @implemented */NTSTATUS NTAPIRtlCompressBuffer(IN USHORT CompressionFormatAndEngine, IN PUCHAR UncompressedBuffer, IN ULONG UncompressedBufferSize, OUT PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, IN ULONG UncompressedChunkSize, OUT PULONG FinalCompressedSize, IN PVOID WorkSpace){ USHORT Format = CompressionFormatAndEngine & COMPRESSION_FORMAT_MASK; /* USHORT Engine = CompressionFormatAndEngine & COMPRESSION_ENGINE_MASK; */
if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) return(STATUS_INVALID_PARAMETER);
if (Format == COMPRESSION_FORMAT_LZNT1) return(RtlpCompressBufferLZNT1(UncompressedBuffer, UncompressedBufferSize, CompressedBuffer, CompressedBufferSize, UncompressedChunkSize, FinalCompressedSize, WorkSpace));
return(STATUS_UNSUPPORTED_COMPRESSION);}
/* * @unimplemented */NTSTATUS NTAPIRtlCompressChunks(IN PUCHAR UncompressedBuffer, IN ULONG UncompressedBufferSize, OUT PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, IN OUT PCOMPRESSED_DATA_INFO CompressedDataInfo, IN ULONG CompressedDataInfoLength, IN PVOID WorkSpace){ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
/* * @unimplemented */NTSTATUS NTAPIRtlDecompressChunks(OUT PUCHAR UncompressedBuffer, IN ULONG UncompressedBufferSize, IN PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, IN PUCHAR CompressedTail, IN ULONG CompressedTailSize, IN PCOMPRESSED_DATA_INFO CompressedDataInfo){ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
/* * @implemented */NTSTATUS NTAPIRtlDecompressFragment(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 NTAPIRtlDecompressBuffer(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 */NTSTATUS NTAPIRtlDescribeChunk(IN USHORT CompressionFormat, IN OUT PUCHAR *CompressedBuffer, IN PUCHAR EndOfCompressedBufferPlus1, OUT PUCHAR *ChunkBuffer, OUT PULONG ChunkSize){ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
/* * @unimplemented */NTSTATUS NTAPIRtlGetCompressionWorkSpaceSize(IN USHORT CompressionFormatAndEngine, OUT PULONG CompressBufferAndWorkSpaceSize, OUT PULONG CompressFragmentWorkSpaceSize){ USHORT Format = CompressionFormatAndEngine & COMPRESSION_FORMAT_MASK; USHORT Engine = CompressionFormatAndEngine & COMPRESSION_ENGINE_MASK;
if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) return(STATUS_INVALID_PARAMETER);
if (Format == COMPRESSION_FORMAT_LZNT1) return(RtlpWorkSpaceSizeLZNT1(Engine, CompressBufferAndWorkSpaceSize, CompressFragmentWorkSpaceSize));
return(STATUS_UNSUPPORTED_COMPRESSION);}
/* * @unimplemented */NTSTATUS NTAPIRtlReserveChunk(IN USHORT CompressionFormat, IN OUT PUCHAR *CompressedBuffer, IN PUCHAR EndOfCompressedBufferPlus1, OUT PUCHAR *ChunkBuffer, IN ULONG ChunkSize){ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
/* EOF */