1 /*******************************************************
2 
3    CoolReader Engine
4 
5    lvstream.cpp:  stream classes implementation
6 
7    (c) Vadim Lopatin, 2000-2009
8    This source code is distributed under the terms of
9    GNU General Public License
10    See LICENSE file for details
11 
12  * In addition, as a special exception, the copyright holders give
13  * permission to link the code of portions of this program with the
14  * UNRAR library under certain conditions as described in each
15  * individual source file, and distribute linked combinations
16  * including the two.
17  * You must obey the GNU General Public License in all respects
18  * for all of the code used other than OpenSSL.  If you modify
19  * file(s) with this exception, you may extend this exception to your
20  * version of the file(s), but you are not obligated to do so.  If you
21  * do not wish to do so, delete this exception statement from your
22  * version.  If you delete this exception statement from all source
23  * files in the program, then also delete it here.
24 
25 *******************************************************/
26 
27 #include "../include/lvstream.h"
28 #include "../include/lvptrvec.h"
29 #include "../include/crtxtenc.h"
30 #include "../include/crlog.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 //#define USE_UNRAR 1
36 
37 #if (USE_ZLIB==1)
38 #include <zlib.h>
39 #endif
40 
41 #if (USE_UNRAR==1)
42 #include <rar.hpp>
43 #endif
44 
45 #if !defined(__SYMBIAN32__) && defined(_WIN32)
46 extern "C" {
47 #include <windows.h>
48 }
49 #else
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <dirent.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <sys/mman.h>
56 #endif
57 
58 #ifdef _LINUX
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <dlfcn.h>
63 #include <errno.h>
64 #endif
65 
66 
67 #ifndef USE_ANSI_FILES
68 
69 #if !defined(__SYMBIAN32__) && defined(_WIN32)
70 #define USE_ANSI_FILES 0
71 #else
72 #define USE_ANSI_FILES 1
73 #endif
74 
75 #endif
76 
77 
78 static LVAssetContainerFactory * _assetContainerFactory = NULL;
79 
80 /// set container to handle filesystem access for paths started with ASSET_PATH_PREFIX (@ sign)
LVSetAssetContainerFactory(LVAssetContainerFactory * asset)81 void LVSetAssetContainerFactory(LVAssetContainerFactory * asset) {
82 	_assetContainerFactory = asset;
83 }
84 
LVExtractAssetPath(lString32 fn)85 lString32 LVExtractAssetPath(lString32 fn) {
86 	if (fn.length() < 2 || fn[0] != ASSET_PATH_PREFIX)
87 		return lString32();
88 	if (fn[1] == '/' || fn[1] == '\\')
89 		return fn.substr(2);
90 	return fn.substr(1);
91 }
92 
93 // LVStorageObject stubs
GetName()94 const lChar32 * LVStorageObject::GetName()
95 {
96     return NULL;
97 }
98 
GetParentContainer()99 LVContainer * LVStorageObject::GetParentContainer()
100 {
101     return NULL;
102 }
103 
SetName(const lChar32 *)104 void LVStorageObject::SetName(const lChar32 *)
105 {
106 }
107 
IsContainer()108 bool LVStorageObject::IsContainer()
109 {
110     return false;
111 }
112 
GetSize()113 lvsize_t LVStorageObject::GetSize( )
114 {
115     lvsize_t sz;
116     if ( GetSize( &sz )!=LVERR_OK )
117         return LV_INVALID_SIZE;
118     return sz;
119 }
120 
121 
122 /// calculate crc32 code for stream, if possible
getcrc32(lUInt32 & dst)123 lverror_t LVNamedStream::getcrc32( lUInt32 & dst )
124 {
125     if ( _crc!=0 ) {
126         dst = _crc;
127         return LVERR_OK;
128     } else {
129         if ( !_crcFailed ) {
130             lverror_t res = LVStream::getcrc32( dst );
131             if ( res==LVERR_OK ) {
132                 _crc = dst;
133                 return LVERR_OK;
134             }
135             _crcFailed = true;
136         }
137         dst = 0;
138         return LVERR_FAIL;
139     }
140 }
141 /// returns stream/container name, may be NULL if unknown
GetName()142 const lChar32 * LVNamedStream::GetName()
143 {
144     if (m_fname.empty())
145         return NULL;
146     return m_fname.c_str();
147 }
148 /// sets stream/container name, may be not implemented for some objects
SetName(const lChar32 * name)149 void LVNamedStream::SetName(const lChar32 * name)
150 {
151     m_fname = name;
152     m_filename.clear();
153     m_path.clear();
154     if (m_fname.empty())
155         return;
156     const lChar32 * fn = m_fname.c_str();
157 
158     const lChar32 * p = fn + m_fname.length() - 1;
159     for ( ;p>fn; p--) {
160         if (p[-1] == '/' || p[-1]=='\\')
161             break;
162     }
163     int pos = (int)(p - fn);
164     if (p>fn)
165         m_path = m_fname.substr(0, pos);
166     m_filename = m_fname.substr(pos, m_fname.length() - pos);
167 }
168 
169 /// Universal Read or write buffer for stream region for non-meped streams
170 // default implementation, with RAM buffer
171 class LVDefStreamBuffer : public LVStreamBuffer
172 {
173 protected:
174     LVStreamRef m_stream;
175     lUInt8 * m_buf;
176     lvpos_t m_pos;
177     lvsize_t m_size;
178     bool m_readonly;
179     bool m_writeonly;
180 public:
create(LVStreamRef stream,lvpos_t pos,lvsize_t size,bool readonly)181     static LVStreamBufferRef create( LVStreamRef stream, lvpos_t pos, lvsize_t size, bool readonly )
182     {
183         LVStreamBufferRef res;
184         switch ( stream->GetMode() ) {
185         case LVOM_ERROR:       ///< to indicate error state
186         case LVOM_CLOSED:        ///< to indicate closed state
187             return res;
188         case LVOM_READ:          ///< readonly mode, use for r/o
189             if ( !readonly )
190                 return res;
191             break;
192         case LVOM_WRITE:         ///< writeonly mode
193         case LVOM_APPEND:        ///< append (readwrite) mode, use for r/w
194         case LVOM_READWRITE:      ///< readwrite mode
195             if ( readonly )
196                 return res;
197             break;
198         }
199         lvsize_t sz;
200         if ( stream->GetSize(&sz)!=LVERR_OK )
201             return res;
202         if ( pos + size > sz )
203             return res; // wrong position/size
204         LVDefStreamBuffer * buf = new LVDefStreamBuffer( stream, pos, size, readonly );
205         if ( !buf->m_buf ) {
206             delete buf;
207             return res;
208         }
209         if ( stream->SetPos( pos )!=LVERR_OK ) {
210             delete buf;
211             return res;
212         }
213         lvsize_t bytesRead = 0;
214         if ( stream->Read( buf->m_buf, size, &bytesRead )!=LVERR_OK || bytesRead!=size ) {
215             delete buf;
216             return res;
217         }
218         return LVStreamBufferRef( buf );
219     }
220 
LVDefStreamBuffer(LVStreamRef stream,lvpos_t pos,lvsize_t size,bool readonly)221     LVDefStreamBuffer( LVStreamRef stream, lvpos_t pos, lvsize_t size, bool readonly )
222     : m_stream( stream ), m_buf( NULL ), m_pos(pos), m_size( size ), m_readonly( readonly )
223     {
224         m_buf = (lUInt8*)malloc( size );
225         m_writeonly = (m_stream->GetMode()==LVOM_WRITE);
226     }
227     /// get pointer to read-only buffer, returns NULL if unavailable
getReadOnly()228     virtual const lUInt8 * getReadOnly()
229     {
230         return m_writeonly ? NULL : m_buf;
231     }
232     /// get pointer to read-write buffer, returns NULL if unavailable
getReadWrite()233     virtual lUInt8 * getReadWrite()
234     {
235         return m_readonly ? NULL : m_buf;
236     }
237 
238     /// get buffer size
getSize()239     virtual lvsize_t getSize()
240     {
241         return m_size;
242     }
243 
244     /// write on close
close()245     virtual bool close()
246     {
247         bool res = true;
248         if ( m_buf ) {
249             if ( !m_readonly ) {
250                 if ( m_stream->SetPos( m_pos )!=LVERR_OK ) {
251                     res = false;
252                 } else {
253                     lvsize_t bytesWritten = 0;
254                     if ( m_stream->Write( m_buf, m_size, &bytesWritten )!=LVERR_OK || bytesWritten!=m_size ) {
255                         res = false;
256                     }
257                 }
258             }
259             free( m_buf );
260         }
261         m_buf = NULL;
262         m_stream = NULL;
263         m_size = 0;
264         m_pos = 0;
265         return res;
266     }
267     /// flush on destroy
~LVDefStreamBuffer()268     virtual ~LVDefStreamBuffer()
269     {
270         close(); // NOLINT: Call to virtual function during destruction
271     }
272 };
273 
274 /// Get read buffer - default implementation, with RAM buffer
GetReadBuffer(lvpos_t pos,lvpos_t size)275 LVStreamBufferRef LVStream::GetReadBuffer( lvpos_t pos, lvpos_t size )
276 {
277     LVStreamBufferRef res;
278     res = LVDefStreamBuffer::create( LVStreamRef(this), pos, size, true );
279     return res;
280 }
281 
282 /// Get read/write buffer - default implementation, with RAM buffer
GetWriteBuffer(lvpos_t pos,lvpos_t size)283 LVStreamBufferRef LVStream::GetWriteBuffer( lvpos_t pos, lvpos_t size )
284 {
285     LVStreamBufferRef res;
286     res = LVDefStreamBuffer::create( LVStreamRef(this), pos, size, false );
287     return res;
288 }
289 
290 
291 #define CRC_BUF_SIZE 16384
292 
293 /// calculate crc32 code for stream, if possible
getcrc32(lUInt32 & dst)294 lverror_t LVStream::getcrc32( lUInt32 & dst )
295 {
296     dst = 0;
297     if ( GetMode() == LVOM_READ || GetMode() == LVOM_APPEND ) {
298         lvpos_t savepos = GetPos();
299         lvsize_t size = GetSize();
300         lUInt8 buf[CRC_BUF_SIZE];
301         SetPos( 0 );
302         lvsize_t bytesRead = 0;
303         for ( lvpos_t pos = 0; pos<size; pos+=CRC_BUF_SIZE ) {
304             lvsize_t sz = size - pos;
305             if ( sz > CRC_BUF_SIZE )
306                 sz = CRC_BUF_SIZE;
307             Read( buf, sz, &bytesRead );
308             if ( bytesRead!=sz ) {
309                 SetPos(savepos);
310                 return LVERR_FAIL;
311             }
312             dst = lStr_crc32( dst, buf, sz );
313         }
314         SetPos( savepos );
315         return LVERR_OK;
316     } else {
317         // not supported
318         return LVERR_NOTIMPL;
319     }
320 }
321 
322 
323 //#if USE__FILES==1
324 #if defined(_LINUX) || defined(_WIN32)
325 
326 class LVFileMappedStream : public LVNamedStream
327 {
328 private:
329 #if defined(_WIN32)
330     HANDLE m_hFile;
331     HANDLE m_hMap;
332 #else
333     int m_fd;
334 #endif
335     lUInt8* m_map;
336     lvsize_t m_size;
337     lvpos_t m_pos;
338 
339     /// Read or write buffer for stream region
340     class LVBuffer : public LVStreamBuffer
341     {
342     protected:
343         LVStreamRef m_stream;
344         lUInt8 * m_buf;
345         lvsize_t m_size;
346         bool m_readonly;
347     public:
LVBuffer(LVStreamRef stream,lUInt8 * buf,lvsize_t size,bool readonly)348         LVBuffer( LVStreamRef stream, lUInt8 * buf, lvsize_t size, bool readonly )
349         : m_stream( stream ), m_buf( buf ), m_size( size ), m_readonly( readonly )
350         {
351         }
352 
353         /// get pointer to read-only buffer, returns NULL if unavailable
getReadOnly()354         virtual const lUInt8 * getReadOnly()
355         {
356             return m_buf;
357         }
358 
359         /// get pointer to read-write buffer, returns NULL if unavailable
getReadWrite()360         virtual lUInt8 * getReadWrite()
361         {
362             return m_readonly ? NULL : m_buf;
363         }
364 
365         /// get buffer size
getSize()366         virtual lvsize_t getSize()
367         {
368             return m_size;
369         }
370 
371         /// flush on destroy
~LVBuffer()372         virtual ~LVBuffer() { }
373 
374     };
375 
376 
377 public:
378 
379     /// Get read buffer (optimal for )
GetReadBuffer(lvpos_t pos,lvpos_t size)380     virtual LVStreamBufferRef GetReadBuffer( lvpos_t pos, lvpos_t size )
381     {
382         LVStreamBufferRef res;
383         if ( !m_map )
384             return res;
385         if ( (m_mode!=LVOM_APPEND && m_mode!=LVOM_READ) || pos + size > m_size || size==0 )
386             return res;
387         return LVStreamBufferRef ( new LVBuffer( LVStreamRef(this), m_map + pos, size, true ) );
388     }
389 
390     /// Get read/write buffer (optimal for )
GetWriteBuffer(lvpos_t pos,lvpos_t size)391     virtual LVStreamBufferRef GetWriteBuffer( lvpos_t pos, lvpos_t size )
392     {
393         LVStreamBufferRef res;
394         if ( !m_map )
395             return res;
396         if ( m_mode!=LVOM_APPEND || pos + size > m_size || size==0 )
397             return res;
398         return LVStreamBufferRef ( new LVBuffer( LVStreamRef(this), m_map + pos, size, false ) );
399     }
400 
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * pNewPos)401     virtual lverror_t Seek( lvoffset_t offset, lvseek_origin_t origin, lvpos_t * pNewPos )
402     {
403         //
404         lvpos_t newpos = m_pos;
405         switch ( origin )
406         {
407         case LVSEEK_SET:
408             newpos = offset;
409             break;
410         case LVSEEK_CUR:
411             newpos += offset;
412             break;
413         case LVSEEK_END:
414             newpos = m_size + offset;
415             break;
416         }
417         if ( newpos>m_size )
418             return LVERR_FAIL;
419         if ( pNewPos!=NULL )
420             *pNewPos = newpos;
421         m_pos = newpos;
422         return LVERR_OK;
423     }
424 
425     /// Tell current file position
426     /**
427         \param pNewPos points to place to store file position
428         \return lverror_t status: LVERR_OK if success
429     */
Tell(lvpos_t * pPos)430     virtual lverror_t Tell( lvpos_t * pPos )
431     {
432         *pPos = m_pos;
433         return LVERR_OK;
434     }
435 
SetPos(lvpos_t p)436     virtual lvpos_t SetPos(lvpos_t p)
437     {
438         if ( p<=m_size ) {
439             m_pos = p;
440             return m_pos;
441         }
442         return (lvpos_t)(~0);
443     }
444 
445     /// Get file position
446     /**
447         \return lvpos_t file position
448     */
GetPos()449     virtual lvpos_t   GetPos()
450     {
451         return m_pos;
452     }
453 
454     /// Get file size
455     /**
456         \return lvsize_t file size
457     */
GetSize()458     virtual lvsize_t  GetSize()
459     {
460         return m_size;
461     }
462 
GetSize(lvsize_t * pSize)463     virtual lverror_t GetSize( lvsize_t * pSize )
464     {
465         *pSize = m_size;
466         return LVERR_OK;
467     }
468 
error()469     lverror_t error()
470     {
471 #if defined(_WIN32)
472 		if ( m_hFile!=NULL ) {
473 			UnMap();
474 			if ( !CloseHandle(m_hFile) )
475 				CRLog::error("Error while closing file handle");
476 			m_hFile = NULL;
477 		}
478 #else
479 		if ( m_fd!= -1 ) {
480             CRLog::trace("Closing mapped file %s", UnicodeToUtf8(GetName()).c_str() );
481 			UnMap();
482             close(m_fd);
483 		}
484         m_fd = -1;
485 #endif
486         m_map = NULL;
487         m_size = 0;
488         m_mode = LVOM_ERROR;
489         return LVERR_FAIL;
490     }
491 
Read(void * buf,lvsize_t count,lvsize_t * nBytesRead)492     virtual lverror_t Read( void * buf, lvsize_t count, lvsize_t * nBytesRead )
493     {
494         if ( !m_map )
495             return LVERR_FAIL;
496         int cnt = (int)count;
497         if ( m_pos + cnt > m_size )
498             cnt = (int)(m_size - m_pos);
499         if ( cnt <= 0 )
500             return LVERR_FAIL;
501         memcpy( buf, m_map + m_pos, cnt );
502         m_pos += cnt;
503         if (nBytesRead)
504             *nBytesRead = cnt;
505         return LVERR_OK;
506     }
507 
Read(lUInt8 * buf)508     virtual bool Read( lUInt8 * buf )
509     {
510         if ( m_pos < m_size ) {
511             *buf = m_map[ m_pos++ ];
512             return true;
513         }
514         return false;
515     }
516 
Read(lUInt16 * buf)517     virtual bool Read( lUInt16 * buf )
518     {
519         if ( m_pos+1 < m_size ) {
520             *buf = m_map[ m_pos ] | ( ( (lUInt16)m_map[ m_pos+1 ] )<<8 );
521             m_pos += 2;
522             return true;
523         }
524         return false;
525     }
526 
Read(lUInt32 * buf)527     virtual bool Read( lUInt32 * buf )
528     {
529         if ( m_pos+3 < m_size ) {
530             *buf = m_map[ m_pos ] | ( ( (lUInt32)m_map[ m_pos+1 ] )<<8 )
531                 | ( ( (lUInt32)m_map[ m_pos+2 ] )<<16 )
532                 | ( ( (lUInt32)m_map[ m_pos+3 ] )<<24 )
533                 ;
534             m_pos += 4;
535             return true;
536         }
537         return false;
538     }
539 
ReadByte()540     virtual int ReadByte()
541     {
542         if ( m_pos < m_size ) {
543             return m_map[ m_pos++ ];
544         }
545         return -1;
546     }
547 
Write(const void * buf,lvsize_t count,lvsize_t * nBytesWritten)548     virtual lverror_t Write( const void * buf, lvsize_t count, lvsize_t * nBytesWritten )
549     {
550         if ( m_mode!=LVOM_APPEND )
551             return LVERR_FAIL;
552         lvsize_t maxSize = (lvsize_t)(m_size - m_pos);
553         if ( maxSize<=0 )
554             return LVERR_FAIL; // end of file reached: resize is not supported yet
555         if ( count > maxSize || count > m_size )
556             count = maxSize;
557         memcpy( m_map + m_pos, buf, count );
558         m_pos += count;
559         if ( nBytesWritten )
560             *nBytesWritten = count;
561         return LVERR_OK;
562     }
563 
Eof()564     virtual bool Eof()
565     {
566         return (m_pos >= m_size);
567     }
568 
CreateFileStream(lString32 fname,lvopen_mode_t mode,int minSize)569     static LVFileMappedStream * CreateFileStream( lString32 fname, lvopen_mode_t mode, int minSize )
570     {
571         LVFileMappedStream * f = new LVFileMappedStream();
572         if ( f->OpenFile( fname, mode, minSize )==LVERR_OK ) {
573             return f;
574         } else {
575             delete f;
576             return NULL;
577         }
578     }
579 
Map()580 	lverror_t Map()
581 	{
582 #if defined(_WIN32)
583 		m_hMap = CreateFileMapping(
584 			m_hFile,
585 			NULL,
586 			(m_mode==LVOM_READ)?PAGE_READONLY:PAGE_READWRITE, //flProtect,
587 			0,
588 			0,
589 			NULL
590 		);
591 		if ( m_hMap==NULL ) {
592 			DWORD err = GetLastError();
593             CRLog::error( "LVFileMappedStream::Map() -- Cannot map file to memory, err=%08x, hFile=%p", err, m_hFile );
594             return error();
595 		}
596 		m_map = (lUInt8*) MapViewOfFile(
597 			m_hMap,
598 			m_mode==LVOM_READ ? FILE_MAP_READ : FILE_MAP_READ|FILE_MAP_WRITE,
599 			0,
600 			0,
601 			m_size
602 		);
603 		if ( m_map==NULL ) {
604             CRLog::error( "LVFileMappedStream::Map() -- Cannot map file to memory" );
605             return error();
606 		}
607 		return LVERR_OK;
608 #else
609         int mapFlags = (m_mode==LVOM_READ) ? PROT_READ : PROT_READ | PROT_WRITE;
610         m_map = (lUInt8*)mmap( 0, m_size, mapFlags, MAP_SHARED, m_fd, 0 );
611         if ( m_map == MAP_FAILED ) {
612             CRLog::error( "LVFileMappedStream::Map() -- Cannot map file to memory" );
613             return error();
614         }
615         return LVERR_OK;
616 #endif
617 	}
618 
UnMap()619 	lverror_t UnMap()
620 	{
621 #if defined(_WIN32)
622 		lverror_t res = LVERR_OK;
623 		if ( m_map!=NULL ) {
624 			if ( !UnmapViewOfFile( m_map ) ) {
625 	            CRLog::error("LVFileMappedStream::UnMap() -- Error while unmapping file");
626 				res = LVERR_FAIL;
627 			}
628 			m_map = NULL;
629 		}
630 		if ( m_hMap!=NULL ) {
631 			if ( !CloseHandle( m_hMap ) ) {
632 	            CRLog::error("LVFileMappedStream::UnMap() -- Error while unmapping file");
633 				res = LVERR_FAIL;
634 			}
635 			m_hMap = NULL;
636 		}
637 		if ( res!=LVERR_OK )
638 			return error();
639 		return res;
640 #else
641         if ( m_map!=NULL && munmap( m_map, m_size ) == -1 ) {
642             m_map = NULL;
643             CRLog::error("LVFileMappedStream::UnMap() -- Error while unmapping file");
644             return error();
645         }
646         return LVERR_OK;
647 #endif
648 	}
649 
SetSize(lvsize_t size)650     virtual lverror_t SetSize( lvsize_t size )
651     {
652         // support only size grow
653         if ( m_mode!=LVOM_APPEND )
654             return LVERR_FAIL;
655         if ( size == m_size )
656             return LVERR_OK;
657         //if ( size < m_size )
658         //    return LVERR_FAIL;
659 
660 		bool wasMapped = false;
661         if ( m_map!=NULL ) {
662 			wasMapped = true;
663 			if ( UnMap()!=LVERR_OK )
664 	            return LVERR_FAIL;
665         }
666         m_size = size;
667 
668 #if defined(_WIN32)
669 		// WIN32
670 		__int64 offset = size - 1;
671         lUInt32 pos_low = (lUInt32)((__int64)offset & 0xFFFFFFFF);
672         LONG pos_high = (long)(((__int64)offset >> 32) & 0xFFFFFFFF);
673 		pos_low = SetFilePointer(m_hFile, pos_low, &pos_high, FILE_BEGIN );
674         if (pos_low == 0xFFFFFFFF) {
675             lUInt32 err = GetLastError();
676             if (err == ERROR_NOACCESS)
677                 pos_low = (lUInt32)offset;
678             else if ( err != ERROR_SUCCESS)
679                 return error();
680         }
681 		DWORD bytesWritten = 0;
682 		if ( !WriteFile( m_hFile, "", 1, &bytesWritten, NULL ) || bytesWritten!=1 )
683 			return error();
684 #else
685 		// LINUX
686 		if ( lseek( m_fd, size-1, SEEK_SET ) == -1 ) {
687             CRLog::error("LVFileMappedStream::SetSize() -- Seek error");
688             return error();
689         }
690         if ( write(m_fd, "", 1) != 1 ) {
691             CRLog::error("LVFileMappedStream::SetSize() -- File resize error");
692             return error();
693         }
694 #endif
695 		if ( wasMapped ) {
696 			if ( Map() != LVERR_OK ) {
697 				return error();
698 			}
699 		}
700         return LVERR_OK;
701     }
702 
OpenFile(lString32 fname,lvopen_mode_t mode,lvsize_t minSize=(lvsize_t)-1)703     lverror_t OpenFile( lString32 fname, lvopen_mode_t mode, lvsize_t minSize = (lvsize_t)-1 )
704     {
705         m_mode = mode;
706         if ( mode!=LVOM_READ && mode!=LVOM_APPEND )
707             return LVERR_FAIL; // not supported
708         if ( minSize==(lvsize_t)-1 ) {
709             if ( !LVFileExists(fname) )
710                 return LVERR_FAIL;
711         }
712         //if ( mode==LVOM_APPEND && minSize<=0 )
713         //    return LVERR_FAIL;
714         SetName(fname.c_str());
715         lString8 fn8 = UnicodeToUtf8( fname );
716 #if defined(_WIN32)
717 		//========================================================
718 		// WIN32 IMPLEMENTATION
719         lUInt32 m = 0;
720         lUInt32 s = 0;
721         lUInt32 c = 0;
722         lString16 fn16 = UnicodeToUtf16( fname );
723         switch (mode) {
724         case LVOM_READWRITE:
725             m |= GENERIC_WRITE|GENERIC_READ;
726             s |= FILE_SHARE_WRITE|FILE_SHARE_READ;
727             c |= OPEN_ALWAYS;
728             break;
729         case LVOM_READ:
730             m |= GENERIC_READ;
731             s |= FILE_SHARE_READ;
732             c |= OPEN_EXISTING;
733             break;
734         case LVOM_WRITE:
735             m |= GENERIC_WRITE;
736             s |= FILE_SHARE_WRITE;
737             c |= CREATE_ALWAYS;
738             break;
739         case LVOM_APPEND:
740             m |= GENERIC_WRITE|GENERIC_READ;
741             s |= FILE_SHARE_WRITE;
742             c |= OPEN_ALWAYS;
743             break;
744         case LVOM_CLOSED:
745         case LVOM_ERROR:
746             crFatalError();
747             break;
748         }
749         m_hFile = CreateFileW( fn16.c_str(), m, s, NULL, c, FILE_ATTRIBUTE_NORMAL, NULL);
750         if (m_hFile == INVALID_HANDLE_VALUE || !m_hFile) {
751 			// unicode not implemented?
752 			lUInt32 err = GetLastError();
753 			if (err==ERROR_CALL_NOT_IMPLEMENTED)
754 				m_hFile = CreateFileA( UnicodeToLocal(fname).c_str(), m, s, NULL, c, FILE_ATTRIBUTE_NORMAL, NULL);
755 			if ( (m_hFile == INVALID_HANDLE_VALUE) || (!m_hFile) ) {
756                 CRLog::error("Error opening file %s", fn8.c_str() );
757                 m_hFile = NULL;
758 				// error
759 				return error();
760 			}
761 		}
762 		// check size
763         lUInt32 hw=0;
764         m_size = GetFileSize( m_hFile, (LPDWORD)&hw );
765 #if LVLONG_FILE_SUPPORT
766         if (hw)
767             m_size |= (((lvsize_t)hw)<<32);
768 #endif
769 
770         if ( mode == LVOM_APPEND && m_size < minSize ) {
771             if ( SetSize( minSize ) != LVERR_OK ) {
772                 CRLog::error( "Cannot set file size for %s", fn8.c_str() );
773                 return error();
774             }
775         }
776 
777 		if ( Map()!=LVERR_OK )
778 			return error();
779 
780 		return LVERR_OK;
781 
782 
783 #else
784 		//========================================================
785 		// LINUX IMPLEMENTATION
786         m_fd = -1;
787 
788         int flags = (mode==LVOM_READ) ? O_RDONLY : O_RDWR | O_CREAT; // | O_SYNC
789         m_fd = open( fn8.c_str(), flags, (mode_t)0666);
790         if (m_fd == -1) {
791             CRLog::error( "Error opening file %s for %s, errno=%d, msg=%s", fn8.c_str(), (mode==LVOM_READ) ? "reading" : "read/write",  (int)errno, strerror(errno) );
792             return error();
793         }
794         struct stat stat;
795         if ( fstat( m_fd, &stat ) ) {
796             CRLog::error( "Cannot get file size for %s", fn8.c_str() );
797             return error();
798         }
799         m_size = (lvsize_t) stat.st_size;
800         if ( mode == LVOM_APPEND && m_size < minSize ) {
801             if ( SetSize( minSize ) != LVERR_OK ) {
802                 CRLog::error( "Cannot set file size for %s", fn8.c_str() );
803                 return error();
804             }
805         }
806 
807         int mapFlags = (mode==LVOM_READ) ? PROT_READ : PROT_READ | PROT_WRITE;
808         m_map = (lUInt8*)mmap( 0, m_size, mapFlags, MAP_SHARED, m_fd, 0 );
809         if ( m_map == MAP_FAILED ) {
810             CRLog::error( "Cannot map file %s to memory", fn8.c_str() );
811             return error();
812         }
813         return LVERR_OK;
814 #endif
815     }
LVFileMappedStream()816     LVFileMappedStream()
817 #if defined(_WIN32)
818 		: m_hFile(NULL), m_hMap(NULL),
819 #else
820 		: m_fd(-1),
821 #endif
822 		m_map(NULL), m_size(0), m_pos(0)
823     {
824         m_mode=LVOM_ERROR;
825     }
~LVFileMappedStream()826     virtual ~LVFileMappedStream()
827     {
828 		// reuse error() to close file
829 		error();
830     }
831 };
832 #endif
833 
834 
835 /// Open memory mapped file
836 /**
837     \param pathname is file name to open (unicode)
838     \param mode is mode file should be opened in (LVOM_READ or LVOM_APPEND only)
839 	\param minSize is minimum file size for R/W mode
840     \return reference to opened stream if success, NULL if error
841 */
LVMapFileStream(const lChar8 * pathname,lvopen_mode_t mode,lvsize_t minSize)842 LVStreamRef LVMapFileStream( const lChar8 * pathname, lvopen_mode_t mode, lvsize_t minSize )
843 {
844 	lString32 fn = LocalToUnicode( lString8(pathname) );
845 	return LVMapFileStream( fn.c_str(), mode, minSize );
846 }
847 
848 
849 //#ifdef _LINUX
850 #undef USE_ANSI_FILES
851 //#endif
852 
853 #if (USE_ANSI_FILES==1)
854 
855 class LVFileStream : public LVNamedStream
856 {
857 private:
858     FILE * m_file;
859 public:
860 
861 
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * pNewPos)862     virtual lverror_t Seek( lvoffset_t offset, lvseek_origin_t origin, lvpos_t * pNewPos )
863     {
864        //
865        int res = -1;
866        switch ( origin )
867        {
868        case LVSEEK_SET:
869            res = fseek( m_file, offset, SEEK_SET );
870            break;
871        case LVSEEK_CUR:
872            res = fseek( m_file, offset, SEEK_CUR );
873            break;
874        case LVSEEK_END:
875            res = fseek( m_file, offset, SEEK_END );
876            break;
877        }
878        if (res==0)
879        {
880           if ( pNewPos )
881               * pNewPos = ftell(m_file);
882           return LVERR_OK;
883        }
884        CRLog::error("error setting file position to %d (%d)", (int)offset, (int)origin );
885        return LVERR_FAIL;
886     }
SetSize(lvsize_t)887     virtual lverror_t SetSize( lvsize_t )
888     {
889         /*
890         int64 sz = m_file->SetSize( size );
891         if (sz==-1)
892            return LVERR_FAIL;
893         else
894            return LVERR_OK;
895         */
896         return LVERR_FAIL;
897     }
Read(void * buf,lvsize_t count,lvsize_t * nBytesRead)898     virtual lverror_t Read( void * buf, lvsize_t count, lvsize_t * nBytesRead )
899     {
900         lvsize_t sz = fread( buf, 1, count, m_file );
901         if (nBytesRead)
902             *nBytesRead = sz;
903         if ( sz==0 )
904         {
905             return LVERR_FAIL;
906         }
907         return LVERR_OK;
908     }
Write(const void * buf,lvsize_t count,lvsize_t * nBytesWritten)909     virtual lverror_t Write( const void * buf, lvsize_t count, lvsize_t * nBytesWritten )
910     {
911         lvsize_t sz = fwrite( buf, 1, count, m_file );
912         if (nBytesWritten)
913             *nBytesWritten = sz;
914         handleAutoSync(sz);
915         if (sz < count)
916         {
917             return LVERR_FAIL;
918         }
919         return LVERR_OK;
920     }
921     /// flushes unsaved data from buffers to file, with optional flush of OS buffers
Flush(bool sync)922     virtual lverror_t Flush( bool sync )
923     {
924         if ( !m_file )
925             return LVERR_FAIL;
926         fflush( m_file );
927         return LVERR_OK;
928     }
Eof()929     virtual bool Eof()
930     {
931         return feof(m_file)!=0;
932     }
CreateFileStream(lString32 fname,lvopen_mode_t mode)933     static LVFileStream * CreateFileStream( lString32 fname, lvopen_mode_t mode )
934     {
935         LVFileStream * f = new LVFileStream;
936         if (f->OpenFile( fname, mode )==LVERR_OK) {
937             return f;
938         } else {
939             delete f;
940             return NULL;
941         }
942     }
OpenFile(lString32 fname,lvopen_mode_t mode)943     lverror_t OpenFile( lString32 fname, lvopen_mode_t mode )
944     {
945         m_mode = mode;
946         m_file = NULL;
947         SetName(fname.c_str());
948         const char * modestr = "r";
949         switch (mode) {
950         case LVOM_READ:
951             modestr = "rb";
952             break;
953         case LVOM_WRITE:
954             modestr = "wb";
955             break;
956         case LVOM_READWRITE:
957         case LVOM_APPEND:
958             modestr = "a+b";
959             break;
960         case LVOM_CLOSED:
961         case LVOM_ERROR:
962             break;
963         }
964         FILE * file = fopen(UnicodeToLocal(fname).c_str(), modestr);
965         if (!file)
966         {
967             //printf("cannot open file %s\n", UnicodeToLocal(fname).c_str());
968             m_mode = LVOM_ERROR;
969             return LVERR_FAIL;
970         }
971         m_file = file;
972         //printf("file %s opened ok\n", UnicodeToLocal(fname).c_str());
973         // set filename
974         SetName( fname.c_str() );
975         return LVERR_OK;
976     }
LVFileStream()977     LVFileStream() : m_file(NULL)
978     {
979         m_mode=LVOM_ERROR;
980     }
~LVFileStream()981     virtual ~LVFileStream()
982     {
983         if (m_file)
984             fclose(m_file);
985     }
986 };
987 
988 #else
989 
990 class LVDirectoryContainer;
991 class LVFileStream : public LVNamedStream
992 {
993     friend class LVDirectoryContainer;
994 protected:
995 #if defined(_WIN32)
996     HANDLE m_hFile;
997 #else
998     int m_fd;
999 #endif
1000     //LVDirectoryContainer * m_parent;
1001     lvsize_t               m_size;
1002     lvpos_t                m_pos;
1003 public:
1004     /// flushes unsaved data from buffers to file, with optional flush of OS buffers
Flush(bool sync)1005     virtual lverror_t Flush( bool sync )
1006     {
1007         CR_UNUSED(sync);
1008 #ifdef _WIN32
1009         if ( m_hFile==INVALID_HANDLE_VALUE || !FlushFileBuffers( m_hFile ) )
1010             return LVERR_FAIL;
1011 #else
1012         if ( m_fd==-1 )
1013             return LVERR_FAIL;
1014         if ( sync ) {
1015 //            CRTimerUtil timer;
1016 //            CRLog::trace("calling fsync");
1017             fsync( m_fd );
1018 //            CRLog::trace("fsync took %d ms", (int)timer.elapsed());
1019         }
1020 #endif
1021         return LVERR_OK;
1022     }
1023 
Eof()1024     virtual bool Eof()
1025     {
1026         return m_size<=m_pos;
1027     }
1028 //    virtual LVContainer * GetParentContainer()
1029 //    {
1030 //        return (LVContainer*)m_parent;
1031 //    }
Read(void * buf,lvsize_t count,lvsize_t * nBytesRead)1032     virtual lverror_t Read( void * buf, lvsize_t count, lvsize_t * nBytesRead )
1033     {
1034 #ifdef _WIN32
1035         //fprintf(stderr, "Read(%08x, %d)\n", buf, count);
1036 
1037         if (m_hFile == INVALID_HANDLE_VALUE || m_mode==LVOM_WRITE ) // || m_mode==LVOM_APPEND
1038             return LVERR_FAIL;
1039         //
1040 		if ( m_pos > m_size )
1041 			return LVERR_FAIL; // EOF
1042 
1043         lUInt32 dwBytesRead = 0;
1044         if (ReadFile( m_hFile, buf, (lUInt32)count, (LPDWORD)&dwBytesRead, NULL )) {
1045             if (nBytesRead)
1046                 *nBytesRead = dwBytesRead;
1047             m_pos += dwBytesRead;
1048 	        return LVERR_OK;
1049         } else {
1050             //DWORD err = GetLastError();
1051 			if (nBytesRead)
1052 				*nBytesRead = 0;
1053             return LVERR_FAIL;
1054         }
1055 
1056 #else
1057         if (m_fd == -1)
1058             return LVERR_FAIL;
1059         ssize_t res = read( m_fd, buf, count );
1060         if ( res!=(ssize_t)-1 ) {
1061             if (nBytesRead)
1062                 *nBytesRead = res;
1063             m_pos += res;
1064             return LVERR_OK;
1065         }
1066         if (nBytesRead)
1067             *nBytesRead = 0;
1068         return LVERR_FAIL;
1069 #endif
1070     }
GetSize(lvsize_t * pSize)1071     virtual lverror_t GetSize( lvsize_t * pSize )
1072     {
1073 #ifdef _WIN32
1074         if (m_hFile == INVALID_HANDLE_VALUE || !pSize)
1075             return LVERR_FAIL;
1076 #else
1077         if (m_fd == -1 || !pSize)
1078             return LVERR_FAIL;
1079 #endif
1080         if (m_size<m_pos)
1081             m_size = m_pos;
1082         *pSize = m_size;
1083         return LVERR_OK;
1084     }
GetSize()1085     virtual lvsize_t GetSize()
1086     {
1087 #ifdef _WIN32
1088         if (m_hFile == INVALID_HANDLE_VALUE)
1089             return 0;
1090         if (m_size<m_pos)
1091             m_size = m_pos;
1092         return m_size;
1093 #else
1094         if (m_fd == -1)
1095             return 0;
1096         if (m_size<m_pos)
1097             m_size = m_pos;
1098         return m_size;
1099 #endif
1100     }
SetSize(lvsize_t size)1101     virtual lverror_t SetSize( lvsize_t size )
1102     {
1103 #ifdef _WIN32
1104         //
1105         if (m_hFile == INVALID_HANDLE_VALUE || m_mode==LVOM_READ )
1106             return LVERR_FAIL;
1107         lvpos_t oldpos = 0;
1108         if (!Tell(&oldpos))
1109             return LVERR_FAIL;
1110         if (!Seek(size, LVSEEK_SET, NULL))
1111             return LVERR_FAIL;
1112         SetEndOfFile( m_hFile);
1113         Seek(oldpos, LVSEEK_SET, NULL);
1114         return LVERR_OK;
1115 #else
1116         if (m_fd == -1)
1117             return LVERR_FAIL;
1118         lvpos_t oldpos = 0;
1119         if (!Tell(&oldpos))
1120             return LVERR_FAIL;
1121         if (!Seek(size, LVSEEK_SET, NULL))
1122             return LVERR_FAIL;
1123         Seek(oldpos, LVSEEK_SET, NULL);
1124         return LVERR_OK;
1125 #endif
1126     }
Write(const void * buf,lvsize_t count,lvsize_t * nBytesWritten)1127     virtual lverror_t Write( const void * buf, lvsize_t count, lvsize_t * nBytesWritten )
1128     {
1129 #ifdef _WIN32
1130         if (m_hFile == INVALID_HANDLE_VALUE || m_mode==LVOM_READ )
1131             return LVERR_FAIL;
1132         //
1133         lUInt32 dwBytesWritten = 0;
1134         if (WriteFile( m_hFile, buf, (lUInt32)count, (LPDWORD)&dwBytesWritten, NULL )) {
1135             if (nBytesWritten)
1136                 *nBytesWritten = dwBytesWritten;
1137             m_pos += dwBytesWritten;
1138             if ( m_size < m_pos )
1139                 m_size = m_pos;
1140             handleAutoSync(dwBytesWritten);
1141             return LVERR_OK;
1142         }
1143         if (nBytesWritten)
1144             *nBytesWritten = 0;
1145         return LVERR_FAIL;
1146 
1147 #else
1148         if (m_fd == -1)
1149             return LVERR_FAIL;
1150         ssize_t res = write( m_fd, buf, count );
1151         if ( res!=(ssize_t)-1 ) {
1152             if (nBytesWritten)
1153                 *nBytesWritten = res;
1154             m_pos += res;
1155             if ( m_size < m_pos )
1156                 m_size = m_pos;
1157             handleAutoSync(res);
1158             return LVERR_OK;
1159         }
1160         if (nBytesWritten)
1161             *nBytesWritten = 0;
1162         return LVERR_FAIL;
1163 #endif
1164     }
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * pNewPos)1165     virtual lverror_t Seek( lvoffset_t offset, lvseek_origin_t origin, lvpos_t * pNewPos )
1166     {
1167 #ifdef _WIN32
1168         //fprintf(stderr, "Seek(%d,%d)\n", offset, origin);
1169         if (m_hFile == INVALID_HANDLE_VALUE)
1170             return LVERR_FAIL;
1171         lUInt32 pos_low = (lUInt32)((__int64)offset & 0xFFFFFFFF);
1172         LONG pos_high = (LONG)(((__int64)offset >> 32) & 0xFFFFFFFF);
1173         lUInt32 m=0;
1174         switch (origin) {
1175         case LVSEEK_SET:
1176             m = FILE_BEGIN;
1177             break;
1178         case LVSEEK_CUR:
1179             m = FILE_CURRENT;
1180             break;
1181         case LVSEEK_END:
1182             m = FILE_END;
1183             break;
1184         }
1185 
1186         pos_low = SetFilePointer(m_hFile, pos_low, &pos_high, m );
1187 		lUInt32 err;
1188         if (pos_low == INVALID_SET_FILE_POINTER && (err = GetLastError())!=ERROR_SUCCESS ) {
1189             //if (err == ERROR_NOACCESS)
1190             //    pos_low = (lUInt32)offset;
1191             //else if ( err != ERROR_SUCCESS)
1192             return LVERR_FAIL;
1193         }
1194         m_pos = pos_low
1195 #if LVLONG_FILE_SUPPORT
1196          | ((lvpos_t)pos_high<<32)
1197 #endif
1198           ;
1199         if (pNewPos)
1200             *pNewPos = m_pos;
1201         return LVERR_OK;
1202 #else
1203         if (m_fd == -1)
1204             return LVERR_FAIL;
1205        //
1206        int res = -1;
1207        switch ( origin )
1208        {
1209        case LVSEEK_SET:
1210            res = lseek( m_fd, offset, SEEK_SET );
1211            break;
1212        case LVSEEK_CUR:
1213            res = lseek( m_fd, offset, SEEK_CUR );
1214            break;
1215        case LVSEEK_END:
1216            res = lseek( m_fd, offset, SEEK_END );
1217            break;
1218        }
1219        if (res!=(off_t)-1)
1220        {
1221            m_pos = res;
1222            if ( pNewPos )
1223                * pNewPos = res;
1224            return LVERR_OK;
1225        }
1226        CRLog::error("error setting file position to %d (%d)", (int)offset, (int)origin );
1227        return LVERR_FAIL;
1228 #endif
1229     }
Close()1230     lverror_t Close()
1231     {
1232 #if defined(_WIN32)
1233         if (m_hFile == INVALID_HANDLE_VALUE)
1234             return LVERR_FAIL;
1235         CloseHandle( m_hFile );
1236         m_hFile = INVALID_HANDLE_VALUE;
1237 #else
1238         if ( m_fd!= -1 ) {
1239             close(m_fd);
1240             m_fd = -1;
1241         }
1242 #endif
1243         SetName(NULL);
1244         return LVERR_OK;
1245     }
CreateFileStream(lString32 fname,lvopen_mode_t mode)1246     static LVFileStream * CreateFileStream( lString32 fname, lvopen_mode_t mode )
1247     {
1248         LVFileStream * f = new LVFileStream;
1249         if (f->OpenFile( fname, mode )==LVERR_OK) {
1250             return f;
1251         } else {
1252             delete f;
1253             return NULL;
1254         }
1255     }
OpenFile(lString32 fname,int mode)1256     lverror_t OpenFile( lString32 fname, int mode )
1257     {
1258         mode = mode & LVOM_MASK;
1259 #if defined(_WIN32)
1260         lUInt32 m = 0;
1261         lUInt32 s = 0;
1262         lUInt32 c = 0;
1263         SetName(fname.c_str());
1264         switch (mode) {
1265         case LVOM_READWRITE:
1266             m |= GENERIC_WRITE|GENERIC_READ;
1267             s |= FILE_SHARE_WRITE|FILE_SHARE_READ;
1268             c |= OPEN_ALWAYS;
1269             break;
1270         case LVOM_READ:
1271             m |= GENERIC_READ;
1272             s |= FILE_SHARE_READ;
1273             c |= OPEN_EXISTING;
1274             break;
1275         case LVOM_WRITE:
1276             m |= GENERIC_WRITE;
1277             s |= FILE_SHARE_WRITE;
1278             c |= CREATE_ALWAYS;
1279             break;
1280         case LVOM_APPEND:
1281             m |= GENERIC_WRITE|GENERIC_READ;
1282             s |= FILE_SHARE_WRITE|FILE_SHARE_READ;
1283             c |= OPEN_ALWAYS;
1284             break;
1285         case LVOM_CLOSED:
1286         case LVOM_ERROR:
1287             crFatalError();
1288             break;
1289         }
1290         lString16 fn16 = UnicodeToUtf16(fname);
1291         m_hFile = CreateFileW( fn16.c_str(), m, s, NULL, c, FILE_ATTRIBUTE_NORMAL, NULL);
1292         if (m_hFile == INVALID_HANDLE_VALUE || !m_hFile) {
1293          // unicode not implemented?
1294             lUInt32 err = GetLastError();
1295             if (err==ERROR_CALL_NOT_IMPLEMENTED)
1296                 m_hFile = CreateFileA( UnicodeToLocal(fname).c_str(), m, s, NULL, c, FILE_ATTRIBUTE_NORMAL, NULL);
1297             if ( (m_hFile == INVALID_HANDLE_VALUE) || (!m_hFile) ) {
1298                 // error
1299                 return LVERR_FAIL;
1300             }
1301         }
1302 
1303         // set file size and position
1304         m_mode = (lvopen_mode_t)mode;
1305         lUInt32 hw=0;
1306         m_size = GetFileSize( m_hFile, (LPDWORD)&hw );
1307 #if LVLONG_FILE_SUPPORT
1308         if (hw)
1309             m_size |= (((lvsize_t)hw)<<32);
1310 #endif
1311         m_pos = 0;
1312 
1313         // set filename
1314         SetName( fname.c_str() );
1315 
1316         // move to end of file
1317         if (mode==LVOM_APPEND)
1318             Seek( 0, LVSEEK_END, NULL );
1319 #else
1320         bool use_sync = (mode & LVOM_FLAG_SYNC)!=0;
1321         m_fd = -1;
1322 
1323         int flags = (mode==LVOM_READ) ? O_RDONLY : O_RDWR | O_CREAT | (use_sync ? O_SYNC : 0) | (mode==LVOM_WRITE ? O_TRUNC : 0);
1324         lString8 fn8 = UnicodeToUtf8(fname);
1325         m_fd = open( fn8.c_str(), flags, (mode_t)0666);
1326         if (m_fd == -1) {
1327 #ifndef ANDROID
1328             CRLog::error( "Error opening file %s for %s", fn8.c_str(), (mode==LVOM_READ) ? "reading" : "read/write" );
1329             //CRLog::error( "Error opening file %s for %s, errno=%d, msg=%s", fn8.c_str(), (mode==LVOM_READ) ? "reading" : "read/write",  (int)errno, strerror(errno) );
1330 #endif
1331             return LVERR_FAIL;
1332         }
1333         struct stat stat;
1334         if ( fstat( m_fd, &stat ) ) {
1335             CRLog::error( "Cannot get file size for %s", fn8.c_str() );
1336             return LVERR_FAIL;
1337         }
1338         m_mode = (lvopen_mode_t)mode;
1339         m_size = (lvsize_t) stat.st_size;
1340 #endif
1341 
1342         SetName(fname.c_str());
1343         return LVERR_OK;
1344     }
LVFileStream()1345     LVFileStream() :
1346 #if defined(_WIN32)
1347             m_hFile(INVALID_HANDLE_VALUE),
1348 #else
1349             m_fd(-1),
1350 #endif
1351             //m_parent(NULL),
1352             m_size(0), m_pos(0)
1353     {
1354     }
~LVFileStream()1355     virtual ~LVFileStream()
1356     {
1357         Close();
1358         //m_parent = NULL;
1359     }
1360 };
1361 #endif
1362 
1363 /// tries to split full path name into archive name and file name inside archive using separator "@/" or "@\"
LVSplitArcName(lString32 fullPathName,lString32 & arcPathName,lString32 & arcItemPathName)1364 bool LVSplitArcName( lString32 fullPathName, lString32 & arcPathName, lString32 & arcItemPathName )
1365 {
1366     int p = fullPathName.pos("@/");
1367     if ( p<0 )
1368         p = fullPathName.pos("@\\");
1369     if ( p<0 )
1370         return false;
1371     arcPathName = fullPathName.substr(0, p);
1372     arcItemPathName = fullPathName.substr(p + 2);
1373     return !arcPathName.empty() && !arcItemPathName.empty();
1374 }
1375 
1376 /// tries to split full path name into archive name and file name inside archive using separator "@/" or "@\"
LVSplitArcName(lString8 fullPathName,lString8 & arcPathName,lString8 & arcItemPathName)1377 bool LVSplitArcName( lString8 fullPathName, lString8 & arcPathName, lString8 & arcItemPathName )
1378 {
1379     int p = fullPathName.pos("@/");
1380     if ( p<0 )
1381         p = fullPathName.pos("@\\");
1382     if ( p<0 )
1383         return false;
1384     arcPathName = fullPathName.substr(0, p);
1385     arcItemPathName = fullPathName.substr(p + 2);
1386     return !arcPathName.empty() && !arcItemPathName.empty();
1387 }
1388 
1389 // facility functions
LVOpenFileStream(const lChar32 * pathname,int mode)1390 LVStreamRef LVOpenFileStream( const lChar32 * pathname, int mode )
1391 {
1392     lString32 fn(pathname);
1393     if (fn.length() > 1 && fn[0] == ASSET_PATH_PREFIX) {
1394     	if (!_assetContainerFactory || mode != LVOM_READ)
1395     		return LVStreamRef();
1396     	lString32 assetPath = LVExtractAssetPath(fn);
1397     	return _assetContainerFactory->openAssetStream(assetPath);
1398     }
1399 #if 0
1400     //defined(_LINUX) || defined(_WIN32)
1401     if ( mode==LVOM_READ ) {
1402         LVFileMappedStream * stream = LVFileMappedStream::CreateFileStream( fn, mode, 0 );
1403         if ( stream != NULL )
1404         {
1405             return LVStreamRef( stream );
1406         }
1407         return LVStreamRef();
1408     }
1409 #endif
1410 
1411     LVFileStream * stream = LVFileStream::CreateFileStream( fn, (lvopen_mode_t)mode );
1412     if ( stream!=NULL )
1413     {
1414         return LVStreamRef( stream );
1415     }
1416     return LVStreamRef();
1417 }
1418 
LVOpenFileStream(const lChar8 * pathname,int mode)1419 LVStreamRef LVOpenFileStream( const lChar8 * pathname, int mode )
1420 {
1421     lString32 fn = Utf8ToUnicode(lString8(pathname));
1422     return LVOpenFileStream( fn.c_str(), mode );
1423 }
1424 
1425 
GetMode()1426 lvopen_mode_t LVTextStream::GetMode()
1427 {
1428     return m_base_stream->GetMode();
1429 }
1430 
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * pNewPos)1431 lverror_t LVTextStream::Seek( lvoffset_t offset, lvseek_origin_t origin, lvpos_t * pNewPos )
1432 {
1433     return m_base_stream->Seek(offset, origin, pNewPos);
1434 }
1435 
Tell(lvpos_t * pPos)1436 lverror_t LVTextStream::Tell( lvpos_t * pPos )
1437 {
1438     return m_base_stream->Tell(pPos);
1439 }
1440 
SetPos(lvpos_t p)1441 lvpos_t   LVTextStream::SetPos(lvpos_t p)
1442 {
1443     return m_base_stream->SetPos(p);
1444 }
1445 
GetPos()1446 lvpos_t   LVTextStream::GetPos()
1447 {
1448     return m_base_stream->GetPos();
1449 }
1450 
SetSize(lvsize_t size)1451 lverror_t LVTextStream::SetSize( lvsize_t size )
1452 {
1453     return m_base_stream->SetSize(size);
1454 }
1455 
Read(void * buf,lvsize_t count,lvsize_t * nBytesRead)1456 lverror_t LVTextStream::Read( void * buf, lvsize_t count, lvsize_t * nBytesRead )
1457 {
1458     return m_base_stream->Read(buf, count, nBytesRead);
1459 }
1460 
Write(const void * buf,lvsize_t count,lvsize_t * nBytesWritten)1461 lverror_t LVTextStream::Write( const void * buf, lvsize_t count, lvsize_t * nBytesWritten )
1462 {
1463     return m_base_stream->Write(buf, count, nBytesWritten);
1464 }
1465 
Eof()1466 bool LVTextStream::Eof()
1467 {
1468     return m_base_stream->Eof();
1469 }
1470 
1471 
1472 class LVDirectoryContainerItemInfo : public LVCommonContainerItemInfo
1473 {
1474     friend class LVDirectoryContainer;
1475 };
1476 
1477 class LVDirectoryContainer : public LVNamedContainer
1478 {
1479 protected:
1480     LVDirectoryContainer * m_parent;
1481 public:
OpenStream(const char32_t * fname,lvopen_mode_t mode)1482     virtual LVStreamRef OpenStream( const char32_t * fname, lvopen_mode_t mode )
1483     {
1484         int found_index = -1;
1485         for (int i=0; i<m_list.length(); i++) {
1486             if ( !lStr_cmp( fname, m_list[i]->GetName() ) ) {
1487                 if ( m_list[i]->IsContainer() ) {
1488                     // found directory with same name!!!
1489                     return LVStreamRef();
1490                 }
1491                 found_index = i;
1492                 break;
1493             }
1494         }
1495         // make filename
1496         lString32 fn = m_fname;
1497         fn << fname;
1498         //const char * fb8 = UnicodeToUtf8( fn ).c_str();
1499         //printf("Opening directory container file %s : %s fname=%s path=%s\n", UnicodeToUtf8( lString32(fname) ).c_str(), UnicodeToUtf8( fn ).c_str(), UnicodeToUtf8( m_fname ).c_str(), UnicodeToUtf8( m_path ).c_str());
1500         LVStreamRef stream( LVOpenFileStream( fn.c_str(), mode ) );
1501         if (!stream) {
1502             return stream;
1503         }
1504         //stream->m_parent = this;
1505         if (found_index<0) {
1506             // add new info
1507             LVDirectoryContainerItemInfo * item = new LVDirectoryContainerItemInfo;
1508             item->m_name = fname;
1509             stream->GetSize(&item->m_size);
1510             Add(item);
1511         }
1512         return stream;
1513     }
GetParentContainer()1514     virtual LVContainer * GetParentContainer()
1515     {
1516         return (LVContainer*)m_parent;
1517     }
GetObjectInfo(int index)1518     virtual const LVContainerItemInfo * GetObjectInfo(int index)
1519     {
1520         if (index>=0 && index<m_list.length())
1521             return m_list[index];
1522         return NULL;
1523     }
GetObjectCount() const1524     virtual int GetObjectCount() const
1525     {
1526         return m_list.length();
1527     }
GetSize(lvsize_t * pSize)1528     virtual lverror_t GetSize( lvsize_t * pSize )
1529     {
1530         if (m_fname.empty())
1531             return LVERR_FAIL;
1532         *pSize = GetObjectCount();
1533         return LVERR_OK;
1534     }
LVDirectoryContainer()1535     LVDirectoryContainer() : m_parent(NULL)
1536     {
1537     }
~LVDirectoryContainer()1538     virtual ~LVDirectoryContainer()
1539     {
1540         SetName(NULL);
1541         Clear();
1542     }
OpenDirectory(const char32_t * path,const char32_t * mask=U"*.*")1543     static LVDirectoryContainer * OpenDirectory( const char32_t * path, const char32_t * mask = U"*.*" )
1544     {
1545         if (!path || !path[0])
1546             return NULL;
1547 
1548 
1549         // container object
1550         LVDirectoryContainer * dir = new LVDirectoryContainer;
1551 
1552         // make filename
1553         lString32 fn( path );
1554         lChar32 lastch = 0;
1555         if ( !fn.empty() )
1556             lastch = fn[fn.length()-1];
1557         if ( lastch!='\\' && lastch!='/' )
1558             fn << dir->m_path_separator;
1559 
1560         dir->SetName(fn.c_str());
1561 
1562 #if !defined(__SYMBIAN32__) && defined(_WIN32)
1563         // WIN32 API
1564         fn << mask;
1565         WIN32_FIND_DATAW data = { 0 };
1566         WIN32_FIND_DATAA dataa = { 0 };
1567         //lString8 bs = DOMString(path).ToAnsiString();
1568         lString16 fn16 = UnicodeToUtf16(fn);
1569         HANDLE hFind = FindFirstFileW(fn16.c_str(), &data);
1570         bool unicode=true;
1571         if (hFind == INVALID_HANDLE_VALUE || !hFind) {
1572             lUInt32 err=GetLastError();
1573             if (err == ERROR_CALL_NOT_IMPLEMENTED) {
1574                 hFind = FindFirstFileA(UnicodeToLocal(fn).c_str(), &dataa);
1575                 unicode=false;
1576                 if (hFind == INVALID_HANDLE_VALUE || !hFind)
1577                 {
1578                     delete dir;
1579                     return NULL;
1580                 }
1581             } else {
1582                 delete dir;
1583                 return NULL;
1584             }
1585         }
1586 
1587         if (unicode) {
1588             // unicode
1589             for (;;) {
1590                 lUInt32 dwAttrs = data.dwFileAttributes;
1591                 wchar_t * pfn = data.cFileName;
1592                 for (int i=0; data.cFileName[i]; i++) {
1593                     if (data.cFileName[i]=='/' || data.cFileName[i]=='\\')
1594                         pfn = data.cFileName + i + 1;
1595                 }
1596 
1597                 if ( (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) ) {
1598                     // directory
1599                     if (!lStr_cmp(pfn, L"..") || !lStr_cmp(pfn, L".")) {
1600                         // .. or .
1601                     } else {
1602                         // normal directory
1603                         LVDirectoryContainerItemInfo * item = new LVDirectoryContainerItemInfo;
1604                         item->m_name = Utf16ToUnicode(pfn);
1605                         item->m_is_container = true;
1606                         dir->Add(item);
1607                     }
1608                 } else {
1609                     // file
1610                     LVDirectoryContainerItemInfo * item = new LVDirectoryContainerItemInfo;
1611                     item->m_name = Utf16ToUnicode(pfn);
1612                     item->m_size = data.nFileSizeLow;
1613                     item->m_flags = data.dwFileAttributes;
1614                     dir->Add(item);
1615                 }
1616 
1617                 if (!FindNextFileW(hFind, &data)) {
1618                     // end of list
1619                     break;
1620                 }
1621 
1622             }
1623         } else {
1624             // ANSI
1625             for (;;) {
1626                 lUInt32 dwAttrs = dataa.dwFileAttributes;
1627                 char * pfn = dataa.cFileName;
1628                 for (int i=0; dataa.cFileName[i]; i++) {
1629                     if (dataa.cFileName[i]=='/' || dataa.cFileName[i]=='\\')
1630                         pfn = dataa.cFileName + i + 1;
1631                 }
1632 
1633                 if ( (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) ) {
1634                     // directory
1635                     if (!strcmp(pfn, "..") || !strcmp(pfn, ".")) {
1636                         // .. or .
1637                     } else {
1638                         // normal directory
1639                         LVDirectoryContainerItemInfo * item = new LVDirectoryContainerItemInfo;
1640                         item->m_name = LocalToUnicode( lString8( pfn ) );
1641                         item->m_is_container = true;
1642                         dir->Add(item);
1643                     }
1644                 } else {
1645                     // file
1646                     LVDirectoryContainerItemInfo * item = new LVDirectoryContainerItemInfo;
1647                     item->m_name = LocalToUnicode( lString8( pfn ) );
1648                     item->m_size = data.nFileSizeLow;
1649                     item->m_flags = data.dwFileAttributes;
1650                     dir->Add(item);
1651                 }
1652 
1653                 if (!FindNextFileA(hFind, &dataa)) {
1654                     // end of list
1655                     break;
1656                 }
1657 
1658             }
1659         }
1660 
1661         FindClose( hFind );
1662 #else
1663         // POSIX
1664         lString32 p( fn );
1665         p.erase( p.length()-1, 1 );
1666         lString8 p8 = UnicodeToLocal( p );
1667         if ( p8.empty() )
1668             p8 = ".";
1669         const char * p8s = p8.c_str();
1670         DIR * d = opendir(p8s);
1671         if ( d ) {
1672             struct dirent * pde;
1673             while ( (pde = readdir(d))!=NULL ) {
1674                 lString8 fpath = p8 + "/" + pde->d_name;
1675                 struct stat st;
1676                 stat( fpath.c_str(), &st );
1677                 if ( S_ISDIR(st.st_mode) ) {
1678                     // dir
1679                     if ( strcmp(pde->d_name, ".") && strcmp(pde->d_name, "..") ) {
1680                         // normal directory
1681                         LVDirectoryContainerItemInfo * item = new LVDirectoryContainerItemInfo;
1682                         item->m_name = LocalToUnicode(lString8(pde->d_name));
1683                         item->m_is_container = true;
1684                         dir->Add(item);
1685                     }
1686                 } else if ( S_ISREG(st.st_mode) ) {
1687                     // file
1688                     LVDirectoryContainerItemInfo * item = new LVDirectoryContainerItemInfo;
1689                     item->m_name = LocalToUnicode(lString8(pde->d_name));
1690                     item->m_size = st.st_size;
1691                     item->m_flags = st.st_mode;
1692                     dir->Add(item);
1693                 }
1694             }
1695             closedir(d);
1696         } else {
1697             delete dir;
1698             return NULL;
1699         }
1700 
1701 
1702 #endif
1703         return dir;
1704     }
1705 };
1706 
1707 class LVCachedStream : public LVNamedStream
1708 {
1709 private:
1710 
1711     #define CACHE_BUF_BLOCK_SHIFT 12
1712     #define CACHE_BUF_BLOCK_SIZE (1<<CACHE_BUF_BLOCK_SHIFT)
1713     class BufItem
1714     {
1715     public:
1716         lUInt32   start;
1717         lUInt32   size;
1718         BufItem * prev;
1719         BufItem * next;
1720         lUInt8    buf[CACHE_BUF_BLOCK_SIZE];
1721 
getIndex()1722         int getIndex() { return start >> CACHE_BUF_BLOCK_SHIFT; }
BufItem()1723         BufItem() : prev(NULL), next(NULL) { }
1724     };
1725 
1726     LVStreamRef m_stream;
1727     int m_bufSize;
1728     lvsize_t    m_size;
1729     lvpos_t     m_pos;
1730     BufItem * * m_buf;
1731     BufItem *   m_head;
1732     BufItem *   m_tail;
1733     int         m_bufItems;
1734     int         m_bufLen;
1735 
1736     /// add item to head
addNewItem(int start)1737     BufItem * addNewItem( int start )
1738     {
1739         //
1740         int index = (start >> CACHE_BUF_BLOCK_SHIFT);
1741         BufItem * item = new BufItem();
1742         if (!m_head)
1743         {
1744             m_head = m_tail = item;
1745         }
1746         else
1747         {
1748             item->next = m_head;
1749             m_head->prev = item;
1750             m_head = item;
1751         }
1752         item->start = start;
1753         int sz = CACHE_BUF_BLOCK_SIZE;
1754         if ( start + sz > (int)m_size )
1755             sz = (int)(m_size - start);
1756         item->size = sz;
1757         m_buf[ index ] = item;
1758         m_bufLen++;
1759         assert( !(m_head && !m_tail) );
1760         return item;
1761     }
1762     /// move item to top
moveToTop(int index)1763     void moveToTop( int index )
1764     {
1765         BufItem * item = m_buf[index];
1766         if ( !item || m_head == item )
1767             return;
1768         if ( m_tail == item )
1769             m_tail = item->prev;
1770         if ( item->next )
1771             item->next->prev = item->prev;
1772         if ( item->prev )
1773             item->prev->next = item->next;
1774         m_head->prev = item;
1775         item->next = m_head;
1776         item->prev = NULL;
1777         m_head = item;
1778         assert( !(m_head && !m_tail) );
1779     }
1780     /// reuse existing item from tail of list
reuseItem(int start)1781     BufItem * reuseItem( int start )
1782     {
1783         //
1784         int rem_index = m_tail->start >> CACHE_BUF_BLOCK_SHIFT;
1785         if (m_tail->prev)
1786             m_tail->prev->next = NULL;
1787         m_tail = m_tail->prev;
1788         BufItem * item = m_buf[rem_index];
1789         m_buf[ rem_index ] = NULL;
1790         int index = (start >> CACHE_BUF_BLOCK_SHIFT);
1791         m_buf[ index ] = item;
1792         item->start = start;
1793         int sz = CACHE_BUF_BLOCK_SIZE;
1794         if ( start + sz > (int)m_size )
1795             sz = (int)(m_size - start);
1796         item->size = sz;
1797         item->next = m_head;
1798         item->prev = NULL;
1799         m_head->prev = item;
1800         m_head = item;
1801         assert( !(m_head && !m_tail) );
1802         return item;
1803     }
1804     /// read item content from base stream
fillItem(BufItem * item)1805     bool fillItem( BufItem * item )
1806     {
1807         //if ( m_stream->SetPos( item->start )==(lvpos_t)(~0) )
1808         if ( m_stream->SetPos( item->start )!=(lvpos_t)item->start )
1809             return false;
1810         //int streamSize=m_stream->GetSize(); int bytesLeft = m_stream->GetSize() - m_stream->GetPos();
1811         lvsize_t bytesRead = 0;
1812         if ( m_stream->Read( item->buf, item->size, &bytesRead )!=LVERR_OK || bytesRead!=item->size )
1813             return false;
1814         return true;
1815     }
addOrReuseItem(int start)1816     BufItem * addOrReuseItem( int start )
1817     {
1818         //assert( !(m_head && !m_tail) );
1819         if ( m_bufLen < m_bufSize )
1820             return addNewItem( start );
1821         else
1822             return reuseItem( start );
1823     }
1824     /// checks several items, loads if necessary
fillFragment(int startIndex,int count)1825     bool fillFragment( int startIndex, int count )
1826     {
1827         if (count<=0 || startIndex<0 || startIndex+count>m_bufItems)
1828         {
1829             return false;
1830         }
1831         int firstne = -1;
1832         int lastne = -1;
1833         int i;
1834         for ( i=startIndex; i<startIndex+count; i++)
1835         {
1836             if ( m_buf[i] )
1837             {
1838                 moveToTop( i );
1839             }
1840             else
1841             {
1842                 if (firstne == -1)
1843                     firstne = i;
1844                 lastne = i;
1845             }
1846         }
1847         if ( firstne<0 )
1848             return true;
1849         for ( i=firstne; i<=lastne; i++)
1850         {
1851             if ( !m_buf[i] )
1852             {
1853                 BufItem * item = addOrReuseItem( i << CACHE_BUF_BLOCK_SHIFT );
1854                 if ( !fillItem ( item ) )
1855                     return false;
1856             }
1857             else
1858             {
1859                 moveToTop( i );
1860             }
1861         }
1862         return true;
1863     }
1864 public:
1865 
LVCachedStream(LVStreamRef stream,int bufSize)1866     LVCachedStream( LVStreamRef stream, int bufSize ) : m_stream(stream), m_pos(0),
1867             m_head(NULL), m_tail(NULL), m_bufLen(0)
1868     {
1869         m_size = m_stream->GetSize();
1870         m_bufItems = (int)((m_size + CACHE_BUF_BLOCK_SIZE - 1) >> CACHE_BUF_BLOCK_SHIFT);
1871         if (!m_bufItems)
1872             m_bufItems = 1;
1873         m_bufSize = (bufSize + CACHE_BUF_BLOCK_SIZE - 1) >> CACHE_BUF_BLOCK_SHIFT;
1874         if (m_bufSize<3)
1875             m_bufSize = 3;
1876         m_buf = new BufItem* [m_bufItems]();
1877         SetName( stream->GetName() );
1878     }
~LVCachedStream()1879     virtual ~LVCachedStream()
1880     {
1881         if (m_buf)
1882         {
1883             for (int i=0; i<m_bufItems; i++)
1884                 if (m_buf[i])
1885                     delete m_buf[i];
1886             delete[] m_buf;
1887         }
1888     }
1889 
1890     /// fastly return already known CRC
getcrc32(lUInt32 & dst)1891     virtual lverror_t getcrc32( lUInt32 & dst )
1892     {
1893         return m_stream->getcrc32( dst );
1894     }
1895 
Eof()1896     virtual bool Eof()
1897     {
1898         return m_pos >= m_size;
1899     }
GetSize()1900     virtual lvsize_t  GetSize()
1901     {
1902         return m_size;
1903     }
1904 
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * newPos)1905     virtual lverror_t Seek(lvoffset_t offset, lvseek_origin_t origin, lvpos_t* newPos)
1906     {
1907         lvpos_t npos = 0;
1908         lvpos_t currpos = m_pos;
1909         switch (origin) {
1910         case LVSEEK_SET:
1911             npos = offset;
1912             break;
1913         case LVSEEK_CUR:
1914             npos = currpos + offset;
1915             break;
1916         case LVSEEK_END:
1917             npos = m_size + offset;
1918             break;
1919         }
1920         if (npos > m_size)
1921             return LVERR_FAIL;
1922         m_pos = npos;
1923         if (newPos)
1924         {
1925             *newPos =  m_pos;
1926         }
1927         return LVERR_OK;
1928     }
1929 
Write(const void *,lvsize_t,lvsize_t *)1930     virtual lverror_t Write(const void*, lvsize_t, lvsize_t*)
1931     {
1932         return LVERR_NOTIMPL;
1933     }
1934 
Read(void * buf,lvsize_t size,lvsize_t * pBytesRead)1935     virtual lverror_t Read(void* buf, lvsize_t size, lvsize_t* pBytesRead)
1936     {
1937         if ( m_pos + size > m_size )
1938             size = m_size - m_pos;
1939         if ( size <= 0 ) {
1940             if ( pBytesRead )
1941                 *pBytesRead = 0;
1942             return LVERR_FAIL;
1943         }
1944         int startIndex = (int)(m_pos >> CACHE_BUF_BLOCK_SHIFT);
1945         int endIndex = (int)((m_pos + size - 1) >> CACHE_BUF_BLOCK_SHIFT);
1946         int count = endIndex - startIndex + 1;
1947         int extraItems = (m_bufSize - count); // max move backward
1948         if (extraItems<0)
1949             extraItems = 0;
1950         char * flags = new char[ count ]();
1951 
1952         //if ( m_stream
1953         int start = (int)m_pos;
1954         lUInt8 * dst = (lUInt8 *) buf;
1955         int dstsz = (int)size;
1956         int i;
1957         int istart = start & (CACHE_BUF_BLOCK_SIZE - 1);
1958         for ( i=startIndex; i<=endIndex; i++ )
1959         {
1960             BufItem * item = m_buf[i];
1961             if (item)
1962             {
1963                 int isz = item->size - istart;
1964                 if (isz > dstsz)
1965                     isz = dstsz;
1966                 memcpy( dst, item->buf + istart, isz );
1967                 flags[i - startIndex] = 1;
1968             }
1969             dstsz -= CACHE_BUF_BLOCK_SIZE - istart;
1970             dst += CACHE_BUF_BLOCK_SIZE - istart;
1971             istart = 0;
1972         }
1973 
1974         dst = (lUInt8 *) buf;
1975 
1976         bool flgFirstNE = true;
1977         istart = start & (CACHE_BUF_BLOCK_SIZE - 1);
1978         dstsz = (int)size;
1979         for ( i=startIndex; i<=endIndex; i++ )
1980         {
1981             if (!flags[ i - startIndex])
1982             {
1983                 if ( !m_buf[i] )
1984                 {
1985                     int fillStart = i;
1986                     if ( flgFirstNE )
1987                     {
1988                         fillStart -= extraItems;
1989                     }
1990                     if (fillStart<0)
1991                         fillStart = 0;
1992                     int fillEnd = fillStart + m_bufSize - 1;
1993                     if (fillEnd>endIndex)
1994                         fillEnd = endIndex;
1995                     bool res = fillFragment( fillStart, fillEnd - fillStart + 1 );
1996                     if ( !res )
1997                     {
1998                         fprintf( stderr, "cannot fill fragment %d .. %d\n", fillStart, fillEnd );
1999                         exit(-1);
2000                     }
2001                     flgFirstNE = false;
2002                 }
2003                 BufItem * item = m_buf[i];
2004                 int isz = item->size - istart;
2005                 if (isz > dstsz)
2006                     isz = dstsz;
2007                 memcpy( dst, item->buf + istart, isz );
2008             }
2009             dst += CACHE_BUF_BLOCK_SIZE - istart;
2010             dstsz -= CACHE_BUF_BLOCK_SIZE - istart;
2011             istart = 0;
2012         }
2013         delete[] flags;
2014 
2015         lvsize_t bytesRead = size;
2016         if ( m_pos + size > m_size )
2017             bytesRead = m_size - m_pos;
2018         m_pos += bytesRead;
2019         if (pBytesRead)
2020             *pBytesRead = bytesRead;
2021         return LVERR_OK;
2022     }
2023 
SetSize(lvsize_t)2024     virtual lverror_t SetSize(lvsize_t)
2025     {
2026         return LVERR_NOTIMPL;
2027     }
2028 };
2029 
2030 
2031 #pragma pack(push, 1)
2032 typedef struct {
2033     lUInt32  Mark;      // 0
2034     lUInt8   UnpVer;    // 4
2035     lUInt8   UnpOS;     // 5
2036     lUInt16  Flags;     // 6
2037     lUInt16  others[11]; //
2038     //lUInt16  Method;    // 8
2039     //lUInt32  ftime;     // A
2040     //lUInt32  CRC;       // E
2041     //lUInt32  PackSize;  //12
2042     //lUInt32  UnpSize;   //16
2043     //lUInt16  NameLen;   //1A
2044     //lUInt16  AddLen;    //1C
2045 
getMethod__anon83b400cc01082046     lUInt16  getMethod() { return others[0]; }    // 8
getftime__anon83b400cc01082047     lUInt32  getftime() { return others[1] | ( (lUInt32)others[2] << 16); }     // A
getCRC__anon83b400cc01082048     lUInt32  getCRC() { return others[3] | ( (lUInt32)others[4] << 16); }       // E
getPackSize__anon83b400cc01082049     lUInt32  getPackSize() { return others[5] | ( (lUInt32)others[6] << 16); }  //12
getUnpSize__anon83b400cc01082050     lUInt32  getUnpSize() { return others[7] | ( (lUInt32)others[8] << 16); }   //16
getNameLen__anon83b400cc01082051     lUInt16  getNameLen() { return others[9]; }   //1A
getAddLen__anon83b400cc01082052     lUInt16  getAddLen() { return others[10]; }    //1C
byteOrderConv__anon83b400cc01082053     void byteOrderConv()
2054     {
2055         //
2056         lvByteOrderConv cnv;
2057         if ( cnv.msf() )
2058         {
2059             cnv.rev( &Mark );
2060             cnv.rev( &Flags );
2061             for ( int i=0; i<11; i++) {
2062                 cnv.rev( &others[i] );
2063             }
2064             //cnv.rev( &Method );
2065             //cnv.rev( &ftime );
2066             //cnv.rev( &CRC );
2067             //cnv.rev( &PackSize );
2068             //cnv.rev( &UnpSize );
2069             //cnv.rev( &NameLen );
2070             //cnv.rev( &AddLen );
2071         }
2072     }
2073 } ZipLocalFileHdr;
2074 #pragma pack(pop)
2075 
2076 #pragma pack(push, 1)
2077 struct ZipHd2
2078 {
2079     lUInt32  Mark;      // 0
2080     lUInt8   PackVer;   // 4
2081     lUInt8   PackOS;
2082     lUInt8   UnpVer;
2083     lUInt8   UnpOS;
2084     lUInt16     Flags;  // 8
2085     lUInt16     Method; // A
2086     lUInt32    ftime;   // C
2087     lUInt32    CRC;     // 10
2088     lUInt32    PackSize;// 14
2089     lUInt32    UnpSize; // 18
2090     lUInt16     NameLen;// 1C
2091     lUInt16     AddLen; // 1E
2092     lUInt16     CommLen;// 20
2093     lUInt16     DiskNum;// 22
2094     //lUInt16     ZIPAttr;// 24
2095     //lUInt32     Attr;   // 26
2096     //lUInt32     Offset; // 2A
2097     lUInt16     _Attr_and_Offset[5];   // 24
getZIPAttrZipHd22098     lUInt16     getZIPAttr() { return _Attr_and_Offset[0]; }
getAttrZipHd22099     lUInt32     getAttr() { return _Attr_and_Offset[1] | ((lUInt32)_Attr_and_Offset[2]<<16); }
getOffsetZipHd22100     lUInt32     getOffset() { return _Attr_and_Offset[3] | ((lUInt32)_Attr_and_Offset[4]<<16); }
setOffsetZipHd22101     void        setOffset(lUInt32 offset) {
2102         _Attr_and_Offset[3] = (lUInt16)(offset & 0xFFFF);
2103         _Attr_and_Offset[4] = (lUInt16)(offset >> 16);
2104     }
byteOrderConvZipHd22105     void byteOrderConv()
2106     {
2107         //
2108         lvByteOrderConv cnv;
2109         if ( cnv.msf() )
2110         {
2111             cnv.rev( &Mark );
2112             cnv.rev( &Flags );
2113             cnv.rev( &Method );
2114             cnv.rev( &ftime );
2115             cnv.rev( &CRC );
2116             cnv.rev( &PackSize );
2117             cnv.rev( &UnpSize );
2118             cnv.rev( &NameLen );
2119             cnv.rev( &AddLen );
2120             cnv.rev( &CommLen );
2121             cnv.rev( &DiskNum );
2122             cnv.rev( &_Attr_and_Offset[0] );
2123             cnv.rev( &_Attr_and_Offset[1] );
2124             cnv.rev( &_Attr_and_Offset[2] );
2125             cnv.rev( &_Attr_and_Offset[3] );
2126             cnv.rev( &_Attr_and_Offset[4] );
2127         }
2128     }
2129 };
2130 #pragma pack(pop)
2131 
2132 //#define ARC_INBUF_SIZE  4096
2133 //#define ARC_OUTBUF_SIZE 16384
2134 #define ARC_INBUF_SIZE  5000
2135 #define ARC_OUTBUF_SIZE 10000
2136 
2137 #if (USE_ZLIB==1)
2138 
2139 class LVZipDecodeStream : public LVNamedStream
2140 {
2141 private:
2142     LVStreamRef m_stream;
2143     lvsize_t    m_start;
2144     lvsize_t    m_packsize;
2145     lvsize_t    m_unpacksize;
2146     z_stream_s  m_zstream;
2147     lvpos_t     m_inbytesleft; // bytes left
2148     lvpos_t     m_outbytesleft;
2149     bool        m_zInitialized;
2150     int         m_decodedpos;
2151     lUInt8 *    m_inbuf;
2152     lUInt8 *    m_outbuf;
2153     lUInt32     m_CRC;
2154     lUInt32     m_originalCRC;
2155     lUInt32     m_decodedCRC;
2156 
2157 
LVZipDecodeStream(LVStreamRef stream,lvsize_t start,lvsize_t packsize,lvsize_t unpacksize,lUInt32 crc)2158     LVZipDecodeStream( LVStreamRef stream, lvsize_t start, lvsize_t packsize, lvsize_t unpacksize, lUInt32 crc )
2159         : m_stream(stream), m_start(start), m_packsize(packsize), m_unpacksize(unpacksize),
2160         m_inbytesleft(0), m_outbytesleft(0), m_zInitialized(false), m_decodedpos(0),
2161         m_inbuf(NULL), m_outbuf(NULL), m_CRC(0), m_originalCRC(crc), m_decodedCRC(0)
2162     {
2163         m_inbuf = new lUInt8[ARC_INBUF_SIZE];
2164         m_outbuf = new lUInt8[ARC_OUTBUF_SIZE];
2165         rewind();
2166     }
2167 
~LVZipDecodeStream()2168     ~LVZipDecodeStream()
2169     {
2170         zUninit();
2171         if (m_inbuf)
2172             delete[] m_inbuf;
2173         if (m_outbuf)
2174             delete[] m_outbuf;
2175     }
2176 
2177     /// Get stream open mode
2178     /** \return lvopen_mode_t open mode */
GetMode()2179     virtual lvopen_mode_t GetMode()
2180     {
2181         return LVOM_READ;
2182     }
2183 
zUninit()2184     void zUninit()
2185     {
2186         if (!m_zInitialized)
2187             return;
2188         inflateEnd(&m_zstream);
2189         m_zInitialized = false;
2190     }
2191 
2192     /// Fill input buffer: returns -1 if fails.
fillInBuf()2193     int fillInBuf()
2194     {
2195         if (m_zstream.avail_in < ARC_INBUF_SIZE / 4 && m_inbytesleft > 0)
2196         {
2197             int inpos = (int)(m_zstream.next_in ? (m_zstream.next_in - m_inbuf) : 0);
2198             if ( inpos > ARC_INBUF_SIZE/2 )
2199             {
2200                 // move rest of data to beginning of buffer
2201                 for ( int i=0; i<(int)m_zstream.avail_in; i++)
2202                     m_inbuf[i] = m_inbuf[ i+inpos ];
2203                 m_zstream.next_in = m_inbuf;
2204                 inpos = 0;
2205             }
2206             int tailpos = inpos + m_zstream.avail_in;
2207             int bytes_to_read = ARC_INBUF_SIZE - tailpos;
2208             if ( bytes_to_read > (int)m_inbytesleft )
2209                 bytes_to_read = (int)m_inbytesleft;
2210             if (bytes_to_read > 0)
2211             {
2212                 lvsize_t bytesRead = 0;
2213                 if ( m_stream->Read( m_inbuf + tailpos, bytes_to_read, &bytesRead ) != LVERR_OK )
2214                 {
2215                     // read error
2216                     m_zstream.avail_in = 0;
2217                     return -1;
2218                 }
2219                 m_CRC = lStr_crc32( m_CRC, m_inbuf + tailpos, (int)(bytesRead) );
2220                 m_zstream.avail_in += (int)bytesRead;
2221                 m_inbytesleft -= bytesRead;
2222             }
2223             else
2224             {
2225                 //check CRC
2226                 if ( m_CRC != m_originalCRC ) {
2227                     CRLog::error("ZIP stream '%s': CRC doesn't match", LCSTR(lString32(GetName())) );
2228                     return -1; // CRC error
2229                 }
2230             }
2231         }
2232         return m_zstream.avail_in;
2233     }
2234 
rewind()2235     bool rewind()
2236     {
2237         zUninit();
2238         // stream
2239         m_stream->SetPos( 0 );
2240 
2241         m_CRC = 0;
2242         memset( &m_zstream, 0, sizeof(m_zstream) );
2243         // inbuf
2244         m_inbytesleft = m_packsize;
2245         m_zstream.next_in = m_inbuf;
2246         m_zstream.avail_in = 0;
2247         fillInBuf();
2248         // outbuf
2249         m_zstream.next_out = m_outbuf;
2250         m_zstream.avail_out = ARC_OUTBUF_SIZE;
2251         m_decodedpos = 0;
2252         m_outbytesleft = m_unpacksize;
2253         // Z
2254         if ( inflateInit2( &m_zstream, -15 ) != Z_OK )
2255         {
2256             return false;
2257         }
2258         m_zInitialized = true;
2259         return true;
2260     }
2261     // returns count of available decoded bytes in buffer
getAvailBytes()2262     inline int getAvailBytes()
2263     {
2264         return (int)(m_zstream.next_out - m_outbuf - m_decodedpos);
2265     }
2266     /// decode next portion of data, returns number of decoded bytes available, -1 if error
decodeNext()2267     int decodeNext()
2268     {
2269         int avail = getAvailBytes();
2270         if (avail>0)
2271             return avail;
2272         // fill in buffer
2273         int in_bytes = fillInBuf();
2274         if (in_bytes<0)
2275             return -1;
2276         // reserve space for output
2277         if (m_decodedpos > ARC_OUTBUF_SIZE/2 || (m_zstream.avail_out < ARC_OUTBUF_SIZE / 4 && m_outbytesleft > 0) )
2278         {
2279 
2280             int outpos = (int)(m_zstream.next_out - m_outbuf);
2281             if ( m_decodedpos > ARC_OUTBUF_SIZE/2 || outpos > ARC_OUTBUF_SIZE*2/4 || m_zstream.avail_out==0 || m_inbytesleft==0 )
2282             {
2283                 // move rest of data to beginning of buffer
2284                 for ( int i=(int)m_decodedpos; i<outpos; i++)
2285                     m_outbuf[i - m_decodedpos] = m_outbuf[ i ];
2286                 //m_inbuf[i - m_decodedpos] = m_inbuf[ i ];
2287                 m_zstream.next_out -= m_decodedpos;
2288                 outpos -= m_decodedpos;
2289                 m_decodedpos = 0;
2290                 m_zstream.avail_out = ARC_OUTBUF_SIZE - outpos;
2291             }
2292         }
2293         // int decoded = m_zstream.avail_out;
2294         int res = inflate( &m_zstream, m_inbytesleft > 0 ? Z_NO_FLUSH : Z_FINISH ); //m_inbytesleft | m_zstream.avail_in
2295         // decoded -= m_zstream.avail_out;
2296         if (res == Z_STREAM_ERROR)
2297         {
2298             return -1;
2299         }
2300         if (res == Z_BUF_ERROR)
2301         {
2302             //return -1;
2303             //res = 0; // DEBUG
2304         }
2305         avail = getAvailBytes();
2306         return avail;
2307     }
2308     /// skip bytes from out stream
skip(int bytesToSkip)2309     bool skip( int bytesToSkip )
2310     {
2311         while (bytesToSkip > 0)
2312         {
2313             int avail = decodeNext();
2314 
2315             if (avail < 0)
2316             {
2317                 return false; // error
2318             }
2319             else if (avail==0)
2320             {
2321                 return true;
2322             }
2323 
2324             if (avail >= bytesToSkip)
2325                 avail = bytesToSkip;
2326 
2327             m_decodedpos += avail;
2328             m_outbytesleft -= avail;
2329             bytesToSkip -= avail;
2330         }
2331         if (bytesToSkip == 0)
2332             return true;
2333         return false;
2334     }
2335     /// decode bytes
read(lUInt8 * buf,int bytesToRead)2336     int read( lUInt8 * buf, int bytesToRead )
2337     {
2338         int bytesRead = 0;
2339         //
2340         while (bytesToRead > 0)
2341         {
2342             int avail = decodeNext();
2343 
2344             if (avail < 0)
2345             {
2346                 return -1; // error
2347             }
2348             else if (avail==0)
2349             {
2350                 avail = decodeNext();
2351                 (void)avail; // never read
2352                 return bytesRead;
2353             }
2354 
2355             int delta = avail;
2356             if (delta > bytesToRead)
2357                 delta = bytesToRead;
2358 
2359 
2360             // copy data
2361             lUInt8 * src = m_outbuf + m_decodedpos;
2362             for (int i=delta; i>0; --i)
2363                 *buf++ = *src++;
2364 
2365             m_decodedpos += delta;
2366             m_outbytesleft -= delta;
2367             bytesRead += delta;
2368             bytesToRead -= delta;
2369         }
2370         return bytesRead;
2371     }
2372 public:
2373 
2374     /// fastly return already known CRC
getcrc32(lUInt32 & dst)2375     virtual lverror_t getcrc32( lUInt32 & dst )
2376     {
2377         if (m_originalCRC != 0)
2378             dst = m_originalCRC;
2379         else {
2380             // invalid CRC in zip header, try to recalc CRC32
2381             if (m_decodedCRC != 0)
2382                 dst = m_decodedCRC;
2383             else {
2384                 lUInt8* tmp_buff = (lUInt8*)malloc(ARC_OUTBUF_SIZE);
2385                 if (!tmp_buff) {
2386                     dst = 0;
2387                     return LVERR_FAIL;
2388                 }
2389                 lvpos_t curr_pos;
2390                 Seek(0, LVSEEK_CUR, &curr_pos);
2391                 Seek(0, LVSEEK_SET, 0);
2392                 lvsize_t bytesRead = 0;
2393                 while (Read(tmp_buff, ARC_OUTBUF_SIZE, &bytesRead) == LVERR_OK) {
2394                     if (bytesRead > 0)
2395                         m_decodedCRC = lStr_crc32(m_decodedCRC, tmp_buff, bytesRead);
2396                     else
2397                         break;
2398                 }
2399                 free(tmp_buff);
2400                 Seek((lvoffset_t)curr_pos, LVSEEK_SET, 0);
2401                 dst = m_decodedCRC;
2402             }
2403         }
2404         return LVERR_OK;
2405     }
2406 
Eof()2407     virtual bool Eof()
2408     {
2409         return m_outbytesleft==0; //m_pos >= m_size;
2410     }
GetSize()2411     virtual lvsize_t  GetSize()
2412     {
2413         return m_unpacksize;
2414     }
GetPos()2415     virtual lvpos_t GetPos()
2416     {
2417         return m_unpacksize - m_outbytesleft;
2418     }
GetPos(lvpos_t * pos)2419     virtual lverror_t GetPos( lvpos_t * pos )
2420     {
2421         if (pos)
2422             *pos = m_unpacksize - m_outbytesleft;
2423         return LVERR_OK;
2424     }
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * newPos)2425     virtual lverror_t Seek(lvoffset_t offset, lvseek_origin_t origin, lvpos_t* newPos)
2426     {
2427         lvpos_t npos = 0;
2428         lvpos_t currpos = GetPos();
2429         switch (origin) {
2430         case LVSEEK_SET:
2431             npos = offset;
2432             break;
2433         case LVSEEK_CUR:
2434             npos = currpos + offset;
2435             break;
2436         case LVSEEK_END:
2437             npos = m_unpacksize + offset;
2438             break;
2439         }
2440         if (npos > m_unpacksize)
2441             return LVERR_FAIL;
2442         if ( npos != currpos )
2443         {
2444             if (npos < currpos)
2445             {
2446                 if ( !rewind() || !skip((int)npos) )
2447                     return LVERR_FAIL;
2448             }
2449             else
2450             {
2451                 skip( (int)(npos - currpos) );
2452             }
2453         }
2454         if (newPos)
2455             *newPos = npos;
2456         return LVERR_OK;
2457     }
Write(const void *,lvsize_t,lvsize_t *)2458     virtual lverror_t Write(const void*, lvsize_t, lvsize_t*)
2459     {
2460         return LVERR_NOTIMPL;
2461     }
Read(void * buf,lvsize_t count,lvsize_t * bytesRead)2462     virtual lverror_t Read(void* buf, lvsize_t count, lvsize_t* bytesRead)
2463     {
2464         int readBytes = read( (lUInt8 *)buf, (int)count );
2465         if ( readBytes<0 )
2466             return LVERR_FAIL;
2467         if ( readBytes!=(int)count ) {
2468             CRLog::trace("ZIP stream: %d bytes read instead of %d", (int)readBytes, (int)count);
2469         }
2470         if (bytesRead)
2471             *bytesRead = (lvsize_t)readBytes;
2472         //CRLog::trace("%d bytes requested, %d bytes read, %d bytes left", count, readBytes, m_outbytesleft);
2473         return LVERR_OK;
2474     }
SetSize(lvsize_t)2475     virtual lverror_t SetSize(lvsize_t)
2476     {
2477         return LVERR_NOTIMPL;
2478     }
Create(LVStreamRef stream,lvpos_t pos,lString32 name,lUInt32 srcPackSize,lUInt32 srcUnpSize)2479     static LVStream * Create( LVStreamRef stream, lvpos_t pos, lString32 name, lUInt32 srcPackSize, lUInt32 srcUnpSize )
2480     {
2481         ZipLocalFileHdr hdr;
2482         unsigned hdr_size = 0x1E; //sizeof(hdr);
2483         if ( stream->Seek( pos, LVSEEK_SET, NULL )!=LVERR_OK )
2484             return NULL;
2485         lvsize_t sz = 0;
2486         if ( stream->Read( &hdr, hdr_size, &sz)!=LVERR_OK || sz!=hdr_size )
2487             return NULL;
2488         hdr.byteOrderConv();
2489         pos += 0x1e + hdr.getNameLen() + hdr.getAddLen();
2490         if ( stream->Seek( pos, LVSEEK_SET, NULL )!=LVERR_OK )
2491             return NULL;
2492         lUInt32 packSize = hdr.getPackSize();
2493         lUInt32 unpSize = hdr.getUnpSize();
2494         if ( packSize==0 && unpSize==0 ) {
2495             // Can happen when local header does not carry these sizes
2496             // Use the ones provided that come from zip central directory
2497             packSize = srcPackSize;
2498             unpSize = srcUnpSize;
2499         }
2500         if ((lvpos_t)(pos + packSize) > (lvpos_t)stream->GetSize())
2501             return NULL;
2502         if (hdr.getMethod() == 0)
2503         {
2504             // store method, copy as is
2505             if ( packSize != unpSize )
2506                 return NULL;
2507             LVStreamFragment * fragment = new LVStreamFragment( stream, pos, packSize);
2508             fragment->SetName( name.c_str() );
2509             return fragment;
2510         }
2511         else if (hdr.getMethod() == 8)
2512         {
2513             // deflate
2514             LVStreamRef srcStream( new LVStreamFragment( stream, pos, packSize) );
2515             LVZipDecodeStream * res = new LVZipDecodeStream( srcStream, pos,
2516                 packSize, unpSize, hdr.getCRC() );
2517             res->SetName( name.c_str() );
2518             return res;
2519         }
2520         else
2521             return NULL;
2522     }
2523 };
2524 
2525 class LVZipArc : public LVArcContainerBase
2526 {
2527 protected:
2528     // whether the alternative "truncated" method was used, or is to be used
2529     bool m_alt_reading_method = false;
2530 public:
isAltReadingMethod()2531     bool isAltReadingMethod() { return m_alt_reading_method; }
setAltReadingMethod()2532     void setAltReadingMethod() { m_alt_reading_method = true; }
2533 
OpenStream(const char32_t * fname,lvopen_mode_t)2534     virtual LVStreamRef OpenStream( const char32_t * fname, lvopen_mode_t /*mode*/ )
2535     {
2536         if ( fname[0]=='/' )
2537             fname++;
2538         int found_index = -1;
2539         for (int i=0; i<m_list.length(); i++) {
2540             if ( m_list[i]->GetName() != NULL && !lStr_cmp( fname, m_list[i]->GetName() ) ) {
2541                 if ( m_list[i]->IsContainer() ) {
2542                     // found directory with same name!!!
2543                     return LVStreamRef();
2544                 }
2545                 found_index = i;
2546                 break;
2547             }
2548         }
2549         if (found_index<0)
2550             return LVStreamRef(); // not found
2551         // make filename
2552         lString32 fn = fname;
2553         LVStreamRef strm = m_stream; // fix strange arm-linux-g++ bug
2554         LVStreamRef stream(
2555 		LVZipDecodeStream::Create(
2556 			strm,
2557 			m_list[found_index]->GetSrcPos(),
2558             fn,
2559             m_list[found_index]->GetSrcSize(),
2560             m_list[found_index]->GetSize() )
2561         );
2562         if (!stream.isNull()) {
2563             stream->SetName(m_list[found_index]->GetName());
2564             // Use buffering?
2565             //return stream;
2566             return stream;
2567             //return LVCreateBufferedStream( stream, ZIP_STREAM_BUFFER_SIZE );
2568         }
2569         return stream;
2570     }
LVZipArc(LVStreamRef stream)2571     LVZipArc( LVStreamRef stream ) : LVArcContainerBase(stream)
2572     {
2573         SetName(stream->GetName());
2574     }
~LVZipArc()2575     virtual ~LVZipArc()
2576     {
2577     }
ReadContents()2578     virtual int ReadContents()
2579     {
2580         lvByteOrderConv cnv;
2581         //bool arcComment = false;
2582         bool truncated = false;
2583 
2584         m_list.clear();
2585         if (!m_stream || m_stream->Seek(0, LVSEEK_SET, NULL)!=LVERR_OK)
2586             return 0;
2587 
2588         SetName( m_stream->GetName() );
2589 
2590 
2591         lvsize_t sz = 0;
2592         if (m_stream->GetSize( &sz )!=LVERR_OK)
2593                 return 0;
2594         lvsize_t m_FileSize = (unsigned)sz;
2595 
2596         char ReadBuf[1024];
2597         lUInt32 NextPosition;
2598         lvpos_t CurPos;
2599         lvsize_t ReadSize;
2600         int Buf;
2601         bool found = false;
2602         CurPos=NextPosition=(int)m_FileSize;
2603         if (CurPos < sizeof(ReadBuf)-18)
2604             CurPos = 0;
2605         else
2606             CurPos -= sizeof(ReadBuf)-18;
2607         // Find End of central directory record (EOCD)
2608         for ( Buf=0; Buf<64 && !found; Buf++ )
2609         {
2610             //SetFilePointer(ArcHandle,CurPos,NULL,FILE_BEGIN);
2611             m_stream->Seek( CurPos, LVSEEK_SET, NULL );
2612             m_stream->Read( ReadBuf, sizeof(ReadBuf), &ReadSize);
2613             if (ReadSize==0)
2614                 break;
2615             for (int I=(int)ReadSize-4;I>=0;I--)
2616             {
2617                 if (ReadBuf[I]==0x50 && ReadBuf[I+1]==0x4b && ReadBuf[I+2]==0x05 &&
2618                     ReadBuf[I+3]==0x06)
2619                 {
2620                     m_stream->Seek( CurPos+I+16, LVSEEK_SET, NULL );
2621                     m_stream->Read( &NextPosition, sizeof(NextPosition), &ReadSize);
2622 		    		cnv.lsf( &NextPosition );
2623                     found=true;
2624                     break;
2625                 }
2626             }
2627             if (CurPos==0)
2628                 break;
2629             if (CurPos<sizeof(ReadBuf)-4)
2630                 CurPos=0;
2631             else
2632                 CurPos-=sizeof(ReadBuf)-4;
2633         }
2634 
2635         truncated = !found;
2636 
2637         // If the main reading method (using zip header at the end of the
2638         // archive) failed, we can try using the alternative method used
2639         // when this zip header is missing ("truncated"), which uses
2640         // local zip headers met along while scanning the zip.
2641         if (m_alt_reading_method)
2642             truncated = true; // do as if truncated
2643         else if (truncated) // archive detected as truncated
2644             // flag that, so there's no need to try that alt method,
2645             // as it was used on first scan
2646             m_alt_reading_method = true;
2647 
2648         if (truncated)
2649             NextPosition=0;
2650 
2651         //================================================================
2652         // get files
2653 
2654 
2655         ZipLocalFileHdr ZipHd1;
2656         ZipHd2 ZipHeader = { 0 };
2657         unsigned ZipHeader_size = 0x2E; //sizeof(ZipHd2); //0x34; //
2658         unsigned ZipHd1_size = 0x1E; //sizeof(ZipHd1); //sizeof(ZipHd1)
2659           //lUInt32 ReadSize;
2660 
2661         for (;;) {
2662 
2663             if (m_stream->Seek( NextPosition, LVSEEK_SET, NULL )!=LVERR_OK)
2664                 return 0;
2665 
2666             if (truncated)
2667             {
2668                 // The offset (that we don't find in a local header, but
2669                 // that we will store in the ZipHeader we're building)
2670                 // happens to be the current position here.
2671                 lUInt32 offset = (lUInt32)m_stream->GetPos();
2672 
2673                 m_stream->Read( &ZipHd1, ZipHd1_size, &ReadSize);
2674                 ZipHd1.byteOrderConv();
2675 
2676                 //ReadSize = fread(&ZipHd1, 1, sizeof(ZipHd1), f);
2677                 if (ReadSize != ZipHd1_size) {
2678                         //fclose(f);
2679                     if (ReadSize==0 && NextPosition==m_FileSize)
2680                         return m_list.length();
2681                     if ( ReadSize==0 )
2682                         return m_list.length();
2683                     return 0;
2684                 }
2685 
2686                 ZipHeader.UnpVer=ZipHd1.UnpVer;
2687                 ZipHeader.UnpOS=ZipHd1.UnpOS;
2688                 ZipHeader.Flags=ZipHd1.Flags;
2689                 ZipHeader.ftime=ZipHd1.getftime();
2690                 ZipHeader.PackSize=ZipHd1.getPackSize();
2691                 ZipHeader.UnpSize=ZipHd1.getUnpSize();
2692                 ZipHeader.NameLen=ZipHd1.getNameLen();
2693                 ZipHeader.AddLen=ZipHd1.getAddLen();
2694                 ZipHeader.Method=ZipHd1.getMethod();
2695                 ZipHeader.setOffset(offset);
2696                 // We may get a last invalid record with NameLen=0, which shouldn't hurt.
2697                 // If it does, use:
2698                 // if (ZipHeader.NameLen == 0) break;
2699             } else {
2700 
2701                 m_stream->Read( &ZipHeader, ZipHeader_size, &ReadSize);
2702 
2703                 ZipHeader.byteOrderConv();
2704                     //ReadSize = fread(&ZipHeader, 1, sizeof(ZipHeader), f);
2705                 if (ReadSize!=ZipHeader_size) {
2706                             if (ReadSize>16 && ZipHeader.Mark==0x06054B50 ) {
2707                                     break;
2708                             }
2709                             //fclose(f);
2710                             return 0;
2711                 }
2712             }
2713 
2714             if (ReadSize==0 || ZipHeader.Mark==0x06054b50 ||
2715                     (truncated && ZipHeader.Mark==0x02014b50) )
2716             {
2717 //                if (!truncated && *(lUInt16 *)((char *)&ZipHeader+20)!=0)
2718 //                    arcComment=true;
2719                 break; //(GETARC_EOF);
2720             }
2721 
2722             //const int NM = 513;
2723             const int max_NM = 4096;
2724             if ( ZipHeader.NameLen>max_NM ) {
2725                 CRLog::error("ZIP entry name length is too big: %d", (int)ZipHeader.NameLen);
2726                 return 0;
2727             }
2728             lUInt32 SizeToRead=(ZipHeader.NameLen<max_NM) ? ZipHeader.NameLen : max_NM;
2729             char fnbuf[max_NM+1];
2730             m_stream->Read( fnbuf, SizeToRead, &ReadSize);
2731 
2732             if (ReadSize!=SizeToRead) {
2733                 CRLog::error("error while reading zip entry name");
2734                 return 0;
2735             }
2736 
2737             fnbuf[SizeToRead]=0;
2738 
2739             long SeekLen=ZipHeader.AddLen+ZipHeader.CommLen;
2740 
2741             LVCommonContainerItemInfo * item = new LVCommonContainerItemInfo();
2742 
2743             if (truncated)
2744                 SeekLen+=ZipHeader.PackSize;
2745 
2746             NextPosition = (lUInt32)m_stream->GetPos();
2747             NextPosition += SeekLen;
2748             m_stream->Seek(NextPosition, LVSEEK_SET, NULL);
2749 
2750             lString32 fName;
2751             if (ZipHeader.PackVer >= 63 && (ZipHeader.Flags & 0x0800) == 0x0800) {
2752                 // Language encoding flag (EFS).  If this bit is set,
2753                 // the filename and comment fields for this file
2754                 // MUST be encoded using UTF-8. (InfoZip APPNOTE-6.3.0)
2755                 //CRLog::trace("ZIP 6.3: Language encoding flag (EFS) enabled, using UTF-8 encoding.");
2756                 fName = Utf8ToUnicode(fnbuf);
2757             } else {
2758                 if (isValidUtf8Data((const unsigned char *)fnbuf, SizeToRead)) {
2759                     //CRLog::trace("autodetected UTF-8 encoding.");
2760                     fName = Utf8ToUnicode(fnbuf);
2761                 } else {
2762                     // {"DOS","Amiga","VAX/VMS","Unix","VM/CMS","Atari ST",
2763                     //  "OS/2","Mac-OS","Z-System","CP/M","TOPS-20",
2764                     //  "Win32","SMS/QDOS","Acorn RISC OS","Win32 VFAT","MVS",
2765                     //  "BeOS","Tandem"};
2766                     // TODO: try to detect proper charset using 0x0008 Extra Field (InfoZip APPNOTE-6.3.5, Appendix D.4).
2767                     const lChar32 * enc_name = (ZipHeader.PackOS==0) ? U"cp866" : U"cp1251";
2768                     //CRLog::trace("detected encoding %s", LCSTR(enc_name));
2769                     const lChar32 * table = GetCharsetByte2UnicodeTable( enc_name );
2770                     fName = ByteToUnicode( lString8(fnbuf), table );
2771                 }
2772             }
2773 
2774             item->SetItemInfo(fName.c_str(), ZipHeader.UnpSize, (ZipHeader.getAttr() & 0x3f));
2775             item->SetSrc( ZipHeader.getOffset(), ZipHeader.PackSize, ZipHeader.Method );
2776 
2777 //#define DUMP_ZIP_HEADERS
2778 #ifdef DUMP_ZIP_HEADERS
2779             CRLog::trace("ZIP entry '%s' unpSz=%d, pSz=%d, m=%x, offs=%x, zAttr=%x, flg=%x", LCSTR(fName), (int)ZipHeader.UnpSize, (int)ZipHeader.PackSize, (int)ZipHeader.Method, (int)ZipHeader.getOffset(), (int)ZipHeader.getZIPAttr(), (int)ZipHeader.getAttr());
2780             //, addL=%d, commL=%d, dn=%d
2781             //, (int)ZipHeader.AddLen, (int)ZipHeader.CommLen, (int)ZipHeader.DiskNum
2782 #endif
2783 
2784             m_list.add(item);
2785         }
2786         int sz2 = m_list.length();
2787         return sz2;
2788     }
2789 
OpenArchieve(LVStreamRef stream)2790     static LVArcContainerBase * OpenArchieve( LVStreamRef stream )
2791     {
2792         // read beginning of file
2793         const lvsize_t hdrSize = 4;
2794         char hdr[hdrSize];
2795         stream->SetPos(0);
2796         lvsize_t bytesRead = 0;
2797         if (stream->Read(hdr, hdrSize, &bytesRead)!=LVERR_OK || bytesRead!=hdrSize)
2798                 return NULL;
2799         stream->SetPos(0);
2800         // detect arc type
2801         if (hdr[0]!='P' || hdr[1]!='K' || hdr[2]!=3 || hdr[3]!=4)
2802                 return NULL;
2803         LVZipArc * arc = new LVZipArc( stream );
2804         int itemCount = arc->ReadContents();
2805         if ( itemCount > 0 && arc->isAltReadingMethod() ) {
2806             CRLog::warn("Zip file truncated: going on with possibly partial content.");
2807         }
2808         else if ( itemCount <= 0 && !arc->isAltReadingMethod() ) {
2809             CRLog::warn("Zip file corrupted or invalid: trying alternative processing...");
2810             arc->setAltReadingMethod();
2811             itemCount = arc->ReadContents();
2812         }
2813         if ( itemCount <= 0 )
2814         {
2815             CRLog::error("Zip file corrupted or invalid: processing failure.");
2816             delete arc;
2817             return NULL;
2818         }
2819         return arc;
2820     }
2821 
2822 };
2823 #endif
2824 
2825 #if (USE_UNRAR==1)
2826 class LVRarArc : public LVArcContainerBase
2827 {
2828 public:
2829 
OpenStream(const lChar32 * fname,lvopen_mode_t mode)2830     virtual LVStreamRef OpenStream( const lChar32 * fname, lvopen_mode_t mode )
2831     {
2832         int found_index = -1;
2833         for (int i=0; i<m_list.length(); i++) {
2834             if ( !lStr_cmp( fname, m_list[i]->GetName() ) ) {
2835                 if ( m_list[i]->IsContainer() ) {
2836                     // found directory with same name!!!
2837                     return LVStreamRef();
2838                 }
2839                 found_index = i;
2840                 break;
2841             }
2842         }
2843         if (found_index<0)
2844             return LVStreamRef(); // not found
2845 
2846         // TODO
2847         return LVStreamRef(); // not found
2848 /*
2849         // make filename
2850         lString32 fn = fname;
2851         LVStreamRef strm = m_stream; // fix strange arm-linux-g++ bug
2852         LVStreamRef stream(
2853 		LVZipDecodeStream::Create(
2854 			strm,
2855 			m_list[found_index]->GetSrcPos(), fn ) );
2856         if (!stream.isNull()) {
2857             return LVCreateBufferedStream( stream, ZIP_STREAM_BUFFER_SIZE );
2858         }
2859         stream->SetName(m_list[found_index]->GetName());
2860         return stream;
2861 */
2862     }
LVRarArc(LVStreamRef stream)2863     LVRarArc( LVStreamRef stream ) : LVArcContainerBase(stream)
2864     {
2865     }
~LVRarArc()2866     virtual ~LVRarArc()
2867     {
2868     }
2869 
ReadContents()2870     virtual int ReadContents()
2871     {
2872         lvByteOrderConv cnv;
2873 
2874         m_list.clear();
2875 
2876         if (!m_stream || m_stream->Seek(0, LVSEEK_SET, NULL)!=LVERR_OK)
2877             return 0;
2878 
2879         SetName( m_stream->GetName() );
2880 
2881         lvsize_t sz = 0;
2882         if (m_stream->GetSize( &sz )!=LVERR_OK)
2883                 return 0;
2884         lvsize_t m_FileSize = (unsigned)sz;
2885 
2886         return m_list.length();
2887     }
2888 
OpenArchieve(LVStreamRef stream)2889     static LVArcContainerBase * OpenArchieve( LVStreamRef stream )
2890     {
2891         // read beginning of file
2892         const lvsize_t hdrSize = 4;
2893         char hdr[hdrSize];
2894         stream->SetPos(0);
2895         lvsize_t bytesRead = 0;
2896         if (stream->Read(hdr, hdrSize, &bytesRead)!=LVERR_OK || bytesRead!=hdrSize)
2897             return NULL;
2898         stream->SetPos(0);
2899         // detect arc type
2900         if (hdr[0]!='R' || hdr[1]!='a' || hdr[2]!='r' || hdr[3]!='!')
2901             return NULL;
2902         LVRarArc * arc = new LVRarArc( stream );
2903         int itemCount = arc->ReadContents();
2904         if ( itemCount <= 0 )
2905         {
2906             delete arc;
2907             return NULL;
2908         }
2909         return arc;
2910     }
2911 
2912 };
2913 #endif // UNRAR
2914 
2915 
2916 
2917 
2918 
2919 class LVMemoryStream : public LVNamedStream
2920 {
2921 protected:
2922 	lUInt8 *               m_pBuffer;
2923 	bool                   m_own_buffer;
2924 	LVContainer *          m_parent;
2925 	lvsize_t               m_size;
2926 	lvsize_t               m_bufsize;
2927 	lvpos_t                m_pos;
2928 	lvopen_mode_t          m_mode;
2929 public:
2930     /// Check whether end of file is reached
2931     /**
2932         \return true if end of file reached
2933     */
Eof()2934     virtual bool Eof()
2935     {
2936         return m_pos>=m_size;
2937     }
GetMode()2938     virtual lvopen_mode_t GetMode()
2939     {
2940         return m_mode;
2941     }
2942     /** \return LVERR_OK if change is ok */
SetMode(lvopen_mode_t mode)2943     virtual lverror_t SetMode( lvopen_mode_t mode )
2944     {
2945     	if ( m_mode==mode )
2946     		return LVERR_OK;
2947     	if ( m_mode==LVOM_WRITE && mode==LVOM_READ ) {
2948     		m_mode = LVOM_READ;
2949     		m_pos = 0;
2950     		return LVERR_OK;
2951     	}
2952     	// TODO: READ -> WRITE/APPEND
2953     	return LVERR_FAIL;
2954     }
GetParentContainer()2955 	virtual LVContainer * GetParentContainer()
2956 	{
2957 		return (LVContainer*)m_parent;
2958 	}
Read(void * buf,lvsize_t count,lvsize_t * nBytesRead)2959 	virtual lverror_t Read( void * buf, lvsize_t count, lvsize_t * nBytesRead )
2960 	{
2961 		if (!m_pBuffer || m_mode==LVOM_WRITE || m_mode==LVOM_APPEND )
2962 			return LVERR_FAIL;
2963 		//
2964 		int bytesAvail = (int)(m_size - m_pos);
2965 		if (bytesAvail>0) {
2966 			int bytesRead = bytesAvail;
2967 			if (bytesRead>(int)count)
2968 				bytesRead = (int)count;
2969 			if (bytesRead>0)
2970 				memcpy( buf, m_pBuffer+(int)m_pos, bytesRead );
2971 			if (nBytesRead)
2972 				*nBytesRead = bytesRead;
2973 			m_pos += bytesRead;
2974 		} else {
2975 			if (nBytesRead)
2976 				*nBytesRead = 0; // EOF
2977 		}
2978 		return LVERR_OK;
2979 	}
GetSize()2980     virtual lvsize_t  GetSize()
2981     {
2982         if (!m_pBuffer)
2983             return (lvsize_t)(-1);
2984         if (m_size<m_pos)
2985             m_size = m_pos;
2986         return m_size;
2987     }
2988 
GetSize(lvsize_t * pSize)2989     virtual lverror_t GetSize( lvsize_t * pSize )
2990 	{
2991 		if (!m_pBuffer || !pSize)
2992 			return LVERR_FAIL;
2993 		if (m_size<m_pos)
2994 			m_size = m_pos;
2995 		*pSize = m_size;
2996 		return LVERR_OK;
2997 	}
2998 	// ensure that buffer is at least new_size long
SetBufSize(lvsize_t new_size)2999 	lverror_t SetBufSize( lvsize_t new_size )
3000 	{
3001 		if (!m_pBuffer || m_mode==LVOM_READ )
3002 			return LVERR_FAIL;
3003 		if (new_size<=m_bufsize)
3004 			return LVERR_OK;
3005 		if (m_own_buffer!=true)
3006 			return LVERR_FAIL; // cannot resize foreign buffer
3007 		//
3008 		int newbufsize = (int)(new_size * 2 + 4096);
3009         m_pBuffer = cr_realloc( m_pBuffer, newbufsize );
3010 		m_bufsize = newbufsize;
3011 		return LVERR_OK;
3012 	}
SetSize(lvsize_t size)3013 	virtual lverror_t SetSize( lvsize_t size )
3014 	{
3015 		//
3016 		if (SetBufSize( size )!=LVERR_OK)
3017 			return LVERR_FAIL;
3018 		m_size = size;
3019 		if (m_pos>m_size)
3020 			m_pos = m_size;
3021 		return LVERR_OK;
3022 	}
Write(const void * buf,lvsize_t count,lvsize_t * nBytesWritten)3023 	virtual lverror_t Write( const void * buf, lvsize_t count, lvsize_t * nBytesWritten )
3024 	{
3025 		if (!m_pBuffer || !buf || m_mode==LVOM_READ )
3026 			return LVERR_FAIL;
3027 		SetBufSize( m_pos+count ); // check buf size
3028 		int bytes_avail = (int)(m_bufsize-m_pos);
3029 		if (bytes_avail>(int)count)
3030 			bytes_avail = (int)count;
3031 		if (bytes_avail>0) {
3032 			memcpy( m_pBuffer+m_pos, buf, bytes_avail );
3033 			m_pos+=bytes_avail;
3034 			if (m_size<m_pos)
3035 				m_size = m_pos;
3036 		}
3037 		if (nBytesWritten)
3038 			*nBytesWritten = bytes_avail;
3039 		return LVERR_OK;
3040 	}
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * pNewPos)3041 	virtual lverror_t Seek( lvoffset_t offset, lvseek_origin_t origin, lvpos_t * pNewPos )
3042 	{
3043 		if (!m_pBuffer)
3044 			return LVERR_FAIL;
3045 		lvpos_t newpos;
3046 		switch (origin) {
3047 		case LVSEEK_SET:
3048 			newpos = offset;
3049 			break;
3050 		case LVSEEK_CUR:
3051 			newpos = m_pos + offset;
3052 			break;
3053 		case LVSEEK_END:
3054 			newpos = m_size + offset;
3055 			break;
3056 		}
3057                 if (newpos>m_size)
3058 			return LVERR_FAIL;
3059 		m_pos = newpos;
3060 		if (pNewPos)
3061 			*pNewPos = m_pos;
3062 		return LVERR_OK;
3063 	}
Close()3064 	lverror_t Close()
3065 	{
3066 		if (!m_pBuffer)
3067 			return LVERR_FAIL;
3068 		if (m_pBuffer && m_own_buffer)
3069             free(m_pBuffer);
3070 		m_pBuffer = NULL;
3071 		m_size = 0;
3072 		m_bufsize = 0;
3073 		m_pos = 0;
3074 		return LVERR_OK;
3075 	}
Create()3076 	lverror_t Create( )
3077 	{
3078 		Close();
3079 		m_bufsize = 4096;
3080 		m_size = 0;
3081 		m_pos = 0;
3082 		m_pBuffer = (lUInt8*)malloc((int)m_bufsize);
3083 		m_own_buffer = true;
3084 		m_mode = LVOM_READWRITE;
3085 		return LVERR_OK;
3086 	}
3087     /// Creates memory stream as copy of another stream.
CreateCopy(LVStreamRef srcStream,lvopen_mode_t mode)3088 	lverror_t CreateCopy( LVStreamRef srcStream, lvopen_mode_t mode )
3089 	{
3090 		Close();
3091         if ( mode!=LVOM_READ || srcStream.isNull() )
3092             return LVERR_FAIL;
3093         lvsize_t sz = srcStream->GetSize();
3094         if ( (int)sz <= 0 || sz > 0x200000 )
3095             return LVERR_FAIL;
3096 		m_bufsize = sz;
3097 		m_size = 0;
3098 		m_pos = 0;
3099 		m_pBuffer = (lUInt8*)malloc((int)m_bufsize);
3100 		if (m_pBuffer) {
3101             lvsize_t bytesRead = 0;
3102             srcStream->Read( m_pBuffer, m_bufsize, &bytesRead );
3103             if ( bytesRead!=m_bufsize ) {
3104                 free(m_pBuffer);
3105                 m_pBuffer = 0;
3106                 m_size = 0;
3107                 m_pos = 0;
3108                 m_bufsize = 0;
3109                 return LVERR_FAIL;
3110             }
3111 		}
3112         m_size = sz;
3113 		m_own_buffer = true;
3114 		m_mode = mode;
3115 		return LVERR_OK;
3116 	}
3117 
3118 
CreateCopy(const lUInt8 * pBuf,lvsize_t size,lvopen_mode_t mode)3119 	lverror_t CreateCopy( const lUInt8 * pBuf, lvsize_t size, lvopen_mode_t mode )
3120 	{
3121 		Close();
3122 		m_bufsize = size;
3123 		m_pos = 0;
3124 		m_pBuffer = (lUInt8*) malloc((int)m_bufsize);
3125 		if (m_pBuffer) {
3126 			memcpy( m_pBuffer, pBuf, (int)size );
3127 		}
3128 		m_own_buffer = true;
3129 		m_mode = mode;
3130         m_size = size;
3131 		if (mode==LVOM_APPEND)
3132 			m_pos = m_size;
3133 		return LVERR_OK;
3134 	}
Open(lUInt8 * pBuf,lvsize_t size)3135 	lverror_t Open( lUInt8 * pBuf, lvsize_t size )
3136 	{
3137                 if (!pBuf)
3138 			return LVERR_FAIL;
3139 		m_own_buffer = false;
3140 		m_pBuffer = pBuf;
3141 		m_bufsize = size;
3142 		// set file size and position
3143 		m_pos = 0;
3144 		m_size = size;
3145 		m_mode = LVOM_READ;
3146 
3147 		return LVERR_OK;
3148 	}
LVMemoryStream()3149 	LVMemoryStream() : m_pBuffer(NULL), m_own_buffer(false), m_parent(NULL), m_size(0), m_pos(0)
3150 	{
3151 	}
~LVMemoryStream()3152 	virtual ~LVMemoryStream()
3153 	{
3154 		Close();
3155 		m_parent = NULL;
3156 	}
3157 };
3158 
3159 #if (USE_ZLIB==1)
LVOpenArchieve(LVStreamRef stream)3160 LVContainerRef LVOpenArchieve( LVStreamRef stream )
3161 {
3162     LVContainerRef ref;
3163     if (stream.isNull())
3164         return ref;
3165 
3166     // try ZIP
3167     ref = LVZipArc::OpenArchieve( stream );
3168     if (!ref.isNull())
3169         return ref;
3170 
3171 #if USE_UNRAR==1
3172     // try RAR
3173     ref = LVRarArc::OpenArchieve( stream );
3174     if (!ref.isNull())
3175         return ref;
3176 #endif
3177     // not found: return null ref
3178     return ref;
3179 }
3180 #endif
3181 
3182 /// Creates memory stream as copy of string contents
LVCreateStringStream(lString8 data)3183 LVStreamRef LVCreateStringStream( lString8 data )
3184 {
3185     LVMemoryStream * stream = new LVMemoryStream();
3186     stream->CreateCopy( (const lUInt8*)data.c_str(), data.length(), LVOM_READ );
3187     return LVStreamRef( stream );
3188 }
3189 
3190 /// Creates memory stream as copy of string contents
LVCreateStringStream(lString32 data)3191 LVStreamRef LVCreateStringStream( lString32 data )
3192 {
3193     return LVCreateStringStream( UnicodeToUtf8( data ) );
3194 }
3195 
LVCreateMemoryStream(void * buf,int bufSize,bool createCopy,lvopen_mode_t mode)3196 LVStreamRef LVCreateMemoryStream( void * buf, int bufSize, bool createCopy, lvopen_mode_t mode )
3197 {
3198     LVMemoryStream * stream = new LVMemoryStream();
3199     if ( !buf )
3200         stream->Create();
3201     else if ( createCopy )
3202         stream->CreateCopy( (lUInt8*)buf, bufSize, mode );
3203     else
3204         stream->Open( (lUInt8*)buf, bufSize );
3205     return LVStreamRef( stream );
3206 }
3207 
LVCreateMemoryStream(LVStreamRef srcStream)3208 LVStreamRef LVCreateMemoryStream( LVStreamRef srcStream )
3209 {
3210     LVMemoryStream * stream = new LVMemoryStream();
3211     if ( stream->CreateCopy(srcStream, LVOM_READ)==LVERR_OK )
3212         return LVStreamRef( stream );
3213     else
3214         delete stream;
3215     return LVStreamRef();
3216 }
3217 
3218 /// Creates memory stream as copy of file contents.
LVCreateMemoryStream(lString32 filename)3219 LVStreamRef LVCreateMemoryStream( lString32 filename )
3220 {
3221     LVStreamRef fs = LVOpenFileStream( filename.c_str(), LVOM_READ );
3222     if ( fs.isNull() )
3223         return fs;
3224     return LVCreateMemoryStream( fs );
3225 }
3226 
LVCreateBufferedStream(LVStreamRef stream,int bufSize)3227 LVStreamRef LVCreateBufferedStream( LVStreamRef stream, int bufSize )
3228 {
3229     if ( stream.isNull() || bufSize < 512 )
3230         return stream;
3231     return LVStreamRef( new LVCachedStream( stream, bufSize ) );
3232 }
3233 
LVPumpStream(LVStreamRef out,LVStreamRef in)3234 lvsize_t LVPumpStream( LVStreamRef out, LVStreamRef in )
3235 {
3236     return LVPumpStream( out.get(), in.get() );
3237 }
3238 
LVPumpStream(LVStream * out,LVStream * in)3239 lvsize_t LVPumpStream( LVStream * out, LVStream * in )
3240 {
3241     char buf[5000];
3242     lvsize_t totalBytesRead = 0;
3243     lvsize_t bytesRead = 0;
3244     in->SetPos(0);
3245     lvsize_t bytesToRead = in->GetSize();
3246     while ( bytesToRead>0 )
3247     {
3248         unsigned blockSize = 5000;
3249         if (blockSize > bytesToRead)
3250             blockSize = bytesToRead;
3251         bytesRead = 0;
3252         if ( in->Read( buf, blockSize, &bytesRead )!=LVERR_OK )
3253             break;
3254         if ( !bytesRead )
3255             break;
3256         out->Write( buf, bytesRead, NULL );
3257         totalBytesRead += bytesRead;
3258         bytesToRead -= bytesRead;
3259     }
3260     return totalBytesRead;
3261 }
3262 
LVDirectoryIsEmpty(const lString8 & path)3263 bool LVDirectoryIsEmpty(const lString8& path) {
3264     return LVDirectoryIsEmpty(Utf8ToUnicode(path));
3265 }
3266 
LVDirectoryIsEmpty(const lString32 & path)3267 bool LVDirectoryIsEmpty(const lString32& path) {
3268     LVContainerRef dir = LVOpenDirectory(path);
3269     if (dir.isNull())
3270         return false;
3271     return dir->GetObjectCount() == 0;
3272 }
3273 
LVOpenDirectory(const lString32 & path,const char32_t * mask)3274 LVContainerRef LVOpenDirectory(const lString32& path, const char32_t * mask) {
3275 	return LVOpenDirectory(path.c_str(), mask);
3276 }
3277 
LVOpenDirectory(const lString8 & path,const char32_t * mask)3278 LVContainerRef LVOpenDirectory(const lString8& path, const char32_t * mask) {
3279 	return LVOpenDirectory(Utf8ToUnicode(path).c_str(), mask);
3280 }
3281 
LVOpenDirectory(const char32_t * path,const char32_t * mask)3282 LVContainerRef LVOpenDirectory( const char32_t * path, const char32_t * mask )
3283 {
3284 	lString32 pathname(path);
3285     if (pathname.length() > 1 && pathname[0] == ASSET_PATH_PREFIX) {
3286     	if (!_assetContainerFactory)
3287     		return LVContainerRef();
3288     	lString32 assetPath = LVExtractAssetPath(pathname);
3289     	return _assetContainerFactory->openAssetContainer(assetPath);
3290     }
3291     LVContainerRef dir(LVDirectoryContainer::OpenDirectory(path, mask));
3292     return dir;
3293 }
3294 
3295 /// Stream base class
3296 class LVTCRStream : public LVNamedStream
3297 {
3298     class TCRCode {
3299     public:
3300         int len;
3301         char * str;
TCRCode()3302         TCRCode()
3303             : len(0), str(NULL)
3304         {
3305         }
set(const char * s,int sz)3306         void set( const char * s, int sz )
3307         {
3308             if ( sz>0 ) {
3309                 str = (char *)malloc( sz + 1 );
3310                 memcpy( str, s, sz );
3311                 str[sz] = 0;
3312                 len = sz;
3313             }
3314         }
~TCRCode()3315         ~TCRCode()
3316         {
3317             if ( str )
3318                 free( str );
3319         }
3320     };
3321     LVStreamRef _stream;
3322     TCRCode _codes[256];
3323     lvpos_t _packedStart;
3324     lvsize_t _packedSize;
3325     lvsize_t _unpSize;
3326     lUInt32 * _index;
3327     lUInt8 * _decoded;
3328     int _decodedSize;
3329     int _decodedLen;
3330     unsigned _partIndex;
3331     lvpos_t _decodedStart;
3332     int _indexSize;
3333     lvpos_t _pos;
3334     //int _indexPos;
3335     #define TCR_READ_BUF_SIZE 4096
3336     lUInt8 _readbuf[TCR_READ_BUF_SIZE];
LVTCRStream(LVStreamRef stream)3337     LVTCRStream( LVStreamRef stream )
3338     : _stream(stream), _index(NULL), _decoded(NULL),
3339       _decodedSize(0), _decodedLen(0), _partIndex((unsigned)-1), _decodedStart(0), _indexSize(0), _pos(0)
3340     {
3341     }
3342 
decodePart(unsigned index)3343     bool decodePart( unsigned index )
3344     {
3345         if ( _partIndex==index )
3346             return true;
3347         lvsize_t bytesRead;
3348         int bytesToRead = TCR_READ_BUF_SIZE;
3349         if ( (index+1)*TCR_READ_BUF_SIZE > _packedSize )
3350             bytesToRead = TCR_READ_BUF_SIZE - ((index+1)*TCR_READ_BUF_SIZE - _packedSize);
3351         if ( bytesToRead<=0 || bytesToRead>TCR_READ_BUF_SIZE )
3352             return false;
3353         if ( _stream->SetPos(_packedStart + index * TCR_READ_BUF_SIZE)==(lvpos_t)(~0) )
3354             return false;
3355         if ( _stream->Read( _readbuf, bytesToRead, &bytesRead )!=LVERR_OK )
3356             return false;
3357         if ( bytesToRead!=(int)bytesRead )
3358             return false;
3359         //TODO
3360         if ( !_decoded ) {
3361             _decodedSize = TCR_READ_BUF_SIZE * 2;
3362             _decoded = (lUInt8 *)malloc( _decodedSize );
3363         }
3364         _decodedLen = 0;
3365         for ( unsigned i=0; i<bytesRead; i++ ) {
3366             TCRCode * item = &_codes[_readbuf[i]];
3367             for ( int j=0; j<item->len; j++ )
3368                 _decoded[_decodedLen++] = item->str[j];
3369             if ( _decodedLen >= _decodedSize - 256 ) {
3370                 _decodedSize += TCR_READ_BUF_SIZE / 2;
3371                 _decoded = cr_realloc( _decoded, _decodedSize );
3372             }
3373         }
3374         _decodedStart = _index[index];
3375         _partIndex = index;
3376         return true;
3377     }
3378 public:
~LVTCRStream()3379     ~LVTCRStream()
3380     {
3381         if ( _index )
3382             free(_index);
3383     }
init()3384     bool init()
3385     {
3386         lUInt8 sz;
3387         char buf[256];
3388         lvsize_t bytesRead;
3389         for ( int i=0; i<256; i++ ) {
3390             if ( _stream->Read( &sz, 1, &bytesRead )!=LVERR_OK || bytesRead!=1 )
3391                 return false;
3392             if ( sz==0 && i!=0 )
3393                 return false; // only first entry may be 0
3394             if ( sz && (_stream->Read( buf, sz, &bytesRead )!=LVERR_OK || bytesRead!=sz) )
3395                 return false;
3396             _codes[i].set( buf, sz );
3397         }
3398         _packedStart = _stream->GetPos();
3399         if ( _packedStart==(lvpos_t)(~0) )
3400             return false;
3401         _packedSize = _stream->GetSize() - _packedStart;
3402         if ( _packedSize<10 || _packedSize>0x8000000 )
3403             return false;
3404         _indexSize = (_packedSize + TCR_READ_BUF_SIZE - 1) / TCR_READ_BUF_SIZE;
3405         _index = (lUInt32*)malloc( sizeof(lUInt32) * (_indexSize + 1) );
3406         lvpos_t pos = 0;
3407         lvsize_t size = 0;
3408         for (;;) {
3409             bytesRead = 0;
3410             int res = _stream->Read( _readbuf, TCR_READ_BUF_SIZE, &bytesRead );
3411             if ( res!=LVERR_OK && res!=LVERR_EOF )
3412                 return false;
3413             if ( bytesRead>0 ) {
3414                 for ( unsigned i=0; i<bytesRead; i++ ) {
3415                     int sz = _codes[_readbuf[i]].len;
3416                     if ( (pos & (TCR_READ_BUF_SIZE-1)) == 0 ) {
3417                         // add pos index
3418                         int index = pos / TCR_READ_BUF_SIZE;
3419                         _index[index] = size;
3420                     }
3421                     size += sz;
3422                     pos ++;
3423                 }
3424             }
3425             if ( res==LVERR_EOF || bytesRead==0 ) {
3426                 if ( _packedStart + pos != _stream->GetSize() )
3427                     return false;
3428                 break;
3429             }
3430         }
3431         _index[ _indexSize ] = size;
3432         _unpSize = size;
3433         return decodePart( 0 );
3434     }
create(LVStreamRef stream,int mode)3435     static LVStreamRef create( LVStreamRef stream, int mode )
3436     {
3437         LVStreamRef res;
3438         if ( stream.isNull() || mode != LVOM_READ )
3439             return res;
3440         static const char * signature = "!!8-Bit!!";
3441         char buf[9];
3442         if ( stream->SetPos(0)!=0 )
3443             return res;
3444         lvsize_t bytesRead = 0;
3445         if ( stream->Read(buf, 9, &bytesRead)!=LVERR_OK
3446             || bytesRead!=9 )
3447             return res;
3448         if (memcmp(signature, buf, 9) != 0)
3449             return res;
3450         LVTCRStream * decoder = new LVTCRStream( stream );
3451         if ( !decoder->init() ) {
3452             delete decoder;
3453             return res;
3454         }
3455         return LVStreamRef ( decoder );
3456     }
3457 
3458     /// Get stream open mode
3459     /** \return lvopen_mode_t open mode */
GetMode()3460     virtual lvopen_mode_t GetMode()
3461     {
3462         return LVOM_READ;
3463     }
3464 
3465     /// Seek (change file pos)
3466     /**
3467         \param offset is file offset (bytes) relateve to origin
3468         \param origin is offset base
3469         \param pNewPos points to place to store new file position
3470         \return lverror_t status: LVERR_OK if success
3471     */
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * pNewPos)3472     virtual lverror_t Seek( lvoffset_t offset, lvseek_origin_t origin, lvpos_t * pNewPos )
3473     {
3474         lvpos_t npos = 0;
3475         lvpos_t currpos = _pos;
3476         switch (origin) {
3477         case LVSEEK_SET:
3478             npos = offset;
3479             break;
3480         case LVSEEK_CUR:
3481             npos = currpos + offset;
3482             break;
3483         case LVSEEK_END:
3484             npos = _unpSize + offset;
3485             break;
3486         }
3487         if (npos >= _unpSize)
3488             return LVERR_FAIL;
3489         _pos = npos;
3490         if ( _pos < _decodedStart || _pos >= _decodedStart + _decodedLen ) {
3491             // load another part
3492             int a = 0;
3493             int b = _indexSize;
3494             int c;
3495             for (;;) {
3496                 c = (a + b) / 2;
3497                 if ( a >= b-1 )
3498                     break;
3499                 if ( _index[c] > _pos )
3500                     b = c;
3501                 else if ( _index[c+1] <= _pos )
3502                     a = c + 1;
3503                 else
3504                     break;
3505             }
3506             if ( _index[c]>_pos || _index[c+1]<=_pos )
3507                 return LVERR_FAIL; // wrong algorithm?
3508             if ( !decodePart( c ) )
3509                 return LVERR_FAIL;
3510         }
3511         if (pNewPos)
3512         {
3513             *pNewPos =  _pos;
3514         }
3515         return LVERR_OK;
3516     }
3517 
3518 
3519     /// Get file position
3520     /**
3521         \return lvpos_t file position
3522     */
GetPos()3523     virtual lvpos_t   GetPos()
3524     {
3525         return _pos;
3526     }
3527 
3528     /// Get file size
3529     /**
3530         \return lvsize_t file size
3531     */
GetSize()3532     virtual lvsize_t  GetSize()
3533     {
3534         return _unpSize;
3535     }
3536 
GetSize(lvsize_t * pSize)3537     virtual lverror_t GetSize( lvsize_t * pSize )
3538     {
3539         *pSize = _unpSize;
3540         return LVERR_OK;
3541     }
3542 
3543     /// Set file size
3544     /**
3545         \param size is new file size
3546         \return lverror_t status: LVERR_OK if success
3547     */
SetSize(lvsize_t)3548     virtual lverror_t SetSize( lvsize_t )
3549     {
3550         return LVERR_FAIL;
3551     }
3552 
3553     /// Read
3554     /**
3555         \param buf is buffer to place bytes read from stream
3556         \param count is number of bytes to read from stream
3557         \param nBytesRead is place to store real number of bytes read from stream
3558         \return lverror_t status: LVERR_OK if success
3559     */
Read(void * buf,lvsize_t count,lvsize_t * nBytesRead)3560     virtual lverror_t Read( void * buf, lvsize_t count, lvsize_t * nBytesRead )
3561     {
3562         // TODO
3563         lvsize_t bytesRead = 0;
3564         lUInt8 * dst = (lUInt8*) buf;
3565         while (count) {
3566             int bytesLeft = _decodedLen - (int)(_pos - _decodedStart);
3567             if ( bytesLeft<=0 || bytesLeft>_decodedLen ) {
3568                 SetPos(_pos);
3569                 bytesLeft = _decodedLen - (int)(_pos - _decodedStart);
3570                 if ( bytesLeft==0 && _pos==_decodedStart+_decodedLen) {
3571                     if (nBytesRead)
3572                         *nBytesRead = bytesRead;
3573                     return bytesRead ? LVERR_OK : LVERR_EOF;
3574                 }
3575                 if ( bytesLeft<=0 || bytesLeft>_decodedLen ) {
3576                     if (nBytesRead)
3577                         *nBytesRead = bytesRead;
3578                     return LVERR_FAIL;
3579                 }
3580             }
3581             lUInt8 * src = _decoded + (_pos - _decodedStart);
3582             unsigned n = count;
3583             if ( n > (unsigned)bytesLeft )
3584                 n = bytesLeft;
3585             for (unsigned i=0; i<n; i++) {
3586                 *dst++ = *src++;
3587             }
3588             count -= n;
3589             // bytesLeft -= n;
3590             bytesRead += n;
3591             _pos += n;
3592         }
3593         if (nBytesRead)
3594             *nBytesRead = bytesRead;
3595         return LVERR_OK;
3596     }
3597 
3598     /// Write
3599     /**
3600         \param buf is data to write to stream
3601         \param count is number of bytes to write
3602         \param nBytesWritten is place to store real number of bytes written to stream
3603         \return lverror_t status: LVERR_OK if success
3604     */
Write(const void *,lvsize_t,lvsize_t *)3605     virtual lverror_t Write( const void *, lvsize_t, lvsize_t *)
3606     {
3607         return LVERR_FAIL;
3608     }
3609 
3610     /// Check whether end of file is reached
3611     /**
3612         \return true if end of file reached
3613     */
Eof()3614     virtual bool Eof()
3615     {
3616         //TODO
3617         return false;
3618     }
3619 };
3620 
3621 /// creates TCR decoder stream for stream
LVCreateTCRDecoderStream(LVStreamRef stream)3622 LVStreamRef LVCreateTCRDecoderStream( LVStreamRef stream )
3623 {
3624     return LVTCRStream::create( stream, LVOM_READ );
3625 }
3626 
3627 /// returns path part of pathname (appended with / or \ delimiter)
LVExtractPath(lString8 pathName,bool appendEmptyPath)3628 lString8 LVExtractPath( lString8 pathName, bool appendEmptyPath) {
3629     return UnicodeToUtf8(LVExtractPath(Utf8ToUnicode(pathName), appendEmptyPath));
3630 }
3631 
3632 /// returns path part of pathname (appended with / or \ delimiter)
LVExtractPath(lString32 pathName,bool appendEmptyPath)3633 lString32 LVExtractPath( lString32 pathName, bool appendEmptyPath )
3634 {
3635     int last_delim_pos = -1;
3636     for ( int i=0; i<pathName.length(); i++ )
3637         if ( pathName[i]=='/' || pathName[i]=='\\' )
3638             last_delim_pos = i;
3639     if ( last_delim_pos==-1 )
3640 #ifdef _LINUX
3641         return lString32(appendEmptyPath ? U"./" : U"");
3642 #else
3643         return lString32(appendEmptyPath ? U".\\" : U"");
3644 #endif
3645     return pathName.substr( 0, last_delim_pos+1 );
3646 }
3647 
3648 /// returns filename part of pathname
LVExtractFilename(lString8 pathName)3649 lString8 LVExtractFilename( lString8 pathName ) {
3650     return UnicodeToUtf8(LVExtractFilename(Utf8ToUnicode(pathName)));
3651 }
3652 
3653 /// returns filename part of pathname
LVExtractFilename(lString32 pathName)3654 lString32 LVExtractFilename( lString32 pathName )
3655 {
3656     int last_delim_pos = -1;
3657     for ( int i=0; i<pathName.length(); i++ )
3658         if ( pathName[i]=='/' || pathName[i]=='\\' )
3659             last_delim_pos = i;
3660     if ( last_delim_pos==-1 )
3661         return pathName;
3662     return pathName.substr( last_delim_pos+1 );
3663 }
3664 
3665 /// returns filename part of pathname without extension
LVExtractFilenameWithoutExtension(lString32 pathName)3666 lString32 LVExtractFilenameWithoutExtension( lString32 pathName )
3667 {
3668     lString32 s = LVExtractFilename( pathName );
3669     int lastDot = -1;
3670     for ( int i=0; i<s.length(); i++ )
3671         if ( s[i]=='.' )
3672             lastDot = i;
3673     if ( lastDot<=0 || lastDot<(int)s.length()-7 )
3674         return s;
3675     return s.substr( 0, lastDot );
3676 }
3677 
3678 /// returns true if absolute path is specified
LVIsAbsolutePath(lString32 pathName)3679 bool LVIsAbsolutePath( lString32 pathName )
3680 {
3681     if ( pathName.empty() )
3682         return false;
3683     lChar32 c = pathName[0];
3684     if ( c=='\\' || c=='/' )
3685         return true;
3686 #ifdef _WIN32
3687     if ( (c>='a' && c<='z') || (c>='A' && c<='Z') ) {
3688         return (pathName[1]==':');
3689     }
3690 #endif
3691     return false;
3692 }
3693 
3694 /// removes first path part from pathname and returns it
LVExtractFirstPathElement(lString32 & pathName)3695 lString32 LVExtractFirstPathElement( lString32 & pathName )
3696 {
3697     if ( pathName.empty() )
3698         return lString32::empty_str;
3699     if ( pathName[0]=='/' || pathName[0]=='\\' )
3700         pathName.erase(0, 1);
3701     int first_delim_pos = -1;
3702     for ( int i=0; i<pathName.length(); i++ )
3703         if ( pathName[i]=='/' || pathName[i]=='\\' ) {
3704             first_delim_pos = i;
3705             break;
3706         }
3707     if ( first_delim_pos==-1 ) {
3708         lString32 res = pathName;
3709         pathName.clear();
3710         return res;
3711     }
3712     lString32 res = pathName.substr(0, first_delim_pos );
3713     pathName.erase(0, first_delim_pos+1 );
3714     return res;
3715 }
3716 
3717 /// appends path delimiter character to end of path, if absent
LVAppendPathDelimiter(lString32 & pathName)3718 void LVAppendPathDelimiter( lString32 & pathName )
3719 {
3720     if ( pathName.empty() || (pathName.length() == 1 && pathName[0] == ASSET_PATH_PREFIX))
3721         return;
3722     lChar32 delim = LVDetectPathDelimiter( pathName );
3723     if ( pathName[pathName.length()-1]!=delim )
3724         pathName << delim;
3725 }
3726 
3727 /// appends path delimiter character to end of path, if absent
LVAppendPathDelimiter(lString8 & pathName)3728 void LVAppendPathDelimiter( lString8 & pathName )
3729 {
3730     if ( pathName.empty() || (pathName.length() == 1 && pathName[0] == ASSET_PATH_PREFIX))
3731         return;
3732     lChar8 delim = LVDetectPathDelimiter(pathName);
3733     if ( pathName[pathName.length()-1]!=delim )
3734         pathName << delim;
3735 }
3736 
3737 /// removes path delimiter from end of path, if present
LVRemoveLastPathDelimiter(lString32 & pathName)3738 void LVRemoveLastPathDelimiter( lString32 & pathName ) {
3739     if (pathName.empty() || (pathName.length() == 1 && pathName[0] == ASSET_PATH_PREFIX))
3740         return;
3741     if (pathName.endsWith("/") || pathName.endsWith("\\"))
3742         pathName = pathName.substr(0, pathName.length() - 1);
3743 }
3744 
3745 /// removes path delimiter from end of path, if present
LVRemoveLastPathDelimiter(lString8 & pathName)3746 void LVRemoveLastPathDelimiter( lString8 & pathName )
3747 {
3748     if (pathName.empty() || (pathName.length() == 1 && pathName[0] == ASSET_PATH_PREFIX))
3749         return;
3750     if (pathName.endsWith("/") || pathName.endsWith("\\"))
3751         pathName = pathName.substr(0, pathName.length() - 1);
3752 }
3753 
3754 /// replaces any found / or \\ separator with specified one
LVReplacePathSeparator(lString32 & pathName,lChar32 separator)3755 void LVReplacePathSeparator( lString32 & pathName, lChar32 separator )
3756 {
3757     lChar32 * buf = pathName.modify();
3758     for ( ; *buf; buf++ )
3759         if ( *buf=='/' || *buf=='\\' )
3760             *buf = separator;
3761 }
3762 
3763 // resolve relative links
LVCombinePaths(lString32 basePath,lString32 newPath)3764 lString32 LVCombinePaths( lString32 basePath, lString32 newPath )
3765 {
3766     if ( newPath[0]=='/' || newPath[0]=='\\' || (newPath.length()>0 && newPath[1]==':' && newPath[2]=='\\') )
3767         return newPath; // absolute path
3768     lChar32 separator = 0;
3769     if (!basePath.empty())
3770         LVAppendPathDelimiter(basePath);
3771     for ( int i=0; i<basePath.length(); i++ ) {
3772         if ( basePath[i]=='/' || basePath[i]=='\\' ) {
3773             separator = basePath[i];
3774             break;
3775         }
3776     }
3777     if ( separator == 0 )
3778         for ( int i=0; i<newPath.length(); i++ ) {
3779             if ( newPath[i]=='/' || newPath[i]=='\\' ) {
3780                 separator = newPath[i];
3781                 break;
3782             }
3783         }
3784     if ( separator == 0 )
3785         separator = '/';
3786     lString32 s = basePath;
3787     LVAppendPathDelimiter( s );
3788     s += newPath;
3789     //LVAppendPathDelimiter( s );
3790     LVReplacePathSeparator( s, separator );
3791     lString32 pattern;
3792     pattern << separator << ".." << separator;
3793     bool changed;
3794     do {
3795         changed = false;
3796         int lastElementStart = 0;
3797         for ( int i=0; i<(int)(s.length()-pattern.length()); i++ ) {
3798             if ( s[i]==separator && s[i+1]!='.' )
3799                 lastElementStart = i + 1;
3800             else if ( s[i]==separator && s[i+1]=='.' && s[i+2]=='.' && s[i+3]==separator ) {
3801                 if ( lastElementStart>=0 ) {
3802                     // /a/b/../c/
3803                     // 0123456789
3804                     //   ^ ^
3805                     s.erase( lastElementStart, i+4-lastElementStart );
3806                     changed = true;
3807                     // lastElementStart = -1;
3808                     break;
3809                 }
3810             }
3811         }
3812     } while ( changed && s.length()>=pattern.length() );
3813     // Replace "/./" inside with "/"
3814     pattern.clear();
3815     pattern << separator << "." << separator;
3816     lString32 replacement;
3817     replacement << separator;
3818     while ( s.replace( pattern, replacement ) ) ;
3819     // Remove "./" at start
3820     if ( s.length()>2 && s[0]=='.' && s[1]==separator )
3821         s.erase(0, 2);
3822     return s;
3823 }
3824 
3825 
3826 /// removes last path part from pathname and returns it
LVExtractLastPathElement(lString32 & pathName)3827 lString32 LVExtractLastPathElement( lString32 & pathName )
3828 {
3829     int l = pathName.length();
3830     if ( l==0 )
3831         return lString32::empty_str;
3832     if ( pathName[l-1]=='/' || pathName[l-1]=='\\' )
3833         pathName.erase(l-1, 1);
3834     int last_delim_pos = -1;
3835     for ( int i=0; i<pathName.length(); i++ )
3836         if ( pathName[i]=='/' || pathName[i]=='\\' )
3837             last_delim_pos = i;
3838     if ( last_delim_pos==-1 ) {
3839         lString32 res = pathName;
3840         pathName.clear();
3841         return res;
3842     }
3843     lString32 res = pathName.substr( last_delim_pos + 1, pathName.length()-last_delim_pos-1 );
3844     pathName.erase( last_delim_pos, pathName.length()-last_delim_pos );
3845     return res;
3846 }
3847 
3848 /// returns path delimiter character
LVDetectPathDelimiter(lString32 pathName)3849 lChar32 LVDetectPathDelimiter( lString32 pathName )
3850 {
3851     for ( int i=0; i<pathName.length(); i++ )
3852         if ( pathName[i]=='/' || pathName[i]=='\\' )
3853             return pathName[i];
3854 #ifdef _LINUX
3855         return '/';
3856 #else
3857         return '\\';
3858 #endif
3859 }
3860 
3861 /// returns path delimiter character
LVDetectPathDelimiter(lString8 pathName)3862 char LVDetectPathDelimiter( lString8 pathName ) {
3863     for ( int i=0; i<pathName.length(); i++ )
3864         if ( pathName[i]=='/' || pathName[i]=='\\' )
3865             return pathName[i];
3866 #ifdef _LINUX
3867         return '/';
3868 #else
3869         return '\\';
3870 #endif
3871 }
3872 
3873 /// returns full path to file identified by pathName, with base directory == basePath
LVMakeRelativeFilename(lString32 basePath,lString32 pathName)3874 lString32 LVMakeRelativeFilename( lString32 basePath, lString32 pathName )
3875 {
3876     if ( LVIsAbsolutePath( pathName ) )
3877         return pathName;
3878     lChar32 delim = LVDetectPathDelimiter( basePath );
3879     lString32 path = LVExtractPath( basePath );
3880     lString32 name = LVExtractFilename( pathName );
3881     lString32 dstpath = LVExtractPath( pathName );
3882     while ( !dstpath.empty() ) {
3883         lString32 element = LVExtractFirstPathElement( dstpath );
3884         if (element == ".") {
3885             // do nothing
3886         } else if (element == "..")
3887             LVExtractLastPathElement( path );
3888         else
3889             path << element << delim;
3890     }
3891     LVAppendPathDelimiter( path );
3892     path << name;
3893     return path;
3894 }
3895 
3896 /// removes path delimiter character from end of path, if exists
LVRemovePathDelimiter(lString32 & pathName)3897 void LVRemovePathDelimiter( lString32 & pathName )
3898 {
3899     int len = pathName.length();
3900     if ( len>0 && pathName != "/" && pathName != "\\" && !pathName.endsWith(":\\") && !pathName.endsWith("\\\\")) {
3901         if ( pathName.lastChar() == '/' || pathName.lastChar() == '\\' )
3902             pathName.erase( pathName.length()-1, 1 );
3903     }
3904 }
3905 
3906 /// removes path delimiter character from end of path, if exists
LVRemovePathDelimiter(lString8 & pathName)3907 void LVRemovePathDelimiter( lString8 & pathName )
3908 {
3909     int len = pathName.length();
3910     if ( len>0 && pathName != "/" && pathName != "\\" && !pathName.endsWith(":\\") && !pathName.endsWith("\\\\")) {
3911         if ( pathName.lastChar() == '/' || pathName.lastChar() == '\\' )
3912             pathName.erase( pathName.length()-1, 1 );
3913     }
3914 }
3915 
3916 /// returns true if specified file exists
LVFileExists(const lString8 & pathName)3917 bool LVFileExists( const lString8 & pathName ) {
3918     return LVFileExists(Utf8ToUnicode(pathName));
3919 }
3920 
3921 /// returns true if specified file exists
LVFileExists(const lString32 & pathName)3922 bool LVFileExists( const lString32 & pathName )
3923 {
3924     lString32 fn(pathName);
3925     if (fn.length() > 1 && fn[0] == ASSET_PATH_PREFIX) {
3926     	if (!_assetContainerFactory)
3927     		return false;
3928     	lString32 assetPath = LVExtractAssetPath(fn);
3929     	return !_assetContainerFactory->openAssetStream(assetPath).isNull();
3930     }
3931 #ifdef _WIN32
3932 	LVStreamRef stream = LVOpenFileStream( pathName.c_str(), LVOM_READ );
3933 	return !stream.isNull();
3934 #else
3935     FILE * f = fopen(UnicodeToUtf8(pathName).c_str(), "rb");
3936     if ( f ) {
3937         fclose( f );
3938         return true;
3939     }
3940     return false;
3941 #endif
3942 }
3943 
3944 /// returns true if directory exists and your app can write to directory
LVDirectoryIsWritable(const lString32 & pathName)3945 bool LVDirectoryIsWritable(const lString32 & pathName) {
3946     lString32 fn = pathName;
3947     LVAppendPathDelimiter(fn);
3948     fn << ".cr3_directory_write_test";
3949     bool res = false;
3950     bool created = false;
3951     {
3952         LVStreamRef stream = LVOpenFileStream(fn.c_str(), LVOM_WRITE);
3953         if (!stream.isNull()) {
3954             created = true;
3955             lvsize_t bytesWritten = 0;
3956             if (stream->Write("TEST", 4, &bytesWritten) == LVERR_OK && bytesWritten == 4) {
3957                 res = true;
3958             }
3959         }
3960     }
3961     if (created)
3962         LVDeleteFile(fn);
3963     return res;
3964 }
3965 
3966 /// returns true if specified directory exists
LVDirectoryExists(const lString32 & pathName)3967 bool LVDirectoryExists( const lString32 & pathName )
3968 {
3969     lString32 fn(pathName);
3970     if (fn.length() > 1 && fn[0] == ASSET_PATH_PREFIX) {
3971     	if (!_assetContainerFactory)
3972     		return false;
3973     	lString32 assetPath = LVExtractAssetPath(fn);
3974     	return !_assetContainerFactory->openAssetContainer(assetPath).isNull();
3975     }
3976     LVContainerRef dir = LVOpenDirectory( pathName.c_str() );
3977     return !dir.isNull();
3978 }
3979 
3980 /// returns true if specified directory exists
LVDirectoryExists(const lString8 & pathName)3981 bool LVDirectoryExists( const lString8 & pathName )
3982 {
3983     lString32 fn(Utf8ToUnicode(pathName));
3984     if (fn.length() > 1 && fn[0] == ASSET_PATH_PREFIX) {
3985     	if (!_assetContainerFactory)
3986     		return false;
3987     	lString32 assetPath = LVExtractAssetPath(fn);
3988     	return !_assetContainerFactory->openAssetContainer(assetPath).isNull();
3989     }
3990     LVContainerRef dir = LVOpenDirectory(fn);
3991     return !dir.isNull();
3992 }
3993 
3994 /// Create directory if not exist
LVCreateDirectory(lString32 path)3995 bool LVCreateDirectory( lString32 path )
3996 {
3997     CRLog::trace("LVCreateDirectory(%s)", UnicodeToUtf8(path).c_str() );
3998     //LVRemovePathDelimiter(path);
3999     if ( path.length() <= 1 )
4000         return false;
4001     if (path[0] == ASSET_PATH_PREFIX) {
4002     	// cannot create directory in asset
4003     	return false;
4004     }
4005     LVContainerRef dir = LVOpenDirectory( path.c_str() );
4006     if ( dir.isNull() ) {
4007         CRLog::trace("Directory %s not found", UnicodeToUtf8(path).c_str());
4008         LVRemovePathDelimiter(path);
4009         lString32 basedir = LVExtractPath( path );
4010         CRLog::trace("Checking base directory %s", UnicodeToUtf8(basedir).c_str());
4011         if ( !LVCreateDirectory( basedir ) ) {
4012             CRLog::error("Failed to create directory %s", UnicodeToUtf8(basedir).c_str());
4013             return false;
4014         }
4015 #ifdef _WIN32
4016         return CreateDirectoryW( UnicodeToUtf16(path).c_str(), NULL )!=0;
4017 #else
4018         //LVRemovePathDelimiter( path );
4019         lString8 path8 = UnicodeToUtf8( path );
4020         CRLog::trace("Creating directory %s", path8.c_str() );
4021         if ( mkdir(path8.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) ) {
4022             CRLog::error("Cannot create directory %s", path8.c_str() );
4023             return false;
4024         }
4025         return true;
4026 #endif
4027     }
4028     CRLog::trace("Directory %s exists", UnicodeToUtf8(path).c_str());
4029     return true;
4030 }
4031 
4032 /// Open memory mapped file
4033 /**
4034     \param pathname is file name to open (unicode)
4035     \param mode is mode file should be opened in (LVOM_READ or LVOM_APPEND only)
4036         \param minSize is minimum file size for R/W mode
4037     \return reference to opened stream if success, NULL if error
4038 */
LVMapFileStream(const lChar32 * pathname,lvopen_mode_t mode,lvsize_t minSize)4039 LVStreamRef LVMapFileStream( const lChar32 * pathname, lvopen_mode_t mode, lvsize_t minSize )
4040 {
4041 #if !defined(_WIN32) && !defined(_LINUX)
4042         // STUB for systems w/o mmap
4043     LVFileStream * stream = LVFileStream::CreateFileStream( pathname, mode );
4044     if ( stream!=NULL )
4045     {
4046         return LVStreamRef( stream );
4047     }
4048     return LVStreamRef();
4049 #else
4050         LVFileMappedStream * stream = LVFileMappedStream::CreateFileStream( lString32(pathname), mode, (int)minSize );
4051         return LVStreamRef(stream);
4052 #endif
4053 }
4054 
4055 /// delete file, return true if file found and successfully deleted
LVDeleteFile(lString32 filename)4056 bool LVDeleteFile( lString32 filename )
4057 {
4058 #ifdef _WIN32
4059     return DeleteFileW( UnicodeToUtf16(filename).c_str() ) ? true : false;
4060 #else
4061     if ( unlink( UnicodeToUtf8( filename ).c_str() ) )
4062         return false;
4063     return true;
4064 #endif
4065 }
4066 
4067 /// rename file
LVRenameFile(lString32 oldname,lString32 newname)4068 bool LVRenameFile(lString32 oldname, lString32 newname) {
4069     lString8 oldname8 = UnicodeToLocal(oldname);
4070     lString8 newname8 = UnicodeToLocal(newname);
4071 #ifdef _WIN32
4072     lString16 oldname16 = UnicodeToUtf16(oldname);
4073     lString16 newname16 = UnicodeToUtf16(newname);
4074     CRLog::trace("Renaming %s to %s", oldname8.c_str(), newname8.c_str());
4075     bool res = MoveFileW(oldname16.c_str(), newname16.c_str()) != 0;
4076     if (!res) {
4077         CRLog::error("Renaming result: %s for renaming of %s to %s", res ? "success" : "failed", oldname8.c_str(), newname8.c_str());
4078         CRLog::error("Last Error: %d", GetLastError());
4079     }
4080     return res;
4081 #else
4082     return LVRenameFile(oldname8, newname8);
4083 #endif
4084 }
4085 
4086 /// rename file
LVRenameFile(lString8 oldname,lString8 newname)4087 bool LVRenameFile(lString8 oldname, lString8 newname) {
4088 #ifdef _WIN32
4089     return LVRenameFile(LocalToUnicode(oldname), LocalToUnicode(newname));
4090 #else
4091     return !rename(oldname.c_str(), newname.c_str());
4092 #endif
4093 }
4094 
4095 /// delete file, return true if file found and successfully deleted
LVDeleteFile(lString8 filename)4096 bool LVDeleteFile( lString8 filename ) {
4097     return LVDeleteFile(Utf8ToUnicode(filename));
4098 }
4099 
4100 /// delete directory, return true if directory is found and successfully deleted
LVDeleteDirectory(lString32 filename)4101 bool LVDeleteDirectory( lString32 filename ) {
4102 #ifdef _WIN32
4103     return RemoveDirectoryW( UnicodeToUtf16(filename).c_str() ) ? true : false;
4104 #else
4105     if ( unlink( UnicodeToUtf8( filename ).c_str() ) )
4106         return false;
4107     return true;
4108 #endif
4109 }
4110 
4111 /// delete directory, return true if directory is found and successfully deleted
LVDeleteDirectory(lString8 filename)4112 bool LVDeleteDirectory( lString8 filename ) {
4113     return LVDeleteDirectory(Utf8ToUnicode(filename));
4114 }
4115 
4116 #define TRACE_BLOCK_WRITE_STREAM 0
4117 
4118 class LVBlockWriteStream : public LVNamedStream
4119 {
4120     LVStreamRef _baseStream;
4121     int _blockSize;
4122     int _blockCount;
4123     lvpos_t _pos;
4124     lvpos_t _size;
4125 
4126 
4127     struct Block
4128     {
4129         lvpos_t block_start;
4130         lvpos_t block_end;
4131         lvpos_t modified_start;
4132         lvpos_t modified_end;
4133         lUInt8 * buf;
4134         int size;
4135         Block * next;
4136 
BlockLVBlockWriteStream::Block4137         Block( lvpos_t start, lvpos_t end, int block_size )
4138             : block_start( start/block_size*block_size ), block_end( end )
4139             , modified_start((lvpos_t)-1), modified_end((lvpos_t)-1)
4140             , size( block_size ), next(NULL)
4141         {
4142             buf = (lUInt8*)calloc(size, sizeof(*buf));
4143             if ( buf ) {
4144     //            modified_start = 0;
4145     //            modified_end = size;
4146             }
4147             else {
4148                 CRLog::error("buffer allocation failed");
4149             }
4150         }
~BlockLVBlockWriteStream::Block4151         ~Block()
4152         {
4153             free(buf);
4154         }
4155 
saveLVBlockWriteStream::Block4156         void save( const lUInt8 * ptr, lvpos_t pos, lvsize_t len )
4157         {
4158 #if TRACE_BLOCK_WRITE_STREAM
4159             CRLog::trace("block %x save %x, %x", (int)block_start, (int)pos, (int)len);
4160 #endif
4161             int offset = (int)(pos - block_start);
4162             if (offset > size || offset < 0 || (int)len > size || offset + (int)len > size) {
4163                 CRLog::error("Unaligned access to block %x", (int)block_start);
4164             }
4165             for (unsigned i = 0; i < len; i++ ) {
4166                 lUInt8 ch1 = buf[offset+i];
4167                 if ( pos+i>block_end || ch1!=ptr[i] ) {
4168                     buf[offset+i] = ptr[i];
4169                     if ( modified_start==(lvpos_t)-1 ) {
4170                         modified_start = pos + i;
4171                         modified_end = modified_start + 1;
4172                     } else {
4173                         if ( modified_start>pos+i )
4174                             modified_start = pos+i;
4175                         if ( modified_end<pos+i+1)
4176                             modified_end = pos+i+1;
4177                         if ( block_end<pos+i+1)
4178                             block_end = pos+i+1;
4179                     }
4180                 }
4181             }
4182 
4183         }
4184 
containsPosLVBlockWriteStream::Block4185         bool containsPos( lvpos_t pos )
4186         {
4187             return pos>=block_start && pos<block_start+size;
4188         }
4189     };
4190 
4191     // list of blocks
4192     Block * _firstBlock;
4193     int _count;
4194 
4195     /// set write bytes limit to call flush(true) automatically after writing of each sz bytes
setAutoSyncSize(lvsize_t sz)4196     void setAutoSyncSize(lvsize_t sz) {
4197         _baseStream->setAutoSyncSize(sz);
4198         handleAutoSync(0);
4199     }
4200 
4201 
4202     /// fills block with data existing in file
readBlock(Block * block)4203     lverror_t readBlock( Block * block )
4204     {
4205         if ( !block->size ) {
4206             CRLog::error("Invalid block size");
4207         }
4208         lvpos_t start = block->block_start;
4209         lvpos_t end = start + _blockSize;
4210         lvpos_t ssize = 0;
4211         lverror_t res = LVERR_OK;
4212         res = _baseStream->GetSize( &ssize);
4213         if ( res!=LVERR_OK )
4214             return res;
4215         if ( end>ssize )
4216             end = ssize;
4217         if ( end<=start )
4218             return LVERR_OK;
4219         _baseStream->SetPos( start );
4220         lvsize_t bytesRead = 0;
4221         block->block_end = end;
4222 #if TRACE_BLOCK_WRITE_STREAM
4223         CRLog::trace("block %x filling from stream %x, %x", (int)block->block_start, (int)block->block_start, (int)(block->block_end-block->block_start));
4224 #endif
4225         res = _baseStream->Read( block->buf, end-start, &bytesRead );
4226         if ( res!=LVERR_OK )
4227             CRLog::error("Error while reading block %x from file of size %x", block->block_start, ssize);
4228         return res;
4229     }
4230 
writeBlock(Block * block)4231     lverror_t writeBlock( Block * block )
4232     {
4233         if ( block->modified_start < block->modified_end ) {
4234 #if TRACE_BLOCK_WRITE_STREAM
4235             CRLog::trace("WRITE BLOCK %x (%x, %x)", (int)block->block_start, (int)block->modified_start, (int)(block->modified_end-block->modified_start));
4236 #endif
4237             _baseStream->SetPos( block->modified_start );
4238             if (block->modified_end > _size) {
4239                 block->modified_end = block->block_end;
4240             }
4241             lvpos_t bytesWritten = 0;
4242             lverror_t res = _baseStream->Write( block->buf + (block->modified_start-block->block_start), block->modified_end-block->modified_start, &bytesWritten );
4243             if ( res==LVERR_OK ) {
4244                 if (_size < block->modified_end)
4245                     _size = block->modified_end;
4246             }
4247             block->modified_end = block->modified_start = (lvpos_t)-1;
4248             return res;
4249         } else
4250             return LVERR_OK;
4251     }
4252 
newBlock(lvpos_t start,int len)4253     Block * newBlock( lvpos_t start, int len )
4254     {
4255         Block * b = new Block( start, start+len, _blockSize );
4256         return b;
4257     }
4258 
4259     /// find block, move to top if found
findBlock(lvpos_t pos)4260     Block * findBlock( lvpos_t pos )
4261     {
4262         for ( Block ** p = &_firstBlock; *p; p=&(*p)->next ) {
4263             Block * item = *p;
4264             if ( item->containsPos(pos) ) {
4265                 if ( item!=_firstBlock ) {
4266 #if TRACE_BLOCK_WRITE_STREAM
4267                     dumpBlocks("before reorder");
4268 #endif
4269                     *p = item->next;
4270                     item->next = _firstBlock;
4271                     _firstBlock = item;
4272 #if TRACE_BLOCK_WRITE_STREAM
4273                     dumpBlocks("after reorder");
4274                     CRLog::trace("found block %x (%x, %x)", (int)item->block_start, (int)item->modified_start, (int)(item->modified_end-item->modified_start));
4275 #endif
4276                 }
4277                 return item;
4278             }
4279         }
4280         return NULL;
4281     }
4282 
4283     // try read block-aligned fragment from cache
readFromCache(void * buf,lvpos_t pos,lvsize_t count)4284     bool readFromCache( void * buf, lvpos_t pos, lvsize_t count )
4285     {
4286         Block * p = findBlock( pos );
4287         if ( p ) {
4288 #if TRACE_BLOCK_WRITE_STREAM
4289             CRLog::trace("read from cache block %x (%x, %x)", (int)p->block_start, (int)pos, (int)(count));
4290 #endif
4291             memcpy( buf, p->buf + (pos-p->block_start), count );
4292             return true;
4293         }
4294         return false;
4295     }
4296 
4297     // write block-aligned fragment to cache
writeToCache(const void * buf,lvpos_t pos,lvsize_t count)4298     lverror_t writeToCache( const void * buf, lvpos_t pos, lvsize_t count )
4299     {
4300         Block * p = findBlock( pos );
4301         if ( p ) {
4302 #if TRACE_BLOCK_WRITE_STREAM
4303             CRLog::trace("saving data to existing block %x (%x, %x)", (int)p->block_start, (int)pos, (int)count);
4304 #endif
4305             p->save( (const lUInt8 *)buf, pos, count );
4306             if ( pos + count > _size )
4307                 _size = pos + count;
4308             return LVERR_OK;
4309         }
4310 #if TRACE_BLOCK_WRITE_STREAM
4311         CRLog::trace("Block %x not found in cache", pos);
4312 #endif
4313         if ( _count>=_blockCount-1 ) {
4314             // remove last
4315             for ( Block * p = _firstBlock; p; p=p->next ) {
4316                 if ( p->next && !p->next->next ) {
4317 #if TRACE_BLOCK_WRITE_STREAM
4318                     dumpBlocks("before remove last");
4319                     CRLog::trace("dropping block %x (%x, %x)", (int)p->next->block_start, (int)p->next->modified_start, (int)(p->next->modified_end-p->next->modified_start));
4320 #endif
4321                     writeBlock( p->next );
4322                     delete p->next;
4323                     _count--;
4324                     p->next = NULL;
4325 #if TRACE_BLOCK_WRITE_STREAM
4326                     dumpBlocks("after remove last");
4327 #endif
4328                 }
4329             }
4330         }
4331         p = newBlock( pos, count );
4332 #if TRACE_BLOCK_WRITE_STREAM
4333         CRLog::trace("creating block %x", (int)p->block_start);
4334 #endif
4335         if ( readBlock( p )!=LVERR_OK ) {
4336             delete p;
4337             return LVERR_FAIL;
4338         }
4339 #if TRACE_BLOCK_WRITE_STREAM
4340         CRLog::trace("saving data to new block %x (%x, %x)", (int)p->block_start, (int)pos, (int)count);
4341 #endif
4342         p->save( (const lUInt8 *)buf, pos, count );
4343         p->next = _firstBlock;
4344         _firstBlock = p;
4345         _count++;
4346         if ( pos + count > _size ) {
4347             _size = pos + count;
4348             p->modified_start = p->block_start;
4349             p->modified_end = p->block_end;
4350         }
4351         return LVERR_OK;
4352     }
4353 
4354 
4355 public:
Flush(bool sync)4356     virtual lverror_t Flush( bool sync ) {
4357         CRTimerUtil infinite;
4358         return Flush(sync, infinite); // NOLINT: Call to virtual function during destruction
4359     }
4360     /// flushes unsaved data from buffers to file, with optional flush of OS buffers
Flush(bool sync,CRTimerUtil & timeout)4361     virtual lverror_t Flush( bool sync, CRTimerUtil & timeout )
4362     {
4363 #if TRACE_BLOCK_WRITE_STREAM
4364         CRLog::trace("flushing unsaved blocks");
4365 #endif
4366         lverror_t res = LVERR_OK;
4367         for ( Block * p = _firstBlock; p; ) {
4368             Block * tmp = p;
4369             if ( writeBlock(p)!=LVERR_OK )
4370                 res = LVERR_FAIL;
4371             p = p->next;
4372             delete tmp;
4373             if (!sync && timeout.expired()) {
4374                 //CRLog::trace("LVBlockWriteStream::flush - timeout expired");
4375                 _firstBlock = p;
4376                 return LVERR_OK;
4377             }
4378 
4379         }
4380         _firstBlock = NULL;
4381         _baseStream->Flush( sync );
4382         return res;
4383     }
4384 
4385 
~LVBlockWriteStream()4386     virtual ~LVBlockWriteStream()
4387     {
4388         Flush( true ); // NOLINT: Call to virtual function during destruction
4389     }
4390 
GetName()4391     virtual const lChar32 * GetName()
4392             { return _baseStream->GetName(); }
GetMode()4393     virtual lvopen_mode_t GetMode()
4394             { return _baseStream->GetMode(); }
4395 
LVBlockWriteStream(LVStreamRef baseStream,int blockSize,int blockCount)4396     LVBlockWriteStream( LVStreamRef baseStream, int blockSize, int blockCount )
4397     : _baseStream( baseStream ), _blockSize( blockSize ), _blockCount( blockCount ), _firstBlock(NULL), _count(0)
4398     {
4399         _pos = _baseStream->GetPos();
4400         _size = _baseStream->GetSize();
4401     }
4402 
GetSize()4403     virtual lvpos_t GetSize()
4404     {
4405         return _size;
4406     }
4407 
Seek(lvoffset_t offset,lvseek_origin_t origin,lvpos_t * pNewPos)4408     virtual lverror_t Seek( lvoffset_t offset, lvseek_origin_t origin, lvpos_t * pNewPos )
4409     {
4410         if ( origin==LVSEEK_CUR ) {
4411             origin = LVSEEK_SET;
4412             offset = _pos + offset;
4413         } else if ( origin==LVSEEK_END ) {
4414             origin = LVSEEK_SET;
4415             offset = _size + offset;
4416         }
4417 
4418         lvpos_t newpos = 0;
4419         lverror_t res = _baseStream->Seek(offset, origin, &newpos);
4420         if ( res==LVERR_OK ) {
4421             if ( pNewPos )
4422                 *pNewPos = newpos;
4423             _pos = newpos;
4424         } else {
4425             CRLog::error("baseStream->Seek(%d,%x) failed: %d", (int)origin, (int)offset, (int)res);
4426         }
4427         return res;
4428     }
4429 
Tell(lvpos_t * pPos)4430     virtual lverror_t Tell( lvpos_t * pPos )
4431     {
4432         *pPos = _pos;
4433         return LVERR_OK;
4434     }
4435     //virtual lverror_t   SetPos(lvpos_t p)
SetPos(lvpos_t p)4436     virtual lvpos_t   SetPos(lvpos_t p)
4437     {
4438         lvpos_t res = _baseStream->SetPos(p);
4439         _pos = _baseStream->GetPos();
4440 //                if ( _size<_pos )
4441 //                    _size = _pos;
4442         return res;
4443     }
GetPos()4444     virtual lvpos_t   GetPos()
4445     {
4446         return _pos;
4447     }
SetSize(lvsize_t size)4448     virtual lverror_t SetSize( lvsize_t size )
4449     {
4450         // TODO:
4451         lverror_t res = _baseStream->SetSize(size);
4452         if ( res==LVERR_OK )
4453             _size = size;
4454         return res;
4455     }
4456 
dumpBlocks(const char * context)4457     void dumpBlocks( const char * context)
4458     {
4459         lString8 buf;
4460         for ( Block * p = _firstBlock; p; p = p->next ) {
4461             char s[1000];
4462             snprintf(s, 999, "%x ", (int)p->block_start);
4463             s[999] = 0;
4464             buf << s;
4465         }
4466         CRLog::trace("BLOCKS (%s): %s   count=%d", context, buf.c_str(), _count);
4467     }
4468 
Read(void * buf,lvsize_t count,lvsize_t * nBytesRead)4469     virtual lverror_t Read( void * buf, lvsize_t count, lvsize_t * nBytesRead )
4470     {
4471 #if TRACE_BLOCK_WRITE_STREAM
4472         CRLog::trace("stream::Read(%x, %x)", (int)_pos, (int)count);
4473         dumpBlocks("before read");
4474 #endif
4475         // slice by block bounds
4476         lvsize_t bytesRead = 0;
4477         lverror_t res = LVERR_OK;
4478 		if ( _pos > _size ) {
4479 			if ( nBytesRead )
4480 				*nBytesRead = bytesRead;
4481 			return LVERR_FAIL;
4482 		}
4483         if ( _pos + count > _size )
4484             count = (int)(_size - _pos);
4485         while ( (int)count>0 && res==LVERR_OK ) {
4486             lvpos_t blockSpaceLeft = _blockSize - (_pos % _blockSize);
4487             if ( blockSpaceLeft > count )
4488                 blockSpaceLeft = count;
4489             lvsize_t blockBytesRead = 0;
4490 
4491             // read from Write buffers if possible, otherwise - from base stream
4492             if ( readFromCache( buf, _pos, blockSpaceLeft ) ) {
4493                 blockBytesRead = blockSpaceLeft;
4494                 res = LVERR_OK;
4495             } else {
4496                 lvpos_t fsize = _baseStream->GetSize();
4497                 if ( _pos + blockSpaceLeft > fsize && fsize < _size) {
4498 #if TRACE_BLOCK_WRITE_STREAM
4499                     CRLog::trace("stream::Read: inconsistent cache state detected: fsize=%d, _size=%d, force flush...", (int)fsize, (int)_size);
4500 #endif
4501                     // Workaround to exclude fatal error in ldomTextStorageChunk::ensureUnpacked()
4502                     // Write cached data to a file stream if the required read block is larger than the rest of the file.
4503                     // This is a very rare case.
4504                     Flush(true);
4505                 }
4506 #if TRACE_BLOCK_WRITE_STREAM
4507                 CRLog::trace("direct reading from stream (%x, %x)", (int)_pos, (int)blockSpaceLeft);
4508 #endif
4509                 _baseStream->SetPos(_pos);
4510                 res = _baseStream->Read(buf, blockSpaceLeft, &blockBytesRead);
4511             }
4512             if ( res!=LVERR_OK )
4513                 break;
4514 
4515             count -= blockBytesRead;
4516             buf = ((char*)buf) + blockBytesRead;
4517             _pos += blockBytesRead;
4518             bytesRead += blockBytesRead;
4519             if ( !blockBytesRead )
4520                 break;
4521         }
4522         if ( nBytesRead && res==LVERR_OK )
4523             *nBytesRead = bytesRead;
4524         return res;
4525     }
4526 
Write(const void * buf,lvsize_t count,lvsize_t * nBytesWritten)4527     virtual lverror_t Write( const void * buf, lvsize_t count, lvsize_t * nBytesWritten )
4528     {
4529 #if TRACE_BLOCK_WRITE_STREAM
4530         CRLog::trace("stream::Write(%x, %x)", (int)_pos, (int)count);
4531         dumpBlocks("before write");
4532 #endif
4533         // slice by block bounds
4534         lvsize_t bytesRead = 0;
4535         lverror_t res = LVERR_OK;
4536         //if ( _pos + count > _size )
4537         //    count = _size - _pos;
4538         while ( count>0 && res==LVERR_OK ) {
4539             lvpos_t blockSpaceLeft = _blockSize - (_pos % _blockSize);
4540             if ( blockSpaceLeft > count )
4541                 blockSpaceLeft = count;
4542             lvsize_t blockBytesWritten = 0;
4543 
4544             // write to Write buffers
4545             res = writeToCache(buf, _pos, blockSpaceLeft);
4546             if ( res!=LVERR_OK )
4547                 break;
4548 
4549             blockBytesWritten = blockSpaceLeft;
4550 
4551             count -= blockBytesWritten;
4552             buf = ((char*)buf) + blockBytesWritten;
4553             _pos += blockBytesWritten;
4554             bytesRead += blockBytesWritten;
4555             if ( _pos>_size )
4556                 _size = _pos;
4557             if ( !blockBytesWritten )
4558                 break;
4559         }
4560         if ( nBytesWritten && res==LVERR_OK )
4561             *nBytesWritten = bytesRead;
4562 #if TRACE_BLOCK_WRITE_STREAM
4563         dumpBlocks("after write");
4564 #endif
4565         return res;
4566     }
Eof()4567     virtual bool Eof()
4568     {
4569         return _pos >= _size;
4570     }
4571 };
4572 
LVCreateBlockWriteStream(LVStreamRef baseStream,int blockSize,int blockCount)4573 LVStreamRef LVCreateBlockWriteStream( LVStreamRef baseStream, int blockSize, int blockCount )
4574 {
4575     if ( baseStream.isNull() || baseStream->GetMode()==LVOM_READ )
4576         return baseStream;
4577     return LVStreamRef( new LVBlockWriteStream(baseStream, blockSize, blockCount) );
4578 }
4579 
4580 
4581 
4582 
4583