Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfs / hfs_fs.c
diff --git a/qemu/roms/openbios/fs/hfs/hfs_fs.c b/qemu/roms/openbios/fs/hfs/hfs_fs.c
new file mode 100644 (file)
index 0000000..2d62ec0
--- /dev/null
@@ -0,0 +1,593 @@
+/*
+ *   Creation Date: <2001/05/06 22:47:23 samuel>
+ *   Time-stamp: <2004/01/12 10:24:35 samuel>
+ *
+ *     /packages/hfs-files
+ *
+ *     HFS world interface
+ *
+ *   Copyright (C) 2001-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 "libc/vsprintf.h"
+#include "libc/diskio.h"
+#include "libhfs.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     hfs_init( void );
+
+typedef struct {
+       enum { FILE, DIR } type;
+       union {
+               hfsdir *dir;
+               hfsfile *file;
+       };
+} hfscommon;
+
+typedef struct {
+       hfsvol *vol;
+       hfscommon *common;
+} hfs_info_t;
+
+DECLARE_NODE( hfs, 0, sizeof(hfs_info_t), "+/packages/hfs-files" );
+
+/************************************************************************/
+/*     Search Functions                                                */
+/************************************************************************/
+
+static int
+_find_file( hfsvol *vol, const char *path, unsigned long type, unsigned long creator )
+{
+       hfsdirent ent;
+       hfsdir *dir;
+       int ret=1;
+
+       if( !(dir=hfs_opendir(vol, path)) )
+               return 1;
+
+       while( ret && !hfs_readdir(dir, &ent) ) {
+               if( ent.flags & HFS_ISDIR )
+                       continue;
+               ret = !(*(unsigned long*)ent.u.file.type == type && *(unsigned long*)ent.u.file.creator == creator );
+       }
+
+       hfs_closedir( dir );
+       return ret;
+}
+
+
+/* ret: 0=success, 1=not_found, 2=not_a_dir */
+static int
+_search( hfsvol *vol, const char *path, const char *sname, hfsfile **ret_fd )
+{
+       hfsdir *dir;
+       hfsdirent ent;
+       int topdir=0, status = 1;
+       char *p, buf[256];
+
+       strncpy( buf, path, sizeof(buf) );
+       if( buf[strlen(buf)-1] != ':' )
+               strncat( buf, ":", sizeof(buf) );
+       buf[sizeof(buf)-1] = 0;
+       p = buf + strlen( buf );
+
+       if( !(dir=hfs_opendir(vol, path)) )
+               return 2;
+
+       /* printk("DIRECTORY: %s\n", path ); */
+
+       while( status && !hfs_readdir(dir, &ent) ) {
+               unsigned long type, creator;
+
+               *p = 0;
+               topdir = 0;
+
+               strncat( buf, ent.name, sizeof(buf) );
+               if( (status=_search(vol, buf, sname, ret_fd)) != 2 )
+                       continue;
+               topdir = 1;
+
+               /* name search? */
+               if( sname ) {
+                       status = strcasecmp( ent.name, sname );
+                       continue;
+               }
+
+               type = *(unsigned long*)ent.u.file.type;
+               creator = *(unsigned long*)ent.u.file.creator;
+
+               /* look for Mac OS ROM, System and Finder in the same directory */
+               if( type == MAC_OS_ROM_TYPE && creator == MAC_OS_ROM_CREATOR ) {
+                       if( strcasecmp(ent.name, MAC_OS_ROM_NAME) )
+                               continue;
+
+                       status = _find_file( vol, path, FINDER_TYPE, FINDER_CREATOR )
+                               || _find_file( vol, path, SYSTEM_TYPE, SYSTEM_CREATOR );
+               }
+       }
+       if( !status && topdir && ret_fd && !(*ret_fd=hfs_open(vol, buf)) ) {
+               printk("Unexpected error: failed to open matched ROM\n");
+               status = 1;
+       }
+
+       hfs_closedir( dir );
+       return status;
+}
+
+static hfsfile *
+_do_search( hfs_info_t *mi, const char *sname )
+{
+       hfsvol *vol = hfs_getvol( NULL );
+
+       mi->common->type = FILE;
+       (void)_search( vol, ":", sname, &mi->common->file );
+
+       return mi->common->file;
+}
+
+
+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(time_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 += 1970;
+
+       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 void
+dir_fs( file_desc_t *fd )
+{
+       hfscommon *common = (hfscommon*)fd;
+       hfsdirent ent;
+
+       if (common->type != DIR)
+               return;
+
+       forth_printf("\n");
+       while( !hfs_readdir(common->dir, &ent) ) {
+               forth_printf("% 10d ", ent.u.file.dsize);
+               print_date(ent.mddate);
+               if( ent.flags & HFS_ISDIR )
+                       forth_printf("%s\\\n", ent.name);
+               else
+                       forth_printf("%s\n", ent.name);
+       }
+}
+*/
+
+/************************************************************************/
+/*     Standard package methods                                                */
+/************************************************************************/
+
+/* ( -- success? ) */
+static void
+hfs_files_open( hfs_info_t *mi )
+{
+       int fd;
+       char *path = my_args_copy();
+
+       const char *s;
+       char buf[256];
+
+       fd = open_ih( my_parent() );
+       if ( fd == -1 ) {
+               free( path );
+               RET( 0 );
+       }
+
+       mi->vol = hfs_mount(fd, 0);
+       if (!mi->vol) {
+               RET( 0 );
+       }
+
+       if( !strncmp(path, "\\\\", 2) ) {
+               hfsvolent ent;
+
+               /* \\ is an alias for the (blessed) system folder */
+               if( hfs_vstat(mi->vol, &ent) < 0 || hfs_setcwd(mi->vol, ent.blessed) ) {
+                       free(path);
+                       RET( -1 );
+               }
+               path += 2;
+       } else {
+               hfs_chdir( mi->vol, ":" );
+       }
+
+       mi->common = malloc(sizeof(hfscommon));
+       if (!mi->common) {
+               free(path);
+               RET( 0 );
+       }
+
+       if (strcmp(path, "\\") == 0) {
+               /* root directory is in fact ":" */
+               mi->common->dir = hfs_opendir(mi->vol, ":");
+               mi->common->type = DIR;
+               free(path);
+               RET( -1 );
+       }
+
+       if (path[strlen(path) - 1] == '\\') {
+               path[strlen(path) - 1] = 0;
+       }
+
+       for( path-- ;; ) {
+               int n;
+
+               s = ++path;
+               path = strchr(s, '\\');
+               if( !path || !path[1])
+                       break;
+               n = MIN( sizeof(buf)-1, (path-s) );
+               if( !n )
+                       continue;
+
+               strncpy( buf, s, n );
+               buf[n] = 0;
+               if( hfs_chdir(mi->vol, buf) ) {
+                       free(mi->common);
+                       free(path);
+                       RET( 0 );
+               }
+       }
+
+       /* support the ':filetype' syntax */
+       if( *s == ':' ) {
+               unsigned long id, oldid = hfs_getcwd(mi->vol);
+               hfsdirent ent;
+               hfsdir *dir;
+
+               s++;
+               id = oldid;
+               hfs_dirinfo( mi->vol, &id, buf );
+               hfs_setcwd( mi->vol, id );
+
+               if( !(dir=hfs_opendir(mi->vol, buf)) ) {
+                       free(mi->common);
+                       free(path);
+                       RET( 0 );
+               }
+               hfs_setcwd( mi->vol, oldid );
+
+               while( !hfs_readdir(dir, &ent) ) {
+                       if( ent.flags & HFS_ISDIR )
+                               continue;
+                       if( !strncmp(s, ent.u.file.type, 4) ) {
+                               mi->common->type = FILE;
+                               mi->common->file = hfs_open( mi->vol, ent.name );
+                               break;
+                       }
+               }
+               hfs_closedir( dir );
+               free(path);
+               RET( -1 );
+       }
+
+       mi->common->dir = hfs_opendir(mi->vol, s);
+       if (!mi->common->dir) {
+               mi->common->file = hfs_open( mi->vol, s );
+               if (mi->common->file == NULL) {
+                       free(mi->common);
+                       free(path);
+                       RET( 0 );
+               }
+               mi->common->type = FILE;
+               free(path);
+               RET( -1 );
+       }
+       mi->common->type = DIR;
+       free(path);
+       
+       RET( -1 );
+}
+
+/* ( -- ) */
+static void
+hfs_files_close( hfs_info_t *mi )
+{
+       hfscommon *common = mi->common;
+       if (common->type == FILE)
+               hfs_close( common->file );
+       else if (common->type == DIR)
+               hfs_closedir( common->dir );
+       free(common);
+}
+
+/* ( buf len -- actlen ) */
+static void
+hfs_files_read( hfs_info_t *mi )
+{
+       int count = POP();
+       char *buf = (char *)cell2pointer(POP());
+
+       hfscommon *common = mi->common;
+       if (common->type != FILE)
+               RET( -1 );
+
+       RET ( hfs_read( common->file, buf, count ) );
+}
+
+/* ( pos.d -- status ) */
+static void
+hfs_files_seek( hfs_info_t *mi )
+{
+       long long pos = DPOP();
+       int offs = (int)pos;
+       int whence = SEEK_SET;
+       int ret;
+       hfscommon *common = mi->common;
+
+       if (common->type != FILE)
+               RET( -1 );
+
+       switch( whence ) {
+       case SEEK_END:
+               whence = HFS_SEEK_END;
+               break;
+       default:
+       case SEEK_SET:
+               whence = HFS_SEEK_SET;
+               break;
+       }
+
+       ret = hfs_seek( common->file, offs, whence );
+       if (ret != -1)
+               RET( 0 );
+       else
+               RET( -1 );
+}
+
+/* ( addr -- size ) */
+static void
+hfs_files_load( hfs_info_t *mi )
+{
+       char *buf = (char *)cell2pointer(POP());
+       int count;
+
+       hfscommon *common = mi->common;
+       if (common->type != FILE)
+               RET( -1 );
+
+       /* Seek to the end in order to get the file size */
+       hfs_seek(common->file, 0, HFS_SEEK_END);
+       count = common->file->pos;
+       hfs_seek(common->file, 0, HFS_SEEK_SET);
+
+       RET ( hfs_read( common->file, buf, count ) );
+}
+
+/* ( -- success? ) */
+static void
+hfs_files_open_nwrom( hfs_info_t *mi )
+{
+       /* Switch to an existing ROM image file on the fs! */
+       if ( _do_search( mi, NULL ) )
+               RET( -1 );
+       
+       RET( 0 );
+}
+
+/* ( -- cstr ) */
+static void
+hfs_files_get_path( hfs_info_t *mi )
+{
+       char buf[256], buf2[256];
+       hfscommon *common = mi->common;
+       hfsvol *vol = hfs_getvol( NULL );
+       hfsdirent ent;
+       int start, ns;
+       unsigned long id;
+
+       if (common->type != FILE)
+               RET( 0 );
+
+       hfs_fstat( common->file, &ent );
+       start = sizeof(buf) - strlen(ent.name) - 1;
+       if( start <= 0 )
+               RET ( 0 );
+       strcpy( buf+start, ent.name );
+       buf[--start] = '\\';
+
+       ns = start;
+       for( id=ent.parid ; !hfs_dirinfo(vol, &id, buf2) ; ) {
+               start = ns;
+               ns -= strlen(buf2);
+               if( ns <= 0 )
+                       RET( 0 );
+               strcpy( buf+ns, buf2 );
+               buf[--ns] = buf[start] = '\\';
+       }
+       if( strlen(buf) >= sizeof(buf) )
+               RET( 0 );
+
+       RET( pointer2cell(strdup(buf+start)) );
+}
+
+/* ( -- cstr ) */
+static void
+hfs_files_get_fstype( hfs_info_t *mi )
+{
+       PUSH( pointer2cell(strdup("HFS")) );
+}
+
+/* ( -- cstr|0 ) */
+static void
+hfs_files_volume_name( hfs_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 method, ( pathstr len ihandle -- ) */
+static void
+hfs_files_dir( hfs_info_t *dummy )
+{
+       hfsvol *volume;
+       hfscommon *common;
+       hfsdirent ent;
+       int i;
+       int fd;
+
+       ihandle_t ih = POP();
+       char *path = pop_fstr_copy();
+
+       fd = open_ih( ih );
+       if ( fd == -1 ) {
+               free( path );
+               return;
+       }
+
+       volume = hfs_mount(fd, 0);
+       if (!volume) {
+               return;
+       }
+
+       common = malloc(sizeof(hfscommon));
+
+       /* HFS paths are colon separated, not backslash separated */
+       for (i = 0; i < strlen(path); i++)
+               if (path[i] == '\\')
+                       path[i] = ':';
+
+       common->dir = hfs_opendir(volume, path);
+
+       forth_printf("\n");
+       while( !hfs_readdir(common->dir, &ent) ) {
+                forth_printf("% 10ld ", ent.u.file.dsize);
+               print_date(ent.mddate);
+               if( ent.flags & HFS_ISDIR )
+                       forth_printf("%s\\\n", ent.name);
+               else
+                       forth_printf("%s\n", ent.name);
+       }
+
+       hfs_closedir( common->dir );
+       hfs_umount( volume );
+
+       close_io( fd );
+
+       free( common );
+       free( path );
+}
+
+/* static method, ( pos.d ih -- flag? ) */
+static void
+hfs_files_probe( hfs_info_t *dummy )
+{
+       ihandle_t ih = POP_ih();
+       long long offs = DPOP();
+       int fd, ret = 0;
+
+       fd = open_ih(ih);
+        if (fd >= 0) {
+                if (hfs_probe(fd, offs)) {
+                        ret = -1;
+                }
+                close_io(fd);
+        } else {
+                ret = -1;
+        }
+
+       RET (ret);
+}
+
+static void
+hfs_initializer( hfs_info_t *dummy )
+{
+       fword("register-fs-package");
+}
+
+NODE_METHODS( hfs ) = {
+       { "probe",      hfs_files_probe },
+       { "open",       hfs_files_open  },
+       { "close",      hfs_files_close },
+       { "read",       hfs_files_read  },
+       { "seek",       hfs_files_seek  },
+       { "load",       hfs_files_load  },
+       { "dir",        hfs_files_dir   },
+
+       /* special */
+       { "open-nwrom",         hfs_files_open_nwrom    },
+       { "get-path",           hfs_files_get_path      },
+       { "get-fstype",         hfs_files_get_fstype    },
+       { "volume-name",        hfs_files_volume_name   },
+
+       { NULL,         hfs_initializer },
+};
+
+void
+hfs_init( void )
+{
+       REGISTER_NODE( hfs );
+}