mirror of
https://gitlab.com/Nanolx/homebrewfilter.git
synced 2024-11-01 23:25:09 +01:00
754 lines
20 KiB
C
754 lines
20 KiB
C
/*
|
|
|
|
libiso -- an ISO9660 DVD devoptab library for the Wii
|
|
|
|
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
|
|
|
|
Modified by Dimok
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
In no event will the authors be held liable for any damages arising from
|
|
the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1.The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software in a
|
|
product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
|
|
2.Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
|
|
3.This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
#include "di2.h"
|
|
#include <errno.h>
|
|
#include <ogc/lwp_watchdog.h>
|
|
#include <ogcsys.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <sys/dir.h>
|
|
#include <sys/iosupport.h>
|
|
#include <sys/statvfs.h>
|
|
|
|
#include "iso.h"
|
|
|
|
#define DEVICE_NAME "dvd"
|
|
|
|
#define FLAG_DIR 2
|
|
#define DIR_SEPARATOR '/'
|
|
#define SECTOR_SIZE 0x800
|
|
#define BUFFER_SIZE 0x8000
|
|
|
|
#define UNUSED __attribute__((unused))
|
|
|
|
typedef struct {
|
|
u8 name_length;
|
|
u8 extended_sectors;
|
|
u32 sector;
|
|
u16 parent;
|
|
char name[ISO_MAXPATHLEN];
|
|
} __attribute__((packed)) PATHTABLE_ENTRY;
|
|
|
|
typedef struct PATH_ENTRY_STRUCT {
|
|
PATHTABLE_ENTRY table_entry;
|
|
u16 index;
|
|
u32 childCount;
|
|
struct PATH_ENTRY_STRUCT *children;
|
|
} PATH_ENTRY;
|
|
|
|
typedef struct DIR_ENTRY_STRUCT {
|
|
char name[ISO_MAXPATHLEN];
|
|
u32 sector;
|
|
u32 size;
|
|
u8 flags;
|
|
u32 fileCount;
|
|
PATH_ENTRY *path_entry;
|
|
struct DIR_ENTRY_STRUCT *children;
|
|
} DIR_ENTRY;
|
|
|
|
typedef struct {
|
|
DIR_ENTRY entry;
|
|
u32 offset;
|
|
bool inUse;
|
|
} FILE_STRUCT;
|
|
|
|
typedef struct {
|
|
DIR_ENTRY entry;
|
|
u32 index;
|
|
bool inUse;
|
|
} DIR_STATE_STRUCT;
|
|
|
|
static u8 * read_buffer = NULL;
|
|
static u8 * cluster_buffer = NULL;
|
|
static u32 cache_start = 0;
|
|
static u32 cache_sectors = 0;
|
|
|
|
static PATH_ENTRY *root = NULL;
|
|
static PATH_ENTRY *current = NULL;
|
|
static bool unicode = false;
|
|
static u64 last_access = 0;
|
|
static s32 dotab_device = -1;
|
|
|
|
static bool is_dir(DIR_ENTRY *entry) {
|
|
return entry->flags & FLAG_DIR;
|
|
}
|
|
|
|
#define OFFSET_EXTENDED 1
|
|
#define OFFSET_SECTOR 6
|
|
#define OFFSET_SIZE 14
|
|
#define OFFSET_FLAGS 25
|
|
#define OFFSET_NAMELEN 32
|
|
#define OFFSET_NAME 33
|
|
|
|
static int internal_read(void *ptr, u64 offset, size_t len)
|
|
{
|
|
u32 sector = offset / SECTOR_SIZE;
|
|
u32 end_sector = (offset + len - 1) / SECTOR_SIZE;
|
|
u32 sectors = MIN(BUFFER_SIZE / SECTOR_SIZE, end_sector - sector + 1);
|
|
u32 sector_offset = offset % SECTOR_SIZE;
|
|
len = MIN(BUFFER_SIZE - sector_offset, len);
|
|
if (cache_sectors && sector >= cache_start && (sector + sectors) <= (cache_start + cache_sectors)) {
|
|
memcpy(ptr, read_buffer + (sector - cache_start) * SECTOR_SIZE + sector_offset, len);
|
|
return len;
|
|
}
|
|
if (DI2_ReadDVD(read_buffer, BUFFER_SIZE / SECTOR_SIZE, sector)) {
|
|
last_access = gettime();
|
|
cache_sectors = 0;
|
|
u32 error;
|
|
if (DI2_GetError(&error)) return -1;
|
|
if ((error & 0xFFFFFF) == 0x020401) { // discid has to be read again
|
|
u64 discid;
|
|
DI2_ReadDiscID(&discid);
|
|
if (DI2_ReadDVD(read_buffer, BUFFER_SIZE / SECTOR_SIZE, sector))
|
|
return -1;
|
|
}
|
|
}
|
|
last_access = gettime();
|
|
cache_start = sector;
|
|
cache_sectors = BUFFER_SIZE / SECTOR_SIZE;
|
|
memcpy(ptr, read_buffer + sector_offset, len);
|
|
return len;
|
|
}
|
|
|
|
static int _read(void *ptr, u64 offset, u32 len)
|
|
{
|
|
s32 read = 0;
|
|
u32 done = 0;
|
|
u8 * dataptr = (u8 *) ptr;
|
|
|
|
while(done < len)
|
|
{
|
|
if((read = internal_read(dataptr+done, offset+done, len-done)) < 0) return read;
|
|
|
|
done += read;
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
static int read_entry(DIR_ENTRY *entry, u8 *buf) {
|
|
u8 extended_sectors = buf[OFFSET_EXTENDED];
|
|
u32 sector = *(u32 *)(buf + OFFSET_SECTOR) + extended_sectors;
|
|
u32 size = *(u32 *)(buf + OFFSET_SIZE);
|
|
u8 flags = buf[OFFSET_FLAGS];
|
|
u8 namelen = buf[OFFSET_NAMELEN];
|
|
|
|
if (namelen == 1 && buf[OFFSET_NAME] == 1) {
|
|
if(entry->path_entry->table_entry.parent > 0)
|
|
{
|
|
DIR_ENTRY *newChildren = realloc(entry->children, sizeof(DIR_ENTRY) * (entry->fileCount + 1));
|
|
if (!newChildren) return -1;
|
|
bzero(newChildren + entry->fileCount, sizeof(DIR_ENTRY));
|
|
entry->children = newChildren;
|
|
DIR_ENTRY *child = &entry->children[entry->fileCount++];
|
|
child->sector = sector;
|
|
child->size = size;
|
|
child->flags = flags;
|
|
strcpy(child->name, "..");
|
|
}
|
|
} else if (namelen == 1 && !buf[OFFSET_NAME]) {
|
|
entry->sector = sector;
|
|
entry->size = size;
|
|
entry->flags = flags;
|
|
} else {
|
|
DIR_ENTRY *newChildren = realloc(entry->children, sizeof(DIR_ENTRY) * (entry->fileCount + 1));
|
|
if (!newChildren) return -1;
|
|
bzero(newChildren + entry->fileCount, sizeof(DIR_ENTRY));
|
|
entry->children = newChildren;
|
|
DIR_ENTRY *child = &entry->children[entry->fileCount++];
|
|
child->sector = sector;
|
|
child->size = size;
|
|
child->flags = flags;
|
|
char *name = child->name;
|
|
if (unicode) {
|
|
u32 i;
|
|
for (i = 0; i < (namelen / 2); i++) name[i] = buf[OFFSET_NAME + i * 2 + 1];
|
|
name[i] = '\x00';
|
|
namelen = i;
|
|
} else {
|
|
memcpy(name, buf + OFFSET_NAME, namelen);
|
|
name[namelen] = '\x00';
|
|
}
|
|
if (!(flags & FLAG_DIR) && namelen >= 2 && name[namelen - 2] == ';') name[namelen - 2] = '\x00';
|
|
}
|
|
|
|
return *buf;
|
|
}
|
|
|
|
static bool read_directory(DIR_ENTRY *dir_entry, PATH_ENTRY *path_entry) {
|
|
u32 sector = path_entry->table_entry.sector;
|
|
u32 remaining = 0;
|
|
u32 sector_offset = 0;
|
|
|
|
do {
|
|
if (_read(cluster_buffer, (u64)sector * SECTOR_SIZE + sector_offset, (SECTOR_SIZE - sector_offset)) != (int) (SECTOR_SIZE - sector_offset)) return false;
|
|
int offset = read_entry(dir_entry, cluster_buffer);
|
|
if (offset == -1) return false;
|
|
if (!remaining) {
|
|
remaining = dir_entry->size;
|
|
dir_entry->path_entry = path_entry;
|
|
}
|
|
sector_offset += offset;
|
|
if (sector_offset >= SECTOR_SIZE || !cluster_buffer[offset]) {
|
|
remaining -= SECTOR_SIZE;
|
|
sector_offset = 0;
|
|
sector++;
|
|
}
|
|
} while (remaining > 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool path_entry_from_path(PATH_ENTRY *path_entry, const char *path) {
|
|
bool found = false;
|
|
bool notFound = false;
|
|
const char *pathPosition = path;
|
|
const char *pathEnd = strchr(path, '\0');
|
|
PATH_ENTRY *entry = root;
|
|
while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
|
|
if (pathPosition >= pathEnd) found = true;
|
|
PATH_ENTRY *dir = entry;
|
|
while (!found && !notFound) {
|
|
const char *nextPathPosition = strchr(pathPosition, DIR_SEPARATOR);
|
|
size_t dirnameLength;
|
|
if (nextPathPosition != NULL) dirnameLength = nextPathPosition - pathPosition;
|
|
else dirnameLength = strlen(pathPosition);
|
|
if (dirnameLength >= ISO_MAXPATHLEN) return false;
|
|
|
|
u32 childIndex = 0;
|
|
while (childIndex < dir->childCount && !found && !notFound) {
|
|
entry = &dir->children[childIndex];
|
|
if (dirnameLength == strnlen(entry->table_entry.name, ISO_MAXPATHLEN - 1) && !strncasecmp(pathPosition, entry->table_entry.name, dirnameLength)) found = true;
|
|
if (!found) childIndex++;
|
|
}
|
|
|
|
if (childIndex >= dir->childCount) {
|
|
notFound = true;
|
|
found = false;
|
|
} else if (!nextPathPosition || nextPathPosition >= pathEnd) {
|
|
found = true;
|
|
} else {
|
|
dir = entry;
|
|
pathPosition = nextPathPosition;
|
|
while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
|
|
if (pathPosition >= pathEnd) found = true;
|
|
else found = false;
|
|
}
|
|
}
|
|
|
|
if (found) memcpy(path_entry, entry, sizeof(PATH_ENTRY));
|
|
return found;
|
|
}
|
|
|
|
static bool find_in_directory(DIR_ENTRY *dir_entry, PATH_ENTRY *parent, const char *base) {
|
|
u32 nl = strlen(base);
|
|
|
|
// there will be no basename if we are looking for root
|
|
if (!nl) {
|
|
return read_directory(dir_entry, parent);
|
|
}
|
|
|
|
// check directories i know about first
|
|
u32 childIndex;
|
|
for (childIndex = 0; childIndex < parent->childCount; childIndex++) {
|
|
PATH_ENTRY *child = parent->children + childIndex;
|
|
if (nl == strnlen(child->table_entry.name, ISO_MAXPATHLEN - 1) && !strncasecmp(base, child->table_entry.name, nl)) {
|
|
// found the thing we're after and it is a directory
|
|
// read it into dir_entry, and return true
|
|
return read_directory(dir_entry, child);
|
|
}
|
|
}
|
|
|
|
// read ourselves into a DIR_ENTRY, look into children for matching file
|
|
if (!read_directory(dir_entry, parent)) return false;
|
|
for (childIndex = 0; childIndex < dir_entry->fileCount; childIndex++) {
|
|
DIR_ENTRY *child = dir_entry->children + childIndex;
|
|
if (nl == strnlen(child->name, ISO_MAXPATHLEN - 1) && !strncasecmp(base, child->name, nl)) {
|
|
// found the thing we're after and it is a file
|
|
// stick it in dir_entry, and return true
|
|
memcpy(dir_entry, child, sizeof(DIR_ENTRY));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static char *dirname(char *path) {
|
|
static char result[1024]; // TODO: find MAXPATHLEN
|
|
strncpy(result, path, 1024 - 1);
|
|
result[1024 - 1] = '\0';
|
|
s32 i;
|
|
for (i = strlen(result) - 1; i >= 0; i--) {
|
|
if (result[i] == DIR_SEPARATOR) {
|
|
result[i] = '\0';
|
|
return result;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static char *basename(char *path) {
|
|
s32 i;
|
|
for (i = strlen(path) - 1; i >= 0; i--) {
|
|
if (path[i] == DIR_SEPARATOR) {
|
|
return path + i + 1;
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
|
|
static bool invalid_drive_specifier(const char *path) {
|
|
if (strchr(path, ':') == NULL) return false;
|
|
int namelen = strlen(DEVICE_NAME);
|
|
if (!strncmp(DEVICE_NAME, path, namelen) && path[namelen] == ':') return false;
|
|
return true;
|
|
}
|
|
|
|
static bool entry_from_path(DIR_ENTRY *dir_entry, const char *const_path) {
|
|
bzero(dir_entry, sizeof(DIR_ENTRY));
|
|
|
|
if (invalid_drive_specifier(const_path)) return false;
|
|
|
|
// get rid of drive specifier
|
|
if (strchr(const_path, ':') != NULL) const_path = strchr(const_path, ':') + 1;
|
|
|
|
char path[strlen(const_path) + 1];
|
|
strcpy(path, const_path);
|
|
|
|
// strip trailing slashes except for root
|
|
u32 len = strlen(path);
|
|
while (len > 1 && path[len - 1] == DIR_SEPARATOR) {
|
|
path[--len] = '\x00';
|
|
|
|
}
|
|
|
|
char *dir = dirname(path);
|
|
char *base = basename(path);
|
|
|
|
PATH_ENTRY parent_entry;
|
|
if (!path_entry_from_path(&parent_entry, dir)) return false;
|
|
bool found = find_in_directory(dir_entry, &parent_entry, base);
|
|
if (!found && dir_entry->children) free(dir_entry->children);
|
|
return found;
|
|
}
|
|
|
|
static int _ISO9660_open_r(struct _reent *r, void *fileStruct, const char *path, int flags UNUSED, int mode UNUSED) {
|
|
FILE_STRUCT *file = (FILE_STRUCT *)fileStruct;
|
|
DIR_ENTRY entry;
|
|
if (!entry_from_path(&entry, path)) {
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
} else if (is_dir(&entry)) {
|
|
if (entry.children) free(entry.children);
|
|
r->_errno = EISDIR;
|
|
return -1;
|
|
}
|
|
|
|
memcpy(&file->entry, &entry, sizeof(DIR_ENTRY));
|
|
file->offset = 0;
|
|
file->inUse = true;
|
|
|
|
return (int)file;
|
|
}
|
|
|
|
static int _ISO9660_close_r(struct _reent *r, int fd) {
|
|
FILE_STRUCT *file = (FILE_STRUCT *)fd;
|
|
if (!file->inUse) {
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
file->inUse = false;
|
|
return 0;
|
|
}
|
|
|
|
static int _ISO9660_read_r(struct _reent *r, int fd, char *ptr, size_t len) {
|
|
FILE_STRUCT *file = (FILE_STRUCT *)fd;
|
|
if (!file->inUse) {
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
if (file->offset >= file->entry.size) {
|
|
r->_errno = EOVERFLOW;
|
|
return 0;
|
|
}
|
|
if (len + file->offset > file->entry.size) {
|
|
r->_errno = EOVERFLOW;
|
|
len = file->entry.size - file->offset;
|
|
}
|
|
if (len <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
u64 offset = file->entry.sector * SECTOR_SIZE + file->offset;
|
|
if ((int) (len = _read(ptr, offset, len)) < 0) {
|
|
r->_errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
file->offset += len;
|
|
return len;
|
|
}
|
|
|
|
static off_t _ISO9660_seek_r(struct _reent *r, int fd, off_t pos, int dir) {
|
|
FILE_STRUCT *file = (FILE_STRUCT *)fd;
|
|
if (!file->inUse) {
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
int position;
|
|
|
|
switch (dir) {
|
|
case SEEK_SET:
|
|
position = pos;
|
|
break;
|
|
case SEEK_CUR:
|
|
position = file->offset + pos;
|
|
break;
|
|
case SEEK_END:
|
|
position = file->entry.size + pos;
|
|
break;
|
|
default:
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (pos > 0 && position < 0) {
|
|
r->_errno = EOVERFLOW;
|
|
return -1;
|
|
}
|
|
|
|
if (position < 0 || (u32) position > file->entry.size) {
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
file->offset = position;
|
|
|
|
return position;
|
|
}
|
|
|
|
static void stat_entry(DIR_ENTRY *entry, struct stat *st) {
|
|
st->st_dev = 69;
|
|
st->st_ino = (ino_t)entry->sector;
|
|
st->st_mode = (is_dir(entry) ? S_IFDIR : S_IFREG) | (S_IRUSR | S_IRGRP | S_IROTH);
|
|
st->st_nlink = 1;
|
|
st->st_uid = 1;
|
|
st->st_gid = 2;
|
|
st->st_rdev = st->st_dev;
|
|
st->st_size = entry->size;
|
|
st->st_atime = 0;
|
|
st->st_spare1 = 0;
|
|
st->st_mtime = 0;
|
|
st->st_spare2 = 0;
|
|
st->st_ctime = 0;
|
|
st->st_spare3 = 0;
|
|
st->st_blksize = SECTOR_SIZE;
|
|
st->st_blocks = (entry->size + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
|
st->st_spare4[0] = 0;
|
|
st->st_spare4[1] = 0;
|
|
}
|
|
|
|
static int _ISO9660_fstat_r(struct _reent *r, int fd, struct stat *st) {
|
|
FILE_STRUCT *file = (FILE_STRUCT *)fd;
|
|
if (!file->inUse) {
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
stat_entry(&file->entry, st);
|
|
return 0;
|
|
}
|
|
|
|
static int _ISO9660_stat_r(struct _reent *r, const char *path, struct stat *st) {
|
|
DIR_ENTRY entry;
|
|
if (!entry_from_path(&entry, path)) {
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
stat_entry(&entry, st);
|
|
if (entry.children) free(entry.children);
|
|
return 0;
|
|
}
|
|
|
|
static int _ISO9660_statvfs_r(struct _reent *r UNUSED, const char *name UNUSED, struct statvfs *buf)
|
|
{
|
|
if(!buf)
|
|
return -1;
|
|
|
|
memset(buf, 0, sizeof(struct statvfs));
|
|
return 0;
|
|
}
|
|
|
|
static int _ISO9660_chdir_r(struct _reent *r, const char *path) {
|
|
DIR_ENTRY entry;
|
|
if (!entry_from_path(&entry, path)) {
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
} else if (!is_dir(&entry)) {
|
|
r->_errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
current = entry.path_entry;
|
|
if (entry.children) free(entry.children);
|
|
return 0;
|
|
}
|
|
|
|
static DIR_ITER *_ISO9660_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) {
|
|
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
|
|
if (!entry_from_path(&state->entry, path)) {
|
|
r->_errno = ENOENT;
|
|
return NULL;
|
|
} else if (!is_dir(&state->entry)) {
|
|
r->_errno = ENOTDIR;
|
|
return NULL;
|
|
}
|
|
state->index = 0;
|
|
state->inUse = true;
|
|
return dirState;
|
|
}
|
|
|
|
static int _ISO9660_dirreset_r(struct _reent *r, DIR_ITER *dirState) {
|
|
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
|
|
if (!state->inUse) {
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
state->index = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int _ISO9660_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) {
|
|
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
|
|
if (!state->inUse) {
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
if (state->index >= state->entry.fileCount) {
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
DIR_ENTRY *entry = &state->entry.children[state->index++];
|
|
strncpy(filename, entry->name, ISO_MAXPATHLEN - 1);
|
|
stat_entry(entry, st);
|
|
return 0;
|
|
}
|
|
|
|
static int _ISO9660_dirclose_r(struct _reent *r, DIR_ITER *dirState) {
|
|
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
|
|
if (!state->inUse) {
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
state->inUse = false;
|
|
if (state->entry.children) free(state->entry.children);
|
|
return 0;
|
|
}
|
|
|
|
static const devoptab_t dotab_iso9660 = {
|
|
DEVICE_NAME,
|
|
sizeof(FILE_STRUCT),
|
|
_ISO9660_open_r,
|
|
_ISO9660_close_r,
|
|
NULL,
|
|
_ISO9660_read_r,
|
|
_ISO9660_seek_r,
|
|
_ISO9660_fstat_r,
|
|
_ISO9660_stat_r,
|
|
NULL,
|
|
NULL,
|
|
_ISO9660_chdir_r,
|
|
NULL,
|
|
NULL,
|
|
sizeof(DIR_STATE_STRUCT),
|
|
_ISO9660_diropen_r,
|
|
_ISO9660_dirreset_r,
|
|
_ISO9660_dirnext_r,
|
|
_ISO9660_dirclose_r,
|
|
_ISO9660_statvfs_r,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
typedef struct {
|
|
char id[8];
|
|
char system_id[32];
|
|
char volume_id[32];
|
|
char zero[8];
|
|
unsigned long total_sector_le, total_sect_be;
|
|
char zero2[32];
|
|
unsigned long volume_set_size, volume_seq_nr;
|
|
unsigned short sector_size_le, sector_size_be;
|
|
unsigned long path_table_len_le, path_table_len_be;
|
|
unsigned long path_table_le, path_table_2nd_le;
|
|
unsigned long path_table_be, path_table_2nd_be;
|
|
u8 root[34];
|
|
char volume_set_id[128], publisher_id[128], data_preparer_id[128], application_id[128];
|
|
char copyright_file_id[37], abstract_file_id[37], bibliographical_file_id[37];
|
|
} __attribute__((packed)) VOLUME_DESCRIPTOR;
|
|
|
|
static VOLUME_DESCRIPTOR *read_volume_descriptor(u8 descriptor) {
|
|
u8 sector;
|
|
for (sector = 16; sector < 32; sector++) {
|
|
if (DI2_ReadDVD(read_buffer, 1, sector)) return NULL;
|
|
if (!memcmp(read_buffer + 1, "CD001\1", 6)) {
|
|
if (*read_buffer == descriptor) return (VOLUME_DESCRIPTOR *)read_buffer;
|
|
else if (*read_buffer == 0xff) return NULL;
|
|
}
|
|
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static PATH_ENTRY *entry_from_index(PATH_ENTRY *entry, u16 index) {
|
|
if (entry->index == index) return entry;
|
|
u32 i;
|
|
for (i = 0; i < entry->childCount; i++) {
|
|
PATH_ENTRY *match = entry_from_index(&entry->children[i], index);
|
|
if (match) return match;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static PATH_ENTRY *add_child_entry(PATH_ENTRY *dir) {
|
|
PATH_ENTRY *newChildren = realloc(dir->children, (dir->childCount + 1) * sizeof(PATH_ENTRY));
|
|
if (!newChildren) return NULL;
|
|
bzero(newChildren + dir->childCount, sizeof(PATH_ENTRY));
|
|
dir->children = newChildren;
|
|
PATH_ENTRY *child = &dir->children[dir->childCount++];
|
|
return child;
|
|
}
|
|
|
|
static bool read_directories() {
|
|
VOLUME_DESCRIPTOR *volume = read_volume_descriptor(2);
|
|
if (volume) unicode = true;
|
|
else if (!(volume = read_volume_descriptor(1))) return false;
|
|
|
|
if (!(root = malloc(sizeof(PATH_ENTRY)))) return false;
|
|
bzero(root, sizeof(PATH_ENTRY));
|
|
root->table_entry.name_length = 1;
|
|
root->table_entry.extended_sectors = volume->root[OFFSET_EXTENDED];
|
|
root->table_entry.sector = *(u32 *)(volume->root + OFFSET_SECTOR);
|
|
root->table_entry.parent = 0;
|
|
root->table_entry.name[0] = '\x00';
|
|
root->index = 1;
|
|
current = root;
|
|
|
|
u32 path_table = volume->path_table_be;
|
|
u32 path_table_len = volume->path_table_len_be;
|
|
u16 i = 1;
|
|
u64 offset = sizeof(PATHTABLE_ENTRY) - ISO_MAXPATHLEN + 2;
|
|
PATH_ENTRY *parent = root;
|
|
while (i < 0xffff && offset < path_table_len) {
|
|
PATHTABLE_ENTRY entry;
|
|
if (_read(&entry, (u64)path_table * SECTOR_SIZE + offset, sizeof(PATHTABLE_ENTRY)) != sizeof(PATHTABLE_ENTRY)) return false; // kinda dodgy - could be reading too far
|
|
if (parent->index != entry.parent) parent = entry_from_index(root, entry.parent);
|
|
if (!parent) return false;
|
|
PATH_ENTRY *child = add_child_entry(parent);
|
|
if (!child) return false;
|
|
memcpy(&child->table_entry, &entry, sizeof(PATHTABLE_ENTRY));
|
|
offset += sizeof(PATHTABLE_ENTRY) - ISO_MAXPATHLEN + child->table_entry.name_length;
|
|
if (child->table_entry.name_length % 2) offset++;
|
|
child->index = ++i;
|
|
|
|
if (unicode) {
|
|
u32 i;
|
|
for (i = 0; i < (child->table_entry.name_length / 2); i++) child->table_entry.name[i] = entry.name[i * 2 + 1];
|
|
child->table_entry.name[i] = '\x00';
|
|
child->table_entry.name_length = i;
|
|
} else {
|
|
child->table_entry.name[child->table_entry.name_length] = '\x00';
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void cleanup_recursive(PATH_ENTRY *entry) {
|
|
u32 i;
|
|
for (i = 0; i < entry->childCount; i++)
|
|
cleanup_recursive(&entry->children[i]);
|
|
if (entry->children) free(entry->children);
|
|
}
|
|
|
|
bool ISO9660_Mount()
|
|
{
|
|
ISO9660_Unmount();
|
|
read_buffer = memalign(32, BUFFER_SIZE);
|
|
cluster_buffer = memalign(32, BUFFER_SIZE);
|
|
if(!read_buffer || !cluster_buffer)
|
|
{
|
|
ISO9660_Unmount();
|
|
return false;
|
|
}
|
|
bool success = read_directories() && (dotab_device = AddDevice(&dotab_iso9660)) >= 0;
|
|
if (success) last_access = gettime();
|
|
else ISO9660_Unmount();
|
|
return success;
|
|
}
|
|
|
|
bool ISO9660_Unmount()
|
|
{
|
|
if (root) {
|
|
cleanup_recursive(root);
|
|
free(root);
|
|
root = NULL;
|
|
}
|
|
if(read_buffer)
|
|
{
|
|
free(read_buffer);
|
|
read_buffer = NULL;
|
|
}
|
|
if(cluster_buffer)
|
|
{
|
|
free(cluster_buffer);
|
|
cluster_buffer = NULL;
|
|
}
|
|
current = root;
|
|
unicode = false;
|
|
cache_sectors = 0;
|
|
last_access = 0;
|
|
if (dotab_device >= 0)
|
|
{
|
|
dotab_device = -1;
|
|
return !RemoveDevice(DEVICE_NAME ":");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
u64 ISO9660_LastAccess()
|
|
{
|
|
return last_access;
|
|
}
|