1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <nel/misc/seven_zip.h>
22 #include <nel/misc/types_nl.h>
23 #include <nel/misc/file.h>
24 #include <nel/misc/path.h>
26 #include <seven_zip/7z.h>
27 #include <seven_zip/7zAlloc.h>
28 #include <seven_zip/7zCrc.h>
29 #include <seven_zip/7zVersion.h>
30 #include <seven_zip/LzmaLib.h>
31 #include <seven_zip/LzmaDec.h>
40 using namespace NLMISC
;
48 /// Input stream class for 7zip archive
49 class CNel7ZipInStream
: public ISeekInStream
51 NLMISC::IStream
*_Stream
;
54 /// Constructor, only allow file stream because 7zip will 'seek' in the stream
55 CNel7ZipInStream(NLMISC::IStream
*s
)
62 // the read function called by 7zip to read data
63 static SRes
readFunc(const ISeekInStream
*object
, void *buffer
, size_t *size
)
67 CNel7ZipInStream
*me
= (CNel7ZipInStream
*)object
;
68 uint len
= (uint
)*size
;
69 me
->_Stream
->serialBuffer((uint8
*)buffer
, len
);
78 // the seek function called by seven zip to seek inside stream
79 static SRes
seekFunc(const ISeekInStream
*object
, Int64
*pos
, ESzSeek origin
)
83 CNel7ZipInStream
*me
= (CNel7ZipInStream
*)object
;
84 sint32 offset
= (sint32
)*pos
;
85 bool ret
= me
->_Stream
->seek(offset
, (NLMISC::IStream::TSeekOrigin
)origin
);
89 *pos
= (Int64
)me
->_Stream
->getPos();
101 bool unpack7Zip(const std::string
&sevenZipFile
, const std::string
&destFileName
)
103 nlinfo("Uncompressing 7zip archive '%s' to '%s'", sevenZipFile
.c_str(), destFileName
.c_str());
107 allocImp
.Alloc
= SzAlloc
;
108 allocImp
.Free
= SzFree
;
110 ISzAlloc allocTempImp
;
111 allocTempImp
.Alloc
= SzAllocTemp
;
112 allocTempImp
.Free
= SzFreeTemp
;
114 // wrap file in a CIFile
115 CIFile
input(sevenZipFile
);
116 CNel7ZipInStream
inStr(&input
);
118 CLookToRead2 lookStream
;
119 lookStream
.realStream
= &inStr
;
120 LookToRead2_CreateVTable(&lookStream
, False
);
122 size_t bufferSize
= 1024;
125 lookStream
.buf
= (Byte
*)ISzAlloc_Alloc(&allocImp
, bufferSize
);
129 nlerror("Unable to allocate %zu bytes", bufferSize
);
133 lookStream
.bufSize
= bufferSize
;
134 lookStream
.realStream
= &inStr
;
135 LookToRead2_Init(&lookStream
);
143 // unpack the file using the 7zip API
144 SRes res
= SzArEx_Open(&db
, &lookStream
.vt
, &allocImp
, &allocTempImp
);
148 nlerror("Failed to open archive file %s", sevenZipFile
.c_str());
152 if (db
.NumFiles
!= 1)
154 nlerror("Seven zip archive with more than 1 file are unsupported");
158 UInt32 blockIndex
= 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
159 Byte
*outBuffer
= 0; /* it must be 0 before first call for each new archive. */
160 size_t outBufferSize
= 0; /* it can have any value before first call (if outBuffer = 0) */
163 size_t outSizeProcessed
= 0;
165 // get the first file
166 res
= SzArEx_Extract(&db
, &lookStream
.vt
, 0, &blockIndex
, &outBuffer
, &outBufferSize
, &offset
, &outSizeProcessed
, &allocImp
, &allocTempImp
);
168 // get the length of first file
169 size_t nameLen
= SzArEx_GetFileNameUtf16(&db
, 0, NULL
);
172 filename
.resize(nameLen
);
174 // write filename into ucstring
175 SzArEx_GetFileNameUtf16(&db
, 0, reinterpret_cast<UInt16
*>(&filename
[0]));
177 // write the extracted file
178 FILE *outputHandle
= nlfopen(destFileName
, "wb+");
180 if (outputHandle
== 0)
182 nlerror("Can not open output file '%s'", destFileName
.c_str());
186 UInt32 processedSize
= (UInt32
)fwrite(outBuffer
+ offset
, 1, outSizeProcessed
, outputHandle
);
188 if (processedSize
!= outSizeProcessed
)
190 nlerror("Failed to write %u char to output file '%s'", outSizeProcessed
-processedSize
, destFileName
.c_str());
194 fclose(outputHandle
);
196 IAlloc_Free(&allocImp
, outBuffer
);
199 SzArEx_Free(&db
, &allocImp
);
201 // ok, all is fine, file is unpacked
205 bool unpackLZMA(const std::string
&lzmaFile
, const std::string
&destFileName
)
207 nldebug("unpackLZMA: decompress LZMA file '%s' to '%s", lzmaFile
.c_str(), destFileName
.c_str());
210 CIFile
inStream(lzmaFile
);
211 uint32 inSize
= inStream
.getFileSize();
213 if (inSize
< LZMA_PROPS_SIZE
+ 8)
215 nlwarning("unpackLZMA: Invalid file size, too small file '%s'", lzmaFile
.c_str());
219 // allocate input buffer for props
220 std::vector
<uint8
> propsBuffer(LZMA_PROPS_SIZE
);
222 // size of LZMA content
223 inSize
-= LZMA_PROPS_SIZE
+ 8;
225 // allocate input buffer for lzma data
226 std::vector
<uint8
> inBuffer(inSize
);
233 inStream
.serialBuffer(&propsBuffer
[0], LZMA_PROPS_SIZE
);
235 // read uncompressed size
236 inStream
.serial(fileSize
);
239 inStream
.serialBuffer(&inBuffer
[0], inSize
);
241 catch(const EReadError
&e
)
243 nlwarning("unpackLZMA: Error while reading '%s': %s", lzmaFile
.c_str(), e
.what());
247 // allocate the output buffer
248 std::vector
<uint8
> outBuffer(fileSize
);
250 // in and out file sizes
251 SizeT outProcessed
= (SizeT
)fileSize
;
252 SizeT inProcessed
= (SizeT
)inSize
;
254 // decompress the file in memory
255 sint res
= LzmaUncompress(&outBuffer
[0], &outProcessed
, &inBuffer
[0], &inProcessed
, &propsBuffer
[0], LZMA_PROPS_SIZE
);
257 if (res
!= 0 || outProcessed
!= fileSize
)
259 nlwarning("unpackLZMA: Failed to decode lzma file '%s' with status %d", lzmaFile
.c_str(), res
);
263 // store on output buffer
264 COFile
outStream(destFileName
);
269 outStream
.serialBuffer(&outBuffer
[0], (uint
)fileSize
);
271 catch(const EFile
&e
)
273 nlwarning("unpackLZMA: Error while writing '%s': %s", destFileName
.c_str(), e
.what());
274 CFile::deleteFile(destFileName
);
281 bool unpackLZMA(const std::string
&lzmaFile
, const std::string
&destFileName
, CHashKey
&sha1
)
283 nldebug("unpackLZMA: decompress LZMA file '%s' to '%s", lzmaFile
.c_str(), destFileName
.c_str());
286 CIFile
inStream(lzmaFile
);
287 uint32 inSize
= inStream
.getFileSize();
289 if (inSize
< LZMA_PROPS_SIZE
+ 8)
291 nlwarning("unpackLZMA: Invalid file size, too small file '%s'", lzmaFile
.c_str());
295 // allocate input buffer for props
296 std::vector
<uint8
> propsBuffer(LZMA_PROPS_SIZE
);
298 // size of LZMA content
299 inSize
-= LZMA_PROPS_SIZE
+ 8;
301 // allocate input buffer for lzma data
302 std::vector
<uint8
> inBuffer(inSize
);
309 inStream
.serialBuffer(&propsBuffer
[0], LZMA_PROPS_SIZE
);
311 // read uncompressed size
312 inStream
.serial(fileSize
);
315 inStream
.serialBuffer(&inBuffer
[0], inSize
);
317 catch(const EReadError
&e
)
319 nlwarning("unpackLZMA: Error while reading '%s': %s", lzmaFile
.c_str(), e
.what());
323 // allocate the output buffer
324 std::vector
<uint8
> outBuffer(fileSize
);
326 // in and out file sizes
327 SizeT outProcessed
= (SizeT
)fileSize
;
328 SizeT inProcessed
= (SizeT
)inSize
;
330 // decompress the file in memory
331 sint res
= LzmaUncompress(&outBuffer
[0], &outProcessed
, &inBuffer
[0], &inProcessed
, &propsBuffer
[0], LZMA_PROPS_SIZE
);
333 if (res
!= 0 || outProcessed
!= fileSize
)
335 nlwarning("unpackLZMA: Failed to decode lzma file '%s' with status %d", lzmaFile
.c_str(), res
);
340 sha1
= getSHA1(&outBuffer
[0], outBuffer
.size());
342 // store on output buffer
343 COFile
outStream(destFileName
);
348 outStream
.serialBuffer(&outBuffer
[0], (uint
)fileSize
);
350 catch(const EFile
&e
)
352 nlwarning("unpackLZMA: Error while writing '%s': %s", destFileName
.c_str(), e
.what());
353 CFile::deleteFile(destFileName
);
360 bool packLZMA(const std::string
&srcFileName
, const std::string
&lzmaFileName
)
362 nldebug("packLZMA: compress '%s' to LZMA file '%s", srcFileName
.c_str(), lzmaFileName
.c_str());
365 CIFile
inStream(srcFileName
);
366 size_t inSize
= inStream
.getFileSize();
371 nlwarning("packLZMA: File '%s' not found or empty", srcFileName
.c_str());
375 // allocate input buffer
376 std::vector
<uint8
> inBuffer(inSize
);
380 // read file in buffer
381 inStream
.serialBuffer(&inBuffer
[0], inSize
);
383 catch(const EReadError
&e
)
385 nlwarning("packLZMA: Error while reading '%s': %s", srcFileName
.c_str(), e
.what());
389 // allocate output buffer
390 size_t outSize
= (11 * inSize
/ 10) + 65536; // worst case = 1.1 * size + 64K
391 std::vector
<uint8
> outBuffer(outSize
);
393 // allocate buffer for props
394 size_t outPropsSize
= LZMA_PROPS_SIZE
;
395 std::vector
<uint8
> outProps(outPropsSize
);
397 // compress with best compression and other default settings
398 sint res
= LzmaCompress(&outBuffer
[0], &outSize
, &inBuffer
[0], inSize
, &outProps
[0], &outPropsSize
, 9, 1 << 27, -1, -1, -1, -1, 1);
404 // store on output buffer
405 COFile
outStream(lzmaFileName
);
407 // unable to create file
408 if (!outStream
.isOpen())
410 nlwarning("packLZMA: Unable to create '%s'", srcFileName
.c_str());
417 outStream
.serialBuffer(&outProps
[0], (uint
)outPropsSize
);
419 // write uncompressed size
420 uint64 uncompressSize
= inSize
;
421 outStream
.serial(uncompressSize
);
424 outStream
.serialBuffer(&outBuffer
[0], (uint
)outSize
);
426 catch(const EFile
&e
)
428 nlwarning("packLZMA: Error while writing '%s': %s", lzmaFileName
.c_str(), e
.what());
429 CFile::deleteFile(lzmaFileName
);
437 nlwarning("packLZMA: Memory allocation error while compressing '%s' (input buffer size: %u, output buffer size: %u)", srcFileName
.c_str(), (uint
)inSize
, (uint
)outSize
);
441 nlwarning("packLZMA: Incorrect parameter while compressing '%s'", srcFileName
.c_str());
444 case SZ_ERROR_OUTPUT_EOF
:
445 nlwarning("packLZMA: Output buffer overflow while compressing '%s' (input buffer size: %u, output buffer size: %u)", srcFileName
.c_str(), (uint
)inSize
, (uint
)outSize
);
448 case SZ_ERROR_THREAD
:
449 nlwarning("packLZMA: Errors in multithreading functions (only for Mt version)");
453 nlwarning("packLZMA: Unknown error (%d) while compressing '%s'", res
, srcFileName
.c_str());