2 * Copyright (C) 2003 by The Joker / Avalaunch team
3 * Copyright (C) 2003-2013 Team XBMC
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)
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.
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/>.
23 #include "utils/log.h"
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.
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.
41 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
46 #include "storage/IoSupport.h"
47 #include "utils/CharsetConverter.h"
48 #include "threads/SingleLock.h"
51 #ifndef TARGET_WINDOWS
52 #include "storage/DetectDVDType.h" // for MODE2_DATA_SIZE etc.
54 #include <cdio/bytesex.h>
55 //#define _DEBUG_OUTPUT 1
57 static CCriticalSection m_critSection;
58 class iso9660 m_isoReader;
59 #define BUFFER_SIZE MODE2_DATA_SIZE
67 void cdio_warn(const char* msg, ...) { CLog::Log(LOGWARNING, "%s", msg); }
71 //******************************************************************************************************************
72 const string iso9660::ParseName(struct iso9660_Directory& isodir)
74 string temp_text = (char*)isodir.FileName;
75 temp_text.resize(isodir.Len_Fi);
76 int iPos = isodir.Len_Fi;
78 if (isodir.FileName[iPos] == 0)
83 if (isodir.FileName[iPos] == 'R' && isodir.FileName[iPos + 1] == 'R')
89 if (isodir.FileName[iPos] == 'N' && isodir.FileName[iPos + 1] == 'M')
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);
99 if ( isascii(isodir.FileName[iPos]) && isascii(isodir.FileName[iPos + 1]))
104 iPos += isodir.FileName[iPos + 2];
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
115 bool iso9660::IsRockRidge(struct iso9660_Directory& isodir)
117 int iPos = isodir.Len_Fi;
119 if (isodir.FileName[iPos] == 0)
124 // found rock ridge in system use field
125 if (isodir.FileName[iPos] == 'R' && isodir.FileName[iPos + 1] == 'R')
131 //******************************************************************************************************************
132 struct iso_dirtree *iso9660::ReadRecursiveDirFromSector( DWORD sector, const char *path )
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);
143 struct iso_directories *point = m_lastpath;
146 while ( point->next )
148 if (strcmp(path, point->path) == 0) return NULL;
156 strTmp = StringUtils::Format("****************** Adding dir : %s\r", path);
157 OutputDebugString( strTmp.c_str() );
160 pDir = (struct iso_dirtree *)malloc(sizeof(struct iso_dirtree));
163 OutputDebugString("out of memory");
170 pDir->dirpointer = NULL;
171 pFile_Pointer = pDir;
172 m_vecDirsAndFiles.push_back(pDir);
175 ::SetFilePointer( m_info.ISO_HANDLE, wSectorSize * sector, 0, FILE_BEGIN );
176 DWORD lpNumberOfBytesRead = 0;
178 pCurr_dir_cache = (char*)malloc( 16*wSectorSize );
179 if (!pCurr_dir_cache )
181 OutputDebugString("out of memory\n");
185 BOOL bResult = ::ReadFile( m_info.ISO_HANDLE, pCurr_dir_cache, wSectorSize, &lpNumberOfBytesRead, NULL );
186 if (!bResult || lpNumberOfBytesRead != wSectorSize)
188 OutputDebugString("unable to read\n");
189 free(pCurr_dir_cache);
192 memcpy( &isodir, pCurr_dir_cache, sizeof(isodir) );
193 memcpy( &curr_dir, pCurr_dir_cache, sizeof(isodir) );
195 DWORD curr_dirSize = from_733(curr_dir.size);
196 if ( curr_dirSize > wSectorSize )
198 free( pCurr_dir_cache );
199 pCurr_dir_cache = (char*)malloc( 16 * from_733(isodir.size) );
200 if (!pCurr_dir_cache )
202 OutputDebugString("out of memory\n");
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)
209 OutputDebugString("unable to read\n");
210 free(pCurr_dir_cache);
214 iso9660searchpointer = 0;
218 m_lastpath = m_paths;
221 m_paths = (struct iso_directories *)malloc(sizeof(struct iso_directories));
224 OutputDebugString("out of memory\n");
227 m_paths->path = NULL;
229 m_paths->next = NULL;
230 m_lastpath = m_paths;
234 while ( m_lastpath->next )
235 m_lastpath = m_lastpath->next;
238 m_lastpath->next = ( struct iso_directories *)malloc( sizeof( struct iso_directories ) );
239 if (!m_lastpath->next )
241 OutputDebugString("out of memory\n");
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 )
251 OutputDebugString("out of memory\n");
254 strcpy( m_lastpath->path, path );
258 if ( isodir.ucRecordLength )
259 iso9660searchpointer += isodir.ucRecordLength;
262 iso9660searchpointer = (iso9660searchpointer - (iso9660searchpointer % wSectorSize)) + wSectorSize;
264 if ( curr_dirSize <= iso9660searchpointer )
268 int isize = min(sizeof(isodir), sizeof(m_info.isodir));
269 memcpy( &isodir, pCurr_dir_cache + iso9660searchpointer, isize);
270 if (!isodir.ucRecordLength)
272 if ( !(isodir.byFlags & Flag_NotExist) )
274 if ( (!( isodir.byFlags & Flag_Directory )) && ( isodir.Len_Fi > 1) )
277 bool bContinue = false;
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);
287 if (!m_info.joliet && isodir.FileName[0] >= 0x20 )
289 temp_text = ParseName(isodir);
294 int semipos = temp_text.find(";", 0);
296 temp_text.erase(semipos, temp_text.length() - semipos);
299 pFile_Pointer->next = (struct iso_dirtree *)malloc(sizeof(struct iso_dirtree));
300 if (!pFile_Pointer->next)
302 OutputDebugString("out of memory\n");
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)
312 OutputDebugString("out of memory");
315 strcpy( pFile_Pointer->path, path );
316 pFile_Pointer->name = (char *)malloc( temp_text.length() + 1);
317 if (!pFile_Pointer->name)
319 OutputDebugString("out of memory");
323 strcpy( pFile_Pointer->name , temp_text.c_str());
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());
330 pFile_Pointer->Location = from_733(isodir.extent);
331 pFile_Pointer->dirpointer = NULL;
332 pFile_Pointer ->Length = from_733(isodir.size);
334 IsoDateTimeToFileTime(&isodir.DateTime, &pFile_Pointer->filetime);
336 pFile_Pointer->type = 1;
341 iso9660searchpointer = 0;
342 memcpy( &curr_dir, pCurr_dir_cache, sizeof(isodir) );
343 memcpy( &isodir, pCurr_dir_cache, sizeof(isodir) );
346 if ( isodir.ucRecordLength )
347 iso9660searchpointer += isodir.ucRecordLength;
352 iso9660searchpointer = (iso9660searchpointer - (iso9660searchpointer % wSectorSize)) + wSectorSize;
354 if ( from_733(curr_dir.size) <= iso9660searchpointer )
356 free( pCurr_dir_cache );
357 pCurr_dir_cache = NULL;
360 memcpy( &isodir, pCurr_dir_cache + iso9660searchpointer, min(sizeof(isodir), sizeof(m_info.isodir)));
361 if (!isodir.ucRecordLength)
363 if ( !(isodir.byFlags & Flag_NotExist) )
365 if ( (( isodir.byFlags & Flag_Directory )) && ( isodir.Len_Fi > 1) )
368 bool bContinue = false;
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);
376 if (!m_info.joliet && isodir.FileName[0] >= 0x20 )
378 temp_text = ParseName(isodir);
384 // int semipos = temp_text.find(";",0); //the directory is not seperate by ";",but by its length
386 // temp_text.erase(semipos,temp_text.length()-semipos);
388 pFile_Pointer->next = (struct iso_dirtree *)malloc(sizeof(struct iso_dirtree));
389 if (!pFile_Pointer->next)
391 OutputDebugString("out of memory");
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);
400 if (!pFile_Pointer->path)
402 OutputDebugString("out of memory");
406 strcpy( pFile_Pointer->path, path );
407 pFile_Pointer->name = (char *)malloc( temp_text.length() + 1);
409 if (!pFile_Pointer->name)
411 OutputDebugString("out of memory");
415 strcpy( pFile_Pointer->name , temp_text.c_str());
417 DWORD dwFileLocation = from_733(isodir.extent);
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());
424 pFile_Pointer->Location = dwFileLocation;
425 pFile_Pointer->dirpointer = NULL;
426 pFile_Pointer->Length = from_733(isodir.size);
428 IsoDateTimeToFileTime(&isodir.DateTime, &pFile_Pointer->filetime);
430 string strPath = path;
431 if ( strlen( path ) > 1 ) strPath += "\\";
432 strPath += temp_text;
434 pFile_Pointer->dirpointer = ReadRecursiveDirFromSector( dwFileLocation, strPath.c_str() );
436 pFile_Pointer->type = 2;
443 //******************************************************************************************************************
446 memset(m_isoFiles, 0, sizeof(m_isoFiles));
449 m_searchpointer = NULL;
455 if (m_hCDROM != NULL)
458 m_hCDROM = CIoSupport::OpenCDROM();
459 CIoSupport::AllocReadBuffer();
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, "\\" );
469 CSingleLock lock(m_critSection);
471 DWORD lpNumberOfBytesRead = 0;
472 ::SetFilePointer( m_info.ISO_HANDLE, 0x8000, 0, FILE_BEGIN );
474 ::ReadFile( m_info.ISO_HANDLE, &m_info.iso, sizeof(m_info.iso), &lpNumberOfBytesRead, NULL );
476 if (strncmp(m_info.iso.szSignature, "CD001", 5))
478 CIoSupport::CloseCDROM( m_info.ISO_HANDLE);
479 CIoSupport::FreeReadBuffer();
480 m_info.ISO_HANDLE = NULL;
490 m_info.HeaderPos = 0x8000;
491 int current = 0x8000;
493 WORD wSectorSize = from_723(m_info.iso.logical_block_size);
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 );
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));
504 int iso9660searchpointer=0;
505 if ( isodir.ucRecordLength )
506 iso9660searchpointer += isodir.ucRecordLength;
508 iso9660searchpointer = (iso9660searchpointer - (iso9660searchpointer % wSectorSize)) + wSectorSize;
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)
516 if ( ( m_info.iso.byZero3[0] == 0x25 ) && ( m_info.iso.byZero3[1] == 0x2f ) && !bResult )
518 switch ( m_info.iso.byZero3[2] )
522 case 0x43 : m_info.HeaderPos = current;
525 // 25 2f 45 or 25 2f 40 or 25 2f 43 = jouliet, and best fitted for reading
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 );
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));
536 memcpy( &m_info.isodir, &m_info.iso.szRootDir, sizeof(m_info.isodir) );
537 ReadRecursiveDirFromSector( from_733(m_info.isodir.extent), "\\" );
540 //******************************************************************************************************************
546 void iso9660::Reset()
550 free(m_info.Curr_dir);
551 m_info.Curr_dir = NULL;
553 if (m_info.Curr_dir_cache)
554 free(m_info.Curr_dir_cache);
555 m_info.Curr_dir_cache = NULL;
559 struct iso_directories* nextpath;
563 nextpath = m_paths->next;
564 if (m_paths->path) free(m_paths->path);
569 for (int i = 0; i < (int)m_vecDirsAndFiles.size(); ++i)
571 struct iso_dirtree* pDir = m_vecDirsAndFiles[i];
572 if (pDir->path) free(pDir->path);
573 if (pDir->name) free(pDir->name);
576 m_vecDirsAndFiles.erase(m_vecDirsAndFiles.begin(), m_vecDirsAndFiles.end());
578 for (intptr_t i = 0; i < MAX_ISO_FILES;++i)
580 FreeFileContext( (HANDLE)i);
585 CIoSupport::CloseCDROM(m_hCDROM);
586 CIoSupport::FreeReadBuffer();
591 //******************************************************************************************************************
592 struct iso_dirtree *iso9660::FindFolder( char *Folder )
596 work = (char *)malloc(from_723(m_info.iso.logical_block_size));
599 struct iso_directories *lastpath = NULL;;
601 if ( strpbrk(Folder, ":") )
602 strcpy(work, strpbrk(Folder, ":") + 1);
604 strcpy(work, Folder);
607 while ( strlen( temp ) > 1 && strpbrk( temp + 1, "\\" ) )
608 temp = strpbrk( temp + 1, "\\" );
610 if ( strlen( work ) > 1 && work[ strlen(work) - 1 ] == '*' )
612 work[ strlen(work) - 1 ] = 0;
614 if ( strlen( work ) > 2 )
615 if ( work[ strlen(work) - 1 ] == '\\' )
616 work[ strlen(work) - 1 ] = 0;
619 lastpath = m_paths->next;
622 if ( !stricmp( lastpath->path, work))
625 return lastpath->dir;
627 lastpath = lastpath->next;
633 //******************************************************************************************************************
634 HANDLE iso9660::FindFirstFile( char *szLocalFolder, WIN32_FIND_DATA *wfdFile )
636 if (m_info.ISO_HANDLE == 0) return (HANDLE)0;
637 memset( wfdFile, 0, sizeof(WIN32_FIND_DATA));
639 m_searchpointer = FindFolder( szLocalFolder );
641 if ( m_searchpointer )
643 m_searchpointer = m_searchpointer->next;
645 if ( m_searchpointer )
647 strcpy(wfdFile->cFileName, m_searchpointer->name );
649 if ( m_searchpointer->type == 2 )
650 wfdFile->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
652 wfdFile->ftLastWriteTime = m_searchpointer->filetime;
653 wfdFile->ftLastAccessTime = m_searchpointer->filetime;
654 wfdFile->ftCreationTime = m_searchpointer->filetime;
656 wfdFile->nFileSizeLow = m_searchpointer->Length;
663 //******************************************************************************************************************
664 int iso9660::FindNextFile( HANDLE szLocalFolder, WIN32_FIND_DATA *wfdFile )
666 memset( wfdFile, 0, sizeof(WIN32_FIND_DATA));
668 if ( m_searchpointer )
669 m_searchpointer = m_searchpointer->next;
671 if ( m_searchpointer )
673 strcpy(wfdFile->cFileName, m_searchpointer->name );
675 if ( m_searchpointer->type == 2 )
676 wfdFile->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
678 wfdFile->ftLastWriteTime = m_searchpointer->filetime;
679 wfdFile->ftLastAccessTime = m_searchpointer->filetime;
680 wfdFile->ftCreationTime = m_searchpointer->filetime;
682 wfdFile->nFileSizeLow = m_searchpointer->Length;
689 //******************************************************************************************************************
690 bool iso9660::FindClose( HANDLE szLocalFolder )
693 if (m_info.Curr_dir_cache) free(m_info.Curr_dir_cache);
694 m_info.Curr_dir_cache = 0;
700 //******************************************************************************************************************
701 string iso9660::GetThinText(BYTE* strTxt, int iLen )
703 // convert from "fat" text (UTF-16BE) to "thin" text (UTF-8)
704 std::u16string strTxtUnicode((char16_t*)strTxt, iLen / 2);
705 CStdString utf8String;
707 g_charsetConverter.utf16BEtoUTF8(strTxtUnicode, utf8String);
712 //************************************************************************************
713 HANDLE iso9660::OpenFile(const char *filename)
715 if (m_info.ISO_HANDLE == NULL) return INVALID_HANDLE_VALUE;
716 HANDLE hContext = AllocFileContext();
717 if (hContext == INVALID_HANDLE_VALUE) return hContext;
719 iso9660::isofile* pContext = GetFileContext(hContext);
721 return INVALID_HANDLE_VALUE;
723 WIN32_FIND_DATA fileinfo;
724 char *pointer, *pointer2;
726 pContext->m_bUseMode2 = false;
727 m_info.curr_filepos = 0;
729 pointer = (char*)filename;
730 while ( strpbrk( pointer, "\\/" ) )
731 pointer = strpbrk( pointer, "\\/" ) + 1;
733 strcpy(work, filename );
736 while ( strpbrk(pointer2 + 1, "\\" ) )
737 pointer2 = strpbrk(pointer2 + 1, "\\" );
741 intptr_t loop = (intptr_t)FindFirstFile( work, &fileinfo );
744 if ( !stricmp(fileinfo.cFileName, pointer ) )
747 loop = FindNextFile( NULL, &fileinfo );
751 FreeFileContext(hContext);
752 return INVALID_HANDLE_VALUE;
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;
767 CSingleLock lock(m_critSection);
768 bError = (CIoSupport::ReadSector(m_info.ISO_HANDLE, pContext->m_dwStartBlock, (char*) & (pContext->m_pBuffer[0])) < 0);
771 bError = (CIoSupport::ReadSectorMode2(m_info.ISO_HANDLE, pContext->m_dwStartBlock, (char*) & (pContext->m_pBuffer[0])) < 0);
773 pContext->m_bUseMode2 = true;
775 if (pContext->m_bUseMode2)
776 pContext->m_dwFileSize = (pContext->m_dwFileSize / 2048) * MODE2_DATA_SIZE;
781 //************************************************************************************
782 void iso9660::CloseFile(HANDLE hFile)
784 iso9660::isofile* pContext = GetFileContext(hFile);
787 if (pContext->m_pBuffer)
789 delete [] pContext->m_pBuffer;
790 pContext->m_pBuffer = NULL;
793 FreeFileContext(hFile);
795 //************************************************************************************
796 bool iso9660::ReadSectorFromCache(iso9660::isofile* pContext, DWORD sector, uint8_t** ppBuffer)
799 DWORD StartSectorInCircBuff = pContext->m_dwCircBuffSectorStart;
800 DWORD SectorsInCircBuff;
802 if ( pContext->m_dwCircBuffEnd >= pContext->m_dwCircBuffBegin )
803 SectorsInCircBuff = pContext->m_dwCircBuffEnd - pContext->m_dwCircBuffBegin;
805 SectorsInCircBuff = CIRC_BUFFER_SIZE - (pContext->m_dwCircBuffBegin - pContext->m_dwCircBuffEnd);
807 // If our sector is already in the circular buffer
808 if ( sector >= StartSectorInCircBuff &&
809 sector < (StartSectorInCircBuff + SectorsInCircBuff) &&
810 SectorsInCircBuff > 0 )
813 DWORD SectorInCircBuff = (sector - StartSectorInCircBuff) +
814 pContext->m_dwCircBuffBegin;
815 if ( SectorInCircBuff >= CIRC_BUFFER_SIZE )
816 SectorInCircBuff -= CIRC_BUFFER_SIZE;
818 *ppBuffer = &(pContext->m_pBuffer[SectorInCircBuff]);
822 // Sector is not cache. Read it in.
823 bool SectorIsAdjacentInBuffer =
824 (StartSectorInCircBuff + SectorsInCircBuff) == sector;
825 if ( SectorsInCircBuff == CIRC_BUFFER_SIZE - 1 ||
826 !SectorIsAdjacentInBuffer)
828 // The cache is full. (Or its not an adjacent request in which we'll
829 // also flush the cache)
831 // If its adjacent, just get rid of the first sector.
832 if ( SectorIsAdjacentInBuffer )
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++;
842 pContext->m_dwCircBuffBegin = pContext->m_dwCircBuffEnd = 0;
843 pContext->m_dwCircBuffSectorStart = 0;
846 // Ok, we're ready to read the sector into the cache
849 CSingleLock lock(m_critSection);
850 if ( pContext->m_bUseMode2 )
852 bError = (CIoSupport::ReadSectorMode2(m_info.ISO_HANDLE, sector, (char*) & (pContext->m_pBuffer[pContext->m_dwCircBuffEnd])) < 0);
856 bError = (CIoSupport::ReadSector(m_info.ISO_HANDLE, sector, (char*) & (pContext->m_pBuffer[pContext->m_dwCircBuffEnd])) < 0);
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;
870 //************************************************************************************
871 void iso9660::ReleaseSectorFromCache(iso9660::isofile* pContext, DWORD sector)
874 DWORD StartSectorInCircBuff = pContext->m_dwCircBuffSectorStart;
875 DWORD SectorsInCircBuff;
877 if ( pContext->m_dwCircBuffEnd >= pContext->m_dwCircBuffBegin )
878 SectorsInCircBuff = pContext->m_dwCircBuffEnd - pContext->m_dwCircBuffBegin;
880 SectorsInCircBuff = CIRC_BUFFER_SIZE - (pContext->m_dwCircBuffBegin - pContext->m_dwCircBuffEnd);
882 // If our sector is in the circular buffer
883 if ( sector >= StartSectorInCircBuff &&
884 sector < (StartSectorInCircBuff + SectorsInCircBuff) &&
885 SectorsInCircBuff > 0 )
887 DWORD SectorsToFlush = sector - StartSectorInCircBuff + 1;
888 pContext->m_dwCircBuffBegin += SectorsToFlush;
890 pContext->m_dwCircBuffSectorStart += SectorsToFlush;
891 if ( pContext->m_dwCircBuffBegin >= CIRC_BUFFER_SIZE )
892 pContext->m_dwCircBuffBegin -= CIRC_BUFFER_SIZE;
895 //************************************************************************************
896 long iso9660::ReadFile(HANDLE hFile, uint8_t *pBuffer, long lSize)
900 DWORD sectorSize = 2048;
901 iso9660::isofile* pContext = GetFileContext(hFile);
902 if (!pContext) return -1;
904 if ( pContext->m_bUseMode2 )
905 sectorSize = MODE2_DATA_SIZE;
907 while (lSize > 0 && pContext->m_dwFilePos < pContext->m_dwFileSize)
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;
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);
916 bError = !ReadSectorFromCache(pContext, pContext->m_dwCurrentBlock, &pSector);
919 DWORD iBytes2Copy = lSize;
920 if (iBytes2Copy > (sectorSize - iOffsetInBuffer) )
921 iBytes2Copy = (DWORD) (sectorSize - iOffsetInBuffer);
924 memcpy( &pBuffer[iBytesRead], &pSector[iOffsetInBuffer], iBytes2Copy);
925 iBytesRead += iBytes2Copy;
926 lSize -= iBytes2Copy;
927 pContext->m_dwFilePos += iBytes2Copy;
929 if ( iBytes2Copy + iOffsetInBuffer == sectorSize )
930 ReleaseSectorFromCache(pContext, pContext->m_dwCurrentBlock);
932 // Why is this done? It is recalculated at the beginning of the loop
933 pContext->m_dwCurrentBlock += BUFFER_SIZE / MODE2_DATA_SIZE;
938 CLog::Log(LOGDEBUG, "iso9660::ReadFile() hit EOF");
942 if (iBytesRead == 0) return -1;
945 //************************************************************************************
946 int64_t iso9660::Seek(HANDLE hFile, int64_t lOffset, int whence)
948 iso9660::isofile* pContext = GetFileContext(hFile);
949 if (!pContext) return -1;
951 int64_t dwFilePos = pContext->m_dwFilePos;
956 pContext->m_dwFilePos = lOffset;
961 pContext->m_dwFilePos += lOffset;
965 pContext->m_dwFilePos = pContext->m_dwFileSize + lOffset;
971 if (pContext->m_dwFilePos > pContext->m_dwFileSize || pContext->m_dwFilePos < 0)
973 pContext->m_dwFilePos = dwFilePos;
974 return pContext->m_dwFilePos;
978 return pContext->m_dwFilePos;
982 //************************************************************************************
983 int64_t iso9660::GetFileSize(HANDLE hFile)
985 iso9660::isofile* pContext = GetFileContext(hFile);
986 if (!pContext) return -1;
987 return pContext->m_dwFileSize;
990 //************************************************************************************
991 int64_t iso9660::GetFilePosition(HANDLE hFile)
993 iso9660::isofile* pContext = GetFileContext(hFile);
994 if (!pContext) return -1;
995 return pContext->m_dwFilePos;
998 //************************************************************************************
999 void iso9660::FreeFileContext(HANDLE hFile)
1001 intptr_t iFile = (intptr_t)hFile;
1002 if (iFile >= 1 && iFile < MAX_ISO_FILES)
1004 if (m_isoFiles[iFile ]) delete m_isoFiles[iFile ];
1005 m_isoFiles[iFile ] = NULL;
1009 //************************************************************************************
1010 HANDLE iso9660::AllocFileContext()
1012 for (intptr_t i = 1; i < MAX_ISO_FILES; ++i)
1014 if (m_isoFiles[i] == NULL)
1016 m_isoFiles[i] = new isofile;
1020 return INVALID_HANDLE_VALUE;
1023 //************************************************************************************
1024 iso9660::isofile* iso9660::GetFileContext(HANDLE hFile)
1026 intptr_t iFile = (intptr_t)hFile;
1027 if (iFile >= 1 && iFile < MAX_ISO_FILES)
1029 return m_isoFiles[iFile];
1034 //************************************************************************************
1035 bool iso9660::IsScanned()
1037 return (m_hCDROM != NULL);
1040 //************************************************************************************
1041 void iso9660::IsoDateTimeToFileTime(iso9660_Datetime* isoDateTime, FILETIME* filetime)
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));
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);