#include "lzma.h" #define _7ZIP_ST #include "lib/LzmaDec.h" #include "lib/LzmaEnc.h" namespace Upp { static voidpf lzma_alloc_new(ISzAllocPtr, size_t size) { voidpf t = new byte[size]; return t; } static void lzma_free_new(ISzAllocPtr, void *address) { if(!address) return; delete[] (byte *)address; } static ISzAlloc g_Alloc = { &lzma_alloc_new, &lzma_free_new }; struct IUppInStream { ISeqInStream s; Stream& in; static SRes Read(const ISeqInStream *pp, void *buf, size_t *size) { IUppInStream *p = (IUppInStream *)pp; int64 h = (size_t)p->in.Get64(buf, *size); if(p->in.IsError() || h < 0) { *size = 0; return SZ_ERROR_READ; } *size = (size_t)h; return SZ_OK; } IUppInStream(Stream& in) : in(in) { s.Read = Read; } }; struct IUppOutStream { ISeqOutStream s; Stream& out; int64 len; static size_t Write(const ISeqOutStream *pp, const void *buf, size_t size) { IUppOutStream *p = (IUppOutStream *)pp; p->out.Put64(buf, size); p->len += size; return p->out.IsError() ? -1 : size; } IUppOutStream(Stream& out) : out(out) { s.Write = Write; len = 0; } }; #define IN_BUF_SIZE (1 << 16) #define OUT_BUF_SIZE (1 << 16) static SRes Decode2(CLzmaDec *state, ISeqOutStream *outStream, ISeqInStream *inStream, UInt64 unpackSize, Gate2 progress) { int thereIsSize = (unpackSize != (UInt64)(Int64)-1); Byte inBuf[IN_BUF_SIZE]; Byte outBuf[OUT_BUF_SIZE]; size_t inPos = 0, inSize = 0, outPos = 0; LzmaDec_Init(state); uint64 todo = unpackSize; for(;;) { if(inPos == inSize) { inSize = IN_BUF_SIZE; RINOK(inStream->Read(inStream, inBuf, &inSize)); inPos = 0; } SRes res; SizeT inProcessed = inSize - inPos; SizeT outProcessed = OUT_BUF_SIZE - outPos; ELzmaFinishMode finishMode = LZMA_FINISH_ANY; ELzmaStatus status; if(thereIsSize && outProcessed > unpackSize) { outProcessed = (SizeT)unpackSize; finishMode = LZMA_FINISH_END; } res = LzmaDec_DecodeToBuf(state, outBuf + outPos, &outProcessed, inBuf + inPos, &inProcessed, finishMode, &status); inPos += inProcessed; outPos += outProcessed; unpackSize -= outProcessed; if(progress(inPos, todo)) return SZ_ERROR_WRITE; if(outStream) if(outStream->Write(outStream, outBuf, outPos) != outPos) return SZ_ERROR_WRITE; outPos = 0; if(res != SZ_OK || thereIsSize && unpackSize == 0) return res; if(inProcessed == 0 && outProcessed == 0) { if(thereIsSize || status != LZMA_STATUS_FINISHED_WITH_MARK) return SZ_ERROR_DATA; return res; } } } static SRes Decode(ISeqOutStream *outStream, ISeqInStream *inStream, Gate2 progress) { UInt64 unpackSize; int i; SRes res = 0; CLzmaDec state; /* header: 5 bytes of LZMA properties and 8 bytes of uncompressed size */ unsigned char header[LZMA_PROPS_SIZE + 8]; /* Read and parse header */ IUppInStream *is = (IUppInStream *)inStream; if(is->in.Get(header, sizeof(header)) != sizeof(header)) return SZ_ERROR_READ; unpackSize = 0; for(i = 0; i < 8; i++) unpackSize += (UInt64)header[LZMA_PROPS_SIZE + i] << (i * 8); LzmaDec_Construct(&state); RINOK(LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc)); res = Decode2(&state, outStream, inStream, unpackSize, progress); LzmaDec_Free(&state, &g_Alloc); return res; } struct LUppProgress { ICompressProgress s; Gate2 progress; int64 total; static SRes Progress(const ICompressProgress *pp, UInt64 inSize, UInt64) { LUppProgress *p = (LUppProgress *)pp; return p->progress(inSize, p->total); } LUppProgress() { s.Progress = Progress; } }; static SRes Encode(ISeqOutStream *outStream, ISeqInStream *inStream, UInt64 fileSize, ICompressProgress *pg, int lvl) { CLzmaEncHandle enc; SRes res; CLzmaEncProps props; enc = LzmaEnc_Create(&g_Alloc); if (enc == 0) return SZ_ERROR_MEM; LzmaEncProps_Init(&props); props.level = lvl; res = LzmaEnc_SetProps(enc, &props); if(res == SZ_OK) { Byte header[LZMA_PROPS_SIZE + 8]; size_t headerSize = LZMA_PROPS_SIZE; int i; res = LzmaEnc_WriteProperties(enc, header, &headerSize); for(i = 0; i < 8; i++) header[headerSize++] = (Byte)(fileSize >> (8 * i)); if(outStream->Write(outStream, header, headerSize) != headerSize) res = SZ_ERROR_WRITE; else { if(res == SZ_OK) res = LzmaEnc_Encode(enc, outStream, inStream, pg, &g_Alloc, &g_Alloc); } } LzmaEnc_Destroy(enc, &g_Alloc, &g_Alloc); return res; } int64 LZMACompress(Stream& out, Stream& in, int64 size, Gate2 progress, int lvl) { IUppInStream lin(in); IUppOutStream lout(out); LUppProgress pg; pg.progress = progress; pg.total = size; return Encode(&lout.s, &lin.s, size, &pg.s, lvl) == SZ_OK && !in.IsError() && !out.IsError() ? lout.len : -1; } int64 LZMACompress(Stream& out, Stream& in, Gate2 progress, int lvl) { return LZMACompress(out, in, in.GetLeft(), progress, lvl); } String LZMACompress(const void *data, int64 len, Gate2 progress, int lvl) { StringStream out; MemReadStream in(data, len); if(LZMACompress(out, in, len, progress, lvl) >= 0) return out; return String::GetVoid(); } String LZMACompress(const String& s, Gate2 progress, int lvl) { return LZMACompress(~s, s.GetLength(), progress, lvl); } int64 LZMADecompress(Stream& out, Stream& in, Gate2 progress) { IUppInStream lin(in); IUppOutStream lout(out); return Decode(&lout.s, &lin.s, progress) == SZ_OK && !in.IsError() && !out.IsError() ? lout.len : -1; } String LZMADecompress(const void *data, int64 len, Gate2 progress) { StringStream out; MemReadStream in(data, len); if(LZMADecompress(out, in, progress) >= 0) return out; return String::GetVoid(); } String LZMADecompress(const String& s, Gate2 progress) { return LZMADecompress(~s, s.GetLength(), progress); } bool LZMACompressFile(const char *dstfile, const char *srcfile, Gate2 progress, int lvl) { FileIn in(srcfile); if(!in) return false; FileOut out(dstfile); if(!out) return false; if(LZMACompress(out, in, (int)in.GetLeft(), progress, lvl) < 0) return false; out.Close(); return !out.IsError(); } bool LZMACompressFile(const char *srcfile, Gate2 progress, int lvl) { String dstfile = String(srcfile) + ".lzma"; return LZMACompressFile(dstfile, srcfile, progress, lvl); } bool LZMADecompressFile(const char *dstfile, const char *srcfile, Gate2 progress) { FileIn in(srcfile); if(!in) return false; FileOut out(dstfile); if(!out) return false; if(LZMADecompress(out, in, progress) < 0) return false; out.Close(); return !out.IsError(); } bool LZMADecompressFile(const char *srcfile, Gate2 progress) { String dstfile = srcfile; if(dstfile.EndsWith(".lzma")) dstfile.Trim(dstfile.GetLength() - 5); else return false; return LZMADecompressFile(dstfile, srcfile, progress); } }