[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / filesystem / iso9660.cpp
1 /*
2  *      Copyright (C) 2003 by The Joker / Avalaunch team
3  *      Copyright (C) 2003-2013 Team XBMC
4  *      http://xbmc.org
5  *
6  *  This Program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2, or (at your option)
9  *  any later version.
10  *
11  *  This Program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with XBMC; see the file COPYING.  If not, see
18  *  <http://www.gnu.org/licenses/>.
19  *
20  */
21
22 #include "system.h"
23 #include "utils/log.h"
24
25 /*
26  Redbook   : CDDA
27  Yellowbook : CDROM
28 ISO9660
29  CD-ROM Mode 1 divides the 2352 byte data area into:
30   -12  bytes of synchronisation
31   -4  bytes of header information
32   -2048 bytes of user information
33   -288 bytes of error correction and detection codes.
34
35  CD-ROM Mode 2 redefines the use of the 2352 byte data area as follows:
36   -12 bytes of synchronisation
37   -4 bytes of header information
38   -2336 bytes of user data.
39
40  ISO9660 specs:
41  http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
42
43
44 */
45 #include "iso9660.h"
46 #include "storage/IoSupport.h"
47 #include "utils/CharsetConverter.h"
48 #include "threads/SingleLock.h"
49 #include "IFile.h"
50
51 #ifndef TARGET_WINDOWS
52 #include "storage/DetectDVDType.h"  // for MODE2_DATA_SIZE etc.
53 #endif
54 #include <cdio/bytesex.h>
55 //#define _DEBUG_OUTPUT 1
56
57 static CCriticalSection m_critSection;
58 class iso9660 m_isoReader;
59 #define BUFFER_SIZE MODE2_DATA_SIZE
60 #define RET_ERR -1
61
62 using namespace std;
63
64 #ifndef HAS_DVD_DRIVE
65 extern "C"
66 {
67   void cdio_warn(const char* msg, ...) { CLog::Log(LOGWARNING, "%s", msg); }
68 }
69 #endif
70
71 //******************************************************************************************************************
72 const string iso9660::ParseName(struct iso9660_Directory& isodir)
73 {
74   string temp_text = (char*)isodir.FileName;
75   temp_text.resize(isodir.Len_Fi);
76   int iPos = isodir.Len_Fi;
77
78   if (isodir.FileName[iPos] == 0)
79   {
80     iPos++;
81   }
82
83   if (isodir.FileName[iPos] == 'R' && isodir.FileName[iPos + 1] == 'R')
84   {
85     // rockridge
86     iPos += 5;
87     do
88     {
89       if (isodir.FileName[iPos] == 'N' && isodir.FileName[iPos + 1] == 'M')
90       {
91         // altername name
92         // "N" "M"  LEN_NM  1  FLAGS  NAMECONTENT
93         // BP1 BP2    BP3      BP4  BP5     BP6-LEN_NM
94         int iNameLen = isodir.FileName[iPos + 2] - 5;
95         temp_text = (char*) & isodir.FileName[iPos + 5];
96         temp_text.resize(iNameLen);
97         iPos += (iNameLen + 5);
98       }
99       if ( isascii(isodir.FileName[iPos]) && isascii(isodir.FileName[iPos + 1]))
100       {
101         // ??
102         // "?" "?"  LEN
103         // BP1 BP2  BP3
104         iPos += isodir.FileName[iPos + 2];
105       }
106     }
107     while (33 + iPos < isodir.ucRecordLength && isodir.FileName[iPos + 2] != 0);
108     // when this isodir.FileName[iPos+2] is equal to 0 it should break out
109     // as it has finished the loop
110     // this is the fix for rockridge support
111   }
112   return temp_text;
113 }
114
115 bool iso9660::IsRockRidge(struct iso9660_Directory& isodir)
116 {
117   int iPos = isodir.Len_Fi;
118
119   if (isodir.FileName[iPos] == 0)
120   {
121     iPos++;
122   }
123
124   // found rock ridge in system use field
125   if (isodir.FileName[iPos] == 'R' && isodir.FileName[iPos + 1] == 'R')
126     return true;
127
128   return false;
129 }
130
131 //******************************************************************************************************************
132 struct iso_dirtree *iso9660::ReadRecursiveDirFromSector( DWORD sector, const char *path )
133 {
134   struct iso_dirtree* pDir = NULL;
135   struct iso_dirtree* pFile_Pointer = NULL;
136   char* pCurr_dir_cache = NULL;
137   DWORD iso9660searchpointer;
138   struct iso9660_Directory isodir;
139   struct iso9660_Directory curr_dir;
140   WORD wSectorSize = from_723(m_info.iso.logical_block_size);
141
142
143   struct iso_directories *point = m_lastpath;
144   if (point)
145   {
146     while ( point->next )
147     {
148       if (strcmp(path, point->path) == 0) return NULL;
149       point = point->next;
150     }
151   }
152
153
154 #ifdef _DEBUG_OUTPUT
155   CStdString strTmp;
156   strTmp = StringUtils::Format("******************   Adding dir : %s\r", path);
157   OutputDebugString( strTmp.c_str() );
158 #endif
159
160   pDir = (struct iso_dirtree *)malloc(sizeof(struct iso_dirtree));
161   if (!pDir)
162   {
163     OutputDebugString("out of memory");
164
165     return NULL;
166   }
167   pDir->next = NULL;
168   pDir->path = NULL;
169   pDir->name = NULL;
170   pDir->dirpointer = NULL;
171   pFile_Pointer = pDir;
172   m_vecDirsAndFiles.push_back(pDir);
173
174
175   ::SetFilePointer( m_info.ISO_HANDLE, wSectorSize * sector, 0, FILE_BEGIN );
176   DWORD lpNumberOfBytesRead = 0;
177
178   pCurr_dir_cache = (char*)malloc( 16*wSectorSize );
179   if (!pCurr_dir_cache )
180   {
181     OutputDebugString("out of memory\n");
182     return NULL;
183   }
184
185   BOOL bResult = ::ReadFile( m_info.ISO_HANDLE, pCurr_dir_cache, wSectorSize, &lpNumberOfBytesRead, NULL );
186   if (!bResult || lpNumberOfBytesRead != wSectorSize)
187   {
188     OutputDebugString("unable to read\n");
189     free(pCurr_dir_cache);
190     return NULL;
191   }
192   memcpy( &isodir, pCurr_dir_cache, sizeof(isodir) );
193   memcpy( &curr_dir, pCurr_dir_cache, sizeof(isodir) );
194
195   DWORD curr_dirSize = from_733(curr_dir.size);
196   if ( curr_dirSize > wSectorSize )
197   {
198     free( pCurr_dir_cache );
199     pCurr_dir_cache = (char*)malloc( 16 * from_733(isodir.size) );
200     if (!pCurr_dir_cache )
201     {
202       OutputDebugString("out of memory\n");
203       return NULL;
204     }
205     ::SetFilePointer( m_info.ISO_HANDLE, wSectorSize * sector, 0, FILE_BEGIN );
206     bResult = ::ReadFile( m_info.ISO_HANDLE, pCurr_dir_cache , curr_dirSize, &lpNumberOfBytesRead, NULL );
207     if (!bResult || lpNumberOfBytesRead != curr_dirSize)
208     {
209       OutputDebugString("unable to read\n");
210       free(pCurr_dir_cache);
211       return NULL;
212     }
213   }
214   iso9660searchpointer = 0;
215
216   if (!m_lastpath)
217   {
218     m_lastpath = m_paths;
219     if ( !m_lastpath )
220     {
221       m_paths = (struct iso_directories *)malloc(sizeof(struct iso_directories));
222       if (!m_paths )
223       {
224         OutputDebugString("out of memory\n");
225         return NULL;
226       }
227       m_paths->path = NULL;
228       m_paths->dir = NULL;
229       m_paths->next = NULL;
230       m_lastpath = m_paths;
231     }
232     else
233     {
234       while ( m_lastpath->next )
235         m_lastpath = m_lastpath->next;
236     }
237   }
238   m_lastpath->next = ( struct iso_directories *)malloc( sizeof( struct iso_directories ) );
239   if (!m_lastpath->next )
240   {
241     OutputDebugString("out of memory\n");
242     return NULL;
243   }
244
245   m_lastpath = m_lastpath->next;
246   m_lastpath->next = NULL;
247   m_lastpath->dir = pDir;
248   m_lastpath->path = (char *)malloc(strlen(path) + 1);
249   if (!m_lastpath->path )
250   {
251     OutputDebugString("out of memory\n");
252     return NULL;
253   }
254   strcpy( m_lastpath->path, path );
255
256   while ( 1 )
257   {
258     if ( isodir.ucRecordLength )
259       iso9660searchpointer += isodir.ucRecordLength;
260     else
261     {
262       iso9660searchpointer = (iso9660searchpointer - (iso9660searchpointer % wSectorSize)) + wSectorSize;
263     }
264     if ( curr_dirSize <= iso9660searchpointer )
265     {
266       break;
267     }
268     int isize = min(sizeof(isodir), sizeof(m_info.isodir));
269     memcpy( &isodir, pCurr_dir_cache + iso9660searchpointer, isize);
270     if (!isodir.ucRecordLength)
271       continue;
272     if ( !(isodir.byFlags & Flag_NotExist) )
273     {
274       if ( (!( isodir.byFlags & Flag_Directory )) && ( isodir.Len_Fi > 1) )
275       {
276         string temp_text ;
277         bool bContinue = false;
278
279         if ( m_info.joliet )
280         {
281           bContinue = true;
282           isodir.FileName[isodir.Len_Fi] = isodir.FileName[isodir.Len_Fi + 1] = 0; //put terminator by its length
283           temp_text = GetThinText(isodir.FileName, isodir.Len_Fi );
284           //     temp_text.resize(isodir.Len_Fi);
285         }
286
287         if (!m_info.joliet && isodir.FileName[0] >= 0x20 )
288         {
289           temp_text = ParseName(isodir);
290           bContinue = true;
291         }
292         if (bContinue)
293         {
294           int semipos = temp_text.find(";", 0);
295           if (semipos >= 0)
296             temp_text.erase(semipos, temp_text.length() - semipos);
297
298
299           pFile_Pointer->next = (struct iso_dirtree *)malloc(sizeof(struct iso_dirtree));
300           if (!pFile_Pointer->next)
301           {
302             OutputDebugString("out of memory\n");
303             break;
304           }
305           m_vecDirsAndFiles.push_back(pFile_Pointer->next);
306           pFile_Pointer = pFile_Pointer->next;
307           pFile_Pointer->next = 0;
308           pFile_Pointer->dirpointer = NULL;
309           pFile_Pointer->path = (char *)malloc(strlen(path) + 1);
310           if (!pFile_Pointer->path)
311           {
312             OutputDebugString("out of memory");
313             return NULL;
314           }
315           strcpy( pFile_Pointer->path, path );
316           pFile_Pointer->name = (char *)malloc( temp_text.length() + 1);
317           if (!pFile_Pointer->name)
318           {
319             OutputDebugString("out of memory");
320             return NULL;
321           }
322
323           strcpy( pFile_Pointer->name , temp_text.c_str());
324 #ifdef _DEBUG_OUTPUT
325           //CStdString strTmp;
326           //strTmp = StringUtils::Format("adding sector : %X, File : %s     size = %u     pos = %x\r",sector,temp_text.c_str(), isodir.dwFileLengthLE, isodir.dwFileLocationLE );
327           //OutputDebugString( strTmp.c_str());
328 #endif
329
330           pFile_Pointer->Location = from_733(isodir.extent);
331           pFile_Pointer->dirpointer = NULL;
332           pFile_Pointer ->Length = from_733(isodir.size);
333
334           IsoDateTimeToFileTime(&isodir.DateTime, &pFile_Pointer->filetime);
335
336           pFile_Pointer->type = 1;
337         }
338       }
339     }
340   }
341   iso9660searchpointer = 0;
342   memcpy( &curr_dir, pCurr_dir_cache, sizeof(isodir) );
343   memcpy( &isodir, pCurr_dir_cache, sizeof(isodir) );
344   while ( 1 )
345   {
346     if ( isodir.ucRecordLength )
347       iso9660searchpointer += isodir.ucRecordLength;
348
349
350     else
351     {
352       iso9660searchpointer = (iso9660searchpointer - (iso9660searchpointer % wSectorSize)) + wSectorSize;
353     }
354     if ( from_733(curr_dir.size) <= iso9660searchpointer )
355     {
356       free( pCurr_dir_cache );
357       pCurr_dir_cache = NULL;
358       return pDir;
359     }
360     memcpy( &isodir, pCurr_dir_cache + iso9660searchpointer, min(sizeof(isodir), sizeof(m_info.isodir)));
361     if (!isodir.ucRecordLength)
362       continue;
363     if ( !(isodir.byFlags & Flag_NotExist) )
364     {
365       if ( (( isodir.byFlags & Flag_Directory )) && ( isodir.Len_Fi > 1) )
366       {
367         string temp_text ;
368         bool bContinue = false;
369         if ( m_info.joliet )
370         {
371           bContinue = true;
372           isodir.FileName[isodir.Len_Fi] = isodir.FileName[isodir.Len_Fi + 1] = 0; //put terminator by its length
373           temp_text = GetThinText(isodir.FileName, isodir.Len_Fi);
374           //     temp_text.resize(isodir.Len_Fi);
375         }
376         if (!m_info.joliet && isodir.FileName[0] >= 0x20 )
377         {
378           temp_text = ParseName(isodir);
379           bContinue = true;
380         }
381         if (bContinue)
382         {
383
384           //     int semipos = temp_text.find(";",0); //the directory is not seperate by ";",but by its length
385           //     if (semipos >= 0)
386           //       temp_text.erase(semipos,temp_text.length()-semipos);
387
388           pFile_Pointer->next = (struct iso_dirtree *)malloc(sizeof(struct iso_dirtree));
389           if (!pFile_Pointer->next)
390           {
391             OutputDebugString("out of memory");
392             return NULL;
393           }
394           m_vecDirsAndFiles.push_back(pFile_Pointer->next);
395           pFile_Pointer = pFile_Pointer->next;
396           pFile_Pointer->next = 0;
397           pFile_Pointer->dirpointer = NULL;
398           pFile_Pointer->path = (char *)malloc(strlen(path) + 1);
399
400           if (!pFile_Pointer->path)
401           {
402             OutputDebugString("out of memory");
403             return NULL;
404           }
405
406           strcpy( pFile_Pointer->path, path );
407           pFile_Pointer->name = (char *)malloc( temp_text.length() + 1);
408
409           if (!pFile_Pointer->name)
410           {
411             OutputDebugString("out of memory");
412             return NULL;
413           }
414
415           strcpy( pFile_Pointer->name , temp_text.c_str());
416
417           DWORD dwFileLocation = from_733(isodir.extent);
418 #ifdef _DEBUG_OUTPUT
419           CStdString strTmp;
420           strTmp = StringUtils::Format("adding directory sector : %X, File : %s     size = %u     pos = %x\r", sector, temp_text.c_str(), from_733(isodir.size), dwFileLocation );
421           OutputDebugString( strTmp.c_str());
422 #endif
423
424           pFile_Pointer->Location = dwFileLocation;
425           pFile_Pointer->dirpointer = NULL;
426           pFile_Pointer->Length = from_733(isodir.size);
427
428           IsoDateTimeToFileTime(&isodir.DateTime, &pFile_Pointer->filetime);
429
430           string strPath = path;
431           if ( strlen( path ) > 1 ) strPath += "\\";
432           strPath += temp_text;
433
434           pFile_Pointer->dirpointer = ReadRecursiveDirFromSector( dwFileLocation, strPath.c_str() );
435
436           pFile_Pointer->type = 2;
437         }
438       }
439     }
440   }
441   return NULL;
442 }
443 //******************************************************************************************************************
444 iso9660::iso9660( )
445 {
446   memset(m_isoFiles, 0, sizeof(m_isoFiles));
447   m_hCDROM = NULL;
448   m_lastpath = NULL;
449   m_searchpointer = NULL;
450   Reset();
451 }
452
453 void iso9660::Scan()
454 {
455   if (m_hCDROM != NULL)
456     return ;
457
458   m_hCDROM = CIoSupport::OpenCDROM();
459   CIoSupport::AllocReadBuffer();
460
461   m_paths = 0;
462   m_lastpath = 0;
463   memset(&m_info, 0, sizeof(m_info));
464   m_info.ISO_HANDLE = m_hCDROM ;
465   m_info.Curr_dir_cache = 0;
466   m_info.Curr_dir = (char*)malloc( 4096 );
467   strcpy( m_info.Curr_dir, "\\" );
468
469   CSingleLock lock(m_critSection);
470
471   DWORD lpNumberOfBytesRead = 0;
472   ::SetFilePointer( m_info.ISO_HANDLE, 0x8000, 0, FILE_BEGIN );
473
474   ::ReadFile( m_info.ISO_HANDLE, &m_info.iso, sizeof(m_info.iso), &lpNumberOfBytesRead, NULL );
475
476   if (strncmp(m_info.iso.szSignature, "CD001", 5))
477   {
478     CIoSupport::CloseCDROM( m_info.ISO_HANDLE);
479     CIoSupport::FreeReadBuffer();
480     m_info.ISO_HANDLE = NULL;
481     m_hCDROM = NULL;
482     m_info.iso9660 = 0;
483     return ;
484   }
485   else
486   {
487     m_info.iso9660 = 1;
488     m_info.joliet = 0;
489
490     m_info.HeaderPos = 0x8000;
491     int current = 0x8000;
492
493     WORD wSectorSize = from_723(m_info.iso.logical_block_size);
494
495     // first check if first file in the current VD has a rock-ridge NM. if it has, disable joliet
496     ::SetFilePointer( m_info.ISO_HANDLE, wSectorSize * from_733(((iso9660_Directory*)(&m_info.iso.szRootDir))->extent), 0, FILE_BEGIN );
497
498     DWORD lpNumberOfBytesRead;
499     char* pCurr_dir_cache = (char*)malloc( 16*wSectorSize );
500     iso9660_Directory isodir;
501     BOOL bResult = ::ReadFile( m_info.ISO_HANDLE, pCurr_dir_cache, wSectorSize, &lpNumberOfBytesRead, NULL );
502     memcpy( &isodir, pCurr_dir_cache, sizeof(isodir));
503
504     int iso9660searchpointer=0;
505     if ( isodir.ucRecordLength )
506       iso9660searchpointer += isodir.ucRecordLength;
507     else
508       iso9660searchpointer = (iso9660searchpointer - (iso9660searchpointer % wSectorSize)) + wSectorSize;
509
510     memcpy( &isodir, pCurr_dir_cache + iso9660searchpointer,min(sizeof(isodir), sizeof(m_info.isodir)));
511     free(pCurr_dir_cache);
512     if (bResult && lpNumberOfBytesRead == wSectorSize)
513       bResult = IsRockRidge(isodir);
514     while ( m_info.iso.byOne != 255)
515     {
516       if ( ( m_info.iso.byZero3[0] == 0x25 ) && ( m_info.iso.byZero3[1] == 0x2f ) && !bResult )
517       {
518         switch ( m_info.iso.byZero3[2] )
519         {
520         case 0x45 :
521         case 0x40 :
522         case 0x43 : m_info.HeaderPos = current;
523           m_info.joliet = 1;
524         }
525         //                        25 2f 45  or   25 2f 40   or 25 2f 43 = jouliet, and best fitted for reading
526       }
527       current += 0x800;
528       ::SetFilePointer( m_info.ISO_HANDLE, current, 0, FILE_BEGIN );
529       ::ReadFile( m_info.ISO_HANDLE, &m_info.iso, sizeof(m_info.iso), &lpNumberOfBytesRead, NULL );
530     }
531     ::SetFilePointer( m_info.ISO_HANDLE, m_info.HeaderPos, 0, FILE_BEGIN );
532     ::ReadFile( m_info.ISO_HANDLE, &m_info.iso, sizeof(m_info.iso), &lpNumberOfBytesRead, NULL );
533     memcpy( &m_info.isodir, m_info.iso.szRootDir, sizeof(m_info.isodir));
534   }
535
536   memcpy( &m_info.isodir, &m_info.iso.szRootDir, sizeof(m_info.isodir) );
537   ReadRecursiveDirFromSector( from_733(m_info.isodir.extent), "\\" );
538 }
539
540 //******************************************************************************************************************
541 iso9660::~iso9660( )
542 {
543   Reset();
544 }
545
546 void iso9660::Reset()
547 {
548
549   if (m_info.Curr_dir)
550     free(m_info.Curr_dir);
551   m_info.Curr_dir = NULL;
552
553   if (m_info.Curr_dir_cache)
554     free(m_info.Curr_dir_cache);
555   m_info.Curr_dir_cache = NULL;
556
557
558
559   struct iso_directories* nextpath;
560
561   while ( m_paths )
562   {
563     nextpath = m_paths->next;
564     if (m_paths->path) free(m_paths->path);
565
566     free (m_paths);
567     m_paths = nextpath;
568   }
569   for (int i = 0; i < (int)m_vecDirsAndFiles.size(); ++i)
570   {
571     struct iso_dirtree* pDir = m_vecDirsAndFiles[i];
572     if (pDir->path) free(pDir->path);
573     if (pDir->name) free(pDir->name);
574     free(pDir);
575   }
576   m_vecDirsAndFiles.erase(m_vecDirsAndFiles.begin(), m_vecDirsAndFiles.end());
577
578   for (intptr_t i = 0; i < MAX_ISO_FILES;++i)
579   {
580     FreeFileContext( (HANDLE)i);
581   }
582
583   if (m_hCDROM)
584   {
585     CIoSupport::CloseCDROM(m_hCDROM);
586     CIoSupport::FreeReadBuffer();
587   }
588   m_hCDROM = NULL;
589 }
590
591 //******************************************************************************************************************
592 struct iso_dirtree *iso9660::FindFolder( char *Folder )
593 {
594   char *work;
595
596   work = (char *)malloc(from_723(m_info.iso.logical_block_size));
597
598   char *temp;
599   struct iso_directories *lastpath = NULL;;
600
601   if ( strpbrk(Folder, ":") )
602     strcpy(work, strpbrk(Folder, ":") + 1);
603   else
604     strcpy(work, Folder);
605
606   temp = work + 1;
607   while ( strlen( temp ) > 1 && strpbrk( temp + 1, "\\" ) )
608     temp = strpbrk( temp + 1, "\\" );
609
610   if ( strlen( work ) > 1 && work[ strlen(work) - 1 ] == '*' )
611   {
612     work[ strlen(work) - 1 ] = 0;
613   }
614   if ( strlen( work ) > 2 )
615     if ( work[ strlen(work) - 1 ] == '\\' )
616       work[ strlen(work) - 1 ] = 0;
617
618   if (m_paths)
619     lastpath = m_paths->next;
620   while ( lastpath )
621   {
622     if ( !stricmp( lastpath->path, work))
623     {
624       free ( work );
625       return lastpath->dir;
626     }
627     lastpath = lastpath->next;
628   }
629   free ( work );
630   return 0;
631 }
632
633 //******************************************************************************************************************
634 HANDLE iso9660::FindFirstFile( char *szLocalFolder, WIN32_FIND_DATA *wfdFile )
635 {
636   if (m_info.ISO_HANDLE == 0) return (HANDLE)0;
637   memset( wfdFile, 0, sizeof(WIN32_FIND_DATA));
638
639   m_searchpointer = FindFolder( szLocalFolder );
640
641   if ( m_searchpointer )
642   {
643     m_searchpointer = m_searchpointer->next;
644
645     if ( m_searchpointer )
646     {
647       strcpy(wfdFile->cFileName, m_searchpointer->name );
648
649       if ( m_searchpointer->type == 2 )
650         wfdFile->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
651
652       wfdFile->ftLastWriteTime = m_searchpointer->filetime;
653       wfdFile->ftLastAccessTime = m_searchpointer->filetime;
654       wfdFile->ftCreationTime = m_searchpointer->filetime;
655
656       wfdFile->nFileSizeLow = m_searchpointer->Length;
657       return (HANDLE)1;
658     }
659   }
660   return (HANDLE)0;
661 }
662
663 //******************************************************************************************************************
664 int iso9660::FindNextFile( HANDLE szLocalFolder, WIN32_FIND_DATA *wfdFile )
665 {
666   memset( wfdFile, 0, sizeof(WIN32_FIND_DATA));
667
668   if ( m_searchpointer )
669     m_searchpointer = m_searchpointer->next;
670
671   if ( m_searchpointer )
672   {
673     strcpy(wfdFile->cFileName, m_searchpointer->name );
674
675     if ( m_searchpointer->type == 2 )
676       wfdFile->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
677
678     wfdFile->ftLastWriteTime = m_searchpointer->filetime;
679     wfdFile->ftLastAccessTime = m_searchpointer->filetime;
680     wfdFile->ftCreationTime = m_searchpointer->filetime;
681
682     wfdFile->nFileSizeLow = m_searchpointer->Length;
683     return 1;
684   }
685
686   return 0;
687 }
688
689 //******************************************************************************************************************
690 bool iso9660::FindClose( HANDLE szLocalFolder )
691 {
692   m_searchpointer = 0;
693   if (m_info.Curr_dir_cache) free(m_info.Curr_dir_cache);
694   m_info.Curr_dir_cache = 0;
695   return true;
696 }
697
698
699
700 //******************************************************************************************************************
701 string iso9660::GetThinText(BYTE* strTxt, int iLen )
702 {
703   // convert from "fat" text (UTF-16BE) to "thin" text (UTF-8)
704   std::u16string strTxtUnicode((char16_t*)strTxt, iLen / 2);
705   CStdString utf8String;
706
707   g_charsetConverter.utf16BEtoUTF8(strTxtUnicode, utf8String);
708
709   return utf8String;
710 }
711
712 //************************************************************************************
713 HANDLE iso9660::OpenFile(const char *filename)
714 {
715   if (m_info.ISO_HANDLE == NULL) return INVALID_HANDLE_VALUE;
716   HANDLE hContext = AllocFileContext();
717   if (hContext == INVALID_HANDLE_VALUE) return hContext;
718
719   iso9660::isofile* pContext = GetFileContext(hContext);
720   if (!pContext)
721     return INVALID_HANDLE_VALUE;
722
723   WIN32_FIND_DATA fileinfo;
724   char *pointer, *pointer2;
725   char work[512];
726   pContext->m_bUseMode2 = false;
727   m_info.curr_filepos = 0;
728
729   pointer = (char*)filename;
730   while ( strpbrk( pointer, "\\/" ) )
731     pointer = strpbrk( pointer, "\\/" ) + 1;
732
733   strcpy(work, filename );
734   pointer2 = work;
735
736   while ( strpbrk(pointer2 + 1, "\\" ) )
737     pointer2 = strpbrk(pointer2 + 1, "\\" );
738
739   *(pointer2 + 1) = 0;
740
741   intptr_t loop = (intptr_t)FindFirstFile( work, &fileinfo );
742   while ( loop > 0)
743   {
744     if ( !stricmp(fileinfo.cFileName, pointer ) )
745       loop = -1;
746     else
747       loop = FindNextFile( NULL, &fileinfo );
748   }
749   if ( loop == 0 )
750   {
751     FreeFileContext(hContext);
752     return INVALID_HANDLE_VALUE;
753   }
754
755   pContext->m_dwCurrentBlock = m_searchpointer->Location;
756   pContext->m_dwFileSize = m_info.curr_filesize = fileinfo.nFileSizeLow;
757   pContext->m_pBuffer = new uint8_t[CIRC_BUFFER_SIZE * BUFFER_SIZE];
758   pContext->m_dwStartBlock = pContext->m_dwCurrentBlock;
759   pContext->m_dwFilePos = 0;
760   pContext->m_dwCircBuffBegin = 0;
761   pContext->m_dwCircBuffEnd = 0;
762   pContext->m_dwCircBuffSectorStart = 0;
763   pContext->m_bUseMode2 = false;
764
765   bool bError;
766
767   CSingleLock lock(m_critSection);
768   bError = (CIoSupport::ReadSector(m_info.ISO_HANDLE, pContext->m_dwStartBlock, (char*) & (pContext->m_pBuffer[0])) < 0);
769   if ( bError )
770   {
771     bError = (CIoSupport::ReadSectorMode2(m_info.ISO_HANDLE, pContext->m_dwStartBlock, (char*) & (pContext->m_pBuffer[0])) < 0);
772     if ( !bError )
773       pContext->m_bUseMode2 = true;
774   }
775   if (pContext->m_bUseMode2)
776     pContext->m_dwFileSize = (pContext->m_dwFileSize / 2048) * MODE2_DATA_SIZE;
777
778   return hContext;
779 }
780
781 //************************************************************************************
782 void iso9660::CloseFile(HANDLE hFile)
783 {
784   iso9660::isofile* pContext = GetFileContext(hFile);
785   if (pContext)
786   {
787     if (pContext->m_pBuffer)
788     {
789       delete [] pContext->m_pBuffer;
790       pContext->m_pBuffer = NULL;
791     }
792   }
793   FreeFileContext(hFile);
794 }
795 //************************************************************************************
796 bool iso9660::ReadSectorFromCache(iso9660::isofile* pContext, DWORD sector, uint8_t** ppBuffer)
797 {
798
799   DWORD StartSectorInCircBuff = pContext->m_dwCircBuffSectorStart;
800   DWORD SectorsInCircBuff;
801
802   if ( pContext->m_dwCircBuffEnd >= pContext->m_dwCircBuffBegin )
803     SectorsInCircBuff = pContext->m_dwCircBuffEnd - pContext->m_dwCircBuffBegin;
804   else
805     SectorsInCircBuff = CIRC_BUFFER_SIZE - (pContext->m_dwCircBuffBegin - pContext->m_dwCircBuffEnd);
806
807   // If our sector is already in the circular buffer
808   if ( sector >= StartSectorInCircBuff &&
809        sector < (StartSectorInCircBuff + SectorsInCircBuff) &&
810        SectorsInCircBuff > 0 )
811   {
812     // Just retrieve it
813     DWORD SectorInCircBuff = (sector - StartSectorInCircBuff) +
814                              pContext->m_dwCircBuffBegin;
815     if ( SectorInCircBuff >= CIRC_BUFFER_SIZE )
816       SectorInCircBuff -= CIRC_BUFFER_SIZE;
817
818     *ppBuffer = &(pContext->m_pBuffer[SectorInCircBuff]);
819   }
820   else
821   {
822     // Sector is not cache.  Read it in.
823     bool SectorIsAdjacentInBuffer =
824       (StartSectorInCircBuff + SectorsInCircBuff) == sector;
825     if ( SectorsInCircBuff == CIRC_BUFFER_SIZE - 1 ||
826          !SectorIsAdjacentInBuffer)
827     {
828       // The cache is full. (Or its not an adjacent request in which we'll
829       // also flush the cache)
830
831       // If its adjacent, just get rid of the first sector.
832       if ( SectorIsAdjacentInBuffer )
833       {
834         // Release the first sector in cache
835         pContext->m_dwCircBuffBegin++;
836         if ( pContext->m_dwCircBuffBegin >= CIRC_BUFFER_SIZE )
837           pContext->m_dwCircBuffBegin -= CIRC_BUFFER_SIZE;
838         pContext->m_dwCircBuffSectorStart++;
839       }
840       else
841       {
842         pContext->m_dwCircBuffBegin = pContext->m_dwCircBuffEnd = 0;
843         pContext->m_dwCircBuffSectorStart = 0;
844       }
845     }
846     // Ok, we're ready to read the sector into the cache
847     bool bError;
848     {
849       CSingleLock lock(m_critSection);
850       if ( pContext->m_bUseMode2 )
851       {
852         bError = (CIoSupport::ReadSectorMode2(m_info.ISO_HANDLE, sector, (char*) & (pContext->m_pBuffer[pContext->m_dwCircBuffEnd])) < 0);
853       }
854       else
855       {
856         bError = (CIoSupport::ReadSector(m_info.ISO_HANDLE, sector, (char*) & (pContext->m_pBuffer[pContext->m_dwCircBuffEnd])) < 0);
857       }
858     }
859     if ( bError )
860       return false;
861     *ppBuffer = &(pContext->m_pBuffer[pContext->m_dwCircBuffEnd]);
862     if ( pContext->m_dwCircBuffEnd == pContext->m_dwCircBuffBegin )
863       pContext->m_dwCircBuffSectorStart = sector;
864     pContext->m_dwCircBuffEnd++;
865     if ( pContext->m_dwCircBuffEnd >= CIRC_BUFFER_SIZE )
866       pContext->m_dwCircBuffEnd -= CIRC_BUFFER_SIZE;
867   }
868   return true;
869 }
870 //************************************************************************************
871 void iso9660::ReleaseSectorFromCache(iso9660::isofile* pContext, DWORD sector)
872 {
873
874   DWORD StartSectorInCircBuff = pContext->m_dwCircBuffSectorStart;
875   DWORD SectorsInCircBuff;
876
877   if ( pContext->m_dwCircBuffEnd >= pContext->m_dwCircBuffBegin )
878     SectorsInCircBuff = pContext->m_dwCircBuffEnd - pContext->m_dwCircBuffBegin;
879   else
880     SectorsInCircBuff = CIRC_BUFFER_SIZE - (pContext->m_dwCircBuffBegin - pContext->m_dwCircBuffEnd);
881
882   // If our sector is in the circular buffer
883   if ( sector >= StartSectorInCircBuff &&
884        sector < (StartSectorInCircBuff + SectorsInCircBuff) &&
885        SectorsInCircBuff > 0 )
886   {
887     DWORD SectorsToFlush = sector - StartSectorInCircBuff + 1;
888     pContext->m_dwCircBuffBegin += SectorsToFlush;
889
890     pContext->m_dwCircBuffSectorStart += SectorsToFlush;
891     if ( pContext->m_dwCircBuffBegin >= CIRC_BUFFER_SIZE )
892       pContext->m_dwCircBuffBegin -= CIRC_BUFFER_SIZE;
893   }
894 }
895 //************************************************************************************
896 long iso9660::ReadFile(HANDLE hFile, uint8_t *pBuffer, long lSize)
897 {
898   bool bError;
899   long iBytesRead = 0;
900   DWORD sectorSize = 2048;
901   iso9660::isofile* pContext = GetFileContext(hFile);
902   if (!pContext) return -1;
903
904   if ( pContext->m_bUseMode2 )
905     sectorSize = MODE2_DATA_SIZE;
906
907   while (lSize > 0 && pContext->m_dwFilePos < pContext->m_dwFileSize)
908   {
909     pContext->m_dwCurrentBlock = (DWORD) (pContext->m_dwFilePos / sectorSize);
910     int64_t iOffsetInBuffer = pContext->m_dwFilePos - (sectorSize * pContext->m_dwCurrentBlock);
911     pContext->m_dwCurrentBlock += pContext->m_dwStartBlock;
912
913     // CLog::Log(LOGDEBUG, "pos:%li cblk:%li sblk:%li off:%li",(long)pContext->m_dwFilePos, (long)pContext->m_dwCurrentBlock,(long)pContext->m_dwStartBlock,(long)iOffsetInBuffer);
914
915     uint8_t* pSector;
916     bError = !ReadSectorFromCache(pContext, pContext->m_dwCurrentBlock, &pSector);
917     if (!bError)
918     {
919       DWORD iBytes2Copy = lSize;
920       if (iBytes2Copy > (sectorSize - iOffsetInBuffer) )
921         iBytes2Copy = (DWORD) (sectorSize - iOffsetInBuffer);
922
923
924       memcpy( &pBuffer[iBytesRead], &pSector[iOffsetInBuffer], iBytes2Copy);
925       iBytesRead += iBytes2Copy;
926       lSize -= iBytes2Copy;
927       pContext->m_dwFilePos += iBytes2Copy;
928
929       if ( iBytes2Copy + iOffsetInBuffer == sectorSize )
930         ReleaseSectorFromCache(pContext, pContext->m_dwCurrentBlock);
931
932       // Why is this done?  It is recalculated at the beginning of the loop
933       pContext->m_dwCurrentBlock += BUFFER_SIZE / MODE2_DATA_SIZE;
934
935     }
936     else
937     {
938       CLog::Log(LOGDEBUG, "iso9660::ReadFile() hit EOF");
939       break;
940     }
941   }
942   if (iBytesRead == 0) return -1;
943   return iBytesRead;
944 }
945 //************************************************************************************
946 int64_t iso9660::Seek(HANDLE hFile, int64_t lOffset, int whence)
947 {
948   iso9660::isofile* pContext = GetFileContext(hFile);
949   if (!pContext) return -1;
950
951   int64_t dwFilePos = pContext->m_dwFilePos;
952   switch (whence)
953   {
954   case SEEK_SET:
955     // cur = pos
956     pContext->m_dwFilePos = lOffset;
957     break;
958
959   case SEEK_CUR:
960     // cur += pos
961     pContext->m_dwFilePos += lOffset;
962     break;
963   case SEEK_END:
964     // end += pos
965     pContext->m_dwFilePos = pContext->m_dwFileSize + lOffset;
966     break;
967   default:
968     return -1;
969   }
970
971   if (pContext->m_dwFilePos > pContext->m_dwFileSize || pContext->m_dwFilePos < 0)
972   {
973     pContext->m_dwFilePos = dwFilePos;
974     return pContext->m_dwFilePos;
975   }
976
977
978   return pContext->m_dwFilePos;
979 }
980
981
982 //************************************************************************************
983 int64_t iso9660::GetFileSize(HANDLE hFile)
984 {
985   iso9660::isofile* pContext = GetFileContext(hFile);
986   if (!pContext) return -1;
987   return pContext->m_dwFileSize;
988 }
989
990 //************************************************************************************
991 int64_t iso9660::GetFilePosition(HANDLE hFile)
992 {
993   iso9660::isofile* pContext = GetFileContext(hFile);
994   if (!pContext) return -1;
995   return pContext->m_dwFilePos;
996 }
997
998 //************************************************************************************
999 void iso9660::FreeFileContext(HANDLE hFile)
1000 {
1001   intptr_t iFile = (intptr_t)hFile;
1002   if (iFile >= 1 && iFile < MAX_ISO_FILES)
1003   {
1004     if (m_isoFiles[iFile ]) delete m_isoFiles[iFile ];
1005     m_isoFiles[iFile ] = NULL;
1006   }
1007 }
1008
1009 //************************************************************************************
1010 HANDLE iso9660::AllocFileContext()
1011 {
1012   for (intptr_t i = 1; i < MAX_ISO_FILES; ++i)
1013   {
1014     if (m_isoFiles[i] == NULL)
1015     {
1016       m_isoFiles[i] = new isofile;
1017       return (HANDLE)i;
1018     }
1019   }
1020   return INVALID_HANDLE_VALUE;
1021 }
1022
1023 //************************************************************************************
1024 iso9660::isofile* iso9660::GetFileContext(HANDLE hFile)
1025 {
1026   intptr_t iFile = (intptr_t)hFile;
1027   if (iFile >= 1 && iFile < MAX_ISO_FILES)
1028   {
1029     return m_isoFiles[iFile];
1030   }
1031   return NULL;
1032 }
1033
1034 //************************************************************************************
1035 bool iso9660::IsScanned()
1036 {
1037   return (m_hCDROM != NULL);
1038 }
1039
1040 //************************************************************************************
1041 void iso9660::IsoDateTimeToFileTime(iso9660_Datetime* isoDateTime, FILETIME* filetime)
1042 {
1043   tm t;
1044   ZeroMemory(&t, sizeof(tm));
1045   t.tm_year=isoDateTime->year;
1046   t.tm_mon=isoDateTime->month-1;
1047   t.tm_mday=isoDateTime->day;
1048   t.tm_hour=isoDateTime->hour;
1049   t.tm_min=isoDateTime->minute;
1050   t.tm_sec=isoDateTime->second + (isoDateTime->gmtoff * (15 * 60));
1051   t.tm_isdst=-1;
1052   mktime(&t);
1053
1054   SYSTEMTIME time;
1055   time.wYear=t.tm_year+1900;
1056   time.wMonth=t.tm_mon+1;
1057   time.wDayOfWeek=t.tm_wday;
1058   time.wDay=t.tm_mday;
1059   time.wHour=t.tm_hour;
1060   time.wMinute=t.tm_min;
1061   time.wSecond=t.tm_sec;
1062   time.wMilliseconds=0;
1063   SystemTimeToFileTime(&time, filetime);
1064 }