mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-16 00:15:08 +01:00
e510233154
*Fixed reset of the loader when loading game with IOS reload and disabled WiiTDB titles (Issue 1874) *Added new 'Inherit' or 'Use global' setting for game settings. If that option is set than the global setting is used for that option. (Issue 1842) *Fixed timeout timer on startup to count correctly. (Issue 1907) *Added two new video modes to force progressive video mode, 'FORCE PAL480p' and 'FORCE NTSC480p' (basically the same but oh well) (Issue 1902) *Added the new 'Return to' procedure for the d2x v4 IOS for Test purpose (didn't test it, need feedback on this one). The old method is used if this procedure fails. Please test it on problematic games (e.g. PoP). (Issue 1892)
1527 lines
37 KiB
C
1527 lines
37 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <malloc.h>
|
|
#include <unistd.h>
|
|
#ifndef DEBUG
|
|
#include <sys/iosupport.h>
|
|
#include <ogc/ipc.h>
|
|
#include <gccore.h>
|
|
#include <ogc/mutex.h>
|
|
#include <ogc/lwp_watchdog.h>
|
|
#endif
|
|
#include "libdvdiso.h"
|
|
#include "di2.h"
|
|
|
|
#define MAXPATH 4096
|
|
#define MAXNAMELEN 208
|
|
|
|
|
|
#define uint8_t unsigned char
|
|
#define uint16_t unsigned short
|
|
#define uint32_t unsigned int
|
|
static mutex_t _DVD_mutex = LWP_MUTEX_NULL;
|
|
static bool dvd_initied = false;
|
|
|
|
static int totalsectors;
|
|
static int totalentries;
|
|
|
|
static char currentpath[MAXPATH];
|
|
|
|
static void freedirentrieslist(void);
|
|
static void dotab_dvd_add(void);
|
|
static int DVD_ScanContent(void);
|
|
|
|
struct DIRENTRY
|
|
{
|
|
char name[MAXNAMELEN + 1];
|
|
uint32_t size_bytes;
|
|
uint32_t size_sectors;
|
|
uint32_t firstsector;
|
|
uint8_t isfile;
|
|
uint8_t isdir;
|
|
uint8_t ishidden;
|
|
|
|
struct DIRENTRY *next;
|
|
|
|
struct DIRENTRY *parent;
|
|
|
|
struct DIRENTRY *child;
|
|
uint32_t fake_inode;
|
|
};
|
|
|
|
static struct DIRENTRY *direntriesptr;
|
|
|
|
struct FILESTATE
|
|
{
|
|
|
|
struct DIRENTRY *dentry;
|
|
uint32_t firstsector;
|
|
uint32_t pos_bytes;
|
|
uint32_t size_bytes;
|
|
uint32_t localsectnum;
|
|
uint8_t *localsectbuf;
|
|
int fd;
|
|
};
|
|
|
|
struct DIR_STATE
|
|
{
|
|
|
|
struct DIRENTRY *firstdentry;
|
|
|
|
struct DIRENTRY *curdentry;
|
|
uint8_t inUse;
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
FILE *fpin;
|
|
|
|
struct _reent
|
|
{
|
|
int _errno;
|
|
};
|
|
|
|
uint32_t fake_ticks = 1000;
|
|
uint32_t gettick(){ return fake_ticks++;};
|
|
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////
|
|
// CACHE FUNCTION DEFINITIONS //
|
|
///////////////////////////////////////////
|
|
#define CACHE_FREE 0xFFFFFFFF
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t sector;
|
|
uint32_t last_used;
|
|
void *ptr;
|
|
}
|
|
|
|
cache_page;
|
|
|
|
static cache_page *ReadAheadCache = NULL;
|
|
static uint32_t RA_pages = 0;
|
|
static uint32_t RA_sectors = 0;
|
|
#define SECTOR_SIZE 0x800
|
|
|
|
static void DestroyReadAheadCache();
|
|
static int ReadSectorFromCache(void *buf, uint32_t sector);
|
|
static void DVDEnableReadAhead(uint32_t pages, uint32_t sectors);
|
|
|
|
///////////////////////////////////////////
|
|
// END CACHE FUNCTION DEFINITIONS //
|
|
///////////////////////////////////////////
|
|
|
|
/*
|
|
void dump_hex(uint8_t *ptr, int len)
|
|
{
|
|
int i,pos=0;
|
|
do
|
|
{
|
|
for(i=0;(i<8)&&(pos<len);i++)
|
|
{
|
|
printf(" %02X",ptr[pos++]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
while(pos<len);
|
|
}
|
|
*/
|
|
|
|
int WIIDVD_Init(bool dvdx)
|
|
{
|
|
int retval;
|
|
direntriesptr = 0;
|
|
totalsectors = 0;
|
|
currentpath[0] = '/';
|
|
currentpath[1] = 0;
|
|
totalentries = 0;
|
|
|
|
#ifndef DEBUG
|
|
retval = DI2_Init(dvdx);
|
|
printf("retval: DI2_Init: %i\n", retval);
|
|
|
|
if (retval >= 0) dvd_initied = true;
|
|
else dvd_initied = false;
|
|
|
|
#else
|
|
fpin = fopen("/dev/sr0", "rb");
|
|
|
|
if (!fpin)
|
|
{
|
|
printf("Error while opening device or file\n");
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef DEBUG
|
|
dotab_dvd_add();
|
|
|
|
#endif
|
|
LWP_MutexInit(&_DVD_mutex, false);
|
|
|
|
return retval;
|
|
}
|
|
|
|
void WIIDVD_Close()
|
|
{
|
|
freedirentrieslist();
|
|
DestroyReadAheadCache();
|
|
#ifndef DEBUG
|
|
DI2_Close();
|
|
#endif
|
|
LWP_MutexDestroy(_DVD_mutex);
|
|
}
|
|
|
|
int WIIDVD_Mount()
|
|
{
|
|
#ifndef DEBUG
|
|
DI2_Mount();
|
|
unsigned int t1, t2;
|
|
t1 = ticks_to_secs(gettime());
|
|
|
|
while (DI2_GetStatus() & DVD_INIT)
|
|
{
|
|
t2 = ticks_to_secs(gettime());
|
|
|
|
if (t2 - t1 > 15) return -1;
|
|
|
|
usleep(5000);
|
|
}
|
|
|
|
#endif
|
|
DVDEnableReadAhead(10, 26); //default init
|
|
|
|
return DVD_ScanContent();
|
|
}
|
|
|
|
void WIIDVD_Unmount()
|
|
{
|
|
freedirentrieslist();
|
|
DestroyReadAheadCache();
|
|
}
|
|
|
|
|
|
int WIIDVD_ReadDVD(void* buf, uint32_t len, uint32_t lba)
|
|
{
|
|
int retval;
|
|
#ifdef DEBUG
|
|
// printf("WIIDVD_ReadDVD: Reading from sector %u len %u\n",lba,len);
|
|
|
|
retval = fseek(fpin, lba * 2048, SEEK_SET);
|
|
|
|
if (retval)printf(" fseek returned %d\n", retval);
|
|
|
|
retval = fread(buf, len, 2048, fpin);
|
|
|
|
if (retval != 2048)printf(" fread returned %d\n", retval);
|
|
|
|
return 0;
|
|
|
|
#else
|
|
retval = DI2_ReadDVD(buf, len, lba);
|
|
|
|
//if(retval)printf("Error %d reading sectors %d->%d\n",retval,lba,lba+len-1);
|
|
return retval;
|
|
|
|
#endif
|
|
}
|
|
|
|
int WIIDVD_DiscPresent()
|
|
{
|
|
#ifdef DEBUG
|
|
return 1;
|
|
#else
|
|
uint32_t val;
|
|
|
|
if (!dvd_initied) return 0;
|
|
|
|
DI2_GetCoverRegister(&val);
|
|
|
|
if (val&0x2) return 1;
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
}
|
|
|
|
static struct DIRENTRY * name_to_dentry(char *filename, uint8_t file_permitted, uint8_t dir_permitted) //filename must be absolute, without dvd: at beginning
|
|
{
|
|
//int done=0;
|
|
|
|
struct DIRENTRY * dentryptr;
|
|
char *charptr, *nextcharptr;
|
|
char nametofind[MAXPATH];
|
|
uint8_t found;
|
|
|
|
if (filename[0] == '/')filename++; //Skip initial /
|
|
|
|
strcpy(nametofind, filename);
|
|
|
|
//printf("name_to_dentry: '%s'\n",nametofind);
|
|
|
|
if (!direntriesptr) return NULL;
|
|
|
|
dentryptr = direntriesptr->child;
|
|
|
|
if (!dentryptr) return NULL;
|
|
|
|
charptr = strtok(nametofind, "/");
|
|
|
|
//printf("charptr: '%s'\n",charptr);
|
|
if (charptr == NULL)
|
|
{
|
|
return direntriesptr;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (charptr == NULL) return NULL;
|
|
|
|
nextcharptr = strtok(NULL, "/");
|
|
|
|
// printf("searching for: '%s'\n",charptr);
|
|
|
|
found = 0;
|
|
|
|
do
|
|
{
|
|
// printf("Comparing '%s' with '%s'\n",dentryptr->name,charptr);
|
|
|
|
if (strcmp(dentryptr->name, charptr) == 0)
|
|
{
|
|
found = 1;
|
|
|
|
if (nextcharptr) //non-last path item
|
|
{
|
|
|
|
if (!dentryptr->isdir) return NULL; //we are searching for a file inside this non-directory item
|
|
|
|
// printf("Is a directory because the following item is '%s'\n",nextcharptr);
|
|
dentryptr = dentryptr->child;
|
|
|
|
if (!dentryptr) return NULL;
|
|
|
|
// printf("Found child '%s'\n",dentryptr->name);
|
|
}
|
|
else //last path item
|
|
{
|
|
if ((dentryptr->isfile) && (!file_permitted)) return NULL; //we don't want files
|
|
|
|
if ((dentryptr->isdir) && (!dir_permitted)) return NULL; //we don't want directory
|
|
|
|
// if(dentryptr->isfile)printf("Found file '%s'\n",dentryptr->name);
|
|
// if(dentryptr->isdir)printf("Found directory '%s'\n",dentryptr->name);
|
|
return dentryptr;
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
dentryptr = dentryptr->next;
|
|
}
|
|
}
|
|
|
|
while ((!found) && (dentryptr));
|
|
|
|
if (!found) return NULL;
|
|
|
|
charptr = nextcharptr;
|
|
}
|
|
|
|
while (dentryptr);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static char *absolute_path_without_device(const char *srcpath, char *destpath)
|
|
{
|
|
//Thanks to Chishm (libfat)
|
|
// Move the path pointer to the start of the actual path
|
|
|
|
if (strchr (srcpath, ':') != NULL)
|
|
{
|
|
srcpath = strchr (srcpath, ':') + 1;
|
|
}
|
|
|
|
if (strchr (srcpath, ':') != NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (srcpath[0] != '/') //relative path
|
|
{
|
|
strcpy(destpath, currentpath);
|
|
strcat(destpath, srcpath);
|
|
}
|
|
else
|
|
{
|
|
strcpy(destpath, srcpath);
|
|
}
|
|
|
|
return destpath;
|
|
}
|
|
|
|
#ifndef DEBUG
|
|
|
|
static int dentrystat(struct DIRENTRY *dentry, struct stat *st)
|
|
{
|
|
if (!st) return -1;
|
|
|
|
if (!dentry) return -1;
|
|
|
|
st->st_dev = 0; //?!??!?
|
|
|
|
st->st_ino = (ino_t)dentry->fake_inode;
|
|
|
|
// st->st_mode=(dentry->isdir ? S_IFDIR : S_IFREG) | (S_IRUSR | S_IRGRP | S_IROTH);
|
|
st->st_mode = (dentry->isdir ? S_IFDIR : S_IFREG);
|
|
|
|
st->st_nlink = 1; // Always one hard link on a ISO entry
|
|
|
|
st->st_uid = 1; // Faked
|
|
|
|
st->st_rdev = st->st_dev;
|
|
|
|
st->st_gid = 2; // Faked
|
|
|
|
st->st_size = dentry->size_bytes;
|
|
|
|
st->st_atime = 0; //FIXME!
|
|
|
|
// st->st_spare1 = 0;
|
|
st->st_mtime = 0; //FIXME;
|
|
|
|
// st->st_spare2 = 0;
|
|
st->st_ctime = 0; //FIXME
|
|
|
|
// st->st_spare3 = 0;
|
|
st->st_blksize = 0x800;
|
|
|
|
st->st_blocks = dentry->size_sectors;
|
|
|
|
st->st_spare4[0] = 0;
|
|
|
|
st->st_spare4[1] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef DEBUG
|
|
|
|
static int _DVD_dirclose_r (struct _reent *r, DIR_ITER *dirState)
|
|
{
|
|
|
|
struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct;
|
|
|
|
// printf("DVD_dirclose\n");
|
|
|
|
if (!(mydirstate->inUse))
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
memset(mydirstate, 0, sizeof(struct DIR_STATE));
|
|
mydirstate->inUse = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int _DVD_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat)
|
|
{
|
|
|
|
struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct;
|
|
|
|
// printf("DVD_dirnext\n");
|
|
|
|
if (!(mydirstate->inUse))
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
if (mydirstate->curdentry == NULL)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
// skip .. at root
|
|
if (mydirstate->curdentry->fake_inode == 4 && strcmp(mydirstate->curdentry->name, "..") == 0)
|
|
{
|
|
mydirstate->curdentry = mydirstate->curdentry->next;
|
|
|
|
if (mydirstate->curdentry == NULL)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
strncpy(filename, mydirstate->curdentry->name, MAXNAMELEN);
|
|
|
|
if (filestat != NULL)
|
|
{
|
|
dentrystat(mydirstate->curdentry, filestat);
|
|
}
|
|
|
|
mydirstate->curdentry = mydirstate->curdentry->next;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _DVD_dirreset_r (struct _reent *r, DIR_ITER *dirState)
|
|
{
|
|
|
|
struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct;
|
|
|
|
// printf("DVD_dirreset\n");
|
|
|
|
if (!(mydirstate->inUse))
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
mydirstate->curdentry = mydirstate->firstdentry;
|
|
return 0;
|
|
}
|
|
|
|
static DIR_ITER* _DVD_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path)
|
|
{
|
|
char path_absolute[MAXPATH];
|
|
|
|
struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct;
|
|
|
|
struct DIRENTRY * dentry;
|
|
|
|
// printf("DVD_diropen\n");
|
|
|
|
if (absolute_path_without_device(path, path_absolute) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
dentry = name_to_dentry(path_absolute, 1, 1); //filename must be absolute, without dvd: at beginning
|
|
|
|
if (!dentry)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return NULL;
|
|
}
|
|
|
|
if (!(dentry->isdir))
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
return NULL;
|
|
}
|
|
|
|
mydirstate->firstdentry = dentry->child;
|
|
mydirstate->curdentry = dentry->child;
|
|
mydirstate->inUse = 1;
|
|
|
|
// printf("firstdentry: '%s'\n",dentry->name);
|
|
return dirState;
|
|
}
|
|
|
|
static int _DVD_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
|
{
|
|
// printf("DVD_statvfs\n");
|
|
|
|
// FAT clusters = POSIX blocks
|
|
buf->f_bsize = 0x800; // File system block size.
|
|
buf->f_frsize = 0x800; // Fundamental file system block size.
|
|
|
|
buf->f_blocks = totalsectors; // Total number of blocks on file system in units of f_frsize.
|
|
buf->f_bfree = 0; // Total number of free blocks.
|
|
buf->f_bavail = 0; // Number of free blocks available to non-privileged process.
|
|
|
|
// Treat requests for info on inodes as clusters
|
|
buf->f_files = totalentries; // Total number of file serial numbers.
|
|
buf->f_ffree = 0; // Total number of free file serial numbers.
|
|
buf->f_favail = 0; // Number of file serial numbers available to non-privileged process.
|
|
|
|
// File system ID. 32bit ioType value
|
|
buf->f_fsid = 0; //??!!?
|
|
|
|
// Bit mask of f_flag values.
|
|
buf->f_flag = ST_NOSUID // No support for ST_ISUID and ST_ISGID file mode bits
|
|
| ST_RDONLY ; // Read only file system
|
|
// Maximum filename length.
|
|
buf->f_namemax = MAXNAMELEN;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int _DVD_chdir_r (struct _reent *r, const char *path)
|
|
{
|
|
char path_absolute[MAXPATH];
|
|
|
|
struct DIRENTRY * dentry;
|
|
|
|
// printf("DVD_chdir\n");
|
|
|
|
if (absolute_path_without_device(path, path_absolute) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
// printf("path_absolute: %s\n",path_absolute);
|
|
dentry = name_to_dentry(path_absolute, 1, 1); //filename must be absolute, without dvd: at beginning
|
|
|
|
if (!dentry)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (!(dentry->isdir))
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
|
|
strcpy(currentpath, path_absolute);
|
|
|
|
if (currentpath[0] != 0)
|
|
{
|
|
if (currentpath[strlen(currentpath) - 1] != '/')strcat(currentpath, "/");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DEBUG
|
|
|
|
static int _DVD_stat_r (struct _reent *r, const char *path, struct stat *st)
|
|
{
|
|
char path_absolute[MAXPATH];
|
|
|
|
struct DIRENTRY * dentry;
|
|
|
|
// printf("DVD_stat\n");
|
|
|
|
if (absolute_path_without_device(path, path_absolute) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
dentry = name_to_dentry(path_absolute, 1, 1); //filename must be absolute, without dvd: at beginning
|
|
|
|
if (!dentry)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
dentrystat(dentry, st);
|
|
return 0;
|
|
}
|
|
|
|
static int _DVD_fstat_r (struct _reent *r, int fd, struct stat *st)
|
|
{
|
|
|
|
struct FILESTATE *filestate = (struct FILESTATE *)fd;
|
|
|
|
// printf("DVD_fstat\n");
|
|
|
|
|
|
if (!filestate)
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
if (!(filestate->dentry->isfile))
|
|
{
|
|
r->_errno = EISDIR;
|
|
return -1;
|
|
}
|
|
|
|
st->st_ino = filestate->dentry->fake_inode;
|
|
st->st_size = filestate->dentry->size_bytes;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static off_t _DVD_seek_r (struct _reent *r, int fd, off_t pos, int dir)
|
|
{
|
|
|
|
struct FILESTATE *filestate = (struct FILESTATE *)fd;
|
|
int newpos = 0;
|
|
|
|
// printf("DVD_seek fd: %d pos:%d ",fd,pos);
|
|
// if(dir==SEEK_SET)printf("SEEK_SET\n");
|
|
// if(dir==SEEK_CUR)printf("SEEK_CUR\n");
|
|
// if(dir==SEEK_END)printf("SEEK_END\n");
|
|
|
|
if (!filestate)
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
if (dir == SEEK_SET)
|
|
{
|
|
newpos = pos;
|
|
}
|
|
|
|
else if (dir == SEEK_CUR)
|
|
{
|
|
newpos = pos + (filestate->pos_bytes);
|
|
|
|
if (newpos > (filestate->size_bytes))newpos = filestate->size_bytes;
|
|
|
|
if (newpos < (filestate->pos_bytes))
|
|
{
|
|
r->_errno = EOVERFLOW;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
else if (dir == SEEK_END)
|
|
{
|
|
newpos = (filestate->size_bytes) - pos;
|
|
|
|
if (newpos < 0)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
// printf(" pos:%d newpos:%d\n",pos,newpos);
|
|
filestate->pos_bytes = newpos;
|
|
|
|
return newpos;
|
|
}
|
|
|
|
static ssize_t _DVD_read_r (struct _reent *r, int fd, char *ptr, size_t len)
|
|
{
|
|
|
|
struct FILESTATE *filestate = (struct FILESTATE *)fd;
|
|
int bytestoread, bytesread, smallread, localpos, sectorneeded, retval;
|
|
|
|
|
|
if (!filestate)
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
// printf("DVD_read fd: %d curpos: %d :len:%d ",fd,filestate->pos_bytes,len);
|
|
|
|
if ((len + (filestate->pos_bytes)) > (filestate->size_bytes))
|
|
{
|
|
len = (filestate->size_bytes) - (filestate->pos_bytes);
|
|
|
|
if (len < 0)len = 0;
|
|
|
|
r->_errno = EOVERFLOW;
|
|
|
|
if (len == 0) return 0;
|
|
}
|
|
|
|
bytestoread = len;
|
|
bytesread = 0;
|
|
|
|
do
|
|
{
|
|
smallread = bytestoread;
|
|
|
|
sectorneeded = ((filestate->pos_bytes) >> 11) + (filestate->firstsector);
|
|
localpos = (filestate->pos_bytes) & 0x7FF;
|
|
|
|
if (smallread > (0x800 - localpos))smallread = 0x800 - localpos;
|
|
|
|
|
|
if (filestate->localsectbuf == NULL)
|
|
{
|
|
filestate->localsectbuf = (uint8_t *)memalign(32, 0x800);
|
|
|
|
if (filestate->localsectbuf == NULL)
|
|
{
|
|
r->_errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
filestate->localsectnum = sectorneeded + 1; //make sure we don't use this empty buffer!
|
|
}
|
|
|
|
if (sectorneeded != filestate->localsectnum)
|
|
{
|
|
filestate->localsectnum = sectorneeded;
|
|
retval = ReadSectorFromCache(filestate->localsectbuf, sectorneeded);
|
|
|
|
if (retval)
|
|
{
|
|
r->_errno = EIO;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// printf("bytestoread: %d localpos: %d, smallread: %d\n",bytestoread,localpos,smallread);
|
|
// dump_hex(&(filestate->localsectbuf[localpos]),smallread);
|
|
memcpy(ptr, (filestate->localsectbuf) + localpos, smallread);
|
|
|
|
ptr += smallread;
|
|
|
|
bytestoread -= smallread;
|
|
|
|
bytesread += smallread;
|
|
|
|
filestate->pos_bytes += smallread;
|
|
}
|
|
|
|
while (bytestoread > 0);
|
|
|
|
// printf("Read %d bytes\n",bytesread);
|
|
return bytesread;
|
|
}
|
|
|
|
static int _DVD_close_r (struct _reent *r, int fd)
|
|
{
|
|
|
|
struct FILESTATE *filestate = (struct FILESTATE *)fd;
|
|
// printf("DVD_close\n");
|
|
|
|
if (filestate == NULL)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (filestate->localsectbuf)free(filestate->localsectbuf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int _DVD_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
|
{
|
|
|
|
struct FILESTATE *filestate = (struct FILESTATE *)fileStruct;
|
|
char file_absolute[MAXPATH];
|
|
|
|
struct DIRENTRY * dentry;
|
|
|
|
// printf("DVD_open\n");
|
|
|
|
if (!WIIDVD_DiscPresent())
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (absolute_path_without_device(path, file_absolute) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
// Determine which mode the file is openned for
|
|
if ((flags & 0x03) != O_RDONLY)
|
|
{
|
|
r->_errno = EROFS;
|
|
return -1;
|
|
}
|
|
|
|
// printf("Trying to open '%s'\n",file_absolute);
|
|
dentry = name_to_dentry(file_absolute, 1, 1); //filename must be absolute, without dvd: at beginning
|
|
|
|
if (!dentry)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (dentry->isdir)
|
|
{
|
|
r->_errno = EISDIR;
|
|
return -1;
|
|
}
|
|
|
|
filestate->dentry = dentry;
|
|
filestate->pos_bytes = 0;
|
|
filestate->size_bytes = dentry->size_bytes;
|
|
filestate->firstsector = dentry->firstsector;
|
|
filestate->localsectnum = 0;
|
|
filestate->localsectbuf = NULL;
|
|
|
|
// printf("Opened file '%s', firstsector: %d\n",filestate->dentry->name,filestate->firstsector);
|
|
return (int)filestate;
|
|
}
|
|
|
|
static uint32_t bothendian32_to_uint32(uint8_t *buff)
|
|
{
|
|
uint32_t val;
|
|
val = ((uint32_t)buff[4]) << 24;
|
|
val |= ((uint32_t)buff[5]) << 16;
|
|
val |= ((uint32_t)buff[6]) << 8;
|
|
val |= ((uint32_t)buff[7]);
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
static uint32_t bigendian32_to_uint32(uint8_t *buff)
|
|
{
|
|
uint32_t val;
|
|
val=((uint32_t)buff[0])<<24;
|
|
val|=((uint32_t)buff[1])<<16;
|
|
val|=((uint32_t)buff[2])<<8;
|
|
val|=((uint32_t)buff[3]);
|
|
return val;
|
|
}
|
|
|
|
static uint16_t bigendian16_to_uint16(uint8_t *buff)
|
|
{
|
|
uint16_t val;
|
|
val=((uint16_t)buff[0])<<8;
|
|
val|=((uint16_t)buff[1]);
|
|
return val;
|
|
}
|
|
*/
|
|
|
|
static void freedirentryandchilds(struct DIRENTRY *dentry)
|
|
{
|
|
|
|
struct DIRENTRY *dentrynext;
|
|
|
|
if (!dentry) return ;
|
|
|
|
do
|
|
{
|
|
dentrynext = dentry->next;
|
|
|
|
if (dentry->child)freedirentryandchilds(dentry->child);
|
|
|
|
free(dentry);
|
|
|
|
dentry = dentrynext;
|
|
}
|
|
|
|
while (dentry);
|
|
|
|
}
|
|
|
|
static void freedirentrieslist()
|
|
{
|
|
freedirentryandchilds(direntriesptr);
|
|
direntriesptr = 0;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
static void debug_dump_direntry(int tablevel, struct DIRENTRY *dentry)
|
|
{
|
|
char spaces[33] = " ";
|
|
spaces[tablevel*2] = 0;
|
|
|
|
if (!dentry)
|
|
{
|
|
printf("%sNull Pointer!!!\n", spaces);
|
|
}
|
|
|
|
|
|
printf("%sName: '%s' FirstSector: %d isfile: %d isdir: %d ishidden: %d\r\n", spaces, dentry->name, dentry->firstsector, dentry->isfile, dentry->isdir, dentry->ishidden);
|
|
|
|
}
|
|
|
|
static void debug_dump_tree_recurse(int level, struct DIRENTRY *dentry, char *path_parent)
|
|
{
|
|
char path[MAXPATH];
|
|
char spaces[33] = " ";
|
|
spaces[level*2] = 0;
|
|
|
|
printf("%sPath: %s\n", spaces, path_parent);
|
|
|
|
do
|
|
{
|
|
if (dentry->isdir)
|
|
{
|
|
if (dentry->child)
|
|
{
|
|
strcpy(path, path_parent);
|
|
strcat(path, dentry->name);
|
|
strcat(path, "/");
|
|
debug_dump_tree_recurse(level + 1, dentry->child, path);
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
printf("%sFile: %s%s\n", spaces, path_parent, dentry->name);
|
|
// debug_dump_direntry(level,dentry);
|
|
}
|
|
|
|
dentry = dentry->next;
|
|
}
|
|
|
|
while (dentry);
|
|
|
|
}
|
|
|
|
static void debug_dump_tree()
|
|
{
|
|
if (direntriesptr)debug_dump_tree_recurse(0, direntriesptr, "dvd:/");
|
|
}
|
|
|
|
#endif
|
|
|
|
static int DVD_ScanContent_recurse(int jolietmode, struct DIRENTRY * parent, uint8_t level, uint32_t sectnum, uint32_t dirlen, int *fake_inodecounter)
|
|
{
|
|
int retval, i;
|
|
uint8_t * ptr, avoid_recursion;
|
|
uint32_t sect_from, sect_to, sect, pos;
|
|
uint8_t len;
|
|
uint8_t rockridge_enabled = 0;
|
|
uint8_t rockridge_bytestoskip = 0;
|
|
|
|
struct DIRENTRY *direntryptr, *direntryptr_prev;
|
|
uint8_t *localsectbuf;
|
|
|
|
localsectbuf = (uint8_t *)memalign(32, 0x800);
|
|
|
|
if (!localsectbuf) return -1;
|
|
|
|
sect_from = sectnum;
|
|
|
|
sect_to = sectnum + ((dirlen - 1) >> 11);
|
|
|
|
// for(i=0;i<level;i++)printf(" ");printf("Start sector: %d End Sector: %d\n",sect_from,sect_to);
|
|
|
|
direntryptr = 0;
|
|
|
|
direntryptr_prev = 0;
|
|
|
|
for (sect = sect_from;sect <= sect_to;sect++)
|
|
{
|
|
// for(i=0;i<=level;i++)printf(" ");printf("Read sector %u\n",sect);
|
|
retval = ReadSectorFromCache(localsectbuf, sect);
|
|
|
|
if (retval != 0)
|
|
{
|
|
//for(i=0;i<=level;i++)printf(" ");printf("Error reading sector %d\n",sect);
|
|
return -1;
|
|
}
|
|
|
|
pos = 0;
|
|
|
|
do
|
|
{
|
|
uint8_t flags, namelen;
|
|
|
|
ptr = &localsectbuf[pos];
|
|
len = ptr[0];
|
|
namelen = ptr[32];
|
|
// printf("len: %d namelen:%d\n",len,namelen);
|
|
|
|
if (len && namelen)
|
|
{
|
|
|
|
direntryptr = (struct DIRENTRY *)malloc(sizeof(struct DIRENTRY));
|
|
|
|
if (direntryptr == NULL) return -1;
|
|
|
|
totalentries++;
|
|
|
|
memset(direntryptr, 0, sizeof(struct DIRENTRY));
|
|
|
|
*fake_inodecounter = (*fake_inodecounter) + 1;
|
|
|
|
direntryptr->fake_inode = *fake_inodecounter;
|
|
|
|
direntryptr->firstsector = bothendian32_to_uint32(&ptr[2]);
|
|
|
|
direntryptr->size_bytes = bothendian32_to_uint32(&ptr[10]);
|
|
|
|
direntryptr->size_sectors = ((direntryptr->size_bytes) - 1) >> 11;
|
|
|
|
flags = ptr[25];
|
|
|
|
direntryptr->next = NULL;
|
|
|
|
direntryptr->isfile = (flags & 0x02) ? 0 : 1;
|
|
|
|
direntryptr->isdir = (flags & 0x02) ? 1 : 0;
|
|
|
|
direntryptr->ishidden = (flags & 0x01) ? 1 : 0;
|
|
|
|
avoid_recursion = 0;
|
|
|
|
namelen = ptr[32];
|
|
|
|
if (jolietmode)
|
|
{
|
|
// if(namelen>128)return -1;
|
|
|
|
// printf("namelen: %d\n",namelen);
|
|
// dump_hex(&ptr[33],32);
|
|
|
|
if (namelen == 1)
|
|
{
|
|
if ((ptr[33] == 0)) // . (self)
|
|
{
|
|
strcpy(direntryptr->name, ".");
|
|
avoid_recursion = 1;
|
|
}
|
|
if ((ptr[33] == 1)) // .. (parent)
|
|
{
|
|
strcpy(direntryptr->name, "..");
|
|
avoid_recursion = 1;
|
|
}
|
|
}
|
|
|
|
else //if namelen != 1
|
|
{
|
|
uint8_t *charptr;
|
|
namelen >>= 1;
|
|
|
|
charptr = &ptr[33];
|
|
|
|
for (i = 0;i < namelen;i++)
|
|
{
|
|
if ((charptr[0] == 0) && (charptr[1] >= 32) && (charptr[1] < 0x80))
|
|
direntryptr->name[i] = charptr[1];
|
|
else
|
|
direntryptr->name[i] = '_'; //we don't support multi-byte characters or extended ascii characters, so replace the strange things with this symbol
|
|
|
|
charptr += 2;
|
|
}
|
|
|
|
direntryptr->name[i] = 0;
|
|
}
|
|
}
|
|
|
|
else //non-joliet mode, so ISO9660:1999 mode with or without RockRidge extension
|
|
{
|
|
unsigned int bytesused;
|
|
|
|
if (namelen > 207) return -1;
|
|
|
|
memcpy(direntryptr->name, &ptr[33], namelen);
|
|
|
|
bytesused = 33 + namelen;
|
|
|
|
if ((namelen&0x01) == 0)bytesused++; //padding
|
|
|
|
if ((namelen == 1) && (direntryptr->name[0] == 0) && (direntryptr->name[1] == 0)) // . (self)
|
|
{
|
|
strcpy(direntryptr->name, ".");
|
|
avoid_recursion = 1;
|
|
|
|
if ((level == 0) && (!jolietmode))
|
|
{
|
|
if ((ptr[bytesused] == 'S') && (ptr[bytesused + 1] == 'P') && (ptr[bytesused + 2] == 7) && (ptr[bytesused + 3] == 1) && (ptr[bytesused + 4] == 0xBE) && (ptr[bytesused + 5] == 0xEF))
|
|
{
|
|
rockridge_enabled = 1;
|
|
rockridge_bytestoskip = ptr[bytesused + 6];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ((namelen == 1) && (direntryptr->name[0] == 1) && (direntryptr->name[1] == 0)) // .. (parent)
|
|
{
|
|
strcpy(direntryptr->name, "..");
|
|
avoid_recursion = 1;
|
|
}
|
|
|
|
|
|
//check for RockRidge extension
|
|
if ((len > bytesused) && (rockridge_enabled)) //we have other data in System Use field
|
|
{
|
|
|
|
uint8_t *RRAttribute_ptr;
|
|
uint8_t RRAttribute_len;
|
|
uint8_t RRAttribute_ver;
|
|
|
|
bytesused += rockridge_bytestoskip;
|
|
// printf("entry %s namelen %d bytesused: %u \n",direntryptr->name,namelen,bytesused);
|
|
// dump_hex(&ptr[bytesused],len-bytesused);
|
|
|
|
do
|
|
{
|
|
RRAttribute_ptr = &ptr[bytesused];
|
|
RRAttribute_len = RRAttribute_ptr[2] - 4;
|
|
RRAttribute_ver = RRAttribute_ptr[3];
|
|
|
|
// printf("Signature: %c%c len(payload): %d ver: %d datalen: %d\n",RRAttribute_ptr[0],RRAttribute_ptr[1],RRAttribute_ptr[2],RRAttribute_ptr[3],RRAttribute_len);
|
|
// dump_hex(RRAttribute_ptr,RRAttribute_len+4);
|
|
|
|
if (RRAttribute_ver == 1)
|
|
{
|
|
|
|
if ((RRAttribute_ptr[0] == 'N') && (RRAttribute_ptr[1] == 'M')) //Name attribute
|
|
{
|
|
namelen = RRAttribute_len - 1;
|
|
memcpy(direntryptr->name, &RRAttribute_ptr[5], namelen);
|
|
direntryptr->name[namelen] = 0;
|
|
}
|
|
}
|
|
|
|
bytesused += RRAttribute_ptr[2];
|
|
}
|
|
|
|
while ((bytesused < len) && (RRAttribute_ptr[2] > 0));
|
|
}
|
|
}
|
|
|
|
if (namelen > 2)
|
|
{
|
|
if ((direntryptr->name[namelen - 2] == ';') && (direntryptr->name[namelen - 1] == '1'))direntryptr->name[namelen - 2] = 0;
|
|
|
|
if (direntryptr->name[namelen - 1] == ';')direntryptr->name[namelen - 1] = 0;
|
|
}
|
|
|
|
if (!direntryptr_prev) //first item in chain
|
|
{
|
|
|
|
if (parent)parent->child = direntryptr;
|
|
else direntriesptr = direntryptr;
|
|
}
|
|
else
|
|
{
|
|
direntryptr_prev->next = direntryptr;
|
|
}
|
|
|
|
// debug_dump_direntry(level,direntryptr);
|
|
|
|
if ((direntryptr->isdir) && (!avoid_recursion))
|
|
DVD_ScanContent_recurse(jolietmode, direntryptr, level + 1, direntryptr->firstsector, direntryptr->size_bytes, fake_inodecounter);
|
|
}
|
|
|
|
pos += len;
|
|
direntryptr_prev = direntryptr;
|
|
}
|
|
|
|
while ((len != 0) && (pos < 2048));
|
|
}
|
|
|
|
free(localsectbuf);
|
|
return 0;
|
|
}
|
|
|
|
static int DVD_ScanContent()
|
|
{
|
|
int retval, currsect, done;
|
|
uint32_t rootsect = 0;
|
|
uint32_t rootdirlength = 0;
|
|
int fake_inodecounter = 1;
|
|
int fstype = -1; //0:standard ISO9660, 1:joliet
|
|
uint8_t *sectbuf;
|
|
uint8_t maxsectorstotry = 100;
|
|
|
|
if (!WIIDVD_DiscPresent()) return -1;
|
|
|
|
sectbuf = (uint8_t *)memalign(32, 0x800);
|
|
|
|
if (!sectbuf) return -2;
|
|
|
|
currsect = 16;
|
|
|
|
done = 0;
|
|
|
|
do
|
|
{
|
|
|
|
//read first volume descriptor
|
|
retval = ReadSectorFromCache(sectbuf, currsect);
|
|
// retval=WIIDVD_ReadDVD(sectbuf,1,currsect);
|
|
|
|
if (retval != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// printf("Descriptor @ sect %d:\n",currsect);
|
|
// dump_hex(sectbuf,7);
|
|
|
|
if ((sectbuf[1] == 67) && (sectbuf[2] == 68) && (sectbuf[3] == 48) && (sectbuf[4] == 48) && (sectbuf[5] == 49) && (sectbuf[6] == 1)) //volume descriptor signature
|
|
{
|
|
|
|
if (sectbuf[0] == 255)done = 1; //end marker;
|
|
|
|
if ((sectbuf[0] == 0x02) && (fstype <= 1)) //Supplementart Volume Descriptor
|
|
{
|
|
// printf("Supplementary Volume Descriptor found @ sect %d\n",currsect);
|
|
// printf(" Escape sequence:");
|
|
// dump_hex(§buf[88],32);
|
|
// printf("\n");
|
|
totalsectors = bothendian32_to_uint32(§buf[80]);
|
|
// printf("Total sectors: %u\n",totalsectors);
|
|
|
|
rootsect = bothendian32_to_uint32(§buf[158]);
|
|
// printf("Root Sector: %d\n",rootsect);
|
|
rootdirlength = bothendian32_to_uint32(§buf[166]);
|
|
// printf("Root Length: %d\n",rootdirlength);
|
|
|
|
if ((sectbuf[88] == 0x25) && (sectbuf[89] == 0x2F) && ((sectbuf[90] == 0x40) || (sectbuf[90] == 0x43) || (sectbuf[90] == 0x45)))
|
|
{
|
|
// if(sectbuf[90]==0x40)printf(" joliet Level1 descriptor found\n");
|
|
// if(sectbuf[90]==0x43)printf(" joliet Level2 descriptor found\n");
|
|
// if(sectbuf[90]==0x45)printf(" joliet Level3 descriptor found\n");
|
|
fstype = 1; //joliet
|
|
}
|
|
|
|
}
|
|
|
|
if ((sectbuf[0] == 0x01) && (fstype <= 0)) //Primary Volume Descriptor
|
|
{
|
|
// printf("Primary Volume Descriptor found @ sect %d\n",currsect);
|
|
totalsectors = bothendian32_to_uint32(§buf[80]);
|
|
// printf("Total sectors: %u\n",totalsectors);
|
|
|
|
rootsect = bothendian32_to_uint32(§buf[158]);
|
|
// printf("Root Sector: %d\n",rootsect);
|
|
rootdirlength = bothendian32_to_uint32(§buf[166]);
|
|
// printf("Root Length: %d\n",rootdirlength);
|
|
fstype = 0;
|
|
}
|
|
|
|
}
|
|
currsect++;
|
|
maxsectorstotry--;
|
|
}
|
|
|
|
while ((!done) && (maxsectorstotry));
|
|
|
|
if (fstype >= 0)
|
|
{
|
|
|
|
struct DIRENTRY *direntryptr;
|
|
|
|
if (direntriesptr)freedirentrieslist();
|
|
|
|
direntryptr = (struct DIRENTRY *)malloc(sizeof(struct DIRENTRY));
|
|
|
|
if (direntryptr == NULL) return -1;
|
|
|
|
totalentries++;
|
|
|
|
memset(direntryptr, 0, sizeof(struct DIRENTRY));
|
|
|
|
direntryptr->fake_inode = fake_inodecounter++;
|
|
|
|
direntryptr->firstsector = rootsect;
|
|
|
|
direntryptr->size_bytes = rootdirlength;
|
|
|
|
direntryptr->size_sectors = ((direntryptr->size_bytes) - 1) >> 11;
|
|
|
|
direntryptr->next = NULL;
|
|
|
|
direntryptr->child = NULL;
|
|
|
|
direntryptr->name[0] = 0;
|
|
|
|
direntryptr->isfile = 0;
|
|
|
|
direntryptr->isdir = 1;
|
|
|
|
direntriesptr = direntryptr;
|
|
|
|
return DVD_ScanContent_recurse(fstype == 1 ? 1 : 0, direntryptr, 0, rootsect, rootdirlength, &fake_inodecounter);
|
|
|
|
// debug_dump_tree();
|
|
}
|
|
|
|
free(sectbuf);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef DEBUG
|
|
const devoptab_t dotab_dvd =
|
|
{
|
|
"dvd",
|
|
|
|
sizeof (struct FILESTATE),
|
|
_DVD_open_r,
|
|
_DVD_close_r,
|
|
NULL,
|
|
_DVD_read_r,
|
|
_DVD_seek_r,
|
|
_DVD_fstat_r,
|
|
_DVD_stat_r,
|
|
NULL,
|
|
NULL,
|
|
_DVD_chdir_r,
|
|
NULL,
|
|
NULL,
|
|
|
|
sizeof (struct DIR_STATE),
|
|
_DVD_diropen_r,
|
|
_DVD_dirreset_r,
|
|
_DVD_dirnext_r,
|
|
_DVD_dirclose_r,
|
|
_DVD_statvfs_r,
|
|
NULL, // device ftruncate_r
|
|
NULL, // device fsync_r
|
|
NULL /* Device data */
|
|
|
|
};
|
|
|
|
|
|
static void dotab_dvd_add(void)
|
|
{
|
|
AddDevice(&dotab_dvd);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUG
|
|
int main(int argc, char *argv[])
|
|
{
|
|
printf("PC DEBUG MODE\n");
|
|
WIIDVD_Init();
|
|
WIIDVD_Mount();
|
|
debug_dump_tree();
|
|
WIIDVD_Close();
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
///////////////////////////////////////////
|
|
// CACHE FUNCTIONS //
|
|
///////////////////////////////////////////
|
|
|
|
|
|
static inline void _DVD_lock()
|
|
{
|
|
LWP_MutexLock(_DVD_mutex);
|
|
}
|
|
|
|
static inline void _DVD_unlock()
|
|
{
|
|
LWP_MutexUnlock(_DVD_mutex);
|
|
}
|
|
|
|
static void DestroyReadAheadCache()
|
|
{
|
|
int i;
|
|
|
|
if (ReadAheadCache == NULL)
|
|
{
|
|
RA_pages = 0;
|
|
RA_sectors = 0;
|
|
return ;
|
|
}
|
|
|
|
for (i = 0; i < RA_pages; i++)
|
|
{
|
|
if (ReadAheadCache[i].ptr != NULL)
|
|
free(ReadAheadCache[i].ptr);
|
|
}
|
|
|
|
free(ReadAheadCache);
|
|
ReadAheadCache = NULL;
|
|
RA_pages = 0;
|
|
RA_sectors = 0;
|
|
}
|
|
|
|
static void DVDEnableReadAhead(uint32_t pages, uint32_t sectors)
|
|
{
|
|
int i, j;
|
|
|
|
DestroyReadAheadCache();
|
|
|
|
if (pages == 0 || sectors == 0) return ;
|
|
|
|
if (sectors > 26)sectors = 26;
|
|
|
|
RA_pages = pages;
|
|
|
|
RA_sectors = sectors;
|
|
|
|
ReadAheadCache = (cache_page *)malloc(sizeof(cache_page) * RA_pages);
|
|
|
|
if (ReadAheadCache == NULL) return ;
|
|
|
|
for (i = 0; i < RA_pages; i++)
|
|
{
|
|
ReadAheadCache[i].sector = CACHE_FREE;
|
|
ReadAheadCache[i].last_used = 0;
|
|
ReadAheadCache[i].ptr = memalign(32, SECTOR_SIZE * RA_sectors);
|
|
|
|
if (ReadAheadCache[i].ptr == NULL)
|
|
{
|
|
for (j = i - 1;j >= 0;j--)
|
|
if (ReadAheadCache[j].ptr)free(ReadAheadCache[j].ptr);
|
|
|
|
free(ReadAheadCache);
|
|
|
|
ReadAheadCache = NULL;
|
|
|
|
return ;
|
|
}
|
|
|
|
memset(ReadAheadCache[i].ptr, 0, SECTOR_SIZE * RA_sectors);
|
|
}
|
|
}
|
|
|
|
static int ReadSectorFromCache(void *buf, uint32_t sector)
|
|
{
|
|
int retval;
|
|
int i, leastUsed;
|
|
|
|
if (ReadAheadCache == NULL) return WIIDVD_ReadDVD(buf, 1, sector);
|
|
|
|
_DVD_lock();
|
|
|
|
leastUsed = 0;
|
|
|
|
for (i = 0; i < RA_pages; i++)
|
|
{
|
|
if ( (sector >= ReadAheadCache[i].sector) && (sector < (ReadAheadCache[i].sector + RA_sectors)) )
|
|
{
|
|
ReadAheadCache[i].last_used = gettick();
|
|
memcpy(buf, ReadAheadCache[i].ptr + ((sector - ReadAheadCache[i].sector) * SECTOR_SIZE), SECTOR_SIZE);
|
|
_DVD_unlock();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
for (i = 1; i < RA_pages; i++)
|
|
{
|
|
if ((ReadAheadCache[i].last_used < ReadAheadCache[leastUsed].last_used))
|
|
leastUsed = i;
|
|
}
|
|
|
|
retval = WIIDVD_ReadDVD(ReadAheadCache[leastUsed].ptr, RA_sectors, sector);
|
|
|
|
if (retval)
|
|
{
|
|
ReadAheadCache[leastUsed].sector = CACHE_FREE;
|
|
ReadAheadCache[leastUsed].last_used = 0;
|
|
_DVD_unlock();
|
|
return retval;
|
|
}
|
|
|
|
ReadAheadCache[leastUsed].sector = sector;
|
|
ReadAheadCache[leastUsed].last_used = gettick();
|
|
memcpy(buf, ReadAheadCache[leastUsed].ptr, SECTOR_SIZE);
|
|
_DVD_unlock();
|
|
|
|
return 0;
|
|
}
|