/*
 *  posix_emu.cpp -- posix and virtual desktop
 *
 *  Basilisk II (C) 1997-2008 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 <ctype.h>


#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 LPCTSTR desktop_name = TEXT("Virtual Desktop");
static const char *custom_icon_name = "Icon\r";
#define my_computer GetString(STR_EXTFS_VOLUME_NAME)

static TCHAR lb1[MAX_PATH_LENGTH];
static TCHAR 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 TCHAR host_drive_list[512];
static TCHAR 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(LPCTSTR rootdir)
{
	bool ret = false;
	TCHAR tst_file[_MAX_PATH], tst_stream[_MAX_PATH];
	_sntprintf( tst_file, lengthof(tst_file), TEXT("%sb2query.tmp"), rootdir );
	_sntprintf( tst_stream, lengthof(tst_stream), TEXT("%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.
	size_t outinx = 0;
	for( TCHAR letter = TEXT('A'); letter <= TEXT('Z'); letter++ ) {
		if(extdrives && !strchr(extdrives,letter)) continue;
		TCHAR rootdir[20];
		_sntprintf( rootdir, lengthof(rootdir), TEXT("%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 < lengthof(host_drive_list)) {
					host_drive_list[outinx] = letter;
					outinx += 2;
				}
		}
	}

	// Set up virtual desktop root.
	// TODO: this should be customizable.
	GetModuleFileName( NULL, virtual_root, lengthof(virtual_root) );
	TCHAR *p = _tcsrchr( virtual_root, TEXT('\\') );
	if(p) {
		_tcscpy( ++p, desktop_name );
	} else {
		// should never happen
		_sntprintf( virtual_root, lengthof(virtual_root), TEXT("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);
			struct my_stat custom_icon_stat;
			int stat_result = my_stat( custom_icon_name, &custom_icon_stat );
			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);
				// need room for the things from around the finfo that set_finfo reads
				static uint8 custom_icon_hfile[ioFlXFndrInfo + SIZEOF_FXInfo];
				memset(custom_icon_hfile, 0, ioFlXFndrInfo + SIZEOF_FXInfo);
				static uint8 * host_finfo = custom_icon_hfile + ioFlFndrInfo;
				uint32 finfo = Host2MacAddr(host_finfo);
				get_finfo(custom_icon_name, finfo, 0, false);
				WriteMacInt16(finfo + fdFlags, kIsInvisible);
				if (stat_result == 0) {
					WriteMacInt32(finfo - ioFlFndrInfo + ioFlCrDat, TimeToMacTime(custom_icon_stat.st_ctime));
					WriteMacInt32(finfo - ioFlFndrInfo + ioFlMdDat, TimeToMacTime(custom_icon_stat.st_mtime));
				}
				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( LPTSTR s )
{
	size_t len = _tcslen(s);

	D(bug(TEXT("charset_mac2host(%s)...\n"), s));

	for( size_t i=len; i-->0; ) {
		bool convert = false;
		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 = true;
				break;
			default:
				if((unsigned char)s[i] < ' ') convert = true;
				break;
		}
		if(convert) {
			TCHAR sml[10];
			_sntprintf( sml, lengthof(sml), TEXT("%%%02X"), s[i] );
			memmove( &s[i+2], &s[i], (_tcslen(&s[i])+1) * sizeof(TCHAR) );
			memmove( &s[i], sml, 3 * sizeof(TCHAR) );
		}
	}
	D(bug(TEXT("charset_mac2host = %s\n"), s));
}

static void make_mask(
	TCHAR *mask,
	LPCTSTR dir,
	LPCTSTR a1,
	LPCTSTR a2
)
{
	_tcscpy( mask, dir );

	size_t len = _tcslen(mask);
	if( len && mask[len-1] != '\\' ) _tcscat( mask, TEXT("\\") );

	if( a1 ) _tcscat( mask, a1 );
	if( a2 ) _tcscat( mask, a2 );
}

// !!UNC
static LPTSTR translate( LPCTSTR path, TCHAR *buffer )
{
	TCHAR *l = host_drive_list;
	const TCHAR *p = path;

	while(*l) {
		if(_totupper(p[1]) == _totupper(*l)) break;
		l += _tcslen(l) + 1;
	}

	if(p[0] == TEXT('\\') && *l && (p[2] == 0 || p[2] == TEXT(':') || p[2] == TEXT('\\'))) {
		p += 2;
		if(*p == TEXT(':')) p++;
		if(*p == TEXT('\\')) p++;
		_sntprintf( buffer, MAX_PATH_LENGTH, TEXT("%c:\\%s"), *l, p );
	} else {
		if(*path == TEXT('\\')) {
			_sntprintf( buffer, MAX_PATH_LENGTH, TEXT("%s%s"), virtual_root, path );
		} else {
			int len = _tcslen(path);
			if(len == 0 || path[len-1] == TEXT('\\')) {
				make_mask( buffer, virtual_root, path, tstr(my_computer).get() );
			} else {
				make_mask( buffer, virtual_root, path, 0 );
			}
		}
	}
	charset_mac2host( buffer );

	return buffer;
}

// helpers
static void strip_trailing_bs( LPTSTR path )
{
	size_t len = _tcslen(path);
	if(len > 0 && path[len-1] == TEXT('\\')) 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( LPCTSTR 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( LPCTSTR source )
{
	HANDLE fh;
	WIN32_FIND_DATA FindFileData;
	int ok, result = 1;
	TCHAR mask[_MAX_PATH];

	D(bug(TEXT("removing folder %s\n"), source));

	make_mask( mask, source, TEXT("*.*"), 0 );

	fh = FindFirstFile( mask, &FindFileData );
	ok = fh != INVALID_HANDLE_VALUE;
	while(ok) {
		make_mask( mask, source, FindFileData.cFileName, 0 );
		D(bug(TEXT("removing item %s\n"), mask));
		int isdir = (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
		if(isdir) {
			// must delete ".finf", ".rsrc" but not ".", ".."
			if(_tcscmp(FindFileData.cFileName,TEXT(".")) && _tcscmp(FindFileData.cFileName,TEXT(".."))) {
				result = myRemoveDirectory( mask );
				if(!result) break;
			}
		} else {
			D(bug(TEXT("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(TEXT("RemoveDirectory %s\n"), source));
		result = RemoveDirectory( source );
	}
	return result;
}

static void make_folders( LPCTSTR path )
{
	TCHAR local_path[_MAX_PATH], *p;
	_tcscpy( local_path, path );
	p = _tcsrchr( local_path, TEXT('\\') );
	if(p) {
		*p = 0;
		if(_tcslen(local_path) > 3) {
			make_folders(local_path);
			_tmkdir(local_path);
		}
	}
}

// !!UNC
static bool is_same_drive( LPCTSTR p1, LPCTSTR p2 )
{
	return _totupper(*p1) == _totupper(*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
static int file_move_copy( LPCTSTR src, LPCTSTR dst, bool delete_old )
{
	int result = 0;
	my_errno = 0;

	D(bug(TEXT("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;
}

static int file_move( LPCTSTR src, LPCTSTR dst )
{
	return file_move_copy( src, dst, true );
}

static int file_copy( LPCTSTR src, LPCTSTR dst )
{
	return file_move_copy( src, dst, false );
}

static int folder_copy( LPCTSTR folder_src, LPCTSTR folder_dst )
{
	HANDLE fh;
	WIN32_FIND_DATA FindFileData;
	int ok, result = 0;
	TCHAR mask[_MAX_PATH];

	D(bug(TEXT("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, TEXT("*.*"), 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;
		TCHAR target[_MAX_PATH];
		make_mask( target, folder_dst, FindFileData.cFileName, 0 );
		D(bug(TEXT("copying item %s -> %s\n"), mask, target));
		if(isdir) {
			if(_tcscmp(FindFileData.cFileName,TEXT(".")) && _tcscmp(FindFileData.cFileName,TEXT(".."))) {
				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 == TEXT('.'))
		{
			ok = 0;
		} else {
			strlcpy( d->de.d_name, d->FindFileData.cFileName, lengthof(d->de.d_name) );
			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 += _tcslen(d->vname_list) + 1;
				if(*d->vname_list) {
					_tcscpy( d->FindFileData.cFileName, d->vname_list );
					d->FindFileData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
				} else {
					// Out of static drive entries. Continue with other stuff.
					TCHAR mask[MAX_PATH_LENGTH];
					make_mask( mask, virtual_root, TEXT("*.*"), 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;
	auto tpath = tstr(path);
	DIR *d = new DIR;
	if(d) {
		memset( d, 0, sizeof(DIR) );
		if(*tpath.get() == 0) {
			d->vname_list = host_drive_list;
			if(d->vname_list) {
				d->h = VIRTUAL_ROOT_ID;
				_tcscpy( d->FindFileData.cFileName, d->vname_list );
				d->FindFileData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
			} else {
				d->h = INVALID_HANDLE_VALUE;
			}
		} else {
			TCHAR mask[MAX_PATH_LENGTH];
			make_mask( mask, MRP(tpath.get()), TEXT("*.*"), 0 );

			D(bug(TEXT("opendir path=%s, mask=%s\n"), tpath.get(), mask));

			d->h = FindFirstFile( mask, &d->FindFileData );
			if(d->h == INVALID_HANDLE_VALUE) {
				delete d;
				d = 0;
			}
		}
	}

	D(bug(TEXT("opendir(%s,%s) = %08x\n"), tpath.get(), MRP(tpath.get()), 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;

	auto tpath = tstr(path);
	int result;

	if(*tpath.get() == 0) {
		/// virtual root
		memset( st, 0, sizeof(struct my_stat) );
		st->st_mode = _S_IFDIR;
		result = 0;
		my_errno = 0;
	} else {
		result = _tstat( MRP(tpath.get()), (struct _stat *)st );
		if(result < 0) {
			my_errno = errno;
		} else {
			my_errno = 0;
		}
	}

	D(bug(TEXT("stat(%s,%s) = %d\n"), tpath.get(), MRP(tpath.get()), 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;
	auto tpath = tstr(path);
	LPCTSTR p = MRP(tpath.get());

	// Windows "open" does not handle _O_CREAT and _O_BINARY as it should
	if(mode & _O_CREAT) {
		if(exists(p)) {
			result = _topen( p, mode & ~_O_CREAT );
			D(bug(TEXT("open-nocreat(%s,%s,%d) = %d\n"), tpath.get(), p, mode, result));
		} else {
			result = _tcreat( p, _S_IWRITE|_S_IREAD );
			if(result < 0) {
				make_folders(p);
				result = _tcreat( p, _S_IWRITE|_S_IREAD );
			}
			D(bug(TEXT("open-creat(%s,%s,%d) = %d\n"), tpath.get(), p, mode, result));
		}
	} else {
		result = _topen( p, mode );
		D(bug(TEXT("open(%s,%s,%d) = %d\n"), tpath.get(), 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;
	auto told_path = tstr(old_path);
	auto tnew_path = tstr(new_path);
	LPCTSTR p_old = MRP(told_path.get());
	LPCTSTR p_new = MRP2(tnew_path.get());

	result = my_access(old_path,0);
	if(result < 0) {
		// my_errno already set
	} else {
		if(is_same_drive(p_old,p_new)) {
			result = _trename( 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(TEXT("rename(%s,%s,%s,%s) = %d\n"), told_path.get(), p_old, tnew_path.get(), p_new, result));
	RESTORE_ERRORS;
	return result;
}

int my_access( const char *path, int mode )
{
	DISABLE_ERRORS;
	auto tpath = tstr(path);
	LPCTSTR p = MRP(tpath.get());
	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 = _taccess(p,mode);
		if(result < 0) {
			my_errno = errno;
		} else {
			my_errno = 0;
		}
	}

	D(bug(TEXT("access(%s,%s,%d) = %d\n"), tpath.get(), p, mode, result));
	RESTORE_ERRORS;
	return result;
}

int my_mkdir( const char *path, int mode )
{
	DISABLE_ERRORS;
	auto tpath = tstr(path);
	LPTSTR p = MRP(tpath.get());
	strip_trailing_bs(p);
	int result = _tmkdir( p );
	if(result < 0) {
		make_folders(p);
		result = _tmkdir( p );
	}
	if(result < 0) {
		my_errno = errno;
	} else {
		my_errno = 0;
	}
	D(bug(TEXT("mkdir(%s,%s,%d) = %d\n"), tpath.get(), p, mode, result));
	RESTORE_ERRORS;
	return result;
}

int my_remove( const char *path )
{
	DISABLE_ERRORS;
	auto tpath = tstr(path);
	LPTSTR p = MRP(tpath.get());
	strip_trailing_bs(p);
	int result;
	if(is_dir(p)) {
		result = myRemoveDirectory( p );
	} else {
		D(bug(TEXT("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(TEXT("remove(%s,%s) = %d\n"), tpath.get(), p, result));
	RESTORE_ERRORS;
	return result;
}

int my_creat( const char *path, int mode )
{
	DISABLE_ERRORS;
	auto tpath = tstr(path);
	LPCTSTR p = MRP(tpath.get());
	int result = _tcreat( p, _S_IWRITE|_S_IREAD ); // note mode
	if(result < 0) {
		make_folders(p);
		result = _tcreat( p, _S_IWRITE|_S_IREAD ); // note mode
	}
	if(result < 0) {
		my_errno = errno;
	} else {
		setmode(result, _O_BINARY);
		my_errno = 0;
	}
	D(bug(TEXT("creat(%s,%s,%d) = %d\n"), tpath.get(), p, mode,result));
	RESTORE_ERRORS;
	return result;
}

int my_chsize( int fd, unsigned int 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;
}

static FILETIME get_file_time(time_t time) {
	FILETIME ft;
	if (time == -1) {
		ft.dwHighDateTime = 0;
		ft.dwLowDateTime = 0;
		return ft;
	}
	unsigned long long result = 11644473600LL;
	result += time;
	result *= 10000000LL;
	ft.dwHighDateTime = (result >> 32);
	ft.dwLowDateTime = (result & 0xFFFFFFFF);
	return ft;
}

int my_utime( const char *path, struct my_utimbuf * my_times )
{
	auto tpath = tstr(path);
	LPCTSTR p = MRP(tpath.get());
	HANDLE f = CreateFile(p, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (f != INVALID_HANDLE_VALUE) {
		FILETIME acTime = get_file_time(my_times->actime);
		FILETIME modTime = get_file_time(my_times->modtime);
		SetFileTime(f, NULL, &acTime, &modTime);
		CloseHandle(f);
		return 0;
	}
	return -1;
}
