pcsx2/CDVDiso.c

835 lines
22 KiB
C

/*
* Original code from libcdvd by Hiryu & Sjeep (C) 2002
* Modified by Florin for PCSX2 emu
* Fixed CdRead by linuzappz
*/
#include <string.h>
#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; i<sectors; i++){
if (CDVDreadTrack(lsn+i, rmode)==-1)
return 0;
buff = CDVDgetBuffer();
if (buff==NULL) return 0;
switch (mode->datapattern){
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)
{
printf("Couldn't Read from CD !\n");
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)
{
printf("CDVD_findfile: could not find dir\n");
return -1;
}
// Get next directory name
dirname = strtok( NULL, "\\/" );
// Read the TOC of the found subdirectory
if (CdRead(localTocEntry.fileLBA,1,toc,&cdReadMode) != TRUE)
{
printf("Couldn't Read from CD !\n");
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;
}
#ifdef DEBUG
printf("CDVD_findfile: found dir, now looking for file\n");
#endif
(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){
printf("Couldn't Read from CD !\n");
return -1;
}
// CdSync(0x00);
(char*)tocEntryPointer = toc;
}
}
#ifdef DEBUG
printf("CDVD_findfile: could not find file\n");
#endif
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);
}