diff options
Diffstat (limited to 'qemu/roms/openbios/fs/hfsplus/hfsp_fs.c')
-rw-r--r-- | qemu/roms/openbios/fs/hfsplus/hfsp_fs.c | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/qemu/roms/openbios/fs/hfsplus/hfsp_fs.c b/qemu/roms/openbios/fs/hfsplus/hfsp_fs.c new file mode 100644 index 000000000..df234bb04 --- /dev/null +++ b/qemu/roms/openbios/fs/hfsplus/hfsp_fs.c @@ -0,0 +1,632 @@ +/* + * Creation Date: <2001/05/05 23:33:49 samuel> + * Time-stamp: <2004/01/12 10:25:39 samuel> + * + * /package/hfsplus-files + * + * HFS+ file system interface (and ROM lookup support) + * + * Copyright (C) 2001, 2002, 2003, 2004 Samuel Rydh (samuel@ibrium.se) + * Copyright (C) 2010 Mark Cave-Ayland (mark.cave-ayland@siriusit.co.uk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation + * + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "fs/fs.h" +#include "libhfsp.h" +#include "volume.h" +#include "record.h" +#include "unicode.h" +#include "blockiter.h" +#include "libc/diskio.h" +#include "libc/vsprintf.h" + +#define MAC_OS_ROM_CREATOR 0x63687270 /* 'chrp' */ +#define MAC_OS_ROM_TYPE 0x74627869 /* 'tbxi' */ +#define MAC_OS_ROM_NAME "Mac OS ROM" + +#define FINDER_TYPE 0x464E4452 /* 'FNDR' */ +#define FINDER_CREATOR 0x4D414353 /* 'MACS' */ +#define SYSTEM_TYPE 0x7A737973 /* 'zsys' */ +#define SYSTEM_CREATOR 0x4D414353 /* 'MACS' */ + +#define VOLNAME_SIZE 64 + +extern void hfsp_init( void ); + +typedef struct { + record rec; + char *path; + off_t pos; +} hfsp_file_t; + +typedef struct { + volume *vol; + hfsp_file_t *hfspfile; +} hfsp_info_t; + +DECLARE_NODE( hfsp, 0, sizeof(hfsp_info_t), "+/packages/hfsplus-files" ); + + +/************************************************************************/ +/* Search implementation */ +/************************************************************************/ + +typedef int (*match_proc_t)( record *r, record *parent, const void *match_data, hfsp_file_t *pt ); + +static int +search_files( record *par, int recursive, match_proc_t proc, const void *match_data, hfsp_file_t *pt ) +{ + hfsp_file_t t; + record r; + int ret = 1; + + t.path = NULL; + + record_init_parent( &r, par ); + do{ + if( r.record.type == HFSP_FOLDER || r.record.type == HFSP_FILE ) + ret = (*proc)( &r, par, match_data, &t ); + + if( ret && r.record.type == HFSP_FOLDER && recursive ) + ret = search_files( &r, 1, proc, match_data, &t ); + + } while( ret && !record_next(&r) ); + + if( !ret && pt ) { + char name[256]; + const char *s2 = t.path ? t.path : ""; + + unicode_uni2asc( name, &r.key.name, sizeof(name)); + + pt->rec = t.rec; + pt->path = malloc( strlen(name) + strlen(s2) + 2 ); + strcpy( pt->path, name ); + if( strlen(s2) ) { + strcat( pt->path, "\\" ); + strcat( pt->path, s2 ); + } + } + + if( t.path ) + free( t.path ); + + return ret; +} + +static int +root_search_files( volume *vol, int recursive, match_proc_t proc, const void *match_data, hfsp_file_t *pt ) +{ + record r; + + record_init_root( &r, &vol->catalog ); + return search_files( &r, recursive, proc, match_data, pt ); +} + +static int +match_file( record *r, record *parent, const void *match_data, hfsp_file_t *pt ) +{ + const char *p = (const char*)match_data; + char name[256]; + int ret=1; + + if( r->record.type != HFSP_FILE ) + return 1; + + (void) unicode_uni2asc(name, &r->key.name, sizeof(name)); + if( !(ret=strcasecmp(p, name)) && pt ) + pt->rec = *r; + + return ret; +} + +static int +match_rom( record *r, record *par, const void *match_data, hfsp_file_t *pt ) +{ + hfsp_cat_file *file = &r->record.u.file; + FInfo *fi = &file->user_info; + int ret = 1; + char buf[256]; + + if( r->record.type == HFSP_FILE && fi->fdCreator == MAC_OS_ROM_CREATOR && fi->fdType == MAC_OS_ROM_TYPE ) { + ret = search_files( par, 0, match_file, "System", NULL ) + || search_files( par, 0, match_file, "Finder", NULL ); + + (void) unicode_uni2asc(buf, &r->key.name, sizeof(buf)); + if( !strcasecmp("BootX", buf) ) + return 1; + + if( !ret && pt ) + pt->rec = *r; + } + return ret; +} + +static int +match_path( record *r, record *par, const void *match_data, hfsp_file_t *pt ) +{ + char name[256], *s, *next, *org; + int ret=1; + + next = org = strdup( (char*)match_data ); + while( (s=strsep( &next, "\\/" )) && !strlen(s) ) + ; + if( !s ) { + free( org ); + return 1; + } + + if( *s == ':' && strlen(s) == 5 ) { + if( r->record.type == HFSP_FILE && !next ) { + /* match type */ + hfsp_cat_file *file = &r->record.u.file; + FInfo *fi = &file->user_info; + int i, type=0; + for( i=1; s[i] && i<=4; i++ ) + type = (type << 8) | s[i]; + /* printk("fi->fdType: %s / %s\n", s+1, b ); */ + if( fi->fdType == type ) { + if( pt ) + pt->rec = *r; + ret = 0; + } + } + } else { + (void) unicode_uni2asc(name, &r->key.name, sizeof(name)); + + if( !strcasecmp(s, name) ) { + if( r->record.type == HFSP_FILE && !next ) { + if( pt ) + pt->rec = *r; + ret = 0; + } else /* must be a directory */ + ret = search_files( r, 0, match_path, next, pt ); + } + } + free( org ); + return ret; +} + + +/************************************************************************/ +/* Standard package methods */ +/************************************************************************/ + +/* ( -- success? ) */ +static void +hfsp_files_open( hfsp_info_t *mi ) +{ + int fd; + char *path = my_args_copy(); + + if ( ! path ) + RET( 0 ); + + fd = open_ih( my_parent() ); + if ( fd == -1 ) { + free( path ); + RET( 0 ); + } + + mi->vol = malloc( sizeof(volume) ); + if (volume_open(mi->vol, fd)) { + free( path ); + close_io( fd ); + RET( 0 ); + } + + mi->hfspfile = malloc( sizeof(hfsp_file_t) ); + + /* Leading \\ means system folder. The finder info block has + * the following meaning. + * + * [0] Prefered boot directory ID + * [3] MacOS 9 boot directory ID + * [5] MacOS X boot directory ID + */ + if( !strncmp(path, "\\\\", 2) ) { + int *p = (int*)&(mi->vol)->vol.finder_info[0]; + int cnid = p[0]; + /* printk(" p[0] = %x, p[3] = %x, p[5] = %x\n", p[0], p[3], p[5] ); */ + if( p[0] == p[5] && p[3] ) + cnid = p[3]; + if( record_init_cnid(&(mi->hfspfile->rec), &(mi->vol)->catalog, cnid) ) + RET ( 0 ); + path += 2; + } else { + record_init_root( &(mi->hfspfile->rec), &(mi->vol)->catalog ); + } + + if( !search_files(&(mi->hfspfile->rec), 0, match_path, path, mi->hfspfile ) ) + RET ( -1 ); + + RET ( -1 ); +} + +/* ( -- ) */ +static void +hfsp_files_close( hfsp_info_t *mi ) +{ + volume_close(mi->vol); + + if( mi->hfspfile->path ) + free( mi->hfspfile->path ); + free( mi->hfspfile ); +} + +/* ( buf len -- actlen ) */ +static void +hfsp_files_read( hfsp_info_t *mi ) +{ + int count = POP(); + char *buf = (char *)cell2pointer(POP()); + + hfsp_file_t *t = mi->hfspfile; + volume *vol = t->rec.tree->vol; + UInt32 blksize = vol->blksize; + hfsp_cat_file *file = &t->rec.record.u.file; + blockiter iter; + char buf2[blksize]; + int act_count, curpos=0; + + blockiter_init( &iter, vol, &file->data_fork, HFSP_EXTENT_DATA, file->id ); + while( curpos + blksize < t->pos ) { + if( blockiter_next( &iter ) ) { + RET ( -1 ); + return; + } + curpos += blksize; + } + act_count = 0; + + while( act_count < count ){ + UInt32 block = blockiter_curr(&iter); + int max = blksize, add = 0, size; + + if( volume_readinbuf( vol, buf2, block ) ) + break; + + if( curpos < t->pos ){ + add += t->pos - curpos; + max -= t->pos - curpos; + } + size = (count-act_count > max)? max : count-act_count; + memcpy( (char *)buf + act_count, &buf2[add], size ); + + curpos += blksize; + act_count += size; + + if( blockiter_next( &iter ) ) + break; + } + + t->pos += act_count; + + RET ( act_count ); +} + +/* ( pos.d -- status ) */ +static void +hfsp_files_seek( hfsp_info_t *mi ) +{ + long long pos = DPOP(); + int offs = (int)pos; + int whence = SEEK_SET; + + hfsp_file_t *t = mi->hfspfile; + hfsp_cat_file *file = &t->rec.record.u.file; + int total = file->data_fork.total_size; + + if( offs == -1 ) { + offs = 0; + whence = SEEK_END; + } + + switch( whence ){ + case SEEK_END: + t->pos = total + offs; + break; + default: + case SEEK_SET: + t->pos = offs; + break; + } + + if( t->pos < 0 ) + t->pos = 0; + + if( t->pos > total ) + t->pos = total; + + RET ( 0 ); +} + +/* ( addr -- size ) */ +static void +hfsp_files_load( hfsp_info_t *mi ) +{ + char *buf = (char *)cell2pointer(POP()); + + hfsp_file_t *t = mi->hfspfile; + volume *vol = t->rec.tree->vol; + UInt32 blksize = vol->blksize; + hfsp_cat_file *file = &t->rec.record.u.file; + int total = file->data_fork.total_size; + blockiter iter; + char buf2[blksize]; + int act_count; + + blockiter_init( &iter, vol, &file->data_fork, HFSP_EXTENT_DATA, file->id ); + + act_count = 0; + + while( act_count < total ){ + UInt32 block = blockiter_curr(&iter); + int max = blksize, size; + + if( volume_readinbuf( vol, buf2, block ) ) + break; + + size = (total-act_count > max)? max : total-act_count; + memcpy( (char *)buf + act_count, &buf2, size ); + + act_count += size; + + if( blockiter_next( &iter ) ) + break; + } + + RET ( act_count ); +} + +/* ( -- cstr ) */ +static void +hfsp_files_get_fstype( hfsp_info_t *mi ) +{ + PUSH( pointer2cell(strdup("HFS+")) ); +} + +/* ( -- cstr ) */ +static void +hfsp_files_get_path( hfsp_info_t *mi ) +{ + char *buf; + hfsp_file_t *t = mi->hfspfile; + + if( !t->path ) + RET ( 0 ); + + buf = malloc(strlen(t->path) + 1); + strncpy( buf, t->path, strlen(t->path) ); + buf[strlen(t->path)] = 0; + + PUSH(pointer2cell(buf)); +} + +/* ( -- success? ) */ +static void +hfsp_files_open_nwrom( hfsp_info_t *mi ) +{ + /* Switch to an existing ROM image file on the fs! */ + if( !root_search_files(mi->vol, 1, match_rom, NULL, mi->hfspfile) ) + RET ( -1 ); + + RET ( 0 ); +} + +/* ( -- cstr|0 ) */ +static void +hfsp_files_volume_name( hfsp_info_t *mi ) +{ + int fd; + char *volname = malloc(VOLNAME_SIZE); + + fd = open_ih(my_self()); + if (fd >= 0) { + get_hfs_vol_name(fd, volname, VOLNAME_SIZE); + close_io(fd); + } else { + volname[0] = '\0'; + } + + PUSH(pointer2cell(volname)); +} + +static const int days_month[12] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static const int days_month_leap[12] = + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static inline int is_leap(int year) +{ + return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); +} + +static void +print_date(uint32_t sec) +{ + unsigned int second, minute, hour, month, day, year; + int current; + const int *days; + + second = sec % 60; + sec /= 60; + + minute = sec % 60; + sec /= 60; + + hour = sec % 24; + sec /= 24; + + year = sec * 100 / 36525; + sec -= year * 36525 / 100; + year += 1904; + + days = is_leap(year) ? days_month_leap : days_month; + + current = 0; + month = 0; + while (month < 12) { + if (sec <= current + days[month]) { + break; + } + current += days[month]; + month++; + } + month++; + + day = sec - current + 1; + + forth_printf("%d-%02d-%02d %02d:%02d:%02d ", + year, month, day, hour, minute, second); +} + +/* static method, ( pathstr len ihandle -- ) */ +static void +hfsp_files_dir( hfsp_info_t *dummy ) +{ + ihandle_t ih = POP_ih(); + char *path = pop_fstr_copy(); + int fd, found; + volume *vol; + record rec, r, folrec; + char name[256], *curfol, *tmppath; + + fd = open_ih(ih); + if ( fd == -1 ) { + free( path ); + RET( 0 ); + } + + vol = malloc( sizeof(volume) ); + if (volume_open(vol, fd)) { + free( path ); + close_io( fd ); + RET( 0 ); + } + + /* First move to the specified folder */ + tmppath = strdup(path); + record_init_root( &rec, &vol->catalog ); + record_init_parent( &r, &rec ); + + /* Remove initial \ or / */ + curfol = strsep(&tmppath, "\\//"); + curfol = strsep(&tmppath, "\\//"); + forth_printf("\n"); + + while (curfol && strlen(curfol)) { + found = 0; + do { + if (r.record.type == HFSP_FOLDER) { + unicode_uni2asc(name, &r.key.name, sizeof(name)); + + if (!strcmp(name, curfol)) { + folrec = r; + found = -1; + } + } + } while ( !record_next(&r) ); + + if (!found) { + forth_printf("Unable to locate path %s on filesystem\n", path); + goto done; + } else { + record_init_parent( &r, &folrec ); + } + + curfol = strsep(&tmppath, "\\//"); + } + + /* Output the directory contents */ + found = 0; + do { + unicode_uni2asc(name, &r.key.name, sizeof(name)); + + if (r.record.type == HFSP_FILE) { + /* Grab the file entry */ + hfsp_cat_file *file = &r.record.u.file; + forth_printf("% 10lld ", file->data_fork.total_size); + print_date(file->create_date); + forth_printf(" %s\n", name); + found = -1; + } + + if (r.record.type == HFSP_FOLDER) { + /* Grab the directory entry */ + hfsp_cat_folder *folder = &r.record.u.folder; + forth_printf(" 0 "); + print_date(folder->create_date); + forth_printf(" %s\\\n", name); + found = -1; + } + + } while ( !record_next(&r) ); + + if (!found) { + forth_printf(" (Empty folder)\n"); + } + +done: + volume_close(vol); + free(vol); + free(path); + if (tmppath) + free(tmppath); +} + +/* static method, ( pos.d ih -- flag? ) */ +static void +hfsp_files_probe( hfsp_info_t *dummy ) +{ + ihandle_t ih = POP_ih(); + long long offs = DPOP(); + int fd, ret = 0; + + fd = open_ih(ih); + if (fd >= 0) { + if (volume_probe(fd, offs)) { + ret = -1; + } + close_io(fd); + } else { + ret = -1; + } + + RET (ret); +} + +static void +hfsp_initializer( hfsp_info_t *dummy ) +{ + fword("register-fs-package"); +} + +NODE_METHODS( hfsp ) = { + { "probe", hfsp_files_probe }, + { "open", hfsp_files_open }, + { "close", hfsp_files_close }, + { "read", hfsp_files_read }, + { "seek", hfsp_files_seek }, + { "load", hfsp_files_load }, + { "dir", hfsp_files_dir }, + + /* special */ + { "open-nwrom", hfsp_files_open_nwrom }, + { "get-path", hfsp_files_get_path }, + { "get-fstype", hfsp_files_get_fstype }, + { "volume-name", hfsp_files_volume_name }, + + { NULL, hfsp_initializer }, +}; + +void +hfsp_init( void ) +{ + REGISTER_NODE( hfsp ); +} |