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