From 238c68c8dfee352cc5fbb30f937219c4ee598e46 Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Fri, 3 Dec 2004 22:03:12 +0000 Subject: [PATCH] Merge and upgrade native filesystem support from B2/Win. The nice "My Computer" icon is now back. Adapt from recent extfs_unix.cpp. --- BasiliskII/src/Windows/extfs_windows.cpp | 385 ++++++++ BasiliskII/src/Windows/posix_emu.cpp | 1119 ++++++++++++++++++++++ BasiliskII/src/Windows/posix_emu.h | 122 +++ 3 files changed, 1626 insertions(+) create mode 100755 BasiliskII/src/Windows/extfs_windows.cpp create mode 100755 BasiliskII/src/Windows/posix_emu.cpp create mode 100755 BasiliskII/src/Windows/posix_emu.h diff --git a/BasiliskII/src/Windows/extfs_windows.cpp b/BasiliskII/src/Windows/extfs_windows.cpp new file mode 100755 index 00000000..f1ed2ccc --- /dev/null +++ b/BasiliskII/src/Windows/extfs_windows.cpp @@ -0,0 +1,385 @@ +/* + * extfs_windows.cpp - MacOS file system for access native file system access, Windows specific stuff + * + * Basilisk II (C) 1997-2004 Christian Bauer + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "sysdeps.h" +#include "extfs.h" +#include "extfs_defs.h" +#include "posix_emu.h" + + +#define DEBUG 0 +#include "debug.h" + + +// Constants +#define HOST_DIRSEP_CHAR '\\' +#define HOST_DIRSEP_STR "\\" + + +// Default Finder flags +const uint16 DEFAULT_FINDER_FLAGS = kHasBeenInited; + + +/* + * Initialization + */ + +void extfs_init(void) +{ + init_posix_emu(); +} + + +/* + * Deinitialization + */ + +void extfs_exit(void) +{ + final_posix_emu(); +} + + +/* + * Add component to path name + */ + +void add_path_component(char *path, const char *component) +{ + int l = strlen(path); + if (l < MAX_PATH_LENGTH-1 && path[l-1] != HOST_DIRSEP_CHAR) { + path[l] = HOST_DIRSEP_CHAR; + path[l+1] = 0; + } + strncat(path, component, MAX_PATH_LENGTH-1); +} + + +/* + * Finder info and resource forks are kept in helper files + * + * Finder info: + * /path/.finf/file + * Resource fork: + * /path/.rsrc/file + * + * The .finf files store a FInfo/DInfo, followed by a FXInfo/DXInfo + * (16+16 bytes) + */ + +static void make_helper_path(const char *src, char *dest, const char *add, bool only_dir = false) +{ + dest[0] = 0; + + // Get pointer to last component of path + const char *last_part = strrchr(src, HOST_DIRSEP_CHAR); + if (last_part) + last_part++; + else + last_part = src; + + // Copy everything before + strncpy(dest, src, last_part-src); + dest[last_part-src] = 0; + + // Add additional component + strncat(dest, add, MAX_PATH_LENGTH-1); + + // Add last component + if (!only_dir) + strncat(dest, last_part, MAX_PATH_LENGTH-1); +} + +static int create_helper_dir(const char *path, const char *add) +{ + char helper_dir[MAX_PATH_LENGTH]; + make_helper_path(path, helper_dir, add, true); + if (helper_dir[strlen(helper_dir) - 1] == HOST_DIRSEP_CHAR) // Remove trailing "\" + helper_dir[strlen(helper_dir) - 1] = 0; + return mkdir(helper_dir, 0777); +} + +static int open_helper(const char *path, const char *add, int flag) +{ + char helper_path[MAX_PATH_LENGTH]; + make_helper_path(path, helper_path, add); + + if ((flag & O_ACCMODE) == O_RDWR || (flag & O_ACCMODE) == O_WRONLY) + flag |= O_CREAT; + int fd = open(helper_path, flag, 0666); + if (fd < 0) { + if (/*errno == ENOENT &&*/ (flag & O_CREAT)) { + // One path component was missing, probably the helper + // directory. Try to create it and re-open the file. + int ret = create_helper_dir(path, add); + if (ret < 0) + return ret; + fd = open(helper_path, flag, 0666); + } + } + return fd; +} + +static int open_finf(const char *path, int flag) +{ + return open_helper(path, ".finf" HOST_DIRSEP_STR, flag); +} + +static int open_rsrc(const char *path, int flag) +{ + return open_helper(path, ".rsrc" HOST_DIRSEP_STR, flag); +} + + +/* + * Get/set finder info for file/directory specified by full path + */ + +struct ext2type { + const char *ext; + uint32 type; + uint32 creator; +}; + +static const ext2type e2t_translation[] = { + {".Z", FOURCC('Z','I','V','M'), FOURCC('L','Z','I','V')}, + {".gz", FOURCC('G','z','i','p'), FOURCC('G','z','i','p')}, + {".hqx", FOURCC('T','E','X','T'), FOURCC('S','I','T','x')}, + {".bin", FOURCC('T','E','X','T'), FOURCC('S','I','T','x')}, + {".pdf", FOURCC('P','D','F',' '), FOURCC('C','A','R','O')}, + {".ps", FOURCC('T','E','X','T'), FOURCC('t','t','x','t')}, + {".sit", FOURCC('S','I','T','!'), FOURCC('S','I','T','x')}, + {".tar", FOURCC('T','A','R','F'), FOURCC('T','A','R',' ')}, + {".uu", FOURCC('T','E','X','T'), FOURCC('S','I','T','x')}, + {".uue", FOURCC('T','E','X','T'), FOURCC('S','I','T','x')}, + {".zip", FOURCC('Z','I','P',' '), FOURCC('Z','I','P',' ')}, + {".8svx", FOURCC('8','S','V','X'), FOURCC('S','N','D','M')}, + {".aifc", FOURCC('A','I','F','C'), FOURCC('T','V','O','D')}, + {".aiff", FOURCC('A','I','F','F'), FOURCC('T','V','O','D')}, + {".au", FOURCC('U','L','A','W'), FOURCC('T','V','O','D')}, + {".mid", FOURCC('M','I','D','I'), FOURCC('T','V','O','D')}, + {".midi", FOURCC('M','I','D','I'), FOURCC('T','V','O','D')}, + {".mp2", FOURCC('M','P','G',' '), FOURCC('T','V','O','D')}, + {".mp3", FOURCC('M','P','G',' '), FOURCC('T','V','O','D')}, + {".wav", FOURCC('W','A','V','E'), FOURCC('T','V','O','D')}, + {".bmp", FOURCC('B','M','P','f'), FOURCC('o','g','l','e')}, + {".gif", FOURCC('G','I','F','f'), FOURCC('o','g','l','e')}, + {".lbm", FOURCC('I','L','B','M'), FOURCC('G','K','O','N')}, + {".ilbm", FOURCC('I','L','B','M'), FOURCC('G','K','O','N')}, + {".jpg", FOURCC('J','P','E','G'), FOURCC('o','g','l','e')}, + {".jpeg", FOURCC('J','P','E','G'), FOURCC('o','g','l','e')}, + {".pict", FOURCC('P','I','C','T'), FOURCC('o','g','l','e')}, + {".png", FOURCC('P','N','G','f'), FOURCC('o','g','l','e')}, + {".sgi", FOURCC('.','S','G','I'), FOURCC('o','g','l','e')}, + {".tga", FOURCC('T','P','I','C'), FOURCC('o','g','l','e')}, + {".tif", FOURCC('T','I','F','F'), FOURCC('o','g','l','e')}, + {".tiff", FOURCC('T','I','F','F'), FOURCC('o','g','l','e')}, + {".htm", FOURCC('T','E','X','T'), FOURCC('M','O','S','S')}, + {".html", FOURCC('T','E','X','T'), FOURCC('M','O','S','S')}, + {".txt", FOURCC('T','E','X','T'), FOURCC('t','t','x','t')}, + {".rtf", FOURCC('T','E','X','T'), FOURCC('M','S','W','D')}, + {".c", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".C", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".cc", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".cpp", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".cxx", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".h", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".hh", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".hpp", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".hxx", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".s", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".S", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".i", FOURCC('T','E','X','T'), FOURCC('R','*','c','h')}, + {".mpg", FOURCC('M','P','E','G'), FOURCC('T','V','O','D')}, + {".mpeg", FOURCC('M','P','E','G'), FOURCC('T','V','O','D')}, + {".mov", FOURCC('M','o','o','V'), FOURCC('T','V','O','D')}, + {".fli", FOURCC('F','L','I',' '), FOURCC('T','V','O','D')}, + {".avi", FOURCC('V','f','W',' '), FOURCC('T','V','O','D')}, + {".qxd", FOURCC('X','D','O','C'), FOURCC('X','P','R','3')}, + {".hfv", FOURCC('D','D','i','m'), FOURCC('d','d','s','k')}, + {".dsk", FOURCC('D','D','i','m'), FOURCC('d','d','s','k')}, + {".img", FOURCC('r','o','h','d'), FOURCC('d','d','s','k')}, + {NULL, 0, 0} // End marker +}; + +void get_finfo(const char *path, uint32 finfo, uint32 fxinfo, bool is_dir) +{ + // Set default finder info + Mac_memset(finfo, 0, SIZEOF_FInfo); + if (fxinfo) + Mac_memset(fxinfo, 0, SIZEOF_FXInfo); + WriteMacInt16(finfo + fdFlags, DEFAULT_FINDER_FLAGS); + WriteMacInt32(finfo + fdLocation, (uint32)-1); + + // Read Finder info file + int fd = open_finf(path, O_RDONLY); + if (fd >= 0) { + ssize_t actual = read(fd, Mac2HostAddr(finfo), SIZEOF_FInfo); + if (fxinfo) + actual += read(fd, Mac2HostAddr(fxinfo), SIZEOF_FXInfo); + close(fd); + if (actual >= SIZEOF_FInfo) + return; + } + + // No Finder info file, translate file name extension to MacOS type/creator + if (!is_dir) { + int path_len = strlen(path); + for (int i=0; e2t_translation[i].ext; i++) { + int ext_len = strlen(e2t_translation[i].ext); + if (path_len < ext_len) + continue; + if (!strcmp(path + path_len - ext_len, e2t_translation[i].ext)) { + WriteMacInt32(finfo + fdType, e2t_translation[i].type); + WriteMacInt32(finfo + fdCreator, e2t_translation[i].creator); + break; + } + } + } +} + +void set_finfo(const char *path, uint32 finfo, uint32 fxinfo, bool is_dir) +{ + // Open Finder info file + int fd = open_finf(path, O_RDWR); + if (fd < 0) + return; + + // Write file + write(fd, Mac2HostAddr(finfo), SIZEOF_FInfo); + if (fxinfo) + write(fd, Mac2HostAddr(fxinfo), SIZEOF_FXInfo); + close(fd); +} + + +/* + * Resource fork emulation functions + */ + +uint32 get_rfork_size(const char *path) +{ + // Open resource file + int fd = open_rsrc(path, O_RDONLY); + if (fd < 0) + return 0; + + // Get size + off_t size = lseek(fd, 0, SEEK_END); + + // Close file and return size + close(fd); + return size < 0 ? 0 : size; +} + +int open_rfork(const char *path, int flag) +{ + return open_rsrc(path, flag); +} + +void close_rfork(const char *path, int fd) +{ + if (fd >= 0) + close(fd); +} + + +/* + * Read "length" bytes from file to "buffer", + * returns number of bytes read (or -1 on error) + */ + +ssize_t extfs_read(int fd, void *buffer, size_t length) +{ + return read(fd, buffer, length); +} + + +/* + * Write "length" bytes from "buffer" to file, + * returns number of bytes written (or -1 on error) + */ + +ssize_t extfs_write(int fd, void *buffer, size_t length) +{ + return write(fd, buffer, length); +} + + +/* + * Remove file/directory (and associated helper files), + * returns false on error (and sets errno) + */ + +bool extfs_remove(const char *path) +{ + // Remove helpers first, don't complain if this fails + char helper_path[MAX_PATH_LENGTH]; + make_helper_path(path, helper_path, ".finf" HOST_DIRSEP_STR, false); + remove(helper_path); + make_helper_path(path, helper_path, ".rsrc" HOST_DIRSEP_STR, false); + remove(helper_path); + + // Now remove file or directory (and helper directories in the directory) + if (remove(path) < 0) { + if (errno == EISDIR || errno == ENOTEMPTY) { + helper_path[0] = 0; + strncpy(helper_path, path, MAX_PATH_LENGTH-1); + add_path_component(helper_path, ".finf"); + rmdir(helper_path); + helper_path[0] = 0; + strncpy(helper_path, path, MAX_PATH_LENGTH-1); + add_path_component(helper_path, ".rsrc"); + rmdir(helper_path); + return rmdir(path) == 0; + } else + return false; + } + return true; +} + + +/* + * Rename/move file/directory (and associated helper files), + * returns false on error (and sets errno) + */ + +bool extfs_rename(const char *old_path, const char *new_path) +{ + // Rename helpers first, don't complain if this fails + char old_helper_path[MAX_PATH_LENGTH], new_helper_path[MAX_PATH_LENGTH]; + make_helper_path(old_path, old_helper_path, ".finf" HOST_DIRSEP_STR, false); + make_helper_path(new_path, new_helper_path, ".finf" HOST_DIRSEP_STR, false); + create_helper_dir(new_path, ".finf" HOST_DIRSEP_STR); + rename(old_helper_path, new_helper_path); + make_helper_path(old_path, old_helper_path, ".rsrc" HOST_DIRSEP_STR, false); + make_helper_path(new_path, new_helper_path, ".rsrc" HOST_DIRSEP_STR, false); + create_helper_dir(new_path, ".rsrc" HOST_DIRSEP_STR); + rename(old_helper_path, new_helper_path); + + // Now rename file + return rename(old_path, new_path) == 0; +} diff --git a/BasiliskII/src/Windows/posix_emu.cpp b/BasiliskII/src/Windows/posix_emu.cpp new file mode 100755 index 00000000..2484a8f1 --- /dev/null +++ b/BasiliskII/src/Windows/posix_emu.cpp @@ -0,0 +1,1119 @@ +/* + * posix_emu.cpp -- posix and virtual desktop + * + * Basilisk II (C) 1997-1999 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// TODO: UNC names. Customizable "Virtual Desktop" location. + +#include "sysdeps.h" +#define NO_POSIX_API_HOOK +#include "posix_emu.h" +#include "user_strings.h" +#include "util_windows.h" +#include "main.h" +#include "extfs_defs.h" +#include "prefs.h" +#include + + +#define DEBUG_EXTFS 0 + +#if DEBUG_EXTFS + +// This must be always on. +#define DEBUG 1 +#undef OutputDebugString +#define OutputDebugString extfs_log_write +extern void extfs_log_write( char *s ); +#define EXTFS_LOG_FILE_NAME "extfs.log" +#include "debug.h" + +enum { + DB_EXTFS_NONE=0, + DB_EXTFS_NORMAL, + DB_EXTFS_LOUD +}; +static int16 debug_extfs = DB_EXTFS_NONE; +static HANDLE extfs_log_file = INVALID_HANDLE_VALUE; + +static void extfs_log_open( char *path ) +{ + if(debug_extfs == DB_EXTFS_NONE) return; + + DeleteFile( path ); + extfs_log_file = CreateFile( + path, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + // FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING, + FILE_FLAG_WRITE_THROUGH, + NULL + ); + if( extfs_log_file == INVALID_HANDLE_VALUE ) { + ErrorAlert( "Could not create the EXTFS log file." ); + } +} + +static void extfs_log_close( void ) +{ + if(debug_extfs == DB_EXTFS_NONE) return; + + if( extfs_log_file != INVALID_HANDLE_VALUE ) { + CloseHandle( extfs_log_file ); + extfs_log_file = INVALID_HANDLE_VALUE; + } +} + +static void extfs_log_write( char *s ) +{ + DWORD bytes_written; + + // should have been checked already. + if(debug_extfs == DB_EXTFS_NONE) return; + + if( extfs_log_file != INVALID_HANDLE_VALUE ) { + + DWORD count = strlen(s); + if (0 == WriteFile(extfs_log_file, s, count, &bytes_written, NULL) || + (int)bytes_written != count) + { + extfs_log_close(); + ErrorAlert( "extfs log file write error (out of disk space?). Log closed." ); + } else { + FlushFileBuffers( extfs_log_file ); + } + } +} +#else + +#define DEBUG 0 +#include "debug.h" + +#endif // DEBUG_EXTFS + +int my_errno = 0; + +#define VIRTUAL_ROOT_ID ((HANDLE)0xFFFFFFFE) + +static const char *desktop_name = "Virtual Desktop"; +static const char *custom_icon_name = "Icon\r"; +#define my_computer GetString(STR_EXTFS_VOLUME_NAME) + +static char lb1[MAX_PATH_LENGTH]; +static char lb2[MAX_PATH_LENGTH]; + +#define MRP(path) translate(path,lb1) +#define MRP2(path) translate(path,lb2) + +#define DISABLE_ERRORS UINT prevmode = SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS) +#define RESTORE_ERRORS SetErrorMode(prevmode); + +static char host_drive_list[512]; +static char virtual_root[248]; // Not _MAX_PATH + +const uint8 my_comp_icon[2670] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0xD8, 0x00, 0x00, 0x08, 0xD8, 0x00, 0x00, 0x00, 0x96, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x79, 0x79, 0x79, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xC0, 0xCC, 0xCC, 0xCC, + 0xCC, 0xD7, 0x97, 0x97, 0x97, 0x97, 0x97, 0xC0, 0xC0, 0xC0, 0xC0, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCD, 0xDB, 0xD9, 0x79, 0x79, 0x7E, 0x79, 0x0C, 0xDD, 0xCD, 0xDD, 0xCD, 0xCD, 0xDC, 0xDD, 0xCD, + 0xCC, 0xED, 0xED, 0x97, 0x97, 0x97, 0x97, 0x0C, 0xE7, 0x78, 0x77, 0x97, 0x97, 0x97, 0x97, 0x97, + 0xDC, 0xED, 0xDE, 0x79, 0x79, 0x79, 0x99, 0x0C, 0xD9, 0x7E, 0x5E, 0x65, 0x5E, 0x65, 0xD9, 0x79, + 0xCD, 0xDE, 0xDD, 0x97, 0xE7, 0x9E, 0x77, 0xC0, 0x97, 0x9D, 0xCD, 0xCC, 0xC7, 0xCC, 0xE7, 0x97, + 0xCC, 0xED, 0xEE, 0x79, 0x79, 0x79, 0x7E, 0xCC, 0x57, 0xD5, 0xD7, 0xD5, 0xDD, 0x5D, 0xD9, 0x7E, + 0xCD, 0xDE, 0xDE, 0x79, 0x97, 0x97, 0x99, 0x0C, 0x87, 0xCD, 0x75, 0xC7, 0x5C, 0x7D, 0xD9, 0x79, + 0xCD, 0xDD, 0xED, 0xE7, 0x7E, 0x79, 0x77, 0xCC, 0xE7, 0xB0, 0x00, 0xC0, 0x0C, 0xCD, 0xE7, 0x97, + 0xDC, 0xED, 0xEE, 0x79, 0x97, 0x86, 0x79, 0xC0, 0xE7, 0xD0, 0x2C, 0xC1, 0xC2, 0xCD, 0xD9, 0x79, + 0xCD, 0xDE, 0xDD, 0x97, 0x99, 0x79, 0x97, 0x0C, 0xE7, 0xB0, 0xD0, 0xDC, 0xCC, 0xCD, 0xD6, 0x87, + 0xDD, 0xDE, 0xED, 0x79, 0x77, 0xE7, 0x79, 0x0C, 0x58, 0xDC, 0x0C, 0x0C, 0xCC, 0xCD, 0xE9, 0x79, + 0xCD, 0xDD, 0xD5, 0x99, 0x97, 0x99, 0x79, 0xC0, 0x87, 0xD0, 0xC0, 0xC0, 0xC0, 0xCD, 0xD7, 0xE7, + 0xDD, 0xDE, 0xD7, 0x97, 0x79, 0x77, 0xE7, 0x0C, 0xE7, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x79, 0x79, + 0xCD, 0xDE, 0xD9, 0x79, 0x97, 0xE9, 0x79, 0x0C, 0x97, 0x79, 0x79, 0x79, 0x79, 0x79, 0x97, 0x97, + 0xDC, 0xED, 0xE7, 0x97, 0x79, 0x97, 0x97, 0x0C, 0xCD, 0xD7, 0xD7, 0xD7, 0xE7, 0xE7, 0x7E, 0x79, + 0xCD, 0xDE, 0x79, 0x79, 0x97, 0x7E, 0x79, 0xC0, 0xCC, 0xCC, 0x0C, 0xCC, 0x0D, 0xCC, 0xDC, 0xDC, + 0xDC, 0xED, 0x97, 0x97, 0x77, 0x99, 0x79, 0xCC, 0xCC, 0xCC, 0xDC, 0xCC, 0xDC, 0xCC, 0xCC, 0x8D, + 0xCD, 0xDE, 0x79, 0x79, 0x96, 0x77, 0x97, 0x97, 0x97, 0x90, 0xCC, 0xCD, 0xCD, 0xDD, 0xDD, 0xCC, + 0xDD, 0xD9, 0x76, 0x87, 0x97, 0x99, 0x7E, 0x7C, 0x0C, 0xCC, 0xDD, 0xDD, 0xED, 0xDE, 0xDD, 0xEE, + 0xDE, 0xD5, 0xBD, 0xDE, 0x79, 0x79, 0x9C, 0xC0, 0xCC, 0xDD, 0xDD, 0xDD, 0xDE, 0xDD, 0xED, 0xDE, + 0xDE, 0xDD, 0xDE, 0xDE, 0x79, 0x79, 0x70, 0xCD, 0xCC, 0xCC, 0xCC, 0xCC, 0xDC, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xED, 0xED, 0x97, 0x97, 0x90, 0xCC, 0x8D, 0xCC, 0xDC, 0xCD, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xEE, 0xDE, 0xDE, 0x79, 0x7E, 0x70, 0xCC, 0x88, 0xDC, 0xCC, 0xCC, 0xCD, 0xDD, 0xDD, 0xDC, + 0xCD, 0xDD, 0xED, 0xED, 0x97, 0x97, 0xEC, 0xCC, 0xCC, 0xCC, 0xDC, 0xCC, 0xCD, 0xDD, 0xED, 0xDD, + 0xDC, 0xED, 0xED, 0xEE, 0x79, 0x79, 0xDC, 0x0D, 0xCC, 0xDC, 0xCC, 0xCD, 0xCC, 0xCC, 0xCC, 0x0C, + 0xDC, 0xDE, 0xDE, 0xED, 0x97, 0xDC, 0xCC, 0xDC, 0xCD, 0xCC, 0xDC, 0xCD, 0xCC, 0xCC, 0xCD, 0xCC, + 0xCC, 0xED, 0xED, 0x79, 0xDD, 0xC0, 0xCD, 0xCC, 0xDC, 0xCD, 0xCC, 0xDC, 0xCC, 0xDC, 0xDD, 0xCD, + 0xCD, 0xED, 0x97, 0x97, 0xDD, 0xCC, 0xCC, 0x00, 0xC0, 0xDD, 0xCD, 0xCC, 0xCC, 0xCD, 0xD0, 0xDC, + 0xDD, 0xF7, 0x99, 0x79, 0x97, 0x9D, 0xDD, 0xDD, 0xCC, 0xC0, 0xCC, 0x0C, 0xDC, 0xDC, 0xCD, 0xCD, + 0xDF, 0x79, 0x77, 0x97, 0x79, 0x79, 0x79, 0x79, 0xDD, 0xDE, 0xDC, 0xCC, 0xCC, 0xC0, 0xC0, 0xDD, + 0xE9, 0x79, 0x97, 0x99, 0x97, 0xE7, 0xE7, 0x97, 0x97, 0x9D, 0x79, 0xDD, 0xDD, 0xDD, 0xCD, 0xDE, + 0x79, 0x79, 0x7E, 0x77, 0x00, 0x00, 0x04, 0x00, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0xF5, + 0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, + 0x2B, 0x2B, 0xF9, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0xF6, + 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0xF7, 0xF7, 0xF7, + 0xF7, 0xF8, 0x81, 0xFA, 0xFA, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, + 0xF7, 0xF8, 0x81, 0x81, 0x81, 0xFA, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xF8, 0xF8, 0x81, 0xFA, 0xFB, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xC2, 0xFB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xFB, 0xC2, 0xC2, 0xC2, + 0xF7, 0xF8, 0x81, 0x81, 0xFB, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0x2B, + 0xA5, 0xC2, 0xC2, 0xFB, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x81, 0xC2, 0xC2, 0xC2, + 0xF7, 0xF8, 0x81, 0x81, 0xFB, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0x2B, + 0xA5, 0xC2, 0xF9, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF9, 0xFB, 0xC2, 0xC2, 0xC2, + 0xF7, 0xF8, 0x81, 0x81, 0xFB, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xF9, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xF9, 0x81, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0xFA, 0x81, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xF9, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0x2B, 0xF9, 0xFB, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0x81, 0x81, 0xFB, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xF9, 0xF5, 0x0A, 0xF6, 0x2B, 0x0A, 0xF6, 0x0A, 0x2B, 0xF9, 0x81, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0x81, 0x81, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xF9, 0xF5, 0xF8, 0xF6, 0x56, 0xF7, 0xF7, 0xF8, 0x2B, 0xF9, 0x81, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0x81, 0x81, 0xFB, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xF9, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0x2B, 0x2B, 0xF9, 0xFB, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0xFA, 0x81, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xF9, 0xF6, 0xF6, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0x2B, 0xF9, 0x81, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0x81, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xC2, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0x81, 0x81, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xA5, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0x81, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0xF7, 0x56, 0xF8, 0x7A, 0x7A, 0x9E, 0x9E, 0x9E, 0x9E, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xF8, 0x56, 0x81, 0xFA, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, + 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0xF7, 0xF7, 0xF8, 0xF8, + 0xF8, 0x56, 0x81, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF7, 0xF7, + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0x56, 0xB9, 0xF8, + 0xF8, 0x56, 0x81, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xF6, 0x2B, 0x2B, 0xF7, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0xF8, + 0xF8, 0x56, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, + 0xF6, 0xF6, 0x2B, 0xF8, 0x56, 0xFA, 0xF9, 0x81, 0x81, 0x81, 0xFA, 0x81, 0x81, 0x81, 0xFB, 0x81, + 0xFB, 0xFB, 0xFB, 0x81, 0xFA, 0xFA, 0xFA, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xF6, 0xF6, + 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFB, + 0x81, 0xFB, 0xF9, 0xFA, 0xFA, 0xFB, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, 0xF7, + 0xF8, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0xF8, 0xF8, 0x56, 0x56, 0xF9, 0xF9, 0xF9, 0xFA, + 0xFA, 0xFA, 0xFA, 0x81, 0x81, 0xFB, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, 0xF7, + 0x93, 0xA0, 0xF7, 0xF7, 0xF8, 0xF7, 0xF7, 0xF8, 0xF7, 0xF7, 0xF7, 0xF7, 0x2B, 0x2B, 0x2B, 0x2B, + 0x2B, 0x2B, 0x81, 0xFB, 0x81, 0xFB, 0xFB, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, 0xF7, + 0xA0, 0xA0, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0x56, 0x56, 0xF9, 0xF9, 0xF9, 0xF8, 0xF7, + 0xF7, 0xF7, 0xFB, 0xFB, 0x81, 0xFB, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF5, 0x2B, 0xF7, + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF9, 0xF9, 0xFB, 0xFB, 0xFB, 0xF8, 0xF9, + 0xF9, 0xF7, 0x81, 0xFB, 0xFB, 0x81, 0xFB, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xF8, 0x2B, 0x2B, 0x56, + 0x2B, 0x2B, 0xF9, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0xF8, + 0xF8, 0xF7, 0xFB, 0xFB, 0x81, 0xFB, 0x81, 0xFB, 0xC2, 0xC2, 0xF8, 0xF8, 0xF6, 0xF6, 0xF9, 0xF8, + 0x2B, 0xF9, 0xF8, 0x2B, 0xF9, 0x2B, 0x2B, 0xF9, 0x2B, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, + 0xF7, 0xF7, 0x81, 0xFB, 0x81, 0xFB, 0xC2, 0xC2, 0xF9, 0xF8, 0xF6, 0xF6, 0xF7, 0xF9, 0xF8, 0x2B, + 0xF9, 0xF8, 0xF6, 0xF9, 0xF8, 0xF6, 0xF9, 0xF8, 0xF6, 0x2B, 0xF9, 0x2B, 0xF9, 0x56, 0x2B, 0xF9, + 0x2B, 0xF9, 0xAC, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xFA, 0xF9, 0xF8, 0x2B, 0x2B, 0x2B, 0xF5, 0xF5, + 0xF5, 0xF5, 0xF9, 0xF8, 0xF6, 0xF9, 0xF8, 0xF6, 0x2B, 0xF8, 0x2B, 0xF9, 0x56, 0x2B, 0x56, 0x2B, + 0x56, 0x81, 0xAC, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xFA, 0xFA, 0xFA, 0xF9, 0xF8, + 0xF8, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0xF5, 0x2B, 0xF9, 0xF6, 0xF9, 0xF8, 0xF7, 0xF9, 0x2B, 0xF9, + 0x81, 0xAC, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xFA, 0xFA, 0xFA, 0xFA, 0xF9, 0xF8, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0xF5, 0xF5, 0xF5, 0x56, 0x81, + 0xAC, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xFA, 0xF9, 0xFA, 0xFA, 0xF9, 0xF9, 0xF8, 0xF8, 0x81, 0x81, + 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0x00, 0x00, 0x01, 0x00, 0x03, 0xFF, 0xFF, 0xE0, + 0x02, 0x00, 0x00, 0x38, 0x02, 0xFF, 0xFF, 0x3C, 0x02, 0xFF, 0xFF, 0x3C, 0x02, 0xFF, 0xFF, 0x3C, + 0x02, 0xF0, 0x0F, 0x3C, 0x02, 0xFF, 0xFF, 0x3C, 0x02, 0xFF, 0xFF, 0x7C, 0x02, 0xE0, 0x1F, 0x7C, + 0x02, 0xE0, 0x1F, 0x7C, 0x02, 0xE0, 0x1F, 0x7C, 0x02, 0xE0, 0x1F, 0x7C, 0x02, 0xE0, 0x1F, 0x78, + 0x02, 0xFF, 0xFF, 0x78, 0x02, 0xFF, 0xFF, 0x78, 0x02, 0x1F, 0xFF, 0x70, 0x02, 0x00, 0x00, 0x70, + 0x03, 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0x1F, 0xFF, 0xFF, + 0x02, 0x00, 0x3F, 0xFF, 0x02, 0x40, 0x00, 0x3F, 0x02, 0xC0, 0x7C, 0x3F, 0x02, 0x00, 0x7D, 0xBF, + 0x0F, 0x20, 0x00, 0x3F, 0x32, 0x49, 0x00, 0x3C, 0xC4, 0x92, 0x2D, 0x70, 0xE0, 0x24, 0x1A, 0xE0, + 0x1F, 0x00, 0xA5, 0xC0, 0x00, 0xFC, 0x03, 0x80, 0x00, 0x03, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0xE0, + 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, + 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, + 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xF8, + 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xF0, + 0x03, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, + 0x07, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, + 0x0F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xE0, + 0x1F, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x1F, 0xFC, 0x10, 0x06, 0x10, 0x06, 0x10, 0x06, 0x10, 0x06, 0x10, 0x06, 0x10, 0x06, 0x10, 0x04, + 0x1F, 0xFC, 0x0F, 0xFE, 0x0F, 0xFF, 0x18, 0x67, 0x34, 0x06, 0x69, 0x64, 0x72, 0xC8, 0x3F, 0xF0, + 0x1F, 0xFC, 0x1F, 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x1F, 0xFC, + 0x1F, 0xFC, 0x07, 0xFF, 0x1F, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x7F, 0xFE, 0xFF, 0xFC, 0x07, 0xF8, + 0x00, 0x00, 0x00, 0x80, 0x79, 0x7C, 0x0C, 0x0C, 0xCC, 0xCC, 0xCD, 0x97, 0x97, 0x90, 0xE7, 0x97, + 0x97, 0x97, 0xDD, 0xD9, 0x79, 0x7C, 0xE7, 0xD5, 0x5E, 0x58, 0xCE, 0xD7, 0x97, 0x9C, 0xDD, 0x5D, + 0x7D, 0xB7, 0xDD, 0x59, 0x79, 0x7C, 0x9D, 0x10, 0x1D, 0xD9, 0xCE, 0xD7, 0x97, 0x9C, 0xDD, 0x0C, + 0xCC, 0xE7, 0xDD, 0xD9, 0x79, 0x7C, 0xED, 0xDD, 0xDD, 0x79, 0xCE, 0xE7, 0xE7, 0x90, 0xE7, 0x77, + 0x97, 0x97, 0xDD, 0x79, 0x79, 0x7C, 0xCC, 0xDC, 0xCD, 0xC8, 0xDD, 0x97, 0x97, 0x99, 0x7C, 0xDD, + 0xDD, 0xDE, 0xDE, 0xDE, 0x7E, 0x7C, 0xCC, 0xCC, 0xCD, 0xCD, 0xDD, 0xDE, 0x99, 0x0C, 0x8C, 0xCC, + 0xCC, 0xCC, 0xCD, 0xED, 0x77, 0xCC, 0xCC, 0xCD, 0xDD, 0xED, 0xCE, 0xDE, 0x9C, 0xCD, 0xCD, 0xCD, + 0x0D, 0xCC, 0xCE, 0xE7, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xCC, 0xDE, 0x99, 0x97, 0x97, 0x9D, 0xDD, + 0xDD, 0xDE, 0xE9, 0x77, 0x00, 0x00, 0x01, 0x00, 0xC2, 0xC2, 0xC2, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, + 0xF6, 0x2B, 0x2B, 0x2B, 0xF7, 0xFA, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xA5, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0xF8, 0x81, 0xFA, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xA5, 0xC2, 0x81, 0xAA, + 0xAA, 0xAA, 0xFB, 0xC2, 0xF8, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xA5, 0xF9, 0x7F, 0x7F, + 0x7F, 0x56, 0x81, 0xC2, 0xF8, 0x81, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xA5, 0xF9, 0x0A, 0xF6, + 0x0A, 0x56, 0xFB, 0xC2, 0xF8, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xA5, 0xF9, 0xF6, 0xF6, + 0xF6, 0x56, 0x81, 0xC2, 0x56, 0x81, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xA5, 0xF9, 0xF9, 0xF9, + 0xF9, 0xF9, 0xC2, 0xC2, 0xF8, 0x81, 0xFB, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0xA5, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0x56, 0xFA, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0x2B, 0x2B, 0x2B, 0xF7, 0xF7, + 0xF7, 0xF7, 0xF7, 0xB9, 0x56, 0x81, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF6, 0x56, 0xF9, + 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0x81, 0xFA, 0x81, 0xC2, 0xC2, 0xC2, 0xF6, 0xF6, 0xF7, 0x2B, 0x2B, + 0x2B, 0xF8, 0xF8, 0xF8, 0xF9, 0xFA, 0x81, 0xFB, 0xC2, 0xC2, 0xF5, 0xF7, 0x93, 0xF7, 0xF7, 0xF7, + 0xF7, 0x2B, 0x2B, 0x2B, 0x2B, 0xFB, 0x81, 0xFB, 0xC2, 0xC2, 0xF5, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, + 0xF9, 0xFB, 0xFB, 0xF9, 0xF7, 0xFB, 0x81, 0xFB, 0xC2, 0xF6, 0xF8, 0xF9, 0x2B, 0xF9, 0x2B, 0xF9, + 0xF6, 0xF8, 0x2B, 0xF8, 0xF7, 0xFB, 0xFB, 0xC2, 0xF9, 0xF7, 0xF9, 0xF7, 0xF9, 0xF7, 0xF9, 0x2B, + 0xF9, 0x2B, 0xF8, 0x2B, 0x81, 0xAC, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xF9, 0xF9, 0xF9, + 0xF9, 0xF9, 0xF9, 0x81, 0xAC, 0xC2, 0xC2, 0xC2, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0xD8, + 0x00, 0x00, 0x08, 0xD8, 0x00, 0x00, 0x00, 0x96, 0x02, 0x1C, 0xC1, 0xC4, 0x18, 0x9C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x96, 0x00, 0x05, 0x69, 0x63, 0x6C, 0x34, 0x00, 0x00, 0x00, 0x32, 0x69, 0x63, + 0x6C, 0x38, 0x00, 0x00, 0x00, 0x3E, 0x49, 0x43, 0x4E, 0x23, 0x00, 0x00, 0x00, 0x4A, 0x69, 0x63, + 0x73, 0x23, 0x00, 0x00, 0x00, 0x56, 0x69, 0x63, 0x73, 0x34, 0x00, 0x00, 0x00, 0x62, 0x69, 0x63, + 0x73, 0x38, 0x00, 0x00, 0x00, 0x6E, 0xBF, 0xB9, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1C, + 0xE2, 0x10, 0xBF, 0xB9, 0xFF, 0xFF, 0x00, 0x00, 0x02, 0x04, 0x02, 0x1C, 0xE1, 0xAC, 0xBF, 0xB9, + 0xFF, 0xFF, 0x00, 0x00, 0x06, 0x08, 0x02, 0x1C, 0xE1, 0xA4, 0xBF, 0xB9, 0xFF, 0xFF, 0x00, 0x00, + 0x07, 0x0C, 0x02, 0x1C, 0xE1, 0xF8, 0xBF, 0xB9, 0xFF, 0xFF, 0x00, 0x00, 0x07, 0x50, 0x02, 0x1C, + 0xE1, 0xDC, 0xBF, 0xB9, 0xFF, 0xFF, 0x00, 0x00, 0x07, 0xD4, 0x02, 0x1C, 0xE1, 0xD0 +}; + +static bool use_streams[ 'Z'-'A'+1 ]; + +static bool is_ntfs_volume( char *rootdir ) +{ + bool ret = false; + char tst_file[_MAX_PATH], tst_stream[_MAX_PATH]; + sprintf( tst_file, "%sb2query.tmp", rootdir ); + sprintf( tst_stream, "%s:AFP_AfpInfo", tst_file ); + if(!exists(tst_file)) { + if(create_file( tst_file, 0 )) { + if(create_file( tst_stream, 0 )) { + ret = true; + } + DeleteFile( tst_file ); + } + } + return ret; +} + + +// !!UNC +void init_posix_emu(void) +{ + if(!validate_stat_struct) { + ErrorAlert( "Invalid struct my_stat -- edit posix_emu.h" ); + QuitEmulator(); + } + +#if DEBUG_EXTFS + debug_extfs = PrefsFindInt16("debugextfs"); + + debug_extfs = DB_EXTFS_LOUD; + + if(debug_extfs != DB_EXTFS_NONE) { + extfs_log_open( EXTFS_LOG_FILE_NAME ); + } +#endif + + // We cannot use ExtFS "RootPath" because of the virtual desktop. + if(PrefsFindBool("enableextfs")) { + PrefsReplaceString("extfs", ""); + } else { + PrefsRemoveItem("extfs"); + D(bug("extfs disabled by user\n")); +#if DEBUG_EXTFS + extfs_log_close(); +#endif + return; + } + + const char *extdrives = PrefsFindString("extdrives"); + + // Set up drive list. + int outinx = 0; + for( char letter = 'A'; letter <= 'Z'; letter++ ) { + if(extdrives && !strchr(extdrives,letter)) continue; + int i = (int)( letter - 'A' ); + char rootdir[20]; + wsprintf( rootdir, "%c:\\", letter ); + use_streams[ letter - 'A' ] = false; + switch(GetDriveType(rootdir)) { + case DRIVE_FIXED: + case DRIVE_REMOTE: + case DRIVE_RAMDISK: + // TODO: NTFS AFP? + // fall + case DRIVE_REMOVABLE: + case DRIVE_CDROM: + if(outinx < sizeof(host_drive_list)) { + host_drive_list[outinx] = letter; + outinx += 2; + } + } + } + + // Set up virtual desktop root. + // TODO: this should be customizable. + GetModuleFileName( NULL, virtual_root, sizeof(virtual_root) ); + char *p = strrchr( virtual_root, '\\' ); + if(p) { + *(++p) = 0; + strcat( virtual_root, desktop_name ); + } else { + // should never happen + sprintf( virtual_root, "C:\\%s", desktop_name ); + } + CreateDirectory( virtual_root, 0 ); + + // Set up an icon looking like "My Computer" + // Can be overwritten just like any other folder custom icon. + if(my_access(custom_icon_name,0) != 0) { + int fd = my_creat( custom_icon_name, 0 ); + if(fd >= 0) { + my_close(fd); + fd = open_rfork( custom_icon_name, O_RDWR|O_CREAT ); + if(fd >= 0) { + my_write( fd, my_comp_icon, sizeof(my_comp_icon) ); + my_close(fd); + static uint8 host_finfo[SIZEOF_FInfo]; + uint32 finfo = Host2MacAddr(host_finfo); + get_finfo(custom_icon_name, finfo, 0, false); + WriteMacInt16(finfo + fdFlags, kIsInvisible); + set_finfo(custom_icon_name, finfo, 0, false); + get_finfo(my_computer, finfo, 0, true); + WriteMacInt16(finfo + fdFlags, ReadMacInt16(finfo + fdFlags) | kHasCustomIcon); + set_finfo(my_computer, finfo, 0, true); + } else { + my_remove(custom_icon_name); + } + } + } +} + +void final_posix_emu(void) +{ +#if DEBUG_EXTFS + extfs_log_close(); +#endif +} + +static void charset_host2mac( char *s ) +{ + int i, len=strlen(s), code; + + for( i=len-3; i>=0; i-- ) { + if( s[i] == '%' && isxdigit(s[i+1]) && isxdigit(s[i+2]) ) { + sscanf( &s[i], "%%%02X", &code ); + memmove( &s[i], &s[i+2], strlen(&s[i+2])+1 ); + s[i] = code; + } + } +} + +static void charset_mac2host( char *s ) +{ + int i, convert, len = strlen(s); + + D(bug("charset_mac2host(%s)...\n", s)); + + for( i=len-1; i>=0; i-- ) { + convert = 0; + switch( (unsigned char)s[i] ) { + // case '\r': // handled by "default" + // case '\n': + // case '\t': + case '/': + // case '\\': // Backslash is tricky -- "s" is a full path! + // case ':': + case '*': + case '?': + case '"': + case '<': + case '>': + case '|': + case '%': + convert = 1; + break; + default: + if((unsigned char)s[i] < ' ') convert = 1; + break; + } + if(convert) { + char sml[10]; + sprintf( sml, "%%%02X", s[i] ); + memmove( &s[i+2], &s[i], strlen(&s[i])+1 ); + memmove( &s[i], sml, 3 ); + } + } + D(bug("charset_mac2host = %s\n", s)); +} + +static void make_mask( + char *mask, + const char *dir, + const char *a1, + const char *a2 +) +{ + strcpy( mask, dir ); + + int len = strlen(mask); + if( len && mask[len-1] != '\\' ) strcat( mask, "\\" ); + + if( a1 ) strcat( mask, a1 ); + if( a2 ) strcat( mask, a2 ); +} + +// !!UNC +static char *translate( const char *path, char *buffer ) +{ + char *l = host_drive_list; + char *p = (char *)path; + + while(*l) { + if(toupper(p[1]) == toupper(*l)) break; + l += strlen(l) + 1; + } + + if(p[0] == '\\' && *l && (p[2] == 0 || p[2] == ':' || p[2] == '\\')) { + p += 2; + if(*p == ':') p++; + if(*p == '\\') p++; + sprintf( buffer, "%c:\\%s", *l, p ); + } else { + if(*path == '\\') { + sprintf( buffer, "%s%s", virtual_root, path ); + } else { + int len = strlen(path); + if(len == 0 || path[len-1] == '\\') { + make_mask( buffer, virtual_root, path, my_computer ); + } else { + make_mask( buffer, virtual_root, path, 0 ); + } + } + } + charset_mac2host( buffer ); + + return buffer; +} + +// helpers +static void strip_trailing_bs( char *path ) +{ + int len = strlen(path); + if(len > 0 && path[len-1] == '\\') path[len-1] = 0; +} + +#if 0 /* defined is util_windows.cpp */ +static int exists( const char *p ) +{ + WIN32_FIND_DATA fdata; + + int result = 0; + + HANDLE h = FindFirstFile( p, &fdata ); + if(h != INVALID_HANDLE_VALUE) { + result = 1; + FindClose( h ); + } + + D(bug("exists(%s) = %d\n", p, result)); + + return result; +} +#endif + +static int is_dir( char *p ) +{ + WIN32_FIND_DATA fdata; + + int result = 0; + + HANDLE h = FindFirstFile( p, &fdata ); + if(h != INVALID_HANDLE_VALUE) { + result = (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + FindClose( h ); + } + return result; +} + +static int myRemoveDirectory( const char *source ) +{ + HANDLE fh; + WIN32_FIND_DATA FindFileData; + int ok, result = 1; + char mask[_MAX_PATH]; + + D(bug("removing folder %s\n", source)); + + make_mask( mask, source, "*.*", 0 ); + + fh = FindFirstFile( mask, &FindFileData ); + ok = fh != INVALID_HANDLE_VALUE; + while(ok) { + make_mask( mask, source, FindFileData.cFileName, 0 ); + D(bug("removing item %s\n", mask)); + int isdir = (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + if(isdir) { + // must delete ".finf", ".rsrc" but not ".", ".." + if(strcmp(FindFileData.cFileName,".") && strcmp(FindFileData.cFileName,"..")) { + result = myRemoveDirectory( mask ); + if(!result) break; + } + } else { + D(bug("DeleteFile %s\n", mask)); + result = DeleteFile( mask ); + if(!result) break; + } + ok = FindNextFile( fh, &FindFileData ); + } + if(fh != INVALID_HANDLE_VALUE) FindClose( fh ); + if(result) { + D(bug("RemoveDirectory %s\n", source)); + result = RemoveDirectory( source ); + } + return result; +} + +static void make_folders( char *path ) +{ + char local_path[_MAX_PATH], *p; + strcpy( local_path, path ); + p = strrchr( local_path, '\\' ); + if(p) { + *p = 0; + if(strlen(local_path) > 3) { + make_folders(local_path); + mkdir(local_path); + } + } +} + +// !!UNC +static bool is_same_drive( char *p1, char *p2 ) +{ + return toupper(*p1) == toupper(*p2); +} + +// Used when the drives are known to be different. +// Can't use MoveFileEx() etc because of the Win9x limitations. +// It would simulate CopyFile*() -- DeleteFile*() anyway +int file_move_copy( char *src, char *dst, bool delete_old ) +{ + int result = 0; + my_errno = 0; + + D(bug("file_copy %s -> %s\n", src, dst)); + + // Fail if exists -- it's up to MacOS to move things to Trash + if(CopyFile(src,dst,TRUE)) { + if(delete_old && !DeleteFile(src)) { + result = -1; + my_errno = EACCES; + } + } else { + result = -1; + if(exists(src)) + my_errno = EACCES; + else + my_errno = ENOENT; + } + return result; +} + +int file_move( char *src, char *dst ) +{ + return file_move_copy( src, dst, true ); +} + +int file_copy( char *src, char *dst ) +{ + return file_move_copy( src, dst, false ); +} + +int folder_copy( char *folder_src, char *folder_dst ) +{ + HANDLE fh; + WIN32_FIND_DATA FindFileData; + int ok, result = 0; + char mask[_MAX_PATH]; + + D(bug("copying folder %s -> \n", folder_src, folder_dst)); + + my_errno = 0; + + if(!CreateDirectory( folder_dst, 0 )) { + my_errno = EACCES; + return -1; + } + + make_mask( mask, folder_src, "*.*", 0 ); + + fh = FindFirstFile( mask, &FindFileData ); + ok = fh != INVALID_HANDLE_VALUE; + while(ok) { + make_mask( mask, folder_src, FindFileData.cFileName, 0 ); + int isdir = (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + char target[_MAX_PATH]; + make_mask( target, folder_dst, FindFileData.cFileName, 0 ); + D(bug("copying item %s -> %s\n", mask, target)); + if(isdir) { + if(strcmp(FindFileData.cFileName,".") && strcmp(FindFileData.cFileName,"..")) { + result = folder_copy( mask, target ); + if(result < 0) break; + } + } else { + result = file_copy( mask, target ); + if(result < 0) break; + } + ok = FindNextFile( fh, &FindFileData ); + } + if(fh != INVALID_HANDLE_VALUE) FindClose( fh ); + return result; +} + +// dir enumeration +void closedir( struct DIR *d ) +{ + DISABLE_ERRORS; + if(d) { + if(d->h != INVALID_HANDLE_VALUE && d->h != VIRTUAL_ROOT_ID) { + FindClose( d->h ); + } + delete d; + } + RESTORE_ERRORS; +} + +static int make_dentry( struct DIR *d ) +{ + int ok = 0; + + memset( &d->de, 0, sizeof(d->de) ); + if(d->h != INVALID_HANDLE_VALUE) { + if( (d->FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && + *d->FindFileData.cFileName == '.') + { + ok = 0; + } else { + strncpy( d->de.d_name, d->FindFileData.cFileName, sizeof(d->de.d_name)-1 ); + d->de.d_name[sizeof(d->de.d_name)-1] = 0; + charset_host2mac( d->de.d_name ); + ok = 1; + } + } + return ok; +} + +struct dirent *readdir( struct DIR *d ) +{ + DISABLE_ERRORS; + + dirent *de = 0; + + if(d) { + if(d->h != INVALID_HANDLE_VALUE) { + if(d->h == VIRTUAL_ROOT_ID) { + make_dentry(d); + de = &d->de; + d->vname_list += strlen(d->vname_list) + 1; + if(*d->vname_list) { + strcpy( d->FindFileData.cFileName, d->vname_list ); + d->FindFileData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + } else { + // Out of static drive entries. Continue with other stuff. + char mask[MAX_PATH_LENGTH]; + make_mask( mask, virtual_root, "*.*", 0 ); + d->h = FindFirstFile( mask, &d->FindFileData ); + } + } else { + int done = 0; + do { + if(make_dentry(d)) { + de = &d->de; + done = 1; + } + if(!FindNextFile( d->h, &d->FindFileData )) { + FindClose( d->h ); + d->h = INVALID_HANDLE_VALUE; + done = 1; + } + } while(!done); + } + } + } + + if(de) { + D(bug("readdir found %s\n", de->d_name)); + } + + RESTORE_ERRORS; + + return de; +} + +struct DIR *opendir( const char *path ) +{ + DISABLE_ERRORS; + DIR *d = new DIR; + if(d) { + memset( d, 0, sizeof(DIR) ); + if(*path == 0) { + d->vname_list = host_drive_list; + if(d->vname_list) { + d->h = VIRTUAL_ROOT_ID; + strcpy( d->FindFileData.cFileName, d->vname_list ); + d->FindFileData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + } else { + d->h = INVALID_HANDLE_VALUE; + } + } else { + char mask[MAX_PATH_LENGTH]; + make_mask( mask, MRP(path), "*.*", 0 ); + + D(bug("opendir path=%s, mask=%s\n", path, mask)); + + d->h = FindFirstFile( mask, &d->FindFileData ); + if(d->h == INVALID_HANDLE_VALUE) { + delete d; + d = 0; + } + } + } + + D(bug("opendir(%s,%s) = %08x\n", path, MRP(path), d)); + + RESTORE_ERRORS; + + return d; +} + +static void dump_stat( const struct my_stat *st ) +{ + D(bug("stat: size = %ld, mode = %ld, a = %ld, m = %ld, c = %ld\n", st->st_size, st->st_mode, st->st_atime, st->st_mtime, st->st_ctime)); +} + + + +// Exported hook functions +int my_stat( const char *path, struct my_stat *st ) +{ + DISABLE_ERRORS; + + int result; + + if(*path == 0) { + /// virtual root + memset( st, 0, sizeof(struct my_stat) ); + st->st_mode = _S_IFDIR; + result = 0; + my_errno = 0; + } else { + result = stat( MRP(path), (struct stat *)st ); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + } + + D(bug("stat(%s,%s) = %d\n", path, MRP(path), result)); + if(result >= 0) dump_stat( st ); + RESTORE_ERRORS; + return result; +} + +int my_fstat( int fd, struct my_stat *st ) +{ + DISABLE_ERRORS; + int result = fstat( fd, (struct stat *)st ); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + D(bug("fstat(%d) = %d\n", fd, result)); + if(result >= 0) dump_stat( st ); + RESTORE_ERRORS; + return result; +} + +int my_open( const char *path, int mode, ... ) +{ + DISABLE_ERRORS; + int result; + char *p = MRP(path); + + // Windows "open" does not handle _O_CREAT and _O_BINARY as it should + if(mode & _O_CREAT) { + if(exists(p)) { + result = open( p, mode & ~_O_CREAT ); + D(bug("open-nocreat(%s,%s,%d) = %d\n", path, p, mode, result)); + } else { + result = creat( p, _S_IWRITE|_S_IREAD ); + if(result < 0) { + make_folders(p); + result = creat( p, _S_IWRITE|_S_IREAD ); + } + D(bug("open-creat(%s,%s,%d) = %d\n", path, p, mode, result)); + } + } else { + result = open( p, mode ); + D(bug("open(%s,%s,%d) = %d\n", path, p, mode, result)); + } + if(result < 0) { + my_errno = errno; + } else { + setmode(result, _O_BINARY); + my_errno = 0; + } + RESTORE_ERRORS; + return result; +} + +int my_rename( const char *old_path, const char *new_path ) +{ + DISABLE_ERRORS; + int result = -1; + char *p_old = MRP(old_path); + char *p_new = MRP2(new_path); + + result = my_access(old_path,0); + if(result < 0) { + // my_errno already set + } else { + if(is_same_drive(p_old,p_new)) { + result = rename( p_old, p_new ); + if(result != 0) { // by definition, rename may also return a positive value to indicate an error + my_errno = errno; + } else { + my_errno = 0; + } + } else { + if(is_dir(p_old)) { + result = folder_copy( p_old, p_new ); + // my_errno already set + if(result >= 0) { + if(myRemoveDirectory( p_old )) { + my_errno = 0; + result = 0; + } else { + // there is no proper error code for this failure. + my_errno = EACCES; + result = -1; + } + } + } else { + result = file_move( p_old, p_new ); + // my_errno already set + } + } + } + D(bug("rename(%s,%s,%s,%s) = %d\n", old_path, p_old, new_path, p_new, result)); + RESTORE_ERRORS; + return result; +} + +int my_access( const char *path, int mode ) +{ + DISABLE_ERRORS; + char *p = MRP(path); + WIN32_FIND_DATA fdata; + + int result; + + if(is_dir(p)) { + // access does not work for folders. + HANDLE h = FindFirstFile( p, &fdata ); + if(h != INVALID_HANDLE_VALUE) { + FindClose( h ); + if(mode == W_OK) { + if( (fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0 ) { + result = 0; + my_errno = 0; + } else { + result = -1; + my_errno = EACCES; + } + } else { + result = 0; + my_errno = 0; + } + } else { + result = -1; + my_errno = ENOENT; + } + } else { + // W_OK, F_OK are ok. + result = access(p,mode); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + } + + D(bug("access(%s,%s,%d) = %d\n", path, MRP(path), mode, result)); + RESTORE_ERRORS; + return result; +} + +int my_mkdir( const char *path, int mode ) +{ + DISABLE_ERRORS; + char *p = MRP(path); + strip_trailing_bs(p); + int result = mkdir( p ); + if(result < 0) { + make_folders(p); + result = mkdir( p ); + } + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + D(bug("mkdir(%s,%s,%d) = %d\n", path, p, mode, result)); + RESTORE_ERRORS; + return result; +} + +int my_remove( const char *path ) +{ + DISABLE_ERRORS; + char *p = MRP(path); + strip_trailing_bs(p); + int result; + if(is_dir(p)) { + result = myRemoveDirectory( p ); + } else { + D(bug("DeleteFile %s\n", p)); + result = DeleteFile( p ); + } + if(result) { + result = 0; + my_errno = 0; + } else { + result = -1; + if(exists(p)) { + my_errno = EACCES; + } else { + my_errno = ENOENT; + } + } + D(bug("remove(%s,%s) = %d\n", path, p, result)); + RESTORE_ERRORS; + return result; +} + +int my_creat( const char *path, int mode ) +{ + DISABLE_ERRORS; + char *p = MRP(path); + int result = creat( p, _S_IWRITE|_S_IREAD ); // note mode + if(result < 0) { + make_folders(p); + result = creat( p, _S_IWRITE|_S_IREAD ); // note mode + } + if(result < 0) { + my_errno = errno; + } else { + setmode(result, _O_BINARY); + my_errno = 0; + } + D(bug("creat(%s,%s,%d) = %d\n", path, p, mode,result)); + RESTORE_ERRORS; + return result; +} + +int my_chsize( int fd, size_t sz ) +{ + DISABLE_ERRORS; + int result = chsize(fd,sz); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + RESTORE_ERRORS; + return result; +} + +int my_close( int fd ) +{ + DISABLE_ERRORS; + int result = close(fd); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + RESTORE_ERRORS; + D(bug("close(%d) = %d\n", fd, result)); + return result; +} + +long my_lseek( int fd, long offset, int origin ) +{ + DISABLE_ERRORS; + int result = lseek( fd, offset, origin ); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + RESTORE_ERRORS; + return result; +} + +int my_read( int fd, void *buffer, unsigned int count ) +{ + DISABLE_ERRORS; + int result = read( fd, buffer, count ); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + RESTORE_ERRORS; + D(bug("read(%ld,%08x,%ld) = %d\n", fd, buffer, count, result)); + + return result; +} + +int my_write( int fd, const void *buffer, unsigned int count ) +{ + DISABLE_ERRORS; + int result = write( fd, buffer, count ); + if(result < 0) { + my_errno = errno; + } else { + my_errno = 0; + } + RESTORE_ERRORS; + D(bug("write(%ld,%08x,%ld) = %d\n", fd, buffer, count, result)); + return result; +} diff --git a/BasiliskII/src/Windows/posix_emu.h b/BasiliskII/src/Windows/posix_emu.h new file mode 100755 index 00000000..c626a0ab --- /dev/null +++ b/BasiliskII/src/Windows/posix_emu.h @@ -0,0 +1,122 @@ +/* + * posix_emu.h + * + * Basilisk II (C) 1997-1999 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "extfs.h" + +void init_posix_emu(void); +void final_posix_emu(void); + +typedef struct dirent { + char d_name[MAX_PATH_LENGTH]; +} dirent; + +typedef struct DIR { + HANDLE h; + WIN32_FIND_DATA FindFileData; + dirent de; + char *vname_list; +} DIR; + +// emulated +DIR *opendir( const char *path ); +void closedir( DIR *d ); +struct dirent *readdir( DIR *d ); + +// access() mode: exists? +#ifndef F_OK +#define F_OK 0 +#endif +// access() mode: can do r/w? +#ifndef W_OK +#define W_OK 6 +#endif + +// hook stat functions to create virtual desktop +// because of errno all used funcs must be hooked. +int my_stat( const char *, struct my_stat * ); +int my_fstat( int, struct my_stat * ); +int my_open( const char *, int, ... ); +int my_rename( const char *, const char * ); +int my_access( const char *, int ); +int my_mkdir( const char *path, int mode ); +int my_remove( const char * ); +int my_creat( const char *path, int mode ); +int my_creat( const char *path, int mode ); +int my_close( int fd ); +long my_lseek( int fd, long, int); +int my_read( int fd, void *, unsigned int); +int my_write( int fd, const void *, unsigned int); +int my_chsize( int fd, unsigned int size ); +int my_locking( int fd, int mode, long nbytes ); + +extern int my_errno; + +// must hook all other functions that manipulate file names +#ifndef NO_POSIX_API_HOOK +#define stat my_stat +#define fstat my_fstat +#define open my_open +#define rename my_rename +#define access my_access +#define mkdir my_mkdir +#define remove my_remove +#define creat my_creat +#define close my_close +#define lseek my_lseek +#define read my_read +#define write my_write +#define ftruncate my_chsize +#define locking my_locking + +#undef errno +#define errno my_errno +#endif //!NO_POSIX_API_HOOK + +#ifndef S_ISDIR +#define S_ISDIR(stat_mode) (((stat_mode) & _S_IFDIR) != 0) +#endif + +// can't #define "stat" unless there's a replacement for "struct stat" +struct my_stat { + _dev_t st_dev; + _ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + _off_t st_size; + time_t st_atime; + time_t st_mtime; + time_t st_ctime; +}; + +// Your compiler may have different "struct stat" -> edit "struct my_stat" +#define validate_stat_struct ( sizeof(struct my_stat) == sizeof(struct stat) ) + +#define st_crtime st_ctime