/* * Original code from libcdvd by Hiryu & Sjeep (C) 2002 * Modified by Florin for PCSX2 emu * Fixed CdRead by linuzappz */ #include #ifdef __LINUX__ #define strnicmp strncasecmp #endif #include "CDVDiso.h" #include "CDVDisodrv.h" struct dir_toc_data{ unsigned int start_LBA; unsigned int num_sectors; unsigned int num_entries; unsigned int current_entry; unsigned int current_sector; unsigned int current_sector_offset; unsigned int inc_dirs; unsigned char extension_list[128+1]; }; //static u8 cdVolDescriptor[2048]; static struct dir_toc_data getDirTocData; static struct cdVolDesc CDVolDesc; void _splitpath2(const char *constpath, char *dir, char *fname){ // 255 char max path-length is an ISO9660 restriction // we must change this for Joliet or relaxed iso restriction support static char pathcopy[1024+1]; char* slash; strncpy(pathcopy, constpath, 1024); slash = strrchr (pathcopy, '/'); // if the path doesn't contain a '/' then look for a '\' if (!slash) slash = strrchr (pathcopy, (int)'\\'); // if a slash was found if (slash != NULL) { // null terminate the path slash[0] = 0; // and copy the path into 'dir' strncpy(dir, pathcopy, 1024); dir[255]=0; // copy the filename into 'fname' strncpy(fname, slash+1, 128); fname[128]=0; } else { dir[0] = 0; strncpy(fname, pathcopy, 128); fname[128]=0; } } // Used in findfile int tolower(int c); int strcasecmp(const char *s1, const char *s2){ while (*s1 != '\0' && tolower(*s1) == tolower(*s2)) { s1++; s2++; } return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2); } // Copy a TOC Entry from the CD native format to our tidier format void TocEntryCopy(struct TocEntry* tocEntry, struct dirTocEntry* internalTocEntry){ int i; int filenamelen; tocEntry->fileSize = internalTocEntry->fileSize; tocEntry->fileLBA = internalTocEntry->fileLBA; tocEntry->fileProperties = internalTocEntry->fileProperties; memcpy(tocEntry->date, internalTocEntry->dateStamp, 7); if (CDVolDesc.filesystemType == 2){ // This is a Joliet Filesystem, so use Unicode to ISO string copy filenamelen = internalTocEntry->filenameLength/2; if (!(tocEntry->fileProperties & 0x02)){ // strip the ;1 from the filename // filenamelen -= 2;//(Florin) nah, do not strip ;1 } for (i=0; i < filenamelen; i++) tocEntry->filename[i] = internalTocEntry->filename[(i<<1)+1]; tocEntry->filename[filenamelen] = 0; } else{ filenamelen = internalTocEntry->filenameLength; if (!(tocEntry->fileProperties & 0x02)){ // strip the ;1 from the filename // filenamelen -= 2;//(Florin) nah, do not strip ;1 } // use normal string copy strncpy(tocEntry->filename,internalTocEntry->filename,128); tocEntry->filename[filenamelen] = 0; } } // Check if a TOC Entry matches our extension list int TocEntryCompare(char* filename, char* extensions){ static char ext_list[129]; char* token; char* ext_point; strncpy(ext_list,extensions,128); ext_list[128]=0; token = strtok( ext_list, " ," ); while( token != NULL ) { // if 'token' matches extension of 'filename' // then return a match ext_point = strrchr(filename,'.'); if (strnicmp(ext_point, token, strlen(token)) == 0) return (TRUE); /* Get next token: */ token = strtok( NULL, " ," ); } // If not match found then return FALSE return (FALSE); } #define CD_SECS 60 /* seconds per minute */ #define CD_FRAMES 75 /* frames per second */ #define CD_MSF_OFFSET 150 /* MSF numbering offset of first frame */ int CdRead(u32 lsn, u32 sectors, void *buf, CdRMode *mode){ u32 i; u8* buff; int rmode; switch (mode->datapattern) { case CdSecS2048: rmode = CDVD_MODE_2048; break; case CdSecS2328: rmode = CDVD_MODE_2328; break; case CdSecS2340: rmode = CDVD_MODE_2340; break; default: return 0; } for (i=0; idatapattern){ case CdSecS2048: memcpy((void*)((uptr)buf+2048*i), buff, 2048);break;//only data case CdSecS2328: memcpy((void*)((uptr)buf+2328*i), buff, 2328);break;//without sync & head & sub case CdSecS2340: memcpy((void*)((uptr)buf+2340*i), buff, 2340);break;//without sync } } return 1; } int DvdRead(u32 lsn, u32 sectors, void *buf, CdRMode *mode){ u32 i; u8* buff; for (i=lsn; i<(lsn+sectors); i++){ if (CDVDreadTrack(i, CDVD_MODE_2048)==-1) return 0; buff = CDVDgetBuffer(); if (buff==NULL) return 0; // switch (mode->datapattern){ // case CdSecS2064: ((u32*)buf)[0] = i + 0x30000; memcpy((u8*)buf+12, buff, 2048); (u8*)buf+= 2064; break; // default: // return 0; // } } return 1; } /************************************************************** * The functions below are not exported for normal file-system * * operations, but are used by the file-system operations, and * * may also be exported for use via RPC * **************************************************************/ int CDVD_GetVolumeDescriptor(void){ // Read until we find the last valid Volume Descriptor int volDescSector; static struct cdVolDesc localVolDesc; #ifdef DEBUG printf("CDVD_GetVolumeDescriptor called\n"); #endif for (volDescSector = 16; volDescSector<20; volDescSector++) { CdRead(volDescSector,1,&localVolDesc,&cdReadMode); // CdSync(0x00); // If this is still a volume Descriptor if (strncmp(localVolDesc.volID, "CD001", 5) == 0) { if ((localVolDesc.filesystemType == 1) || (localVolDesc.filesystemType == 2)) { memcpy(&CDVolDesc, &localVolDesc, sizeof(struct cdVolDesc)); } } else break; } #ifdef DEBUG if (CDVolDesc.filesystemType == 1) printf("CD FileSystem is ISO9660\n"); else if (CDVolDesc.filesystemType == 2) printf("CD FileSystem is Joliet\n"); else printf("Could not detect CD FileSystem type\n"); #endif // CdStop(); return TRUE; } int CDVD_findfile(char* fname, struct TocEntry* tocEntry){ static char filename[128+1]; static char pathname[1024+1]; static char toc[2048]; char* dirname; static struct TocEntry localTocEntry; // used for internal checking only int found_dir; int num_dir_sectors; int current_sector; int dir_lba; struct dirTocEntry* tocEntryPointer; #ifdef DEBUG printf("CDVD_findfile called\n"); #endif //make sure we have good cdReadMode cdReadMode.trycount = 0; cdReadMode.spindlctrl = CdSpinStm; cdReadMode.datapattern = CdSecS2048; _splitpath2(fname, pathname, filename); // Find the TOC for a specific directory if (CDVD_GetVolumeDescriptor() != TRUE){ #ifdef RPC_LOG RPC_LOG("Could not get CD Volume Descriptor\n"); #endif return -1; } // Read the TOC of the root directory if (CdRead(CDVolDesc.rootToc.tocLBA,1,toc,&cdReadMode) != TRUE){ #ifdef RPC_LOG RPC_LOG("Couldn't Read from CD !\n"); #endif return -1; } //CdSync(0x00); // point the tocEntryPointer at the first real toc entry (char*)tocEntryPointer = toc; num_dir_sectors = (tocEntryPointer->fileSize+2047) >> 11; //round up fix current_sector = tocEntryPointer->fileLBA; (char*)tocEntryPointer += tocEntryPointer->length; (char*)tocEntryPointer += tocEntryPointer->length; localTocEntry.fileLBA = CDVolDesc.rootToc.tocLBA; // while (there are more dir names in the path) dirname = strtok( pathname, "\\/" ); while( dirname != NULL ) { found_dir = FALSE; /* while(tocEntryPointer->length > 0) { // while there are still more directory entries then search through // for the one we want if (tocEntryPointer->fileProperties & 0x02) { // Copy the CD format TOC Entry to our format TocEntryCopy(&localTocEntry, tocEntryPointer); // If this TOC Entry is a directory, // then see if it has the right name if (strcasecmp(dirname,localTocEntry.filename) == 0) { // if the name matches then we've found the directory found_dir = TRUE; break; } } // point to the next entry (char*)tocEntryPointer += tocEntryPointer->length; } */ while(1) { if ((tocEntryPointer->length == 0) || (((char*)tocEntryPointer-toc)>=2048)) { num_dir_sectors--; if (num_dir_sectors > 0) { // If we've run out of entries, but arent on the last sector // then load another sector current_sector++; if (CdRead(current_sector,1,toc,&cdReadMode) != TRUE) { return -1; } // CdSync(0x00); (char*)tocEntryPointer = toc; } else { // Couldnt find the directory, and got to end of directory return -1; } } if (tocEntryPointer->fileProperties & 0x02) { TocEntryCopy(&localTocEntry, tocEntryPointer); // If this TOC Entry is a directory, // then see if it has the right name if (strcmp(dirname,localTocEntry.filename) == 0) { // if the name matches then we've found the directory found_dir = TRUE; break; } } // point to the next entry (char*)tocEntryPointer += tocEntryPointer->length; } // If we havent found the directory name we wanted then fail if (found_dir != TRUE) { return -1; } // Get next directory name dirname = strtok( NULL, "\\/" ); // Read the TOC of the found subdirectory if (CdRead(localTocEntry.fileLBA,1,toc,&cdReadMode) != TRUE) { return -1; } // CdSync(0x00); num_dir_sectors = (localTocEntry.fileSize+2047) >> 11; //round up fix current_sector = localTocEntry.fileLBA; // and point the tocEntryPointer at the first real toc entry (char*)tocEntryPointer = toc; (char*)tocEntryPointer += tocEntryPointer->length; (char*)tocEntryPointer += tocEntryPointer->length; } (char*)tocEntryPointer = toc; num_dir_sectors = (tocEntryPointer->fileSize+2047) >> 11; //round up fix dir_lba = tocEntryPointer->fileLBA; (char*)tocEntryPointer = toc; (char*)tocEntryPointer += tocEntryPointer->length; (char*)tocEntryPointer += tocEntryPointer->length; while (num_dir_sectors > 0) { while(tocEntryPointer->length != 0) { // Copy the CD format TOC Entry to our format TocEntryCopy(&localTocEntry, tocEntryPointer); if ((strnicmp(localTocEntry.filename, filename, strlen(filename)) == 0) || ((filename[strlen(filename)-2] == ';') && (localTocEntry.filename[strlen(localTocEntry.filename)-2] == ';') && (strnicmp(localTocEntry.filename, filename, strlen(filename)-2) == 0))) { // if the filename matches then copy the toc Entry tocEntry->fileLBA = localTocEntry.fileLBA; tocEntry->fileProperties = localTocEntry.fileProperties; tocEntry->fileSize = localTocEntry.fileSize; strcpy(tocEntry->filename, localTocEntry.filename); memcpy(tocEntry->date, localTocEntry.date, 7); #ifdef DEBUG printf("CDVD_findfile: found file\n"); #endif return TRUE; } (char*)tocEntryPointer += tocEntryPointer->length; } num_dir_sectors--; if (num_dir_sectors > 0) { dir_lba++; if (CdRead(dir_lba,1,toc,&cdReadMode) != TRUE){ return -1; } // CdSync(0x00); (char*)tocEntryPointer = toc; } } return FALSE; } // This is the RPC-ready function which takes the request to start the tocEntry retrieval int CDVD_GetDir_RPC_request(char* pathname, char* extensions, unsigned int inc_dirs){ // int dir_depth = 1; static char toc[2048]; char* dirname; int found_dir; int num_dir_sectors; unsigned int toc_entry_num; struct dirTocEntry* tocEntryPointer; static struct TocEntry localTocEntry; int current_sector; // store the extension list statically for the retrieve function strncpy(getDirTocData.extension_list, extensions, 128); getDirTocData.extension_list[128]=0; getDirTocData.inc_dirs = inc_dirs; // Find the TOC for a specific directory if (CDVD_GetVolumeDescriptor() != TRUE){ #ifdef RPC_LOG RPC_LOG("[RPC:cdvd] Could not get CD Volume Descriptor\n"); #endif return -1; } #ifdef RPC_LOG RPC_LOG("[RPC:cdvd] Getting Directory Listing for: \"%s\"\n", pathname); #endif // Read the TOC of the root directory if (CdRead(CDVolDesc.rootToc.tocLBA,1,toc,&cdReadMode) != TRUE){ #ifdef RPC_LOG RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); #endif return -1; } //CdSync(0x00); // point the tocEntryPointer at the first real toc entry (char*)tocEntryPointer = toc; num_dir_sectors = (tocEntryPointer->fileSize+2047) >> 11; current_sector = tocEntryPointer->fileLBA; (char*)tocEntryPointer += tocEntryPointer->length; (char*)tocEntryPointer += tocEntryPointer->length; // use strtok to get the next dir name // if there isnt one, then assume we want the LBA // for the current one, and exit the while loop // if there is another dir name then increment dir_depth // and look through dir table entries until we find the right name // if we dont find the right name // before finding an entry at a higher level (lower num), then return nothing localTocEntry.fileLBA = CDVolDesc.rootToc.tocLBA; // while (there are more dir names in the path) dirname = strtok( pathname, "\\/" ); while( dirname != NULL ){ found_dir = FALSE; while(1){ if ((tocEntryPointer->length == 0) || (((char*)tocEntryPointer-toc)>=2048)) { num_dir_sectors--; if (num_dir_sectors > 0){ // If we've run out of entries, but arent on the last sector // then load another sector current_sector++; if (CdRead(current_sector,1,toc,&cdReadMode) != TRUE){ #ifdef RPC_LOG RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); #endif return -1; } //CdSync(0x00); (char*)tocEntryPointer = toc; } else{ // Couldnt find the directory, and got to end of directory return -1; } } if (tocEntryPointer->fileProperties & 0x02){ TocEntryCopy(&localTocEntry, tocEntryPointer); // If this TOC Entry is a directory, // then see if it has the right name if (strcmp(dirname,localTocEntry.filename) == 0){ // if the name matches then we've found the directory found_dir = TRUE; #ifdef RPC_LOG RPC_LOG("[RPC: ] Found directory %s in subdir at sector %d\n",dirname,current_sector); RPC_LOG("[RPC: ] LBA of found subdirectory = %d\n",localTocEntry.fileLBA); #endif break; } } // point to the next entry (char*)tocEntryPointer += tocEntryPointer->length; } // If we havent found the directory name we wanted then fail if (found_dir != TRUE) return -1; // Get next directory name dirname = strtok( NULL, "\\/" ); // Read the TOC of the found subdirectory if (CdRead(localTocEntry.fileLBA,1,toc,&cdReadMode) != TRUE){ #ifdef RPC_LOG RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); #endif return -1; } //CdSync(0x00); num_dir_sectors = (localTocEntry.fileSize+2047) >> 11; current_sector = localTocEntry.fileLBA; // and point the tocEntryPointer at the first real toc entry (char*)tocEntryPointer = toc; (char*)tocEntryPointer += tocEntryPointer->length; (char*)tocEntryPointer += tocEntryPointer->length; } // We know how much data we need to read in from the DirTocHeader // but we need to read in at least 1 sector before we can get this value // Now we need to COUNT the number of entries (dont do anything with info at this point) // so set the tocEntryPointer to point to the first actual file entry // This is a bit of a waste of reads since we're not actually copying the data out yet, // but we dont know how big this TOC might be, so we cant allocate a specific size (char*)tocEntryPointer = toc; // Need to STORE the start LBA and number of Sectors, for use by the retrieve func. getDirTocData.start_LBA = localTocEntry.fileLBA; getDirTocData.num_sectors = (tocEntryPointer->fileSize+2047) >> 11; getDirTocData.num_entries = 0; getDirTocData.current_entry = 0; getDirTocData.current_sector = getDirTocData.start_LBA; getDirTocData.current_sector_offset = 0; num_dir_sectors = getDirTocData.num_sectors; (char*)tocEntryPointer = toc; (char*)tocEntryPointer += tocEntryPointer->length; (char*)tocEntryPointer += tocEntryPointer->length; toc_entry_num=0; while(1){ if ((tocEntryPointer->length == 0) || (((char*)tocEntryPointer-toc)>=2048)){ // decrease the number of dirs remaining num_dir_sectors--; if (num_dir_sectors > 0){ // If we've run out of entries, but arent on the last sector // then load another sector getDirTocData.current_sector++; if (CdRead(getDirTocData.current_sector,1,toc,&cdReadMode) != TRUE){ #ifdef RPC_LOG RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); #endif return -1; } //CdSync(0x00); (char*)tocEntryPointer = toc; // continue; } else{ getDirTocData.num_entries = toc_entry_num; getDirTocData.current_sector = getDirTocData.start_LBA; return (toc_entry_num); } } // We've found a file/dir in this directory // now check if it matches our extension list (if there is one) TocEntryCopy(&localTocEntry, tocEntryPointer); if (localTocEntry.fileProperties & 0x02){ // If this is a subdir, then check if we want to include subdirs if (getDirTocData.inc_dirs){ toc_entry_num++; } } else{ if (strlen(getDirTocData.extension_list) > 0){ if (TocEntryCompare(localTocEntry.filename, getDirTocData.extension_list) == TRUE){ // increment the number of matching entries counter toc_entry_num++; } } else{ toc_entry_num++; } } (char*)tocEntryPointer += tocEntryPointer->length; } // THIS SHOULD BE UNREACHABLE - // since we are trying to count ALL matching entries, rather than upto a limit // STORE total number of TOC entries getDirTocData.num_entries = toc_entry_num; getDirTocData.current_sector = getDirTocData.start_LBA; // we've reached the toc entry limit, so return how many we've done return (toc_entry_num); } // This function can be called repeatedly after CDVD_GetDir_RPC_request to get the actual entries // buffer (tocEntry) must be 18KB in size, and this will be filled with a maximum of 128 entries in one go int CDVD_GetDir_RPC_get_entries(struct TocEntry tocEntry[], int req_entries){ static char toc[2048]; int toc_entry_num; struct dirTocEntry* tocEntryPointer; if (CdRead(getDirTocData.current_sector,1,toc,&cdReadMode) != TRUE){ #ifdef RPC_LOG RPC_LOG("[RPC:cdvd] Couldn't Read from CD !\n"); #endif return -1; } //CdSync(0x00); if (getDirTocData.current_entry == 0){ // if this is the first read then make sure we point to the first real entry (char*)tocEntryPointer = toc; (char*)tocEntryPointer += tocEntryPointer->length; (char*)tocEntryPointer += tocEntryPointer->length; getDirTocData.current_sector_offset = (char*)tocEntryPointer - toc; } else{ (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; } if (req_entries > 128) req_entries = 128; for (toc_entry_num=0; toc_entry_num < req_entries;){ if ((tocEntryPointer->length == 0) || (getDirTocData.current_sector_offset >= 2048)){ // decrease the number of dirs remaining getDirTocData.num_sectors--; if (getDirTocData.num_sectors > 0){ // If we've run out of entries, but arent on the last sector // then load another sector getDirTocData.current_sector++; if (CdRead(getDirTocData.current_sector,1,toc,&cdReadMode) != TRUE){ #ifdef RPC_LOG RPC_LOG("[RPC:cdvd] Couldn't Read from CD !\n"); #endif return -1; } //CdSync(0x00); getDirTocData.current_sector_offset = 0; (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; // continue; } else{ return (toc_entry_num); } } // This must be incremented even if the filename doesnt match extension list getDirTocData.current_entry++; // We've found a file in this directory // now check if it matches our extension list (if there is one) // Copy the entry regardless, as it makes the comparison easier // if it doesn't match then it will just be overwritten TocEntryCopy(&tocEntry[toc_entry_num], tocEntryPointer); if (tocEntry[toc_entry_num].fileProperties & 0x02){ // If this is a subdir, then check if we want to include subdirs if (getDirTocData.inc_dirs) { toc_entry_num++; } getDirTocData.current_sector_offset += tocEntryPointer->length; (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; } else{ if (strlen(getDirTocData.extension_list) > 0){ if (TocEntryCompare(tocEntry[toc_entry_num].filename, getDirTocData.extension_list) == TRUE){ // increment the number of matching entries counter toc_entry_num++; } getDirTocData.current_sector_offset += tocEntryPointer->length; (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; } else{ toc_entry_num++; getDirTocData.current_sector_offset += tocEntryPointer->length; (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; } } /* if (strlen(getDirTocData.extension_list) > 0) { if (TocEntryCompare(tocEntry[toc_entry_num].filename, getDirTocData.extension_list) == TRUE) { // increment this here, rather than in the main for loop // since this should count the number of matching entries toc_entry_num++; } getDirTocData.current_sector_offset += tocEntryPointer->length; (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; } else { toc_entry_num++; getDirTocData.current_sector_offset += tocEntryPointer->length; (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; } */ } return (toc_entry_num); }