Coverage Report

Created: 2026-10-28 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreDeflate.cpp
Line
Count
Source
1
/*
2
 -----------------------------------------------------------------------------
3
 This source file is part of OGRE
4
 (Object-oriented Graphics Rendering Engine)
5
 For the latest info, see http://www.ogre3d.org/
6
 
7
 Copyright (c) 2000-2014 Torus Knot Software Ltd
8
 
9
 Permission is hereby granted, free of charge, to any person obtaining a copy
10
 of this software and associated documentation files (the "Software"), to deal
11
 in the Software without restriction, including without limitation the rights
12
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
 copies of the Software, and to permit persons to whom the Software is
14
 furnished to do so, subject to the following conditions:
15
 
16
 The above copyright notice and this permission notice shall be included in
17
 all copies or substantial portions of the Software.
18
 
19
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
 THE SOFTWARE.
26
 -----------------------------------------------------------------------------
27
 */
28
#include "OgreStableHeaders.h"
29
30
#if OGRE_NO_ZIP_ARCHIVE == 0
31
32
#include "OgreDeflate.h"
33
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
34
#include "macUtils.h"
35
#endif
36
#include "OgreFileSystemLayer.h"
37
#include "OgreFileSystemUtil.h"
38
#include "OgreRoot.h"
39
40
#define MINIZ_HEADER_FILE_ONLY
41
#include <miniz.h>
42
43
namespace Ogre
44
{
45
    // memory implementations
46
    static void* OgreZalloc(void* opaque, size_t items, size_t size)
47
0
    {
48
0
        return OGRE_MALLOC(items * size, MEMCATEGORY_GENERAL);
49
0
    }
50
    static void OgreZfree(void* opaque, void* address)
51
0
    {
52
0
        OGRE_FREE(address, MEMCATEGORY_GENERAL);
53
0
    }
54
0
    #define OGRE_DEFLATE_TMP_SIZE 16384
55
    //---------------------------------------------------------------------
56
    DeflateStream::DeflateStream(const DataStreamPtr& compressedStream, const String& tmpFileName, size_t avail_in)
57
0
    : DataStream(compressedStream->getAccessMode())
58
0
    , mCompressedStream(compressedStream)
59
0
    , mTempFileName(tmpFileName)
60
0
    , mZStream(0)
61
0
    , mCurrentPos(0)
62
0
    , mAvailIn(avail_in)
63
0
    , mTmp(0)
64
0
    , mStreamType(ZLib)
65
0
    {
66
0
        init();
67
0
    }
68
    //---------------------------------------------------------------------
69
    DeflateStream::DeflateStream(const String& name, const DataStreamPtr& compressedStream, const String& tmpFileName, size_t avail_in)
70
0
    : DataStream(name, compressedStream->getAccessMode())
71
0
    , mCompressedStream(compressedStream)
72
0
    , mTempFileName(tmpFileName)
73
0
    , mZStream(0)
74
0
    , mCurrentPos(0)
75
0
    , mAvailIn(avail_in)
76
0
    , mTmp(0)
77
0
    , mStreamType(ZLib)
78
0
    {
79
0
        init();
80
0
    }
81
    //---------------------------------------------------------------------
82
    DeflateStream::DeflateStream(const String& name, const DataStreamPtr& compressedStream, StreamType streamType, const String& tmpFileName, size_t avail_in)
83
0
    : DataStream(name, compressedStream->getAccessMode())
84
0
    , mCompressedStream(compressedStream)
85
0
    , mTempFileName(tmpFileName)
86
0
    , mZStream(0)
87
0
    , mCurrentPos(0)
88
0
    , mAvailIn(avail_in)
89
0
    , mTmp(0)
90
0
    , mStreamType(streamType)
91
0
    {
92
0
        init();
93
0
    }
94
    //---------------------------------------------------------------------
95
    size_t DeflateStream::getAvailInForSinglePass()
96
0
    {
97
0
        size_t ret = OGRE_DEFLATE_TMP_SIZE;
98
99
        // if we are doing particial-uncompressing
100
0
        if(mAvailIn>0)
101
0
        {
102
0
            if(mAvailIn<ret)
103
0
                ret = mAvailIn;
104
0
            mAvailIn -= ret;
105
0
        }
106
107
0
        return ret;
108
0
    }
109
    //---------------------------------------------------------------------
110
    void DeflateStream::init()
111
0
    {
112
0
        mZStream = OGRE_ALLOC_T(z_stream, 1, MEMCATEGORY_GENERAL);
113
0
        mZStream->zalloc = OgreZalloc;
114
0
        mZStream->zfree = OgreZfree;
115
        
116
0
        if (getAccessMode() == READ)
117
0
        {
118
0
            mTmp = (unsigned char*)OGRE_MALLOC(OGRE_DEFLATE_TMP_SIZE, MEMCATEGORY_GENERAL);
119
0
            size_t restorePoint = mCompressedStream->tell();
120
            // read early chunk
121
0
            mZStream->next_in = mTmp;
122
0
            mZStream->avail_in = static_cast<uint>(mCompressedStream->read(mTmp, getAvailInForSinglePass()));
123
            
124
0
            int windowBits = (mStreamType == Deflate) ? -MAX_WBITS : (mStreamType == GZip) ? 16 + MAX_WBITS : MAX_WBITS;
125
0
            if (inflateInit2(mZStream, windowBits) != Z_OK)
126
0
            {
127
0
                mStreamType = Invalid;
128
0
            }
129
            
130
0
            if (mStreamType != Invalid)
131
0
            {
132
                // in fact, inflateInit on some implementations doesn't try to read
133
                // anything. We need to at least read something to test
134
0
                Bytef testOut[4];
135
0
                size_t savedIn = mZStream->avail_in;
136
0
                mZStream->avail_out = 4;
137
0
                mZStream->next_out = testOut;
138
0
                if (inflate(mZStream, Z_SYNC_FLUSH) != Z_OK)
139
0
                    mStreamType = Invalid;
140
                // restore for reading
141
0
                mZStream->avail_in = static_cast<uint>(savedIn);
142
0
                mZStream->next_in = mTmp;
143
144
0
                inflateReset(mZStream);
145
0
            }
146
147
0
            if (mStreamType == Invalid)
148
0
            {
149
                // Not compressed data!
150
                // Fail gracefully, fall back on reading the underlying stream direct
151
0
                destroy();
152
0
                mCompressedStream->seek(restorePoint);
153
0
            }               
154
0
        }
155
0
        else 
156
0
        {
157
0
            if(mTempFileName.empty())
158
0
            {
159
                // Write to temp file
160
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WINRT
161
#  if OGRE_FILESYSTEM_USE_WIDECHAR
162
                // _tempnam returns a name in the ANSI code page, which cannot
163
                // represent every directory. Use the wide variant and store the
164
                // result as UTF-8, like every other Ogre path.
165
                wchar_t* wtmpname = _wtempnam(FileSystemUtil::toWide(".").c_str(), L"ogre");
166
                if (!wtmpname)
167
                {
168
                    // Having no file name here will cause various problems later.
169
                    OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Temporary file name generation failed.", "DeflateStream::init");
170
                }
171
                else
172
                {
173
                    mTempFileName = FileSystemUtil::fromWide(wtmpname);
174
                    free(wtmpname);
175
                }
176
#  else
177
                char* tmpname = _tempnam(".", "ogre");
178
                if (!tmpname)
179
                {
180
                    // Having no file name here will cause various problems later.
181
                    OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Temporary file name generation failed.", "DeflateStream::init");
182
                }
183
                else
184
                {
185
                    mTempFileName = tmpname;
186
                    free(tmpname);
187
                }
188
#  endif
189
#elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
190
                mTempFileName = macTempFileName();
191
#else
192
0
                char tmpname[] = "/tmp/ogreXXXXXX";
193
0
                if (mkstemp(tmpname) == -1)
194
0
                    OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Temporary file name generation failed.", "DeflateStream::init");
195
196
0
                mTempFileName = tmpname;
197
0
#endif
198
0
            }
199
200
0
            mTmpWriteStream = _openFileStream(mTempFileName, std::ios::binary | std::ios::out);
201
            
202
0
        }
203
204
0
    }
205
    //---------------------------------------------------------------------
206
    void DeflateStream::destroy()
207
0
    {
208
0
        if (getAccessMode() == READ)
209
0
            inflateEnd(mZStream);
210
211
0
        OGRE_FREE(mZStream, MEMCATEGORY_GENERAL);
212
0
        mZStream = 0;
213
0
        OGRE_FREE(mTmp, MEMCATEGORY_GENERAL);
214
0
        mTmp = 0;
215
0
    }
216
    //---------------------------------------------------------------------
217
    DeflateStream::~DeflateStream()
218
0
    {
219
0
        close();
220
0
    }
221
    //---------------------------------------------------------------------
222
    size_t DeflateStream::read(void* buf, size_t count)
223
0
    {
224
0
        if (mStreamType == Invalid)
225
0
        {
226
0
            return mCompressedStream->read(buf, count);
227
0
        }
228
        
229
0
        if (getAccessMode() & WRITE)
230
0
        {
231
0
            return mTmpWriteStream->read(buf, count);
232
0
        }
233
0
        else 
234
0
        {
235
236
0
            size_t restorePoint = mCompressedStream->tell();
237
            // read from cache first
238
0
            size_t cachereads = mReadCache.read(buf, count);
239
            
240
0
            size_t newReadUncompressed = 0;
241
242
0
            if (cachereads < count)
243
0
            {
244
0
                mZStream->avail_out = static_cast<uint>(count - cachereads);
245
0
                mZStream->next_out = (Bytef*)buf + cachereads;
246
                
247
0
                while (mZStream->avail_out)
248
0
                {
249
                    // Pull next chunk of compressed data from the underlying stream
250
0
                    if (!mZStream->avail_in && !mCompressedStream->eof())
251
0
                    {
252
0
                        mZStream->avail_in = static_cast<uint>(mCompressedStream->read(mTmp, getAvailInForSinglePass()));
253
0
                        mZStream->next_in = mTmp;
254
0
                    }
255
                    
256
0
                    if (mZStream->avail_in || mZStream->avail_out)
257
0
                    {
258
0
                        int availpre = mZStream->avail_out;
259
0
                        mStatus = inflate(mZStream, Z_SYNC_FLUSH);
260
0
                        size_t readUncompressed = availpre - mZStream->avail_out;
261
0
                        newReadUncompressed += readUncompressed;
262
0
                        if (mStatus != Z_OK)
263
0
                        {
264
                            // End of data, or error
265
0
                            if (mStatus != Z_STREAM_END)
266
0
                            {
267
0
                                mCompressedStream->seek(restorePoint);
268
0
                                OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Error in compressed stream");
269
0
                            }
270
0
                            else 
271
0
                            {
272
                                // back up the stream so that it can be used from the end onwards                                                   
273
0
                                long unusedCompressed = mZStream->avail_in;
274
0
                                mCompressedStream->skip(-unusedCompressed);
275
0
                            }
276
277
0
                            break;
278
0
                        }
279
0
                    }
280
0
                }
281
            
282
                // Cache the last bytes read not from cache
283
0
                mReadCache.cacheData((char*)buf + cachereads, newReadUncompressed);
284
0
            }
285
            
286
0
            mCurrentPos += newReadUncompressed + cachereads;
287
            
288
0
            return newReadUncompressed + cachereads;
289
0
        }
290
0
    }
291
    //---------------------------------------------------------------------
292
    size_t DeflateStream::write(const void* buf, size_t count)
293
0
    {
294
0
        if ((getAccessMode() & WRITE) == 0)
295
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
296
0
                        "Not a writable stream", "DeflateStream::write");
297
        
298
0
        return mTmpWriteStream->write(buf, count);
299
0
    }
