diff options
| author | Wiimm <wiimm@wiimm.de> | 2023-09-22 17:19:28 +0200 |
|---|---|---|
| committer | Wiimm <wiimm@wiimm.de> | 2023-09-22 17:19:28 +0200 |
| commit | 336da838ce43edae40dd708dd465f1f43a2c312d (patch) | |
| tree | c7825c2937e1f79f5c6fc31e45c25eca053833d6 /project/src/lib-lzma.c | |
| parent | eb71ae7bdb97df01bf6cf0f2adc0f84e59e16fcb (diff) | |
SZS 2.38a
Diffstat (limited to '')
| -rw-r--r-- | project/src/lib-lzma.c | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/project/src/lib-lzma.c b/project/src/lib-lzma.c new file mode 100644 index 0000000..65a0d96 --- /dev/null +++ b/project/src/lib-lzma.c @@ -0,0 +1,589 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2023 by Dirk Clemens <wiimm@wiimm.de> * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#ifndef NO_LZMA + #include "lib-szs.h" + #include "lib-lzma.h" + #include "liblzma/LzmaEnc.h" + #include "liblzma/LzmaDec.h" +#endif + +// https://7-zip.org/sdk.html + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LZMA helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void * AllocLZMA ( ISzAllocPtr p, size_t size ) +{ + return MALLOC(size); +} + +static void FreeLZMA ( ISzAllocPtr p, void *ptr ) +{ + FREE(ptr); +} + +static ISzAlloc lzma_alloc = { AllocLZMA, FreeLZMA }; + +/////////////////////////////////////////////////////////////////////////////// + +int IsLZ +( + // returns + // -1: not LZ data + // 0: seems to be LZ; compression level is unknown + // 1..9: seems to be LZ; compression level is returned + + cvp data, // NULL or data to investigate + uint size // size of 'data' +) +{ + const uint delta = sizeof(wlz_header_t)+sizeof(u32); + const u8 *d = data; + if ( !d || size < 0x20 || memcmp(d,LZ_MAGIC,4) ) + { + return -1; + } + + const u32 magic = be24(d+delta); + if ( magic != LZMA_MAGIC_NUM3 ) + return -1; + + return GetComprLevelLZMA(le32(d+delta+1)); +} + +/////////////////////////////////////////////////////////////////////////////// + +int IsLZMA +( + // returns + // -1: not LZMA data + // 0: seems to be LZMA data; compression level is unknown + // 1..9: seems to be LZMA data; compression level is returned + + cvp data, // NULL or data to investigate + uint size // size of 'data' +) +{ + const u8 *d = data; + if ( !d || size < 20 ) + return -1; + + const u32 magic = be24(d); + if ( magic != LZMA_MAGIC_NUM3 ) + return -1; + + return GetComprLevelLZMA(le32(d+1)); +} + +/////////////////////////////////////////////////////////////////////////////// + +int CalcCompressionLevelLZMA +( + int compr_level // valid are 1..9 / 0: use default value +) +{ + return compr_level < 1 + ? LZMA_DEFAULT_COMPR + : compr_level < 9 + ? compr_level + : 9; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int lzma_dict_size[] = +{ + 0, // level 0 + 0x0100000, // level 1 + 0x0200000, // level 2 + 0x0400000, // level 3 + 0x0400000, // level 4 + 0x0800000, // level 5 + 0x0800000, // level 6 + 0x1000000, // level 7 + 0x2000000, // level 8 + 0x4000000, // level 9 +}; + +/////////////////////////////////////////////////////////////////////////////// + +int GetDicSizeByLevel ( int level ) +{ + if ( level < 1 ) + level = 1; + else if ( level > 9 ) + level = 9; + return lzma_dict_size[level]; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetComprLevelLZMA ( u32 dict_size ) +{ + for ( int level = 9; level >= 1; level -- ) + if ( dict_size >= lzma_dict_size[level] ) + return level; + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetMessageLZMA +( + int err, // error code + ccp unkown_error // result for unkown error codes +) +{ + switch (err) + { + case SZ_OK: return "OK"; + + case SZ_ERROR_ARCHIVE: return "Archive error"; + case SZ_ERROR_CRC: return "CRC error"; + case SZ_ERROR_DATA: return "Data error"; + case SZ_ERROR_FAIL: return "Operation failed"; + case SZ_ERROR_INPUT_EOF: return "Need more input"; + case SZ_ERROR_MEM: return "Memory allocation error"; + case SZ_ERROR_NO_ARCHIVE: return "No archive"; + case SZ_ERROR_OUTPUT_EOF: return "Putput buffer overflow"; + case SZ_ERROR_PARAM: return "Incorrect parameter"; + case SZ_ERROR_PROGRESS: return "Some break from progress callback"; + case SZ_ERROR_READ: return "Reading error"; + case SZ_ERROR_THREAD: return "Errors in multithreading functions"; + case SZ_ERROR_UNSUPPORTED: return "Unsupported properties"; + case SZ_ERROR_WRITE: return "Writing error"; + } + + return unkown_error; +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Encode LZMA /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeLZMAbuf +( + void *dest, // valid destination buffer + uint dest_size, // size of 'dest' + uint *dest_written, // store num bytes written to 'dest', never NULL + + const void *src, // source buffer + uint src_size, // size of source buffer + + int compr_level, // valid are 1..9 / 0: use default value + bool add_dec_size // true: add decompressed size +) +{ + #ifdef NO_LZMA + DASSERT(dest_written); + *dest_written = 0; + return ERROR0(ERR_LZMA,"LZMA compression not supported\n"); + #else // !NO_LZMA + + DASSERT(dest); + DASSERT( dest_size > 20 ); + DASSERT(dest_written); + DASSERT(src); + + const int header_size = LZMA_PROPS_SIZE + 8; + compr_level = CalcCompressionLevelLZMA(compr_level); + size_t out_len = 0; + PRINT0("EncodeLZMAbuf() %u bytes, level = %u\n",src_size,compr_level); + + if (add_dec_size) + { + *(u32*)dest = htonl(src_size); + dest += sizeof(u32); + dest_size -= sizeof(u32); + } + + CLzmaEncHandle lzma = LzmaEnc_Create(&lzma_alloc); + if (!lzma) + return SZ_ERROR_MEM; + + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = compr_level; + props.dictSize = GetDicSizeByLevel(compr_level); + LzmaEncProps_Normalize(&props); + + SRes res = LzmaEnc_SetProps(lzma,&props); + if ( res == SZ_OK ) + { + size_t prop_size = LZMA_PROPS_SIZE; + res = LzmaEnc_WriteProperties(lzma,dest,&prop_size); +// write_le64(dest+LZMA_PROPS_SIZE,src_size); + write_le64(dest+LZMA_PROPS_SIZE,~0ull); // stream mode + dest += header_size; + dest_size -= header_size; + + if ( res == SZ_OK ) + { + out_len = dest_size; + size_t in_len = src_size; + res = LzmaEnc_MemEncode( lzma, dest, &out_len, src, in_len, + true, 0, &lzma_alloc, &lzma_alloc ); + } + } + LzmaEnc_Destroy( lzma,&lzma_alloc,&lzma_alloc); + *dest_written = out_len + header_size; + if (add_dec_size) + *dest_written += sizeof(u32); + #endif + return res == SZ_OK ? ERR_OK : ERR_LZMA; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeLZMA +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest', never NULL + bool use_iobuf, // true: allow the usage of 'iobuf' + uint header_size, // insert 'header_size' bytes before dest data + bool add_dec_size, // true: add decompressed size + + const void *src, // source buffer + uint src_size, // size of source buffer + + int compr_level // valid are 1..9 / 0: use default value +) +{ + DASSERT(dest_ptr); + DASSERT(dest_written); + DASSERT(src); + + char *dest_alloced = 0, *dest; + uint dest_size = src_size + src_size/100 + 600 + 20; + if ( dest_size <= sizeof(iobuf) && use_iobuf ) + { + dest = iobuf; + dest_size = sizeof(iobuf); + } + else + { + dest_alloced = MALLOC(dest_size+header_size); + memset(dest_alloced,0,header_size); + dest = dest_alloced + header_size; + } + + enumError err = EncodeLZMAbuf( dest, dest_size, dest_written, + src, src_size, compr_level, add_dec_size ); + if (err) + { + FREE(dest_alloced); + *dest_ptr = 0; + *dest_written = 0; + } + else if (dest_alloced) + { + *dest_written += header_size; + *dest_ptr = REALLOC(dest_alloced,*dest_written); + } + else + { + DASSERT(dest==iobuf); + dest = MALLOC(*dest_written+header_size); + memset(dest,0,header_size); + memcpy(dest+header_size,iobuf,*dest_written); + *dest_ptr = (u8*)dest; + *dest_written += header_size; + } + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Decode LZMA /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeLZMAbin +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest', never NULL + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer + uint src_size // size of source buffer +) +{ + DASSERT(dest_ptr); + DASSERT(dest_written); + DASSERT( src || !src_size ); + + *dest_ptr = 0; + *dest_written = 0; + + if ( !src || src_size <= LZMA_PROPS_SIZE + 8 ) + return ERR_NOTHING_TO_DO; + + #ifdef NO_LZMA + return ERROR0(ERR_LZMA,"LZMA compression not supported\n"); + #else // !NO_LZMA + + CLzmaDec lzma; + LzmaDec_Construct(&lzma); + SRes res = LzmaDec_Allocate(&lzma,src,LZMA_PROPS_SIZE,&lzma_alloc); + if ( res != SZ_OK ) + return ERROR0(ERR_LZMA, + "Error while setup LZMA properties: %s\n", GetMessageLZMA(res,"?") ); + + src += LZMA_PROPS_SIZE; + src_size -= LZMA_PROPS_SIZE; + + size_t in_len = le64(src); + src += 8; + src_size -= 8; + + FastBuf_t destbuf; + InitializeFastBuf(&destbuf,sizeof(destbuf)); + + const int outbuf_size = 0x200000; + u8 *outbuf = MALLOC(outbuf_size); + + LzmaDec_Init(&lzma); + + while ( src_size > 0 ) + { + size_t out_len = outbuf_size; + ELzmaFinishMode finish = LZMA_FINISH_ANY; + ELzmaStatus status; + + res = LzmaDec_DecodeToBuf(&lzma,outbuf,&out_len,src,&in_len,finish,&status); + PRINT0("DECODED, res=%s, stat=%d, in=%zu/%u out=%zu/%u\n", + GetMessageLZMA(res,"?"), status, + in_len, src_size, out_len, outbuf_size ); + + if ( res == SZ_OK && !in_len && !out_len && status != LZMA_STATUS_FINISHED_WITH_MARK ) + res = SZ_ERROR_DATA; + + if ( res != SZ_OK ) + { + ERROR0(ERR_LZMA, + "Error while reading LZMA stream: %s\n", GetMessageLZMA(res,"?") ); + goto error; + } + + AppendFastBuf(&destbuf,outbuf,out_len); + + if ( in_len < src_size ) + { + src_size -= in_len; + src += in_len; + } + else + break; + + if ( status == LZMA_STATUS_FINISHED_WITH_MARK ) + break; + } + + LzmaDec_Free(&lzma,&lzma_alloc); + FREE(outbuf); + *dest_ptr = MEMDUP(destbuf.buf,GetFastBufLen(&destbuf)); + *dest_written = GetFastBufLen(&destbuf); + ResetFastBuf(&destbuf); + return ERR_OK; + + error:; + LzmaDec_Free(&lzma,&lzma_alloc); + FREE(outbuf); + ResetFastBuf(&destbuf); + return ERR_LZMA; + + #endif // !NO_LZMA +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeLZMApart +( + // decompress until dest buffer full or end of source reached + // return ERR_WARNING for an incomplete decompression + + void *dest_buf, // result: store read data here + uint dest_size, // size of 'dest_buf' + uint *dest_written, // store num bytes written to 'dest_ptr', never NULL + + const void *src, // source buffer + uint src_size // size of source buffer +) +{ + DASSERT(dest_ptr); + DASSERT(dest_written); + DASSERT( src || !src_size ); + + *dest_written = 0; + + if ( !src || src_size <= LZMA_PROPS_SIZE + 8 ) + return ERR_NOTHING_TO_DO; + + #ifdef NO_LZMA + return ERROR0(ERR_LZMA,"LZMA compression not supported\n"); + #else // !NO_LZMA + + CLzmaDec lzma; + LzmaDec_Construct(&lzma); + SRes res = LzmaDec_Allocate(&lzma,src,LZMA_PROPS_SIZE,&lzma_alloc); + if ( res != SZ_OK ) + return ERROR0(ERR_LZMA, + "Error while setup LZMA properties: %s\n", GetMessageLZMA(res,"?") ); + + src += LZMA_PROPS_SIZE; + src_size -= LZMA_PROPS_SIZE; + + size_t in_len = le64(src); + src += 8; + src_size -= 8; + + LzmaDec_Init(&lzma); + + size_t out_len = dest_size; + ELzmaFinishMode finish = LZMA_FINISH_ANY; + ELzmaStatus status; + + res = LzmaDec_DecodeToBuf(&lzma,dest_buf,&out_len,src,&in_len,finish,&status); + PRINT0("DECODED, res=%s, stat=%d, in=%zu/%u out=%zu/%u\n", + GetMessageLZMA(res,"?"), status, + in_len, src_size, out_len, dest_size ); + + if ( res == SZ_OK && !in_len && !out_len && status != LZMA_STATUS_FINISHED_WITH_MARK ) + res = SZ_ERROR_DATA; + + if ( res != SZ_OK ) + { + ERROR0(ERR_LZMA, + "Error while reading LZMA stream: %s\n", GetMessageLZMA(res,"?") ); + goto error; + } + + LzmaDec_Free(&lzma,&lzma_alloc); + *dest_written = out_len; + return in_len < src_size ? ERR_WARNING : ERR_OK; + + error:; + LzmaDec_Free(&lzma,&lzma_alloc); + return ERR_LZMA; + + #endif // !NO_LZMA +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeLZMAsize +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest', never NULL + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer, first 4 bytes = dest size + uint src_size // size of source buffer +) +{ + DASSERT(dest_ptr); + DASSERT(dest_written); + DASSERT(src); + DASSERT( src_size >= sizeof(u32) ); + + uint dest_size = ntohl(*(u32*)src)+4; // +4: to suppress abort because dest buff is full + u8 *dest = MALLOC(dest_size+header_size); + memset(dest,0,header_size); + *dest_written = header_size; + + enumError err = DecodeLZMApart(dest+header_size,dest_size,dest_written,src+4,src_size-4); + if (err) + { + FREE(dest); + *dest_ptr = 0; + *dest_written = 0; + } + else + *dest_ptr = dest; + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XZ helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IsXZ +( + // returns + // -1: not XZ data + // 0: seems to be LZMA data; compression level is unknown + // 1..9: seems to be LZMA data; compression level is returned + + cvp data, // NULL or data to investigate + uint size // size of 'data' +) +{ + const u8 *d = data; + if ( !d || size < 20 ) + return -1; + + u64 magic = be48(d); + if ( magic != XZ_MAGIC_NUM6 ) + return -1; + + switch(d[16]) + { + case 0x10: return 1; + case 0x12: return 2; + case 0x14: return 4; + case 0x16: return 6; + case 0x18: return 7; + case 0x1a: return 8; + case 0x1c: return 9; + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + |
