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