300
    //---------------------------------------------------------------------
301
    void DeflateStream::compressFinal()
302
0
    {
303
        // Prevent reenterancy
304
0
        if( !mTmpWriteStream )
305
0
            return;
306
        
307
        // Close temp stream
308
0
        mTmpWriteStream->close();
309
0
        mTmpWriteStream.reset();
310
        
311
        // Copy & compress
312
        // We do this rather than compress directly because some code seeks
313
        // around while writing (e.g. to update size blocks) which is not
314
        // possible when compressing on the fly
315
        
316
0
        int ret, flush;
317
0
        char in[OGRE_DEFLATE_TMP_SIZE];
318
0
        char out[OGRE_DEFLATE_TMP_SIZE];
319
        
320
0
        int windowBits = (mStreamType == Deflate) ? -MAX_WBITS : (mStreamType == GZip) ? 16 + MAX_WBITS : MAX_WBITS;
321
0
        if (deflateInit2(mZStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY) != Z_OK)
322
0
        {
323
0
            destroy();
324
0
            OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
325
0
                        "Error initialising deflate compressed stream!",
326
0
                        "DeflateStream::init");
327
0
        }
328
        
329
0
        DataStreamPtr inFile = Root::openFileStream(mTempFileName, "");
330
        
331
0
        do 
