mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-16 00:15:08 +01:00
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;
|
||
|
}
|