From be1b9663e90fbda40afc64206e805fdf3500758f Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 12 Oct 2016 12:18:23 -0400 Subject: [PATCH] host fst code (part 1) --- src/Makefile | 2 +- src/fst.h | 182 ++++ src/gsos.h | 164 ++++ src/host_fst.c | 2475 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sim65816.c | 9 +- 5 files changed, 2830 insertions(+), 2 deletions(-) create mode 100644 src/fst.h create mode 100644 src/gsos.h create mode 100644 src/host_fst.c diff --git a/src/Makefile b/src/Makefile index e6ef9cb..36a9075 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ OBJECTS1 = adb.o clock.o config.o debug.o dis.o engine_c.o scc.o iwm.o \ joystick_driver.o moremem.o paddles.o parallel.o printer.o sim65816.o \ smartport.o sound.o sound_driver.o video.o scc_socket_driver.o glog.o \ - imagewriter.o scc_imagewriter.o scc_llap.o + imagewriter.o scc_imagewriter.o scc_llap.o host_fst.o ATOBJ = atbridge/aarp.o atbridge/atbridge.o atbridge/elap.o atbridge/llap.o atbridge/port.o PCAPOBJ = atbridge/pcap_delay.o TFEOBJ = tfe/tfe.o tfe/tfearch.o tfe/tfesupp.o diff --git a/src/fst.h b/src/fst.h new file mode 100644 index 0000000..a58ed5e --- /dev/null +++ b/src/fst.h @@ -0,0 +1,182 @@ +/* generated on Tue Oct 18 20:54:09 2016 */ + +#define GSString255_length 0 +#define GSString255_text 2 + +#define GSString32_length 0 +#define GSString32_text 2 + +#define ResultBuf255_bufSize 0 +#define ResultBuf255_bufString 2 + +#define ResultBuf32_bufSize 0 +#define ResultBuf32_bufString 2 + +#define TimeRec_second 0 +#define TimeRec_minute 1 +#define TimeRec_hour 2 +#define TimeRec_year 3 +#define TimeRec_day 4 +#define TimeRec_month 5 +#define TimeRec_extra 6 +#define TimeRec_weekDay 7 + +#define CreateRecGS_pCount 0 +#define CreateRecGS_pathname 2 +#define CreateRecGS_access 6 +#define CreateRecGS_fileType 8 +#define CreateRecGS_auxType 10 +#define CreateRecGS_storageType 14 +#define CreateRecGS_eof 16 +#define CreateRecGS_resourceEOF 20 + +#define CreateRec_pathname 0 +#define CreateRec_fAccess 4 +#define CreateRec_fileType 6 +#define CreateRec_auxType 8 +#define CreateRec_storageType 12 +#define CreateRec_createDate 14 +#define CreateRec_createTime 16 + +#define DirEntryRecGS_pCount 0 +#define DirEntryRecGS_refNum 2 +#define DirEntryRecGS_flags 4 +#define DirEntryRecGS_base 6 +#define DirEntryRecGS_displacement 8 +#define DirEntryRecGS_name 10 +#define DirEntryRecGS_entryNum 14 +#define DirEntryRecGS_fileType 16 +#define DirEntryRecGS_eof 18 +#define DirEntryRecGS_blockCount 22 +#define DirEntryRecGS_createDateTime 26 +#define DirEntryRecGS_modDateTime 34 +#define DirEntryRecGS_access 42 +#define DirEntryRecGS_auxType 44 +#define DirEntryRecGS_fileSysID 48 +#define DirEntryRecGS_optionList 50 +#define DirEntryRecGS_resourceEOF 54 +#define DirEntryRecGS_resourceBlocks 58 + +#define DirEntryRec_refNum 0 +#define DirEntryRec_flags 2 +#define DirEntryRec_base 4 +#define DirEntryRec_displacement 6 +#define DirEntryRec_nameBuffer 8 +#define DirEntryRec_entryNum 12 +#define DirEntryRec_fileType 14 +#define DirEntryRec_endOfFile 16 +#define DirEntryRec_blockCount 20 +#define DirEntryRec_createTime 24 +#define DirEntryRec_modTime 32 +#define DirEntryRec_access 40 +#define DirEntryRec_auxType 42 +#define DirEntryRec_fileSysID 46 + +#define FileInfoRecGS_pCount 0 +#define FileInfoRecGS_pathname 2 +#define FileInfoRecGS_access 6 +#define FileInfoRecGS_fileType 8 +#define FileInfoRecGS_auxType 10 +#define FileInfoRecGS_storageType 14 +#define FileInfoRecGS_createDateTime 16 +#define FileInfoRecGS_modDateTime 24 +#define FileInfoRecGS_optionList 32 +#define FileInfoRecGS_eof 36 +#define FileInfoRecGS_blocksUsed 40 +#define FileInfoRecGS_resourceEOF 44 +#define FileInfoRecGS_resourceBlocks 48 + +#define FileRec_pathname 0 +#define FileRec_fAccess 4 +#define FileRec_fileType 6 +#define FileRec_auxType 8 +#define FileRec_storageType 12 +#define FileRec_createDate 14 +#define FileRec_createTime 16 +#define FileRec_modDate 18 +#define FileRec_modTime 20 +#define FileRec_blocksUsed 22 + +#define OpenRecGS_pCount 0 +#define OpenRecGS_refNum 2 +#define OpenRecGS_pathname 4 +#define OpenRecGS_requestAccess 8 +#define OpenRecGS_resourceNumber 10 +#define OpenRecGS_access 12 +#define OpenRecGS_fileType 14 +#define OpenRecGS_auxType 16 +#define OpenRecGS_storageType 20 +#define OpenRecGS_createDateTime 22 +#define OpenRecGS_modDateTime 30 +#define OpenRecGS_optionList 38 +#define OpenRecGS_eof 42 +#define OpenRecGS_blocksUsed 46 +#define OpenRecGS_resourceEOF 50 +#define OpenRecGS_resourceBlocks 54 + +#define OpenRec_openRefNum 0 +#define OpenRec_openPathname 2 +#define OpenRec_ioBuffer 6 + +#define VolumeRecGS_pCount 0 +#define VolumeRecGS_devName 2 +#define VolumeRecGS_volName 6 +#define VolumeRecGS_totalBlocks 10 +#define VolumeRecGS_freeBlocks 14 +#define VolumeRecGS_fileSysID 18 +#define VolumeRecGS_blockSize 20 +#define VolumeRecGS_characteristics 22 +#define VolumeRecGS_deviceID 24 + +#define VolumeRec_deviceName 0 +#define VolumeRec_volName 4 +#define VolumeRec_totalBlocks 8 +#define VolumeRec_freeBlocks 12 +#define VolumeRec_fileSysID 16 + +#define JudgeNameRecGS_pCount 0 +#define JudgeNameRecGS_fileSysID 2 +#define JudgeNameRecGS_nameType 4 +#define JudgeNameRecGS_syntax 6 +#define JudgeNameRecGS_maxLen 10 +#define JudgeNameRecGS_name 12 +#define JudgeNameRecGS_nameFlags 16 + +#define PositionRecGS_pCount 0 +#define PositionRecGS_refNum 2 +#define PositionRecGS_position 4 + +#define MarkRec_markRefNum 0 +#define MarkRec_position 2 + +#define EOFRecGS_pCount 0 +#define EOFRecGS_refNum 2 +#define EOFRecGS_eof 4 + +#define EOFRec_eofRefNum 0 +#define EOFRec_eofPosition 2 + +#define IORecGS_pCount 0 +#define IORecGS_refNum 2 +#define IORecGS_dataBuffer 4 +#define IORecGS_requestCount 8 +#define IORecGS_transferCount 12 +#define IORecGS_cachePriority 16 + +#define FileIORec_fileRefNum 0 +#define FileIORec_dataBuffer 2 +#define FileIORec_requestCount 6 +#define FileIORec_transferCount 10 + +#define SetPositionRecGS_pCount 0 +#define SetPositionRecGS_refNum 2 +#define SetPositionRecGS_base 4 +#define SetPositionRecGS_displacement 6 + +#define DevNumRecGS_pCount 0 +#define DevNumRecGS_devName 2 +#define DevNumRecGS_devNum 6 + +#define DevNumRec_devName 0 +#define DevNumRec_devNum 4 + diff --git a/src/gsos.h b/src/gsos.h new file mode 100644 index 0000000..da5bac1 --- /dev/null +++ b/src/gsos.h @@ -0,0 +1,164 @@ +#define readEnableAllowWrite 0x0000 +#define readEnable 0x0001 +#define writeEnable 0x0002 +#define readWriteEnable 0x0003 +#define fileInvisible 0x0004 /* Invisible bit */ +#define backupNeeded 0x0020 /* backup needed bit: CreateRec/ OpenRec access field. (Must be 0 in requestAccess field ) */ +#define renameEnable 0x0040 /* rename enable bit: CreateRec/ OpenRec access and requestAccess fields */ +#define destroyEnable 0x0080 /* destroy enable bit: CreateRec/ OpenRec access and requestAccess fields */ +#define startPlus 0x0000 /* base -> setMark = displacement */ +#define eofMinus 0x0001 /* base -> setMark = eof - displacement */ +#define markPlus 0x0002 /* base -> setMark = mark + displacement */ +#define markMinus 0x0003 /* base -> setMark = mark - displacement */ + +/* cachePriority Codes */ +#define cacheOff 0x0000 /* do not cache blocks invloved in this read */ +#define cacheOn 0x0001 /* cache blocks invloved in this read if possible */ + +/* Error Codes */ +#define badSystemCall 0x0001 /* bad system call number */ +#define invalidPcount 0x0004 /* invalid parameter count */ +#define gsosActive 0x0007 /* GS/OS already active */ + +#ifndef devNotFound /* device not found */ + #define devNotFound 0x0010 +#endif + +#define invalidDevNum 0x0011 /* invalid device number */ +#define drvrBadReq 0x0020 /* bad request or command */ +#define drvrBadCode 0x0021 /* bad control or status code */ +#define drvrBadParm 0x0022 /* bad call parameter */ +#define drvrNotOpen 0x0023 /* character device not open */ +#define drvrPriorOpen 0x0024 /* character device already open */ +#define irqTableFull 0x0025 /* interrupt table full */ +#define drvrNoResrc 0x0026 /* resources not available */ +#define drvrIOError 0x0027 /* I/O error */ +#define drvrNoDevice 0x0028 /* device not connected */ +#define drvrBusy 0x0029 /* call aborted; driver is busy */ +#define drvrWrtProt 0x002B /* device is write protected */ +#define drvrBadCount 0x002C /* invalid byte count */ +#define drvrBadBlock 0x002D /* invalid block address */ +#define drvrDiskSwitch 0x002E /* disk has been switched */ +#define drvrOffLine 0x002F /* device off line/ no media present */ +#define badPathSyntax 0x0040 /* invalid pathname syntax */ +#define tooManyFilesOpen 0x0042 /* too many files open on server volume */ +#define invalidRefNum 0x0043 /* invalid reference number */ + +#ifndef pathNotFound /* subdirectory does not exist */ + #define pathNotFound 0x0044 +#endif + +#define volNotFound 0x0045 /* volume not found */ + +#ifndef fileNotFound /* file not found */ + #define fileNotFound 0x0046 +#endif + +#define dupPathname 0x0047 /* create or rename with existing name */ +#define volumeFull 0x0048 /* volume full error */ +#define volDirFull 0x0049 /* volume directory full */ +#define badFileFormat 0x004A /* version error (incompatible file format) */ + +#ifndef badStoreType /* unsupported (or incorrect) storage type */ + #define badStoreType 0x004B +#endif + +#ifndef eofEncountered /* end-of-file encountered */ + #define eofEncountered 0x004C +#endif + +#define outOfRange 0x004D /* position out of range */ +#define invalidAccess 0x004E /* access not allowed */ +#define buffTooSmall 0x004F /* buffer too small */ +#define fileBusy 0x0050 /* file is already open */ +#define dirError 0x0051 /* directory error */ +#define unknownVol 0x0052 /* unknown volume type */ + +#ifndef paramRangeErr /* parameter out of range */ + #define paramRangeErr 0x0053 +#endif + +#define outOfMem 0x0054 /* out of memory */ +#define dupVolume 0x0057 /* duplicate volume name */ +#define notBlockDev 0x0058 /* not a block device */ + +#ifndef invalidLevel /* specifield level outside legal range */ + #define invalidLevel 0x0059 +#endif + +#define damagedBitMap 0x005A /* block number too large */ +#define badPathNames 0x005B /* invalid pathnames for ChangePath */ +#define notSystemFile 0x005C /* not an executable file */ +#define osUnsupported 0x005D /* Operating System not supported */ + +#ifndef stackOverflow /* too many applications on stack */ + #define stackOverflow 0x005F +#endif + +#define dataUnavail 0x0060 /* Data unavailable */ +#define endOfDir 0x0061 /* end of directory has been reached */ +#define invalidClass 0x0062 /* invalid FST call class */ +#define resForkNotFound 0x0063 /* file does not contain required resource */ +#define invalidFSTID 0x0064 /* error - FST ID is invalid */ +#define invalidFSTop 0x0065 /* invalid FST operation */ +#define fstCaution 0x0066 /* FST handled call, but result is weird */ +#define devNameErr 0x0067 /* device exists with same name as replacement name */ +#define defListFull 0x0068 /* device list is full */ +#define supListFull 0x0069 /* supervisor list is full */ +#define fstError 0x006a /* generic FST error */ +#define resExistsErr 0x0070 /* cannot expand file, resource already exists */ +#define resAddErr 0x0071 /* cannot add resource fork to this type file */ +#define networkError 0x0088 /* generic network error */ + +/* fileSys IDs */ +#define proDOSFSID 0x0001 /* ProDOS/SOS */ +#define dos33FSID 0x0002 /* DOS 3.3 */ +#define dos32FSID 0x0003 /* DOS 3.2 */ +#define dos31FSID 0x0003 /* DOS 3.1 */ +#define appleIIPascalFSID 0x0004 /* Apple II Pascal */ +#define mfsFSID 0x0005 /* Macintosh (flat file system) */ +#define hfsFSID 0x0006 /* Macintosh (hierarchical file system) */ +#define lisaFSID 0x0007 /* Lisa file system */ +#define appleCPMFSID 0x0008 /* Apple CP/M */ +#define charFSTFSID 0x0009 /* Character FST */ +#define msDOSFSID 0x000A /* MS/DOS */ +#define highSierraFSID 0x000B /* High Sierra */ +#define iso9660FSID 0x000C /* ISO 9660 */ +#define appleShareFSID 0x000D /* ISO 9660 */ + +/* FSTInfo.attributes Codes */ +#define characterFST 0x4000 /* character FST */ +#define ucFST 0x8000 /* SCM should upper case pathnames before passing them to the FST */ + +/* QuitRec.flags Codes */ +#define onStack 0x8000 /* place state information about quitting program on the quit return stack */ +#define restartable 0x4000 /* the quitting program is capable of being restarted from its dormant memory */ + +/* storageType Codes */ +#define seedling 0x0001 /* standard file with seedling structure */ +#define standardFile 0x0001 /* standard file type (no resource fork) */ +#define sapling 0x0002 /* standard file with sapling structure */ +#define tree 0x0003 /* standard file with tree structure */ +#define pascalRegion 0x0004 /* UCSD Pascal region on a partitioned disk */ +#define extendedFile 0x0005 /* extended file type (with resource fork) */ +#define directoryFile 0x000D /* volume directory or subdirectory file */ + +/* version Codes */ +#define minorRelNumMask 0x00FF /* minor release number */ +#define majorRelNumMask 0x7F00 /* major release number */ +#define finalRelNumMask 0x8000 /* final release number */ + +/* Other Constants */ +#define isFileExtended 0x8000 /* GetDirEntryGS */ + +/* DControl Codes */ +#define resetDevice 0x0000 +#define formatDevice 0x0001 +#define eject 0x0002 +#define setConfigParameters 0x0003 +#define setWaitStatus 0x0004 +#define setFormatOptions 0x0005 +#define assignPartitionOwner 0x0006 +#define armSignal 0x0007 +#define disarmSignal 0x0008 +#define setPartitionMap 0x0009 diff --git a/src/host_fst.c b/src/host_fst.c new file mode 100644 index 0000000..9bd14d8 --- /dev/null +++ b/src/host_fst.c @@ -0,0 +1,2475 @@ +/* + * host_fst.c + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "defc.h" +#include "gsos.h" +#include "fst.h" + +#ifdef __APPLE__ +#include +#include +#include +#endif + + +#ifdef _WIN32 +#include +#define ftruncate(a,b) _chsize(a,b) +#endif + +#ifndef XATTR_FINDERINFO_NAME +#define XATTR_FINDERINFO_NAME "com.apple.FinderInfo" +#endif + +#ifndef XATTR_RESOURCEFORK_NAME +#define XATTR_RESOURCEFORK_NAME "com.apple.ResourceFork" +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +extern Engine_reg engine; + + +/* direct page offsets */ + +#define dp_call_number 0x30 +#define dp_param_blk_ptr 0x32 +#define dp_dev1_num 0x36 +#define dp_dev2_num 0x38 +#define dp_path1_ptr 0x3a +#define dp_path2_ptr 0x3e +#define dp_path_flag 0x42 + +#define global_buffer 0x009a00 + + +struct fd_entry { + struct fd_entry *next; + char *path; + int fd; + int st_mode; + int access; + int resource; + DIR *dirp; + int displacement; +}; + + +static struct fd_entry *fd_head = NULL; + +static ino_t root_ino = 0; +static dev_t root_dev = 0; +static char *root = NULL; +static int read_only = 0; + +char *g_cfg_host_path = ""; // must not be null. +int g_cfg_host_read_only = 0; + + +/* + * simple malloc pool to simplify code. Should never need > 4 allocations. + * + */ +static void *gc[16]; +static void **gc_ptr = &gc[0]; +static void *gc_malloc(size_t size) { + if (gc_ptr == &gc[16]) { + errno = ENOMEM; + return NULL; + } + + void *ptr = malloc(size); + if (ptr) { + *gc_ptr++ = ptr; + } + return ptr; +} + + +static void gc_free(void) { + + while (gc_ptr > gc) free(*--gc_ptr); + +} + +static char *append_path(const char *a, const char *b) { + int aa = strlen(a); + int bb = strlen(b); + + char *cp = gc_malloc(aa + bb + 2); + if (!cp) return NULL; + memcpy(cp, a, aa); + int len = aa; + if (len && cp[len-1] != '/' && b[0] != '/') cp[len++] = '/'; + memcpy(cp + len, b, bb); + len += bb; + cp[len] = 0; + while (len > 2 && cp[len - 1] == '/') cp[--len] = 0; + return cp; +} + + +static char *append_string(const char *a, const char *b) { + int aa = strlen(a); + int bb = strlen(b); + + char *cp = gc_malloc(aa + bb + 2); + if (!cp) return NULL; + memcpy(cp, a, aa); + int len = aa; + memcpy(cp + len, b, bb); + len += bb; + cp[len] = 0; + return cp; +} + + + +static word32 map_errno() { + switch(errno) { + case 0: return 0; + case EBADF: + return invalidAccess; + case EDQUOT: + case EFBIG: + return volumeFull; + case ENOENT: + return fileNotFound; + case ENOTDIR: + return pathNotFound; + case ENOMEM: + return outOfMem; + default: + return drvrIOError; + } +} + + +static struct fd_entry *find_fd(int fd) { + struct fd_entry *head = fd_head; + + while(head) { + if (head->fd == fd) return head; + head = head->next; + } + return NULL; +} + +static word32 remove_fd(int fd) { + word32 rv = invalidRefNum; + + struct fd_entry *prev = NULL; + struct fd_entry *head = fd_head; + while (head) { + if (head->fd == fd) { + if (prev) prev->next = head->next; + else fd_head = head->next; + + rv = 0; + + int ok; + if (head->dirp) ok = closedir(head->dirp); + else ok = close(head->fd); + if (ok < 0) rv = map_errno(); + free(head->path); + free(head); + break; + } + prev = head; + head = head->next; + } + return rv; +} + + +static ssize_t safe_read(int fd, void *buffer, size_t count) { + for (;;) { + ssize_t ok = read(fd, buffer, count); + if (ok >= 0) return ok; + if (ok < 0 && errno == EINTR) continue; + return ok; + } +} + +static ssize_t safe_write(int fd, const void *buffer, size_t count) { + for (;;) { + ssize_t ok = write(fd, buffer, count); + if (ok >= 0) return ok; + if (ok < 0 && errno == EINTR) continue; + return ok; + } +} + +struct file_info { + + time_t create_date; + time_t modified_date; + word16 access; + word16 storage_type; + word16 file_type; + word32 aux_type; + word32 eof; + word32 blocks; + word32 resource_eof; + word32 resource_blocks; + mode_t st_mode; + int has_fi; + byte finder_info[32]; +}; + +static int hex(byte c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c + 10 - 'a'; + if (c >= 'A' && c <= 'F') return c + 10 - 'A'; + return 0; +} + + +static int finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) { + + if (!memcmp("pdos", buffer + 4, 4)) + { + if (buffer[0] == 'p') { + *file_type = buffer[1]; + *aux_type = (buffer[2] << 8) | buffer[3]; + return 0; + } + if (!memcmp("PSYS", buffer, 4)) { + *file_type = 0xff; + *aux_type = 0x0000; + return 0; + } + if (!memcmp("PS16", buffer, 4)) { + *file_type = 0xb3; + *aux_type = 0x0000; + return 0; + } + + // old mpw method for encoding. + if (!isxdigit(buffer[0]) && isxdigit(buffer[1]) && buffer[2] == ' ' && buffer[3] == ' ') + { + *file_type = (hex(buffer[0]) << 8) | hex(buffer[1]); + *aux_type = 0; + return 0; + } + } + if (!memcmp("TEXT", buffer, 4)) { + *file_type = 0x04; + *aux_type = 0x0000; + return 0; + } + if (!memcmp("BINA", buffer, 4)) { + *file_type = 0x00; + *aux_type = 0x0000; + return 0; + } + if (!memcmp("dImgdCpy", buffer, 8)) { + *file_type = 0xe0; + *aux_type = 0x0005; + return 0; + } + + if (!memcmp("MIDI", buffer, 4)) { + *file_type = 0xd7; + *aux_type = 0x0000; + return 0; + } + + if (!memcmp("AIFF", buffer, 4)) { + *file_type = 0xd8; + *aux_type = 0x0000; + return 0; + } + + if (!memcmp("AIFC", buffer, 4)) { + *file_type = 0xd8; + *aux_type = 0x0001; + return 0; + } + + return -1; +} + +static int file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type) { + if (file_type > 0xff || aux_type > 0xffff) return -1; + + if (!file_type && aux_type == 0x0000) { + memcpy(buffer, "BINApdos", 8); + return 0; + } + + if (file_type == 0x04 && aux_type == 0x0000) { + memcpy(buffer, "TEXTpdos", 8); + return 0; + } + + if (file_type == 0xff && aux_type == 0x0000) { + memcpy(buffer, "PSYSpdos", 8); + return 0; + } + + if (file_type == 0xb3 && aux_type == 0x0000) { + memcpy(buffer, "PS16pdos", 8); + return 0; + } + + if (file_type == 0xd7 && aux_type == 0x0000) { + memcpy(buffer, "MIDIpdos", 8); + return 0; + } + if (file_type == 0xd8 && aux_type == 0x0000) { + memcpy(buffer, "AIFFpdos", 8); + return 0; + } + if (file_type == 0xd8 && aux_type == 0x0001) { + memcpy(buffer, "AIFCpdos", 8); + return 0; + } + if (file_type == 0xe0 && aux_type == 0x0005) { + memcpy(buffer, "dImgdCpy", 8); + return 0; + } + + + memcpy(buffer, "p pdos", 8); + buffer[1] = (file_type) & 0xff; + buffer[2] = (aux_type >> 8) & 0xff; + buffer[3] = (aux_type) & 0xff; + return 0; +} + + +#if defined __APPLE__ +static void get_file_xinfo(const char *path, struct file_info *fi) { + + ssize_t tmp; + tmp = getxattr(path, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0); + if (tmp < 0) tmp = 0; + fi->resource_eof = tmp; + fi->resource_blocks = (tmp + 511) / 512; + + tmp = getxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0); + if (tmp == 16 || tmp == 32){ + fi->has_fi = 1; + + finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); + } +} +#elif defined _WIN32 +static void get_file_xinfo(const char *path, struct file_info *fi) { + + struct stat st; + + char *p = append_string(path, ":" XATTR_RESOURCEFORK_NAME); + if (stat(p, &st) == 0) { + fi->resource_eof = st.st_size; + fi->resource_blocks = st.st_blocks; + } + p = append_string(path, ":" XATTR_FINDERINFO_NAME); + int fd = open(p, O_RDONLY); + if (fd >= 0) { + int tmp = read(fd, fi->finder_info, 32); + if (tmp == 16 || tmp == 32) { + fi->has_fi = 1; + finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); + } + close(fd); + } + + +} +#elif defined __sun +static void get_file_xinfo(const char *path, struct file_info *fi) { + + struct stat st; + + // can't stat an xattr directly? + int fd; + fd = attropen(path, XATTR_RESOURCEFORK_NAME, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + fi->resource_eof = st.st_size; + fi->resource_blocks = st.st_blocks; + } + close(fd); + } + + fd = attropen(path, XATTR_FINDERINFO_NAME, O_RDONLY); + if (fd >= 0) { + int tmp = read(fd, fi->finder_info, 32); + if (tmp == 16 || tmp == 32) { + fi->has_fi = 1; + finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); + } + close(fd); + } +} +#elif defined __linux__ +static void get_file_xinfo(const char *path, struct file_info *fi) { + + ssize_t tmp; + tmp = getxattr(path, "user.apple.ResourceFork", NULL, 0); + if (tmp < 0) tmp = 0; + fi->resource_eof = tmp; + fi->resource_blocks = (tmp + 511) / 512; + + tmp = getxattr(path, "user.apple.FinderInfo", fi->finder_info, 32, 0, 0); + if (tmp == 16 || tmp == 32){ + fi->has_fi = 1; + + finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); + } +} +#else +static void get_file_xinfo(const char *path, struct file_info *fi) { +} +#endif + +static word32 get_file_info(const char *path, struct file_info *fi) { + struct stat st; + memset(fi, 0, sizeof(*fi)); + + int ok = stat(path, &st); + if (ok < 0) return map_errno(); + + fi->eof = st.st_size; + fi->blocks = st.st_blocks; + + fi->create_date = st.st_ctime; + fi->modified_date = st.st_mtime; + +#if defined __APPLE__ + fi->create_date = st.st_birthtime; +#endif + + + fi->st_mode = st.st_mode; + + if (S_ISDIR(st.st_mode)) { + fi->storage_type = 0x0d; + fi->file_type = 0x0f; + if (st.st_ino == root_ino && st.st_dev == root_dev) + fi->storage_type = 0x0f; + } else if (S_ISREG(st.st_mode)) { + fi->file_type = 0x06; + if (st.st_size < 0x200) fi->storage_type = seedling; + else if (st.st_size < 0x20000) fi->storage_type = 0x0002; + else fi->storage_type = 0x0003; + } else { + fi->storage_type = st.st_mode & S_IFMT; + fi->file_type = 0; + } + // 0x01 = read enable + // 0x02 = write enable + // 0x04 = invisible + // 0x08 = reserved + // 0x10 = reserved + // 0x20 = backup needed + // 0x40 = rename enable + // 0x80 = destroy enable + + fi->access = 0xc3; // placeholder... + + if (S_ISREG(st.st_mode)) { + get_file_xinfo(path, fi); + } + + // get file type/aux type + + if (fi->resource_eof) fi->storage_type = 0x0005; + + return 0; +} + + + + +#if defined __APPLE__ +static word32 set_file_info(const char *path, struct file_info *fi) { + + int ok; + struct attrlist list; + unsigned i = 0; + struct timespec dates[2]; + + if (fi->has_fi && fi->storage_type != 0x0d) { + ok = setxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0); + if (ok < 0) return map_errno(); + } + + + memset(&list, 0, sizeof(list)); + memset(dates, 0, sizeof(dates)); + + list.bitmapcount = ATTR_BIT_MAP_COUNT; + list.commonattr = 0; + + if (fi->create_date) + { + dates[i++].tv_sec = fi->create_date; + list.commonattr |= ATTR_CMN_CRTIME; + } + + if (fi->modified_date) + { + dates[i++].tv_sec = fi->modified_date; + list.commonattr |= ATTR_CMN_MODTIME; + } + + ok = 0; + if (i) ok = setattrlist(path, &list, dates, i * sizeof(struct timespec), 0); + return 0; +} +#elif definde _WIN32 + + +static void UnixTimeToFileTime(time_t t, LPFILETIME pft) +{ + if (t) { + LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000; + pft->dwLowDateTime = (DWORD)ll; + pft->dwHighDateTime = ll >> 32; + } else { + pft->dwLowDateTime = 0; + pft->dwHighDateTime = 0; + } + +} + + +static word32 set_file_info(const char *path, struct file_info *fi) { + + if (fi->has_fi && fi->storage_type != 0x0d) { + char *path = append_string(path, ":" XATTR_FINDERINFO_NAME); + int fd = open(path, O_WRONLY | O_CREAT, 0666); + if (fd < 0) return map_errno(); + write(fd, fi->finder_info, 32); + close(fd); + } + + if (fi->create_date || fi->modified_date) { + // SetFileInformationByHandle can modify dates. + int fd = open(path, O_RDONLY); // ? + if (fd < 0) return 0; + + // just use FILETIME in the file_info if WIN32? + + struct FILE_BASIC_INFO fbi; + memset(&fbi, 0, sizeof(fbi)); + + LONGLONG ll; + + if (fi->create_date) { + UnixTimeToFileTime(fi->create_date, &fbi.CreationTime); + } + if (fi->modified_date) { + FILETIME ft; + UnixTimeToFileTime(fi->modified_date, &ft); + fbi.ChangeTime = ft; + fbi.LastAccessTime = ft; + fbi.LastWriteTime = ft; + } + + + Handle h = get_osfhandle(fd); + BOOL ok = SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(fbi)); + close(fd); + } + return 0; +} + +#elif defined __sun +static word32 set_file_info(const char *path, struct file_info *fi) { + + if (fi->has_fi && fi->storage_type != 0x0d) { + int fd = attropen(path, XATTR_FINDERINFO_NAME, O_WRONLY | O_CREAT, 0666); + if (fd < 0) return map_errno(); + write(fd, fi->finder_info, 32); + close(fd); + } + + if (fi->modified_date) { + + struct timeval times[2]; + + memset(times, 0, sizeof(times)); + + + times[0] = 0; // access + times[1].tv_sec = fi.modified_date; // modified + + int ok = utimes(path); + if (ok < 0) return map_errno(); + } + return 0; +} +#elif defined __linux__ +static word32 set_file_info(const char *path, struct file_info *fi) { + + if (fi->has_fi && fi->storage_type != 0x0d) { + int ok = setxattr(path, "user.apple.FinderInfo", fi->finder_info, 32, 0); + if (ok < 0) return map_errno(); + } + + if (fi->modified_date) { + + struct timeval times[2]; + + memset(times, 0, sizeof(times)); + + + times[0] = 0; // access + times[1].tv_sec = fi.modified_date; // modified + + int ok = utimes(path); + if (ok < 0) return map_errno(); + } + return 0; +} + +#else + +static word32 set_file_info(const char *path, struct file_info *fi) { + + if (fi.modified_date) { + + struct timeval times[2]; + + memset(times, 0, sizeof(times)); + + + times[0] = 0; // access + times[1].tv_sec = fi.modified_date; // modified + + int ok = utimes(path); + if (ok < 0) return map_errno(); + } + return 0; +} + +#endif + + +/* + * if this is an absolute path, verify and skip past :Host: + * + */ +static const char *check_path(const char *in, word32 *error) { + + word32 tmp; + if (!error) error = &tmp; + + *error = 0; + if (!in) return ""; + + /* check for .. */ + const char *cp = in; + do { + while (*cp == '/') ++cp; + if (cp[0] == '.' && cp[1] == '.' && (cp[2] == '/' || cp[2] == 0)) + { + *error = badPathSyntax; + return NULL; + } + cp = strchr(cp, '/'); + } while(cp); + + + if (in[0] != '/') return in; + + if (strncasecmp(in, "/Host", 5) == 0 && (in[5] == '/' || in[5] == 0)) { + in += 5; + while (*in == '/') ++in; + return in; + } + *error = volNotFound; + return NULL; +} + + +static char * get_gsstr(word32 ptr) { + if (!ptr) return NULL; + int length = get_memory16_c(ptr, 0); + ptr += 2; + char *str = gc_malloc(length + 1); + for (int i = 0; i < length; ++i) { + char c = get_memory_c(ptr+i, 0); + if (c == ':') c = '/'; + str[i] = c; + } + str[length] = 0; + return str; +} + +static char * get_pstr(word32 ptr) { + if (!ptr) return NULL; + int length = get_memory16_c(ptr, 0); + ptr += 2; + char *str = gc_malloc(length + 1); + for (int i = 0; i < length; ++i) { + char c = get_memory_c(ptr+i, 0); + if (c == ':') c = '/'; + str[i] = c; + } + str[length] = 0; + return str; +} + +static word32 set_gsstr(word32 ptr, const char *str) { + + if (!ptr) return paramRangeErr; + + int l = str ? strlen(str) : 0; + + + word32 cap = get_memory16_c(ptr, 0); + ptr += 2; + + if (cap < 4) return paramRangeErr; + + set_memory16_c(ptr, l, 0); + ptr += 2; + + if (cap < l + 4) return buffTooSmall; + + + for (int i = 0; i < l; ++i) { + char c = *str++; + if (c == '/') c = ':'; + set_memory_c(ptr++, c, 0); + } + return 0; +} + +static word32 set_gsstr_truncate(word32 ptr, const char *str) { + + if (!ptr) return paramRangeErr; + + int l = str ? strlen(str) : 0; + + + word32 cap = get_memory16_c(ptr, 0); + ptr += 2; + + if (cap < 4) return paramRangeErr; + + set_memory16_c(ptr, l, 0); + ptr += 2; + + // get dir entry copies data even + // if buffTooSmall... + int rv = 0; + if (cap < l + 4) { + l = cap - 4; + rv = buffTooSmall; + } + + for (int i = 0; i < l; ++i) { + char c = *str++; + if (c == '/') c = ':'; + set_memory_c(ptr++, c, 0); + } + return rv; +} + +static word32 set_pstr(word32 ptr, const char *str) { + + if (!ptr) return paramRangeErr; + + int l = str ? strlen(str) : 0; + + if (l > 255) return buffTooSmall; + // / is the pascal separator. + set_memory_c(ptr++, l, 0); + for (int i = 0; i < l; ++i) { + set_memory_c(ptr++, *str++, 0); + } + return 0; +} + + +static word32 set_option_list(word32 ptr, word16 fstID, const byte *data, int size) { + if (!ptr) return 0; + + // totalSize + word32 cap = get_memory16_c(ptr, 0); + ptr += 2; + + if (cap < 4) return paramRangeErr; + + // reqSize + set_memory16_c(ptr, size + 2, 0); + ptr += 2; + + if (cap < size + 6) return buffTooSmall; + + + // fileSysID. + set_memory16_c(ptr, fstID, 0); + ptr += 2; + + for (int i = 0; i < size; ++i) + set_memory_c(ptr++, *data++, 0); + + return 0; +} + +/* + * converts time_t to a gs/os readhextime date/time record. + */ + +static void set_date_time_rec(word32 ptr, time_t time) { + + if (time == 0) { + for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0); + return; + } + + struct tm *tm = localtime(&time); + if (tm->tm_sec == 60) tm->tm_sec = 59; /* leap second */ + + set_memory_c(ptr++, tm->tm_sec, 0); + set_memory_c(ptr++, tm->tm_min, 0); + set_memory_c(ptr++, tm->tm_hour, 0); + set_memory_c(ptr++, tm->tm_year, 0); + set_memory_c(ptr++, tm->tm_mday - 1, 0); + set_memory_c(ptr++, tm->tm_mon, 0); + set_memory_c(ptr++, 0, 0); + set_memory_c(ptr++, tm->tm_wday + 1, 0); +} + +/* + * converts time_t to a prodos16 date/time record. + */ +static void set_date_time(word32 ptr, time_t time) { + + if (time == 0) { + for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0); + return; + } + + struct tm *tm = localtime(&time); + + word16 tmp = 0; + tmp |= (tm->tm_year % 100) << 9; + tmp |= tm->tm_mon << 5; + tmp |= tm->tm_mday; + + set_memory16_c(ptr, tmp, 0); + ptr += 2; + + tmp = 0; + tmp |= tm->tm_hour << 8; + tmp |= tm->tm_min; + set_memory16_c(ptr, tmp, 0); +} + + +static time_t get_date_time(word32 ptr) { + + word16 a = get_memory16_c(ptr + 0, 0); + word16 b = get_memory16_c(ptr + 2, 0); + if (!a && !b) return 0; + + struct tm tm; + memset(&tm, 0, sizeof(tm)); + + tm.tm_year = (a >> 9) & 0x7f; + tm.tm_mon = ((a >> 5) & 0x0f) - 1; + tm.tm_mday = (a >> 0) & 0x1f; + + tm.tm_hour = (b >> 8) & 0x1f; + tm.tm_min = (b >> 0) & 0x3f; + tm.tm_sec = 0; + + tm.tm_isdst = -1; + + // 00 - 39 => 2000-2039 + // 40 - 99 => 1940-1999 + if (tm.tm_year < 40) tm.tm_year += 100; + + + return mktime(&tm); +} + +static time_t get_date_time_rec(word32 ptr) { + + byte buffer[8]; + for (int i = 0; i < 8; ++i) buffer[i] = get_memory_c(ptr++, 0); + + if (!memcmp(buffer, "\x00\x00\x00\x00\x00\x00\x00\x00", 8)) return 0; + + struct tm tm; + memset(&tm, 0, sizeof(tm)); + + tm.tm_sec = buffer[0]; + tm.tm_min = buffer[1]; + tm.tm_hour = buffer[2]; + tm.tm_year = buffer[3]; + tm.tm_mday = buffer[4] + 1; + tm.tm_mon = buffer[5]; + tm.tm_isdst = -1; + + return mktime(&tm); +} + + +static char *get_path1(void) { + word32 direct = engine.direct; + word16 flags = get_memory16_c(direct + dp_path_flag, 0); + if (flags & (1 << 14)) + return get_gsstr( get_memory24_c(direct + dp_path1_ptr, 0)); + return NULL; +} + +static char *get_path2(void) { + word32 direct = engine.direct; + word16 flags = get_memory16_c(direct + dp_path_flag, 0); + if (flags & (1 << 6)) + return get_gsstr( get_memory24_c(direct + dp_path2_ptr, 0)); + return NULL; +} + + +#define SEC() engine.psr |= 1 +#define CLC() engine.psr &= ~1 + + +static word32 fst_shutdown(void) { + + // close any remaining files. + struct fd_entry *head = fd_head; + while (head) { + struct fd_entry *tmp = head->next; + if (head->dirp) closedir(head->dirp); + else close(head->fd); + free(head->path); + free(head); + head = tmp; + } + return 0; +} + +static word32 fst_startup(void) { + // if restart, close any previous files. + + struct stat st; + + if (!g_cfg_host_path) return invalidFSTop; + if (!*g_cfg_host_path) return invalidFSTop; + if (root) free(root); + root = strdup(g_cfg_host_path); + + read_only = g_cfg_host_read_only; + + fst_shutdown(); + + if (stat(root, &st) < 0) { + fprintf(stderr, "%s does not exist\n", root); + return invalidFSTop; + } + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s is not a directory\n", root); + return invalidFSTop; + } + + root_ino = st.st_ino; + root_dev = st.st_dev; + + return 0; +} + + +static word32 fst_create(int class, const char *path) { + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + struct file_info fi; + memset(&fi, 0, sizeof(fi)); + + word16 pcount = 0; + if (class) { + pcount = get_memory16_c(pb, 0); + if (pcount >= 2) fi.access = get_memory16_c(pb + CreateRecGS_access, 0); + if (pcount >= 3) fi.file_type = get_memory16_c(pb + CreateRecGS_fileType, 0); + if (pcount >= 4) fi.aux_type = get_memory32_c(pb + CreateRecGS_auxType, 0); + if (pcount >= 5) fi.storage_type = get_memory16_c(pb + CreateRecGS_storageType, 0); + if (pcount >= 6) fi.eof = get_memory32_c(pb + CreateRecGS_eof, 0); + if (pcount >= 7) fi.resource_eof = get_memory32_c(pb + CreateRecGS_resourceEOF, 0); + + + if (pcount >= 4) { + file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + fi.has_fi = 1; + } + + + } else { + + fi.access = get_memory16_c(pb + CreateRec_fAccess, 0); + fi.file_type = get_memory16_c(pb + CreateRec_fileType, 0); + fi.aux_type = get_memory32_c(pb + CreateRec_auxType, 0); + fi.storage_type = get_memory16_c(pb + CreateRec_storageType, 0); + fi.create_date = get_date_time(pb + CreateRec_createDate); + + file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + fi.has_fi = 1; + } + int ok; + + if (fi.storage_type == 0x0d) { + ok = mkdir(path, 0777); + if (ok < 0) return map_errno(); + return 0; + } + if (fi.storage_type <= 3) { + // normal file. + ok = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (ok < 0) return map_errno(); + // set ftype, auxtype... + set_file_info(path, &fi); + close(ok); + + if (class) { + if (pcount >= 5) set_memory16_c(pb + CreateRecGS_storageType, 1, 0); + } else { + set_memory16_c(pb + CreateRec_storageType, 1, 0); + } + + return 0; + } + if (fi.storage_type == 0x05) { + // create an extended file... + // doesn't do anything particular yet. + + ok = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (ok < 0) return map_errno(); + close(ok); + // set ftype, auxtype... + return 0; + } + + if (fi.storage_type == 0x8005) { + // convert an existing file to an extended file. + // this checks that the file exists and has a 0-sized resource. + word32 rv = get_file_info(path, &fi); + if (rv) return rv; + if (fi.storage_type == extendedFile) return resExistsErr; + if (fi.storage_type < seedling || fi.storage_type > tree) return resAddErr; + return 0; + } + + // other storage types? + return badStoreType; +} + + +static word32 fst_destroy(int class, const char *path) { + + struct stat st; + + if (!path) return badStoreType; + + if (stat(path, &st) < 0) { + return map_errno(); + } + + // can't delete volume root. + if (st.st_ino == root_ino && st.st_dev == root_dev) { + return badStoreType; + } + + int ok = S_ISDIR(st.st_mode) ? rmdir(path) : unlink(path); + + + if (ok < 0) return map_errno(); + return 0; +} + +static word32 fst_set_file_info(int class, const char *path) { + + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + struct file_info fi; + memset(&fi, 0, sizeof(fi)); + + + // load up existing file types / finder info. + get_file_xinfo(path, &fi); + + word32 option_list = 0; + if (class) { + word16 pcount = get_memory16_c(pb, 0); + + if (pcount >= 2) fi.access = get_memory16_c(pb + FileInfoRecGS_access, 0); + if (pcount >= 3) fi.file_type = get_memory16_c(pb + FileInfoRecGS_fileType, 0); + if (pcount >= 4) fi.aux_type = get_memory32_c(pb + FileInfoRecGS_auxType, 0); + // reserved. + //if (pcount >= 5) fi.storage_type = get_memory16_c(pb + FileInfoRecGS_storageType, 0); + if (pcount >= 6) fi.create_date = get_date_time_rec(pb + FileInfoRecGS_createDateTime); + if (pcount >= 7) fi.modified_date = get_date_time_rec(pb + FileInfoRecGS_modDateTime); + if (pcount >= 8) option_list = get_memory24_c(pb + FileInfoRecGS_optionList, 0); + // remainder reserved + + if (pcount >= 4) { + file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + fi.has_fi = 1; + } + + } else { + fi.access = get_memory16_c(pb + FileRec_fAccess, 0); + fi.file_type = get_memory16_c(pb + FileRec_fileType, 0); + fi.aux_type = get_memory32_c(pb + FileRec_auxType, 0); + // reserved. + //fi.storage_type = get_memory32_c(pb + FileRec_storageType, 0); + fi.create_date = get_date_time(pb + FileRec_createDate); + fi.modified_date = get_date_time(pb + FileRec_modDate); + + file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + fi.has_fi = 1; + } + + if (option_list) { + // total size, req size, fst id, data... + int total_size = get_memory16_c(option_list + 0, 0); + int req_size = get_memory16_c(option_list + 2, 0); + int fst_id = get_memory16_c(option_list + 4, 0); + + int size = req_size - 6; + if ((fst_id == proDOSFSID || fst_id == hfsFSID || fst_id == appleShareFSID) && size >= 32) { + fi.has_fi = 1; + for (int i = 0; i <32; ++i) + fi.finder_info[i] = get_memory_c(option_list + 6 + i, 0); + } + } + + + return set_file_info(path, &fi); +} + +static word32 fst_get_file_info(int class, const char *path) { + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + struct file_info fi; + int rv = 0; + + rv = get_file_info(path, &fi); + if (rv) return rv; + + if (class) { + + word16 pcount = get_memory16_c(pb, 0); + + if (pcount >= 2) set_memory16_c(pb + FileInfoRecGS_access, fi.access, 0); + if (pcount >= 3) set_memory16_c(pb + FileInfoRecGS_fileType, fi.file_type, 0); + if (pcount >= 4) set_memory32_c(pb + FileInfoRecGS_auxType, fi.aux_type, 0); + if (pcount >= 5) set_memory16_c(pb + FileInfoRecGS_storageType, fi.storage_type, 0); + + if (pcount >= 6) set_date_time_rec(pb + FileInfoRecGS_createDateTime, fi.create_date); + if (pcount >= 7) set_date_time_rec(pb + FileInfoRecGS_modDateTime, fi.modified_date); + + if (pcount >= 8) { + word16 fst_id = hfsFSID; + //if (fi.storage_type == 0x0f) fst_id = mfsFSID; + word32 option_list = get_memory24_c(pb + FileInfoRecGS_optionList, 0); + rv = set_option_list(option_list, fst_id, fi.finder_info, fi.has_fi ? 32 : 0); + } + if (pcount >= 9) set_memory32_c(pb + FileInfoRecGS_eof, fi.eof, 0); + if (pcount >= 10) set_memory32_c(pb + FileInfoRecGS_blocksUsed, fi.blocks, 0); + if (pcount >= 11) set_memory32_c(pb + FileInfoRecGS_resourceEOF, fi.resource_eof, 0); + if (pcount >= 12) set_memory32_c(pb + FileInfoRecGS_resourceBlocks, fi.resource_blocks, 0); + + } else { + + set_memory16_c(pb + FileRec_fAccess, fi.access, 0); + set_memory16_c(pb + FileRec_fileType, fi.file_type, 0); + set_memory32_c(pb + FileRec_auxType, fi.aux_type, 0); + set_memory16_c(pb + FileRec_storageType, fi.storage_type, 0); + + set_date_time(pb + FileRec_createDate, fi.create_date); + set_date_time(pb + FileRec_modDate, fi.modified_date); + + set_memory32_c(pb + FileRec_blocksUsed, fi.blocks, 0); + } + + return rv; +} + +static word32 fst_judge_name(int class, char *path) { + + if (class == 0) return invalidClass; + + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + word16 pcount = get_memory16_c(pb, 0); + word16 name_type = get_memory16_c(pb + JudgeNameRecGS_nameType, 0); + word32 name = pcount >= 5 ? get_memory24_c(pb + JudgeNameRecGS_name, 0) : 0; + + // 255 max length. + if (pcount >= 4) set_memory16_c(pb + JudgeNameRecGS_maxLen, 255, 0); + if (pcount >= 6) set_memory16_c(pb + JudgeNameRecGS_nameFlags, 0, 0); + + word16 nameFlags = 0; + word16 rv = 0; + + if (name) { + word16 cap = get_memory16_c(name, 0); + if (cap < 4) return buffTooSmall; + word16 length = get_memory16_c(name + 2, 0); + if (length == 0) { + nameFlags |= 1 << 13; + rv = set_gsstr(name, "A"); + } else { + // if volume name, only allow "Host" ? + if (length > 255) nameFlags |= 1 << 14; + for (int i = 0; i < length; ++i) { + char c = get_memory_c(name + 4 + i, 0); + if (c == 0 || c == ':' || c == '/' || c == '\\') { + nameFlags |= 1 << 15; + set_memory_c(name + 4 + i, '.', 0); + } + } + } + } + if (pcount >= 6) set_memory16_c(pb + JudgeNameRecGS_nameFlags, nameFlags, 0); + return rv; +} + +static word32 fst_volume(int class) { + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + word32 rv = 0; + if (class) { + word16 pcount = get_memory16_c(pb, 0); + if (pcount >= 2) rv = set_gsstr(get_memory24_c(pb + VolumeRecGS_volName, 0), ":Host"); + // finder bug -- if used blocks is 0, doesn't display header. + if (pcount >= 3) set_memory32_c(pb + VolumeRecGS_totalBlocks, 0x007fffff, 0); + if (pcount >= 4) set_memory32_c(pb + VolumeRecGS_freeBlocks, 0x007fffff-1, 0); + if (pcount >= 5) set_memory16_c(pb + VolumeRecGS_fileSysID, mfsFSID, 0); + if (pcount >= 6) set_memory16_c(pb + VolumeRecGS_blockSize, 512, 0); + // handled via gs/os + //if (pcount >= 7) set_memory16_c(pb + VolumeRecGS_characteristics, 0, 0); + //if (pcount >= 8) set_memory16_c(pb + VolumeRecGS_deviceID, 0, 0); + } else { + + // prodos 16 uses / sep. + rv = set_pstr(get_memory24_c(pb + VolumeRec_volName, 0), "/Host"); + set_memory32_c(pb + VolumeRec_totalBlocks, 0x007fffff, 0); + set_memory32_c(pb + VolumeRec_freeBlocks, 0x007fffff-1, 0); + set_memory16_c(pb + VolumeRec_fileSysID, mfsFSID, 0); + } + + return rv; + +} + +static word32 fst_clear_backup(int class, const char *path) { + return invalidFSTop; +} + +static int open_data_fork(const char *path, word16 *access, word16 *error) { + + int fd = -1; + for (;;) { + + switch(*access) { + case readEnableAllowWrite: + case readWriteEnable: + fd = open(path, O_RDWR); + break; + case readEnable: + fd = open(path, O_RDONLY); + break; + case writeEnable: + fd = open(path, O_WRONLY); + break; + } + + if (*access == readEnableAllowWrite) { + if (fd < 0) { + *access = readEnable; + continue; + } + *access = readWriteEnable; + } + break; + } + if (fd < 0) { + *error = map_errno(); + } + return fd; +} +#if defined __APPLE__ +static int open_resource_fork(const char *path, word16 *access, word16 *error) { + // os x / hfs/apfs don't need to specifically create a resource fork. + // or do they? + + char *rpath = append_path(path, _PATH_RSRCFORKSPEC); + + int fd = -1; + for (;;) { + + switch(*access) { + case readEnableAllowWrite: + case readWriteEnable: + fd = open(rpath, O_RDWR | O_CREAT, 0666); + break; + case readEnable: + fd = open(rpath, O_RDONLY); + break; + case writeEnable: + fd = open(rpath, O_WRONLY | O_CREAT, 0666); + break; + } + + if (*access == readEnableAllowWrite) { + if (fd < 0) { + *access = readEnable; + continue; + } + *access = readWriteEnable; + } + break; + } + if (fd < 0) { + *error = map_errno(); + } + return fd; + + + +} +#elif defined _WIN32 +static int open_resource_fork(const char *path, word16 *access, word16 *error) { + char *rpath = append_string(path, ":" XATTR_RESOURCEFORK_NAME); + + int fd = -1; + for (;;) { + + switch(*access) { + case readEnableAllowWrite: + case readWriteEnable: + fd = open(rpath, O_RDWR | O_CREAT, 0666); + break; + case readEnable: + fd = open(rpath, O_RDONLY); + break; + case writeEnable: + fd = open(rpath, O_WRONLY | O_CREAT, 0666); + break; + } + + if (*access == readEnableAllowWrite) { + if (fd < 0) { + *access = readEnable; + continue; + } + *access = readWriteEnable; + } + break; + } + if (fd < 0) { + *error = map_errno(); + } + return fd; +} +#elif defined __sun +static int open_resource_fork(const char *path, word16 *access, word16 *error) { + int tmp = open(path, O_RDONLY); + if (tmp < 0) { + *error = map_errno(); + return -1; + } + + int fd = -1; + for(;;) { + + switch(*access) { + case readEnableAllowWrite: + case readWriteEnable: + fd = openat(tmp, XATTR_RESOURCEFORK_NAME, O_RDWR | O_CREAT | O_XATTR, 0666); + break; + case readEnable: + fd = openat(tmp, XATTR_RESOURCEFORK_NAME, O_RDONLY | O_XATTR); + break; + case writeEnable: + fd = openat(tmp, XATTR_RESOURCEFORK_NAME, O_WRONLY | O_CREAT | O_XATTR, 0666); + break; + } + + if (*access == readEnableAllowWrite) { + if (fd < 0) { + *access = readEnable; + continue; + } + *access = readWriteEnable; + } + break; + } + + if (fd < 0) { + *error = map_errno(); + close(tmp); + return -1; + } + close(tmp); + + return fd; +} +#elif defined __linux__ +static int open_resource_fork(const char *path, word16 *access, word16 *error) { + *error = resForkNotFound; + return -1; +} +#else +static int open_resource_fork(const char *path, word16 *access, word16 *error) { + *error = resForkNotFound; + return -1; +} +#endif + + +static word32 fst_open(int class, const char *path) { + + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + struct file_info fi; + word16 rv = 0; + + rv = get_file_info(path, &fi); + if (rv) return rv; + + + int fd; + + word16 pcount = 0; + word16 request_access = readEnableAllowWrite; + word16 access = 0; + word16 resource_number = 0; + if (class) { + pcount = get_memory16_c(pb, 0); + if (pcount >= 3) request_access = get_memory16_c(pb + OpenRecGS_requestAccess, 0); + if (pcount >= 4) resource_number = get_memory16_c(pb + OpenRecGS_resourceNumber, 0); + } + + if (resource_number > 1) return paramRangeErr; + if (access > 3) return paramRangeErr; + + // special access checks for directories. + if (S_ISDIR(fi.st_mode)) { + if (resource_number) return resForkNotFound; + switch (request_access) { + case readEnableAllowWrite: + request_access = readEnable; + break; + case writeEnable: + case readWriteEnable: + return invalidAccess; + } + } + + if (read_only) { + switch (request_access) { + case readEnableAllowWrite: + request_access = readEnable; + break; + case readWriteEnable: + case writeEnable: + return invalidAccess; + break; + } + } + + access = request_access; + if (resource_number) fd = open_resource_fork(path, &access, &rv); + else fd = open_data_fork(path, &access, &rv); + + if (fd < 0) return rv; + + // todo -- use id separate from fd? + if (fd > 65535) { + close(fd); + return tooManyFilesOpen; + } + + + + if (class) { + if (pcount >= 5) set_memory16_c(pb + OpenRecGS_access, access, 0); + if (pcount >= 6) set_memory16_c(pb + OpenRecGS_fileType, fi.file_type, 0); + if (pcount >= 7) set_memory32_c(pb + OpenRecGS_auxType, fi.aux_type, 0); + if (pcount >= 8) set_memory16_c(pb + OpenRecGS_storageType, fi.storage_type, 0); + + if (pcount >= 9) set_date_time_rec(pb + OpenRecGS_createDateTime, fi.create_date); + if (pcount >= 10) set_date_time_rec(pb + OpenRecGS_modDateTime, fi.modified_date); + + if (pcount >= 11) { + word16 fst_id = hfsFSID; + //if (fi.storage_type == 0x0f) fst_id = mfsFSID; + + word32 option_list = get_memory24_c(pb + OpenRecGS_optionList, 0); + word32 tmp = set_option_list(option_list, fst_id, fi.finder_info, fi.has_fi ? 32 : 0); + if (!rv) rv = tmp; + } + + if (pcount >= 12) set_memory32_c(pb + OpenRecGS_eof, fi.eof, 0); + if (pcount >= 13) set_memory32_c(pb + OpenRecGS_blocksUsed, fi.blocks, 0); + if (pcount >= 14) set_memory32_c(pb + OpenRecGS_resourceEOF, fi.resource_eof, 0); + if (pcount >= 15) set_memory32_c(pb + OpenRecGS_resourceBlocks, fi.resource_blocks, 0); + + + } + // prodos 16 doesn't return anything in the parameter block. + + struct fd_entry *e = calloc(sizeof(struct fd_entry), 1); + if (!e) { + close(fd); + return outOfMem; + } + + e->fd = fd; + e->st_mode = fi.st_mode; + e->resource = resource_number; + e->access = access; + if (S_ISDIR(fi.st_mode)) { + e->dirp = fdopendir(fd); + } + e->path = strdup(path); + e->next = fd_head; + fd_head = e; + + engine.xreg = fd; + engine.yreg = access; // actual access, needed in fcr. + + return rv; +} + +static word32 fst_read(int class) { + + int fd = engine.yreg; + struct fd_entry *e = find_fd(fd); + + if (!e) return invalidRefNum; + + if (!S_ISREG(e->st_mode)) return badStoreType; + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + word32 data_buffer = 0; + word32 request_count = 0; + word32 transfer_count = 0; + + if (class) { + data_buffer = get_memory24_c(pb + IORecGS_dataBuffer, 0); + request_count = get_memory24_c(pb + IORecGS_requestCount, 0); + // pre-zero transfer count + set_memory32_c(pb + IORecGS_transferCount, 0, 0); + } else { + data_buffer = get_memory24_c(pb + FileIORec_dataBuffer, 0); + request_count = get_memory24_c(pb + FileIORec_requestCount, 0); + set_memory32_c(pb + FileIORec_transferCount, 0, 0); + } + + if (request_count == 0) return 0; + + + word16 newline_mask; + word32 rv = 0; + ssize_t ok; + + newline_mask = get_memory16_c(global_buffer, 0); + if (newline_mask) { + byte newline_table[256]; + for (int i = 0; i < 256; ++i) + newline_table[i] = get_memory_c(global_buffer + 2 + i, 0); + + for (word32 i = 0 ; i < request_count; ++i) { + byte b; + ok = safe_read(fd, &b, 1); + if (ok < 0) return map_errno(); + if (ok == 0) break; + transfer_count++; + set_memory_c(data_buffer++, b, 0); + if (newline_table[b & newline_mask]) break; + } + if (transfer_count == 0) rv = eofEncountered; + } + else { + byte *data = gc_malloc(request_count); + if (!data) return outOfMem; + + ok = safe_read(fd, data, request_count); + if (ok < 0) rv = map_errno(); + if (ok == 0) rv = eofEncountered; + if (ok > 0) { + transfer_count = ok; + for (size_t i = 0; i < ok; ++i) { + set_memory_c(data_buffer + i, data[i], 0); + } + } + } + + if (transfer_count) { + if (class) + set_memory32_c(pb + IORecGS_transferCount, ok, 0); + else + set_memory32_c(pb + FileIORec_transferCount, ok, 0); + } + + return rv; +} + +static word32 fst_write(int class) { + + int fd = engine.yreg; + struct fd_entry *e = find_fd(fd); + + if (!e) return invalidRefNum; + + if (!(e->access & writeEnable)) + return invalidAccess; + + if (!S_ISREG(e->st_mode)) return badStoreType; + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + word32 data_buffer = 0; + word32 request_count = 0; + + if (class) { + data_buffer = get_memory24_c(pb + IORecGS_dataBuffer, 0); + request_count = get_memory24_c(pb + IORecGS_requestCount, 0); + // pre-zero transfer count + set_memory32_c(pb + IORecGS_transferCount, 0, 0); + } else { + data_buffer = get_memory24_c(pb + FileIORec_dataBuffer, 0); + request_count = get_memory24_c(pb + FileIORec_requestCount, 0); + set_memory32_c(pb + FileIORec_transferCount, 0, 0); + } + + if (request_count == 0) return 0; + byte *data = gc_malloc(request_count); + if (!data) return outOfMem; + + for (word32 i = 0; i < request_count; ++i) { + data[i] = get_memory_c(data_buffer + i,0); + } + + word32 rv = 0; + ssize_t ok = safe_write(fd, data, request_count); + if (ok < 0) rv = map_errno(); + if (ok > 0) { + if (class) + set_memory32_c(pb + IORecGS_transferCount, ok, 0); + else + set_memory32_c(pb + FileIORec_transferCount, ok, 0); + } + return rv; +} + +static word32 fst_close(int class) { + + int fd = engine.yreg; + + return remove_fd(fd); + +} + +static word32 fst_flush(int class) { + + int fd = engine.yreg; + struct fd_entry *e = find_fd(fd); + + if (!e) return invalidRefNum; + + int ok = fsync(e->fd); + + if (ok < 0) return map_errno(); + return 0; +} + +static off_t get_offset(int fd, word16 base, word32 displacement) { + + off_t pos = lseek(fd, 0, SEEK_CUR); + off_t eof = lseek(fd, 0, SEEK_END); + lseek(fd, pos, SEEK_SET); + + switch (base) { + case startPlus: + return displacement; + break; + case eofMinus: + return eof - displacement; + break; + case markPlus: + return pos + displacement; + break; + case markMinus: + return pos - displacement; + break; + default: + return -1; + } + + +} + +static word32 fst_set_mark(int class) { + + int fd = engine.yreg; + + struct fd_entry *e = find_fd(fd); + if (!e) return invalidRefNum; + + if (!S_ISREG(e->st_mode)) return badStoreType; + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + word16 base = 0; + word32 displacement = 0; + if (class) { + base = get_memory16_c(pb + SetPositionRecGS_base, 0); + displacement = get_memory32_c(pb + SetPositionRecGS_displacement, 0); + } else { + displacement = get_memory32_c(pb + MarkRec_position, 0); + } + if (base > markMinus) return paramRangeErr; + + off_t offset = get_offset(fd, base, displacement); + if (offset < 0) return outOfRange; + + off_t ok = lseek(fd, offset, SEEK_SET); + if (ok < 0) return map_errno(); + return 0; +} + +static word32 fst_set_eof(int class) { + + int fd = engine.yreg; + + struct fd_entry *e = find_fd(fd); + if (!e) return invalidRefNum; + + if (!S_ISREG(e->st_mode)) return badStoreType; + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + word16 base = 0; + word32 displacement = 0; + if (class) { + base = get_memory16_c(pb + SetPositionRecGS_base, 0); + displacement = get_memory32_c(pb + SetPositionRecGS_displacement, 0); + } else { + displacement = get_memory32_c(pb + EOFRec_eofPosition, 0); + } + + if (base > markMinus) return paramRangeErr; + + off_t offset = get_offset(fd, base, displacement); + if (offset < 0) return outOfRange; + + int ok = ftruncate(fd, offset); + if (ok < 0) return map_errno(); + return 0; + +} + +static word32 fst_get_mark(int class) { + + int fd = engine.yreg; + + struct fd_entry *e = find_fd(fd); + if (!e) return invalidRefNum; + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + off_t pos = 0; + + if (S_ISREG(e->st_mode)) { + + pos = lseek(fd, 0, SEEK_CUR); + if (pos < 0) return map_errno(); + } else return badStoreType; + + if (class) { + set_memory32_c(pb + PositionRecGS_position, pos, 0); + } else { + set_memory32_c(pb + MarkRec_position, pos, 0); + } + + return 0; +} + +static word32 fst_get_eof(int class) { + + int fd = engine.yreg; + + struct fd_entry *e = find_fd(fd); + if (!e) return invalidRefNum; + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + off_t eof = 0; + off_t pos = 0; + + if (S_ISREG(e->st_mode)) { + +#if _WIN32 + eof = filelength(fd); + if (eof == -1) return map_errno(); +#else + + pos = lseek(fd, 0, SEEK_CUR); + eof = lseek(fd, 0, SEEK_END); + if (eof < 0) return map_errno(); + lseek(fd, pos, SEEK_SET); +#endif + } else return badStoreType; + + if (class) { + set_memory32_c(pb + PositionRecGS_position, eof, 0); + } else { + set_memory32_c(pb + MarkRec_position, eof, 0); + } + + return 0; +} + +/* + * exclude files from get_dir_entries. + * + */ +static int exclude_dir_entry(const char *name) { + if (!name[0]) return 1; + if (name[0] == '.') { + return 1; + /* + if (!strcmp(name, ".")) return 1; + if (!strcmp(name, "..")) return 1; + if (!strncmp(name, "._", 2)) return 1; // ._ resource fork + if (!strcmp(name, ".fseventsd")) return 1; + */ + } + return 0; +} + +static word32 fst_get_dir_entry(int class) { + + int fd = engine.yreg; + + struct fd_entry *e = find_fd(fd); + if (!e) return invalidRefNum; + + if (!e->dirp) return badFileFormat; + + word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); + + word16 base = 0; + word16 pcount = 0; + word32 displacement = 0; + word32 name = 0; + + if (class) { + pcount = get_memory16_c(pb, 0); + base = get_memory16_c(pb + DirEntryRecGS_base, 0); + displacement = get_memory16_c(pb + DirEntryRecGS_displacement, 0); + name = get_memory24_c(pb + DirEntryRecGS_name, 0); + } else { + base = get_memory16_c(pb + DirEntryRec_base, 0); + displacement = get_memory16_c(pb + DirEntryRec_displacement, 0); + name = get_memory24_c(pb + DirEntryRec_nameBuffer, 0); + } + + + if (base > 2) return paramRangeErr; + if (base == 0 && displacement == 0) { + // count them up. + int count = 0; + rewinddir(e->dirp); + for (;;) { + struct dirent *d = readdir(e->dirp); + if (!d) break; + if (exclude_dir_entry(d->d_name)) continue; + count++; + } + rewinddir(e->dirp); + e->displacement = 0; + + if (class) { + if (pcount >= 6) set_memory16_c(pb + DirEntryRecGS_entryNum, count, 0); + } + else { + set_memory16_c(pb + DirEntryRec_entryNum, count, 0); + } + + return 0; + } + if (base == 2) { + // displacement is subtracted from current displacement + if (displacement > e->displacement) return endOfDir; //? + base = 0; + displacement = e->displacement - displacement; + } + + if (base == 1 && displacement == 0) { + if (e->displacement) { + base = 0; + displacement = e->displacement; + } + else displacement = 1; + } + + + if (base == 0) { + rewinddir(e->dirp); + e->displacement = 0; + } + + struct dirent *d; + for(;;) { + d = readdir(e->dirp); + if (d == NULL) return endOfDir; + if (exclude_dir_entry(d->d_name)) continue; + e->displacement++; + if (--displacement == 0) break; + } + + word32 rv = 0; + + char *fullpath = append_path(e->path, d->d_name); + struct file_info fi; + rv = get_file_info(fullpath, &fi); + + + // p16 and gs/os both use truncating c1 output string. + rv = set_gsstr_truncate(name, d->d_name); + + if (class) { + + + if (pcount > 2) set_memory16_c(pb + DirEntryRecGS_flags, fi.storage_type == 0x05 ? 0x8000 : 0, 0); + + if (pcount >= 6) set_memory16_c(pb + DirEntryRecGS_entryNum, e->displacement, 0); + if (pcount >= 7) set_memory16_c(pb + DirEntryRecGS_fileType, fi.file_type, 0); + if (pcount >= 8) set_memory32_c(pb + DirEntryRecGS_eof, fi.eof, 0); + if (pcount >= 9) set_memory32_c(pb + DirEntryRecGS_blockCount, fi.blocks, 0); + + if (pcount >= 10) set_date_time_rec(pb + DirEntryRecGS_createDateTime, fi.create_date); + if (pcount >= 11) set_date_time_rec(pb + DirEntryRecGS_modDateTime, fi.modified_date); + + if (pcount >= 12) set_memory16_c(pb + DirEntryRecGS_access, fi.access, 0); + if (pcount >= 13) set_memory32_c(pb + DirEntryRecGS_auxType, fi.aux_type, 0); + if (pcount >= 14) set_memory16_c(pb + DirEntryRecGS_fileSysID, mfsFSID, 0); + + if (pcount >= 15) { + word16 fst_id = hfsFSID; + //if (fi.storage_type == 0x0f) fst_id = mfsFSID; + word32 option_list = get_memory24_c(pb + DirEntryRecGS_optionList, 0); + word32 tmp = set_option_list(option_list, fst_id, fi.finder_info, fi.has_fi ? 32 : 0); + if (!rv) rv = tmp; + } + + if (pcount >= 16) set_memory32_c(pb + DirEntryRecGS_resourceEOF, fi.resource_eof, 0); + if (pcount >= 17) set_memory32_c(pb + DirEntryRecGS_resourceBlocks, fi.resource_blocks, 0); + } + else { + set_memory16_c(pb + DirEntryRec_entryNum, e->displacement, 0); + if (pcount > 2) set_memory16_c(pb + DirEntryRec_flags, fi.storage_type == 0x05 ? 0x8000 : 0, 0); + + set_memory16_c(pb + DirEntryRec_entryNum, e->displacement, 0); + set_memory16_c(pb + DirEntryRec_fileType, fi.file_type, 0); + set_memory32_c(pb + DirEntryRec_endOfFile, fi.eof, 0); + set_memory32_c(pb + DirEntryRec_blockCount, fi.blocks, 0); + + set_date_time_rec(pb + DirEntryRec_createTime, fi.create_date); + set_date_time_rec(pb + DirEntryRec_modTime, fi.modified_date); + + set_memory16_c(pb + DirEntryRec_access, fi.access, 0); + set_memory32_c(pb + DirEntryRec_auxType, fi.aux_type, 0); + set_memory16_c(pb + DirEntryRec_fileSysID, mfsFSID, 0); + + } + + return rv; +} + +static word32 fst_change_path(int class, const char *path1, const char *path2) { + + /* make sure they're not trying to rename the volume... */ + struct stat st; + if (stat(path1, &st) < 0) return map_errno(); + if (st.st_dev == root_dev && st.st_ino == root_ino) + return invalidAccess; + + // rename will delete any previous file. + if (rename(path1, path2) < 0) return map_errno(); + return 0; +} + +static word32 fst_format(int class) { + return notBlockDev; +} + +static word32 fst_erase(int class) { + return notBlockDev; +} + +static const char *call_name(word16 call) { + static char* class1[] = { + // 0x00 + "", + "CreateGS", + "DestroyGS", + "", + "ChangePathGS", + "SetFileInfoGS", + "GetFileInfoGS", + "JudgeNameGS", + "VolumeGS", + "", + "", + "ClearBackupGS", + "", + "", + "", + "", + + // 0x10 + "OpenGS", + "", + "ReadGS", + "WriteGS", + "CloseGS", + "FlushGS", + "SetMarkGS", + "GetMarkGS", + "SetEOFGS", + "GetEOFGS", + "", + "", + "GetDirEntryGS", + "", + "", + "", + + // 0x20 + "", + "", + "", + "", + "FormatGS", + "EraseDiskGS", + }; + + + static char* class0[] = { + // 0x00 + "", + "CREATE", + "DESTROY", + "", + "CHANGE_PATH", + "SET_FILE_INFO", + "GET_FILE_INFO", + "", + "VOLUME", + "", + "", + "CLEAR_BACKUP_BIT", + "", + "", + "", + "", + + // 0X10 + "OPEN", + "", + "READ", + "WRITE", + "CLOSE", + "FLUSH", + "SET_MARK", + "GET_MARK", + "SET_EOF", + "GET_EOF", + "", + "", + "GET_DIR_ENTRY", + "", + "", + "", + + // 0X20 + "", + "", + "", + "", + "FORMAT", + "ERASE_DISK", + }; + + + + if (call & 0x8000) { + static char *sys[] = { + "", + "fst_startup", + "fst_shutdown", + "fst_remove_vcr", + "fst_deferred_flush" + }; + call &= ~0x8000; + + if (call < sizeof(sys) / sizeof(sys[0])) + return sys[call]; + + return ""; + } + + int class = call >> 13; + call &= 0x1fff; + switch(class) { + case 0: + if (call < sizeof(class0) / sizeof(class0[0])) + return class0[call]; + break; + case 1: + if (call < sizeof(class1) / sizeof(class1[0])) + return class1[call]; + break; + + } + return ""; +} + +static const char *error_name(word16 error) { + static char *errors[] = { + "", + "badSystemCall", + "", + "", + "invalidPcount", + "", + "", + "gsosActive", + "", + "", + "", + "", + "", + "", + "", + "", + // 0x10 + "devNotFound", + "invalidDevNum", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + // 0x20 + "drvrBadReq", + "drvrBadCode", + "drvrBadParm", + "drvrNotOpen", + "drvrPriorOpen", + "irqTableFull", + "drvrNoResrc", + "drvrIOError", + "drvrNoDevice", + "drvrBusy", + "", + "drvrWrtProt", + "drvrBadCount", + "drvrBadBlock", + "drvrDiskSwitch", + "drvrOffLine", + // 0x30 + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + // 0x40 + "badPathSyntax", + "", + "tooManyFilesOpen", + "invalidRefNum", + "pathNotFound", + "volNotFound", + "fileNotFound", + "dupPathname", + "volumeFull", + "volDirFull", + "badFileFormat", + "badStoreType", + "eofEncountered", + "outOfRange", + "invalidAccess", + "buffTooSmall", + // 0x50 + "fileBusy", + "dirError", + "unknownVol", + "paramRangeErr", + "outOfMem", + "", + "", + "dupVolume", + "notBlockDev", + "invalidLevel", + "damagedBitMap", + "badPathNames", + "notSystemFile", + "osUnsupported", + "", + "stackOverflow", + // 0x60 + "dataUnavail", + "endOfDir", + "invalidClass", + "resForkNotFound", + "invalidFSTID", + "invalidFSTop", + "fstCaution", + "devNameErr", + "defListFull", + "supListFull", + "fstError", + "", + "", + "", + "", + "", + //0x70 + "resExistsErr", + "resAddErr", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + //0x80 + "", + "", + "", + "", + "", + "", + "", + "", + "networkError" + }; + + if (error < sizeof(errors) / sizeof(errors[0])) + return errors[error]; + return ""; +} + +void host_fst(void) { + + /* + * input: + * c = set + * a = default error code + * x = gs/os callnum + * y = [varies] + * + * output: + * c = set/clear + * a = error code/0 + * x = varies + * y = varies + */ + + + word32 acc = 0; + word16 call = engine.xreg; + + fprintf(stderr, "Host FST: %04x %s\n", call, call_name(call)); + + + if (call & 0x8000) { + // system level. + switch(call) { + case 0x8001: + acc = fst_startup(); + break; + case 0x8002: + acc = fst_shutdown(); + break; + default: + acc = badSystemCall; + break; + } + } else { + + int class = call >> 13; + call &= 0x1fff; + + if (class > 1) { + SEC(); + engine.acc = invalidClass; + return; + } + char *path1 = get_path1(); + char *path2 = get_path2(); + char *path3 = NULL; + char *path4 = NULL; + const char *cp; + + switch(call & 0xff) { + case 0x01: + cp = check_path(path1, &acc); + if (acc) break; + path3 = append_path(root, cp); + + acc = fst_create(class, path3); + break; + case 0x02: + cp = check_path(path1, &acc); + if (acc) break; + path3 = append_path(root, cp); + + acc = fst_destroy(class, path3); + break; + case 0x04: + + cp = check_path(path1, &acc); + if (acc) break; + path3 = append_path(root, cp); + + cp = check_path(path2, &acc); + if (acc) break; + path4 = append_path(root, cp); + + acc = fst_change_path(class, path3, path4); + break; + case 0x05: + cp = check_path(path1, &acc); + if (acc) break; + path3 = append_path(root, cp); + + acc = fst_set_file_info(class, path3); + break; + case 0x06: + cp = check_path(path1, &acc); + if (acc) break; + path3 = append_path(root, cp); + acc = fst_get_file_info(class, path3); + break; + case 0x07: + acc = fst_judge_name(class, path1); + break; + case 0x08: + acc = fst_volume(class); + break; + case 0x0b: + cp = check_path(path1, &acc); + if (acc) break; + path3 = append_path(root, cp); + acc = fst_clear_backup(class, path3); + break; + case 0x10: + cp = check_path(path1, &acc); + if (acc) break; + path3 = append_path(root, cp); + acc = fst_open(class, path3); + break; + case 0x012: + acc = fst_read(class); + break; + case 0x013: + acc = fst_write(class); + break; + case 0x14: + acc = fst_close(class); + break; + case 0x15: + acc = fst_flush(class); + break; + case 0x16: + acc = fst_set_mark(class); + break; + case 0x17: + acc = fst_get_mark(class); + break; + case 0x18: + acc = fst_set_eof(class); + break; + case 0x19: + acc = fst_get_eof(class); + break; + case 0x1c: + acc = fst_get_dir_entry(class); + break; + case 0x24: + acc = fst_format(class); + break; + case 0x25: + acc = fst_erase(class); + break; + + default: + acc = invalidFSTop; + break; + } + + + } + + if (acc) fprintf(stderr, " %02x %s\n", acc, error_name(acc)); + + gc_free(); + + engine.acc = acc; + if (acc) SEC(); + else CLC(); +} \ No newline at end of file diff --git a/src/sim65816.c b/src/sim65816.c index bebd51d..1352149 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -2681,14 +2681,21 @@ do_mvn(word32 banks) } #endif +extern void host_fst(void); + void do_wdm(word32 arg) { switch(arg) { case 0x8d: /* Bouncin Ferno does WDM 8d */ break; + + case 0xff: /* fst */ + host_fst(); + break; + default: - halt_printf("do_wdm: %02x!\n", arg); + halt_printf("do_wdm: %02x! pc = $%06x\n", arg, engine.kpc - 2); } }