332
0
        {
333
0
            size_t read = inFile->read(in, OGRE_DEFLATE_TMP_SIZE);
334
0
            if (read < OGRE_DEFLATE_TMP_SIZE && !inFile->eof())
335
0
            {
336
0
                deflateEnd(mZStream);
337
0
                OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
338
0
                            "Error reading temp uncompressed stream!",
339
0
                            "DeflateStream::init");
340
0
            }
341
0
            mZStream->avail_in = (uInt)read;
342
0
            flush = inFile->eof() ? Z_FINISH : Z_NO_FLUSH;
343
0
            mZStream->next_in = (Bytef*)in;
344
            
345
            /* run deflate() on input until output buffer not full, finish
346
             compression if all of source has been read in */
347
0
            do 
348
0
            {
349
0
                mZStream->avail_out = OGRE_DEFLATE_TMP_SIZE;
350
0
                mZStream->next_out = (Bytef*)out;
351
0
                ret = deflate(mZStream, flush);    /* no bad return value */
352
0
                assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
353
0
                size_t compressed = OGRE_DEFLATE_TMP_SIZE - mZStream->avail_out;
354
0
                mCompressedStream->write(out, compressed);
355
0
            } while (mZStream->avail_out == 0);
356
0
            assert(mZStream->avail_in == 0);     /* all input will be used */
