snes9xgx/source/ngc/dvd.cpp

572 lines
13 KiB
C++
Raw Normal View History

/****************************************************************************
* Snes9x 1.51 Nintendo Wii/Gamecube Port
*
* softdev July 2006
* svpe & crunchy2 June 2007
* Tantric September 2008
*
* dvd.cpp
*
* DVD I/O functions
***************************************************************************/
#include <gccore.h>
#include <ogcsys.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2008-08-12 05:25:16 +02:00
#ifdef WII_DVD
2008-08-23 05:20:54 +02:00
extern "C" {
#include <di/di.h>
2008-08-23 05:20:54 +02:00
}
#endif
2008-09-27 00:28:57 +02:00
#include "memmap.h"
2008-08-12 05:25:16 +02:00
#include "menudraw.h"
2008-09-09 19:36:48 +02:00
#include "snes9xGX.h"
2008-08-12 05:25:16 +02:00
#include "unzip.h"
2008-08-16 02:02:08 +02:00
u64 dvddir = 0;
u64 dvdrootdir = 0;
int dvddirlength = 0;
2008-09-10 07:57:37 +02:00
bool isWii = false;
#ifdef HW_DOL
/** DVD I/O Address base **/
volatile unsigned long *dvd = (volatile unsigned long *) 0xCC006000;
#endif
/** Due to lack of memory, we'll use this little 2k keyhole for all DVD operations **/
unsigned char DVDreadbuffer[2048] ATTRIBUTE_ALIGN (32);
unsigned char dvdbuffer[2048];
2008-08-12 05:25:16 +02:00
/****************************************************************************
* dvd_read
*
* The only DVD function we need - you gotta luv gc-linux self-boots!
* returns: 1 - ok ; 0 - error
***************************************************************************/
int
dvd_read (void *dst, unsigned int len, u64 offset)
{
unsigned char *buffer = (unsigned char *) (unsigned int) DVDreadbuffer;
if (len > 2048)
return 0; /*** We only allow 2k reads **/
DCInvalidateRange ((void *) buffer, len);
// don't read past the end of the DVD (1.5 GB for GC DVD, 4.7 GB for DVD)
if(offset < 0x57057C00 || (isWii && offset < 0x118244F00LL))
{
2008-08-20 09:58:55 +02:00
#ifdef HW_DOL
2008-08-20 09:58:55 +02:00
dvd[0] = 0x2E;
dvd[1] = 0;
dvd[2] = 0xA8000000;
2008-08-22 22:23:32 +02:00
dvd[3] = (u32)(offset >> 2);
dvd[4] = len;
dvd[5] = (u32) buffer;
dvd[6] = len;
dvd[7] = 3; /*** Enable reading with DMA ***/
while (dvd[7] & 1);
memcpy (dst, buffer, len);
2008-08-20 09:58:55 +02:00
if (dvd[0] & 0x4) /* Ensure it has completed */
return 0;
2008-08-20 09:58:55 +02:00
return 1;
2008-08-20 09:58:55 +02:00
#elif WII_DVD
int ret = 1;
2008-08-22 22:23:32 +02:00
ret = DI_ReadDVD(dst, len >> 11, (u32)(offset >> 11));
if (ret==0)
return 1;
else
return 0;
#endif
}
2008-08-20 09:58:55 +02:00
return 0;
}
/** Minimal ISO Directory Definition **/
#define RECLEN 0 /* Record length */
#define EXTENT 6 /* Extent */
#define FILE_LENGTH 14 /* File length (BIG ENDIAN) */
#define FILE_FLAGS 25 /* File flags */
#define FILENAME_LENGTH 32 /* Filename length */
#define FILENAME 33 /* ASCIIZ filename */
/** Minimal Primary Volume Descriptor **/
#define PVDROOT 0x9c
static int IsJoliet = 0;
/****************************************************************************
* Primary Volume Descriptor
*
* The PVD should reside between sector 16 and 31.
* This is for single session DVD only.
***************************************************************************/
int
getpvd ()
{
2008-08-16 02:02:08 +02:00
int sector = 16;
u32 rootdir32;
2008-08-16 02:02:08 +02:00
dvddir = dvddirlength = 0;
IsJoliet = -1;
/** Look for Joliet PVD first **/
2008-08-16 02:02:08 +02:00
while (sector < 32)
{
if (dvd_read (&dvdbuffer, 2048, (u64)(sector << 11)))
2008-08-16 02:02:08 +02:00
{
if (memcmp (&dvdbuffer, "\2CD001\1", 8) == 0)
{
memcpy(&rootdir32, &dvdbuffer[PVDROOT + EXTENT], 4);
dvddir = (u64)rootdir32;
dvddir <<= 11;
dvdrootdir = dvddir;
memcpy (&dvddirlength, &dvdbuffer[PVDROOT + FILE_LENGTH], 4);
IsJoliet = 1;
break;
}
2008-08-16 02:02:08 +02:00
}
else
return 0; /*** Can't read sector! ***/
sector++;
}
2008-08-16 02:02:08 +02:00
if (IsJoliet > 0) /*** Joliet PVD Found ? ***/
return 1;
2008-08-16 02:02:08 +02:00
sector = 16;
/*** Look for standard ISO9660 PVD ***/
2008-08-16 02:02:08 +02:00
while (sector < 32)
{
2008-08-16 02:02:08 +02:00
if (dvd_read (&dvdbuffer, 2048, sector << 11))
{
if (memcmp (&dvdbuffer, "\1CD001\1", 8) == 0)
{
memcpy (&rootdir32, &dvdbuffer[PVDROOT + EXTENT], 4);
dvddir = (u64)rootdir32;
dvddir <<= 11;
dvdrootdir = dvddir;
memcpy (&dvddirlength, &dvdbuffer[PVDROOT + FILE_LENGTH], 4);
IsJoliet = 0;
break;
}
}
else
return 0; /*** Can't read sector! ***/
sector++;
}
2008-08-16 02:02:08 +02:00
return (IsJoliet == 0);
}
2008-08-16 02:02:08 +02:00
/****************************************************************************
* TestDVD()
*
* Tests if a ISO9660 DVD is inserted and available
***************************************************************************/
2008-08-16 02:02:08 +02:00
bool TestDVD()
{
2008-08-16 02:02:08 +02:00
if (!getpvd())
{
#ifdef HW_DOL
2008-08-16 02:02:08 +02:00
DVD_Mount();
#elif WII_DVD
DI_Mount();
while(DI_GetStatus() & DVD_INIT);
#endif
2008-08-16 02:02:08 +02:00
if (!getpvd())
return false;
}
2008-08-16 02:02:08 +02:00
return true;
}
/****************************************************************************
* getentry
*
* Support function to return the next file entry, if any
* Declared static to avoid accidental external entry.
***************************************************************************/
static int diroffset = 0;
static int
getentry (int entrycount)
{
2008-08-16 02:02:08 +02:00
char fname[512]; /* Huge, but experience has determined this */
char *ptr;
char *filename;
char *filenamelength;
char *rr;
int j;
u32 offset32;
2008-08-16 02:02:08 +02:00
/* Basic checks */
if (entrycount >= MAXFILES)
return 0;
2008-08-16 02:02:08 +02:00
if (diroffset >= 2048)
return 0;
2008-08-16 02:02:08 +02:00
/** Decode this entry **/
if (dvdbuffer[diroffset]) /* Record length available */
{
2008-08-16 02:02:08 +02:00
/* Update offsets into sector buffer */
ptr = (char *) &dvdbuffer[0];
ptr += diroffset;
filename = ptr + FILENAME;
filenamelength = ptr + FILENAME_LENGTH;
/* Check for wrap round - illegal in ISO spec,
* but certain crap writers do it! */
if ((diroffset + dvdbuffer[diroffset]) > 2048)
return 0;
if (*filenamelength)
{
2008-08-16 02:02:08 +02:00
memset (&fname, 0, 512);
if (!IsJoliet) /*** Do ISO 9660 first ***/
strcpy (fname, filename);
else
{ /*** The more tortuous unicode joliet entries ***/
for (j = 0; j < (*filenamelength >> 1); j++)
{
fname[j] = filename[j * 2 + 1];
}
fname[j] = 0;
if (strlen (fname) >= MAXJOLIET)
fname[MAXJOLIET - 1] = 0;
2008-08-16 02:02:08 +02:00
if (strlen (fname) == 0)
fname[0] = filename[0];
}
if (strlen (fname) == 0) // root entry
{
fname[0] = 0; // we'll skip it by setting the filename to 0 length
}
else
{
if (fname[0] == 1)
{
if(dvddir == dvdrootdir) // at root already, don't show ..
fname[0] = 0;
else
strcpy (fname, "..");
}
else
{
/*
* Move *filenamelength to t,
* Only to stop gcc warning for noobs :)
*/
int t = *filenamelength;
fname[t] = 0;
}
}
/** Rockridge Check **/
2008-08-16 02:02:08 +02:00
rr = strstr (fname, ";");
if (rr != NULL)
*rr = 0;
2008-08-16 02:02:08 +02:00
strcpy (filelist[entrycount].filename, fname);
fname[MAXDISPLAY - 1] = 0;
2008-08-16 02:02:08 +02:00
strcpy (filelist[entrycount].displayname, fname);
2008-08-16 02:02:08 +02:00
memcpy (&offset32, &dvdbuffer[diroffset + EXTENT], 4);
2008-08-16 02:02:08 +02:00
filelist[entrycount].offset = (u64)offset32;
memcpy (&filelist[entrycount].length, &dvdbuffer[diroffset + FILE_LENGTH], 4);
memcpy (&filelist[entrycount].flags, &dvdbuffer[diroffset + FILE_FLAGS], 1);
2008-08-16 02:02:08 +02:00
filelist[entrycount].offset <<= 11;
filelist[entrycount].flags = filelist[entrycount].flags & 2;
2008-08-16 02:02:08 +02:00
/*** Prepare for next entry ***/
2008-08-16 02:02:08 +02:00
diroffset += dvdbuffer[diroffset];
return 1;
}
}
2008-08-16 02:02:08 +02:00
return 0;
}
/****************************************************************************
2008-08-12 05:25:16 +02:00
* parseDVDdirectory
*
* This function will parse the directory tree.
2008-08-16 02:02:08 +02:00
* It relies on dvddir and dvddirlength being pre-populated by a call to
* getpvd, a previous parse or a menu selection.
*
* The return value is number of files collected, or 0 on failure.
***************************************************************************/
int
2008-08-12 05:25:16 +02:00
ParseDVDdirectory ()
{
2008-08-16 02:02:08 +02:00
int pdlength;
u64 pdoffset;
u64 rdoffset;
int len = 0;
int filecount = 0;
2008-08-16 02:02:08 +02:00
// initialize selection
selection = offset = 0;
2008-08-12 05:25:16 +02:00
2008-08-16 02:02:08 +02:00
pdoffset = rdoffset = dvddir;
pdlength = dvddirlength;
filecount = 0;
2008-08-16 02:02:08 +02:00
// Clear any existing values
memset (&filelist, 0, sizeof (FILEENTRIES) * MAXFILES);
/*** Get as many files as possible ***/
2008-08-16 02:02:08 +02:00
while (len < pdlength)
{
if (dvd_read (&dvdbuffer, 2048, pdoffset) == 0)
return 0;
diroffset = 0;
while (getentry (filecount))
{
if(strlen(filelist[filecount].filename) > 0 && filecount < MAXFILES)
filecount++;
}
len += 2048;
pdoffset = rdoffset + len;
}
// Sort the file list
qsort(filelist, filecount, sizeof(FILEENTRIES), FileSortCallback);
return filecount;
}
/****************************************************************************
* DirectorySearch
*
* Searches for the directory name specified within the current directory
* Returns the index of the directory, or -1 if not found
***************************************************************************/
2008-08-16 02:02:08 +02:00
int DirectorySearch(char dir[512])
{
for (int i = 0; i < maxfiles; i++ )
if (strcmp(filelist[i].filename, dir) == 0)
return i;
2008-08-17 07:25:33 +02:00
return -1;
2008-08-16 02:02:08 +02:00
}
/****************************************************************************
* SwitchDVDFolder
*
* Recursively searches for any directory path 'dir' specified
* Also loads the directory contents via ParseDVDdirectory()
* It relies on dvddir, dvddirlength, and filelist being pre-populated
***************************************************************************/
2008-08-16 02:02:08 +02:00
bool SwitchDVDFolder(char * dir, int maxDepth)
{
if(maxDepth > 8) // only search to a max depth of 8 levels
return false;
bool lastdir = false;
2008-08-17 07:25:33 +02:00
char * nextdir = NULL;
2008-08-16 02:02:08 +02:00
unsigned int t = strcspn(dir, "/");
2008-08-16 02:02:08 +02:00
if(t != strlen(dir))
nextdir = dir + t + 1; // next directory path to find
else
lastdir = true;
2008-08-16 02:02:08 +02:00
dir[t] = 0;
int dirindex = DirectorySearch(dir);
2008-08-17 07:25:33 +02:00
if(dirindex >= 0)
{
2008-08-16 02:02:08 +02:00
dvddir = filelist[dirindex].offset;
dvddirlength = filelist[dirindex].length;
maxfiles = ParseDVDdirectory();
if(lastdir)
return true;
else
return SwitchDVDFolder(nextdir, maxDepth++);
}
2008-08-16 02:02:08 +02:00
return false;
}
2008-08-17 07:25:33 +02:00
bool SwitchDVDFolder(char origdir[])
2008-08-16 02:02:08 +02:00
{
2008-08-17 07:25:33 +02:00
// make a copy of origdir so we don't mess with original
char dir[200];
strcpy(dir, origdir);
char * dirptr = dir;
2008-08-16 02:02:08 +02:00
// strip off leading/trailing slashes on the directory path
// we don't want to screw up our recursion!
if(dir[0] == '/')
2008-08-17 07:25:33 +02:00
dirptr = dirptr + 1;
2008-08-16 02:02:08 +02:00
if(dir[strlen(dir)-1] == '/')
dir[strlen(dir)-1] = 0;
2008-08-17 07:25:33 +02:00
return SwitchDVDFolder(dirptr, 0);
}
2008-08-12 05:25:16 +02:00
/****************************************************************************
* LoadDVDFile
* This function will load a file from DVD, in BIN, SMD or ZIP format.
2008-08-16 02:02:08 +02:00
* The values for offset and length are inherited from dvddir and
* dvddirlength.
2008-08-12 05:25:16 +02:00
*
* The buffer parameter should re-use the initial ROM buffer.
***************************************************************************/
2008-08-12 05:25:16 +02:00
int
2008-09-27 00:28:57 +02:00
LoadDVDFile ()
2008-08-12 05:25:16 +02:00
{
2008-08-16 02:02:08 +02:00
int offset;
int blocks;
int i;
u64 discoffset;
char readbuffer[2048];
2008-09-27 00:28:57 +02:00
unsigned char *rbuffer = (unsigned char *) Memory.ROM;
2008-08-16 02:02:08 +02:00
// How many 2k blocks to read
blocks = dvddirlength / 2048;
offset = 0;
discoffset = dvddir;
ShowAction ((char*) "Loading...");
dvd_read (readbuffer, 2048, discoffset);
if (!IsZipFile (readbuffer))
{
for (i = 0; i < blocks; i++)
{
dvd_read (readbuffer, 2048, discoffset);
2008-09-27 00:28:57 +02:00
memcpy (rbuffer + offset, readbuffer, 2048);
2008-08-16 02:02:08 +02:00
offset += 2048;
discoffset += 2048;
}
2008-08-12 05:25:16 +02:00
2008-08-16 02:02:08 +02:00
/*** And final cleanup ***/
if (dvddirlength % 2048)
{
i = dvddirlength % 2048;
dvd_read (readbuffer, 2048, discoffset);
2008-09-27 00:28:57 +02:00
memcpy (rbuffer + offset, readbuffer, i);
2008-08-16 02:02:08 +02:00
}
}
else
{
2008-09-27 00:28:57 +02:00
return UnZipFile (rbuffer, discoffset); // unzip from dvd
2008-08-16 02:02:08 +02:00
}
return dvddirlength;
2008-08-12 05:25:16 +02:00
}
/****************************************************************************
* uselessinquiry
*
* As the name suggests, this function is quite useless.
* It's only purpose is to stop any pending DVD interrupts while we use the
* memcard interface.
*
* libOGC tends to foul up if you don't, and sometimes does if you do!
***************************************************************************/
#ifdef HW_DOL
void uselessinquiry ()
{
2008-08-16 02:02:08 +02:00
dvd[0] = 0;
dvd[1] = 0;
dvd[2] = 0x12000000;
dvd[3] = 0;
dvd[4] = 0x20;
dvd[5] = 0x80000000;
dvd[6] = 0x20;
dvd[7] = 1;
2008-08-16 02:02:08 +02:00
while (dvd[7] & 1);
}
/****************************************************************************
* dvd_motor_off( )
* Turns off DVD drive motor so it doesn't make noise (Gamecube only)
***************************************************************************/
2008-09-28 05:12:15 +02:00
void dvd_motor_off ()
{
dvd[0] = 0x2e;
dvd[1] = 0;
dvd[2] = 0xe3000000;
dvd[3] = 0;
dvd[4] = 0;
dvd[5] = 0;
dvd[6] = 0;
dvd[7] = 1; // Do immediate
while (dvd[7] & 1);
/*** PSO Stops blackscreen at reload ***/
dvd[0] = 0x14;
dvd[1] = 0;
}
2008-08-20 09:58:55 +02:00
/****************************************************************************
* dvd_driveid
*
* Gets and returns the dvd driveid
***************************************************************************/
2008-08-20 09:58:55 +02:00
int dvd_driveid()
{
static unsigned char *inquiry=(unsigned char *)0x80000004;
dvd[0] = 0x2e;
dvd[1] = 0;
dvd[2] = 0x12000000;
dvd[3] = 0;
dvd[4] = 0x20;
dvd[5] = 0x80000000;
dvd[6] = 0x20;
dvd[7] = 3;
while( dvd[7] & 1 )
;
DCFlushRange((void *)0x80000000, 32);
return (int)inquiry[2];
}
2008-09-10 07:57:37 +02:00
#endif
/****************************************************************************
* SetDVDDriveType()
*
* Sets the DVD drive ID for use to determine disc size (1.5 GB or 4.7 GB)
***************************************************************************/
2008-09-10 07:57:37 +02:00
void SetDVDDriveType()
{
#ifdef HW_RVL
isWii = true;
#else
int drvid = dvd_driveid ();
if ( drvid == 4 || drvid == 6 || drvid == 8 )
isWii = false;
else
isWii = true;
#endif
}