357
            
358
            /* done when last data in file processed */
359
0
        } while (flush != Z_FINISH);
360
0
        assert(ret == Z_STREAM_END);        /* stream will be complete */
361
0
                (void)ret;
362
0
        deflateEnd(mZStream);
363
364
0
        inFile->close();
365
0
        FileSystemLayer::removeFile(mTempFileName);
366
                        
367
0
    }
368
    //---------------------------------------------------------------------
369
    void DeflateStream::skip(long count)
370
0
    {
371
0
        if (mStreamType == Invalid)
372
0
        {
373
0
            mCompressedStream->skip(count);
374
0
            return;
375
0
        }
376
        
377
0
        if (getAccessMode() & WRITE)
378
0
        {
379
0
            mTmpWriteStream->skip(count);
380
0
        }
381
0
        else 
382
0
        {
383
0
            if (count > 0)
384
0
            {
385
0
                if (!mReadCache.ff(count))
386
0
                {
387
0
                    OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
388
0
                                "You can only skip within the cache range in a deflate stream.",
389
0
                                "DeflateStream::skip");
390
0
                }
391
0
            }
392
0
            else if (count < 0)
393
0
            {
394
0
                if (!mReadCache.rewind((size_t)(-count)))
395
0
                {
396
0
                    OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
397
0
                                "You can only skip within the cache range in a deflate stream.",
398
0
                                "DeflateStream::skip");
399
0
                }
400
0
            }
401
0
        }       
402
0
        mCurrentPos = static_cast<size_t>(static_cast<long>(mCurrentPos) + count);
403
        
404
        
405
0
    }
406
    //---------------------------------------------------------------------
407
    void DeflateStream::seek( size_t pos )
408
0
    {
409
0
        if (mStreamType == Invalid)
410
0
        {
411
0
            mCompressedStream->seek(pos);
412
0
            return;
413
0
        }
414
0
        if (getAccessMode() & WRITE)
415
0
        {
416
0
            mTmpWriteStream->seek(pos);
417
0
        }
418
0
        else
419
0
        {
420
0
            if (pos == 0)
421
0
            {
422
0
                mCurrentPos = 0;
423
0
                mZStream->next_in = mTmp;
424
0
                mCompressedStream->seek(0);
425
0
                mZStream->avail_in = static_cast<uint>(mCompressedStream->read(mTmp, getAvailInForSinglePass()));
426
0
                inflateReset(mZStream);
427
0
                mReadCache.clear();
428
0
            }
429
0
            else 
430
0
            {
431
0
                skip(pos - tell());
432
0
            }
433
0
        }       
434
0
    }
435
    //---------------------------------------------------------------------
436
    size_t DeflateStream::tell(void) const
437
0
    {
438
0
        if (mStreamType == Invalid)
439
0
        {
440
0
            return mCompressedStream->tell();
441
0
        }
442
0
        else if(getAccessMode() & WRITE) 
443
0
        {
444
0
            return mTmpWriteStream->tell();
445
0
        }
446
0
        else
447
0
        {
448
0
            return mCurrentPos;
449
0
        }
450
451
0
    }
452
    //---------------------------------------------------------------------
453
    bool DeflateStream::eof(void) const
454
0
    {
455
0
        if (getAccessMode() & WRITE)
456
0
            return mTmpWriteStream->eof();
457
0
        else 
458
0
        {
459
0
            if (mStreamType == Invalid)
460
0
                return mCompressedStream->eof();
461
0
            else
462
0
                return mCompressedStream->eof() && mStatus == Z_STREAM_END;
463
0
        }
464
0
    }
465
    //---------------------------------------------------------------------
466
    void DeflateStream::close(void)
467
0
    {
468
0
        if (getAccessMode() & WRITE)
469
0
            compressFinal();
470
471
0
        destroy();
472
473
0
        mAccess = 0;
474
475
        // don't close underlying compressed stream in case used for something else
476
0
    }
477
    //---------------------------------------------------------------------
478
    
479
    
480
}
481
482
#endif