//--------------------------------------------------------------------------- // // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS // [ Host File System for the X68000 ] // // Note: This functionality is specific to the X68000 operating system. // It is highly unlikely that this will work for other platforms. //--------------------------------------------------------------------------- #pragma once //--------------------------------------------------------------------------- // // Status code definitions // //--------------------------------------------------------------------------- #define FS_INVALIDFUNC 0xFFFFFFFF ///< Executed an invalid function #define FS_FILENOTFND 0xFFFFFFFE ///< The selected file can not be found #define FS_DIRNOTFND 0xFFFFFFFD ///< The selected directory can not be found #define FS_OVEROPENED 0xFFFFFFFC ///< There are too many files open #define FS_CANTACCESS 0xFFFFFFFB ///< Can not access the direcory or volume #define FS_NOTOPENED 0xFFFFFFFA ///< The selected handle is not opened #define FS_INVALIDMEM 0xFFFFFFF9 ///< Memory management has been destroyed #define FS_OUTOFMEM 0xFFFFFFF8 ///< Insufficient memory for execution #define FS_INVALIDPTR 0xFFFFFFF7 ///< Selected an invalid memory management pointer #define FS_INVALIDENV 0xFFFFFFF6 ///< Selected an invalid environment #define FS_ILLEGALFMT 0xFFFFFFF5 ///< The exeucted file is in an invalid format #define FS_ILLEGALMOD 0xFFFFFFF4 ///< Invalid open access mode #define FS_INVALIDPATH 0xFFFFFFF3 ///< Mistake in selected file name #define FS_INVALIDPRM 0xFFFFFFF2 ///< Called with an invalid parameter #define FS_INVALIDDRV 0xFFFFFFF1 ///< Mistake in selected drive #define FS_DELCURDIR 0xFFFFFFF0 ///< Unable to delete the current directory #define FS_NOTIOCTRL 0xFFFFFFEF ///< Unable to use IOCTRL with the device #define FS_LASTFILE 0xFFFFFFEE ///< Can not find any more files #define FS_CANTWRITE 0xFFFFFFED ///< Selected file can not be written #define FS_DIRALREADY 0xFFFFFFEC ///< Selected directory is already registered #define FS_CANTDELETE 0xFFFFFFEB ///< Can not delete because of a file #define FS_CANTRENAME 0xFFFFFFEA ///< Can not rename because of a file #define FS_DISKFULL 0xFFFFFFE9 ///< Can not create a file because the disk is full #define FS_DIRFULL 0xFFFFFFE8 ///< Can not create a file because the directory is full #define FS_CANTSEEK 0xFFFFFFE7 ///< Can not seek in the selected location #define FS_SUPERVISOR 0xFFFFFFE6 ///< Selected supervisor in supervisor mode #define FS_THREADNAME 0xFFFFFFE5 ///< A thread with this name already exists #define FS_BUFWRITE 0xFFFFFFE4 ///< Writing to inter-process communication buffers is disallowed #define FS_BACKGROUND 0xFFFFFFE3 ///< Unable to start a background process #define FS_OUTOFLOCK 0xFFFFFFE0 ///< Insufficient lock space #define FS_LOCKED 0xFFFFFFDF ///< Can not access because it is locked #define FS_DRIVEOPENED 0xFFFFFFDE ///< Selected drive has an open handler #define FS_LINKOVER 0xFFFFFFDD ///< The symbolic link is nested over 16 times #define FS_FILEEXIST 0xFFFFFFB0 ///< The file exists #define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< No media inserted #define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< Write protected #define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< Invalid command number #define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< Invalid unit number #define HUMAN68K_PATH_MAX 96 ///< Longest path allowed in Human68k //=========================================================================== // /// Human68k name space // //=========================================================================== namespace Human68k { /// File attribute bit enum attribute_t { AT_READONLY = 0x01, ///< Read only attribute AT_HIDDEN = 0x02, ///< Hidden attribute AT_SYSTEM = 0x04, ///< System attribute AT_VOLUME = 0x08, ///< Volume label attribute AT_DIRECTORY = 0x10, ///< Directory attribute AT_ARCHIVE = 0x20, ///< Archive attribute AT_ALL = 0xFF, ///< All attribute bits are 1 }; /// File open modes enum open_t { OP_READ = 0, ///< Read OP_WRITE = 1, ///< Write OP_FULL = 2, ///< Read/Write OP_MASK = 0x0F, ///< Decision mask OP_SHARE_NONE = 0x10, ///< Sharing forbidden OP_SHARE_READ = 0x20, ///< Read sharing OP_SHARE_WRITE = 0x30, ///< Write sharing OP_SHARE_FULL = 0x40, ///< Read/Write sharing OP_SHARE_MASK = 0x70, ///< Sharing decision mask OP_SPECIAL = 0x100, ///< Dictionary access }; /// Seek types enum seek_t { SK_BEGIN = 0, ///< From the beginning of a file SK_CURRENT = 1, ///< From the current location SK_END = 2, ///< From the end of the file }; /// Media byte enum media_t { MEDIA_2DD_10 = 0xE0, ///< 2DD/10 sector MEDIA_1D_9 = 0xE5, ///< 1D/9 sector MEDIA_2D_9 = 0xE6, ///< 2D/9 sector MEDIA_1D_8 = 0xE7, ///< 1D/8 sector MEDIA_2D_8 = 0xE8, ///< 2D/8 sector MEDIA_2HT = 0xEA, ///< 2HT MEDIA_2HS = 0xEB, ///< 2HS MEDIA_2HDE = 0xEC, ///< 2DDE MEDIA_1DD_9 = 0xEE, ///< 1DD/9 sector MEDIA_1DD_8 = 0xEF, ///< 1DD/8 sector MEDIA_MANUAL = 0xF1, ///< Remote drive (manual eject) MEDIA_REMOVABLE = 0xF2, ///< Remote drive (removable) MEDIA_REMOTE = 0xF3, ///< Remote drive MEDIA_DAT = 0xF4, ///< SCSI-DAT MEDIA_CDROM = 0xF5, ///< SCSI-CDROM MEDIA_MO = 0xF6, ///< SCSI-MO MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD MEDIA_SASI_HD = 0xF8, ///< SASI-HD MEDIA_RAMDISK = 0xF9, ///< RAM disk MEDIA_2HQ = 0xFA, ///< 2HQ MEDIA_2DD_8 = 0xFB, ///< 2DD/8 sector MEDIA_2DD_9 = 0xFC, ///< 2DD/9 sector MEDIA_2HC = 0xFD, ///< 2HC MEDIA_2HD = 0xFE, ///< 2HD }; struct namests_t { BYTE wildcard; ///< Wildcard array BYTE drive; ///< Drive number BYTE path[65]; ///< Path (subdirectory +/) BYTE name[8]; ///< File name (PADDING 0x20) BYTE ext[3]; ///< Extension (PADDING 0x20) BYTE add[10]; ///< File name addition (PADDING 0x00) void GetCopyPath(BYTE* szPath) const; void GetCopyFilename(BYTE* szFilename) const; }; struct files_t { BYTE fatr; ///< + 0 search attribute; read-only // BYTE drive; ///< + 1 drive number; read-only DWORD sector; ///< + 2 directory sector; DOS _FILES first address substitute // WORD cluster; ///< + 6 directory cluster; details unknown (unused) WORD offset; ///< + 8 directory entry; write-only // BYTE name[8]; ///< +10 working file name; write-only (unused) // BYTE ext[3]; ///< +18 working extension; write-only (unused) BYTE attr; ///< +21 file attribute; write-only WORD time; ///< +22 last change time of day; write-only WORD date; ///< +24 last change date; write-only DWORD size; ///< +26 file size; write-only BYTE full[23]; ///< +30 full name; write-only }; struct fcb_t { // BYTE pad00[6]; ///< + 0~+ 5 (unused) DWORD fileptr; ///< + 6~+ 9 file pointer // BYTE pad01[4]; ///< +10~+13 (unused) WORD mode; ///< +14~+15 open mode // BYTE pad02[16]; ///< +16~+31 (unused) // DWORD zero; ///< +32~+35 zeros are written when opened (unused) // BYTE name[8]; ///< +36~+43 file name (PADDING 0x20) (unused) // BYTE ext[3]; ///< +44~+46 extension (PADDING 0x20) (unused) BYTE attr; ///< +47 file attribute // BYTE add[10]; ///< +48~+57 file name addition (PADDING 0x00) (unused) WORD time; ///< +58~+59 last change time of day WORD date; ///< +60~+61 last change date // WORD cluster; ///< +62~+63 cluster number (unused) DWORD size; ///< +64~+67 file size // BYTE pad03[28]; ///< +68~+95 FAT cache (unused) }; struct capacity_t { WORD freearea; ///< + 0 Number of available clusters WORD clusters; ///< + 2 Total number of clusters WORD sectors; ///< + 4 Number of sectors per cluster WORD bytes; ///< + 6 Number of bytes per sector }; struct ctrldrive_t { BYTE status; ///< +13 status BYTE pad[3]; ///< Padding }; struct dpb_t { WORD sector_size; ///< + 0 Number of bytes in one sector BYTE cluster_size; ///< + 2 Number sectors in one cluster -1 BYTE shift; ///< + 3 Number of cluster→sector shifts WORD fat_sector; ///< + 4 FAT first sector number BYTE fat_max; ///< + 6 FAT storage quantity BYTE fat_size; ///< + 7 FAT controlled sector number (excluding duplicates) WORD file_max; ///< + 8 Number of files in the root directory WORD data_sector; ///< +10 First sector number of data storage WORD cluster_max; ///< +12 Total number of clusters +1 WORD root_sector; ///< +14 First sector number of root directory // DWORD driverentry; ///< +16 Device driver pointer (unused) BYTE media; ///< +20 Media identifier // BYTE flag; ///< +21 Flag used by DPB (unused) }; /// Directory entry struct struct dirent_t { BYTE name[8]; ///< + 0 File name (PADDING 0x20) BYTE ext[3]; ///< + 8 Extension (PADDING 0x20) BYTE attr; ///< +11 File attribute BYTE add[10]; ///< +12 File name addition (PADDING 0x00) WORD time; ///< +22 Last change time of day WORD date; ///< +24 Last change date WORD cluster; ///< +26 Cluster number DWORD size; ///< +28 File size }; /// IOCTRL parameter union union ioctrl_t { BYTE buffer[8]; ///< Access in byte units DWORD param; ///< Parameter (First 4 bytes) WORD media; ///< Media byte (First 2 bytes) }; /// Command line parameter struct /** The driver itself is included in the beginning of the argument, so setting to a length longer than HUMAN68K_PATH_MAX */ struct argument_t { BYTE buf[256]; ///< Command line argument }; } /// Number of FILES buffers /** Under normal circumstances it's enough with just a few buffers, but Human68k multitasking may lead to multiple threads working deeply in the system, which is why this value is set this high. Default is 20 buffers. */ #define XM6_HOST_FILES_MAX 20 /// Number of FCB buffers /** This decides how many files can be opened at the same time. Default is 100 files. */ #define XM6_HOST_FCB_MAX 100 /// Max number of virtual clusters and sectors /** Number of virtual sectors used for accessing the first sector of a file entity. Allocating a generous amount to exceed the number of threads lzdsys uses for access. Default is 10 sectors. */ #define XM6_HOST_PSEUDO_CLUSTER_MAX 10 /// Number of caches for directory entries /** Human68k carries out a large number of checks of directory entries when doing an operation inside a subdirectory. This specifies the number of caches used to speed up this operation. Cache is allocated per drive. The more you add the faster it gets, but use too many and the host OS gets under a heavy load, so be careful. Default is 16. */ #define XM6_HOST_DIRENTRY_CACHE_MAX 16 /// Max number of entries that can be stored per directory /** When a large number of files are stored in a directory, a larger amount of data than contemporanous applications can handle will be returned. This may lead to errors such as partial data being recognized, performance dropping significantly, or OOM crashes. To guard against this, an upper limit is defined here. In the case of a particular file manager, the upper limit is 2560 files. This is one good example to use as reference. Default is around 60000 entries. (Upper limit of the FAT root directory) */ #define XM6_HOST_DIRENTRY_FILE_MAX 65535 /// Max number of patterns for file name deduplication /** The file names on the Human68k side are automatically created based on the file system on the host side. However, Human68k have stricter file name length restrictions than the host has. Because of this, there is a risk that file name duplication will occur. When this happens, WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve the duplication. Theoretically, there are over 60 million (36^5) unique file names that can be generated by this method. However, in reality any more than a few hundred deduplications will take excessive processing time. So here an upper limit to deduplication is set in order to maintain system performance. If a system is operated with common sense, you should only need a few dozen deduplication patterns, so this value can be kept low to further improve performance. In the case deduplication is not carried out, multiple files with the same name will be created. When trying to access such files, only the first entry will ever be accessed. Default is 36 patterns. */ #define XM6_HOST_FILENAME_PATTERN_MAX 36 /// Duplicate file identification mark /** A symbol used to distinguish between host and Human68k files. Do not use a command shell escape character, or similar protected symbol. Default is '@'. */ #define XM6_HOST_FILENAME_MARK '@' /// WINDRV operational flags /** Normally set to 0. When put in the OS trash can for deletion, it is set to 1. Other values are reserved for future use. Can be used for future extentions such as internal operational flags or mock media byte. */ enum { WINDRV_OPT_REMOVE = 0x00000001, ///< Bit 0: File delete process 0:Directly 1:Trash can WINDRV_OPT_ALPHABET = 0x00000020, ///< Bit 5: File name comparison; Alphabet distinction 0:No 1:Yes 0:-C 1:+C WINDRV_OPT_COMPARE_LENGTH = 0x00000040, ///< Bit 6: File name comparison; String length (unimplemented) 0:18+3 1:8+3 0:+T 1:-T WINDRV_OPT_CONVERT_LENGTH = 0x00000080, ///< Bit 7: File name conversion; String length 0:18+3 1:8+3 0:-A 1:+A WINDRV_OPT_CONVERT_SPACE = 0x00000100, ///< Bit 8: File name conversion; Space 0:No 1:'_' WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, ///< Bit 9: File name conversion; Invalid char 0:No 1:'_' WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, ///< Bit10: File name conversion; Middle hyphen 0:No 1:'_' WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, ///< Bit11: File name conversion; Initial hyphen 0:No 1:'_' WINDRV_OPT_CONVERT_PERIODS = 0x00001000, ///< Bit12: File name conversion; Middle period 0:No 1:'_' WINDRV_OPT_CONVERT_PERIOD = 0x00002000, ///< Bit13: File name conversion; Initial period 0:No 1:'_' WINDRV_OPT_REDUCED_SPACE = 0x00010000, ///< Bit16: File name reduction; Space 0:No 1:Reduced WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, ///< Bit17: File name reduction; Invalid char 0:No 1:Reduced WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, ///< Bit18: File name reduction Middle hyphen 0:No 1:Reduced WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, ///< Bit19: File name reduction Initial hyphen 0:No 1:Reduced WINDRV_OPT_REDUCED_PERIODS = 0x00100000, ///< Bit20: File name reduction Middle period 0:No 1:Reduced WINDRV_OPT_REDUCED_PERIOD = 0x00200000, ///< Bit21: File name reduction Initial period 0:No 1:Reduced // Bit24~30 Duplicate file identification mark 0:Automatic 1~127:Chars }; /// ファイルシステム動作フラグ /** 通常は0にする。リードオンリーでマウントしたいドライブの場合は1にする。 それ以外の値は将来のための予約とする。 判定が困難なデバイス(自作USBストレージとか)のための保険用。 */ enum { FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: 強制書き込み禁止 FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: 強制リムーバブルメディア FSFLAG_MANUAL = 0x00000004, ///< Bit2: 強制手動イジェクト }; //=========================================================================== // /// まるっとリングリスト /// /// 先頭(root.next)が最も新しいオブジェクト。 /// 末尾(root.prev)が最も古い/未使用オブジェクト。 /// コード効率追求のため、delete時は必ずポインタをアップキャストすること。 // //=========================================================================== class CRing { public: // 基本ファンクション CRing() { Init(); } ///< デフォルトコンストラクタ ~CRing() { Remove(); } ///< デストラクタ final void Init() { next = prev = this; } ///< 初期化 CRing* Next() const { return next; } ///< 次の要素を取得 CRing* Prev() const { return prev; } ///< 前の要素を取得 void Insert(CRing* pRoot) { // 該当オブジェクトを切り離し ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; // リング先頭へ挿入 ASSERT(pRoot); ASSERT(pRoot->next); next = pRoot->next; prev = pRoot; pRoot->next->prev = this; pRoot->next = this; } ///< オブジェクト切り離し & リング先頭へ挿入 void InsertTail(CRing* pRoot) { // 該当オブジェクトを切り離し ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; // リング末尾へ挿入 ASSERT(pRoot); ASSERT(pRoot->prev); next = pRoot; prev = pRoot->prev; pRoot->prev->next = this; pRoot->prev = this; } ///< オブジェクト切り離し & リング末尾へ挿入 void InsertRing(CRing* pRoot) { if (next == prev) return; // リング先頭へ挿入 ASSERT(pRoot); ASSERT(pRoot->next); pRoot->next->prev = prev; prev->next = pRoot->next; pRoot->next = next; next->prev = pRoot; // 自分自身を空にする next = prev = this; } ///< 自分以外のオブジェクト切り離し & リング先頭へ挿入 void Remove() { // 該当オブジェクトを切り離し ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; // 安全のため自分自身を指しておく (何度切り離しても問題ない) next = prev = this; } ///< オブジェクト切り離し private: CRing* next; ///< 次の要素 CRing* prev; ///< 前の要素 }; //=========================================================================== // /// ディレクトリエントリ ファイル名 // //=========================================================================== class CHostFilename { public: // 基本ファンクション CHostFilename(); ///< デフォルトコンストラクタ static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< オフセット位置取得 void SetHost(const TCHAR* szHost); ///< ホスト側の名称を設定 const TCHAR* GetHost() const { return m_szHost; } ///< ホスト側の名称を取得 void ConvertHuman(int nCount = -1); ///< Human68k側の名称を変換 void CopyHuman(const BYTE* szHuman); ///< Human68k側の名称を複製 BOOL isReduce() const; ///< Human68k側の名称が加工されたか調査 BOOL isCorrect() const { return m_bCorrect; } ///< Human68k側のファイル名規則に合致しているか調査 const BYTE* GetHuman() const { return m_szHuman; } ///< Human68kファイル名を取得 const BYTE* GetHumanLast() const { return m_pszHumanLast; } ///< Human68kファイル名を取得 const BYTE* GetHumanExt() const { return m_pszHumanExt; }///< Human68kファイル名を取得 void SetEntryName(); ///< Human68kディレクトリエントリを設定 void SetEntryAttribute(BYTE nHumanAttribute) { m_dirHuman.attr = nHumanAttribute; } ///< Human68kディレクトリエントリを設定 void SetEntrySize(DWORD nHumanSize) { m_dirHuman.size = nHumanSize; } ///< Human68kディレクトリエントリを設定 void SetEntryDate(WORD nHumanDate) { m_dirHuman.date = nHumanDate; } ///< Human68kディレクトリエントリを設定 void SetEntryTime(WORD nHumanTime) { m_dirHuman.time = nHumanTime; } ///< Human68kディレクトリエントリを設定 void SetEntryCluster(WORD nHumanCluster) { m_dirHuman.cluster = nHumanCluster; } ///< Human68kディレクトリエントリを設定 const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; } ///< Human68kディレクトリエントリを取得 BOOL CheckAttribute(DWORD nHumanAttribute) const; ///< Human68kディレクトリエントリの属性判定 BOOL isSameEntry(const Human68k::dirent_t* pdirHuman) const { ASSERT(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; } ///< Human68kディレクトリエントリの一致判定 // パス名操作 static const BYTE* SeparateExt(const BYTE* szHuman); ///< Human68kファイル名から拡張子を分離 private: static BYTE* CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLast); ///< Human68k側のファイル名要素をコピー const BYTE* m_pszHumanLast; ///< 該当エントリのHuman68k内部名の終端位置 const BYTE* m_pszHumanExt; ///< 該当エントリのHuman68k内部名の拡張子位置 BOOL m_bCorrect; ///< 該当エントリのHuman68k内部名が正しければ真 BYTE m_szHuman[24]; ///< 該当エントリのHuman68k内部名 Human68k::dirent_t m_dirHuman; ///< 該当エントリのHuman68k全情報 TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 (可変長) }; //=========================================================================== // /// ディレクトリエントリ パス名 /// /// Human68k側のパス名は、必ず先頭が/で始まり、末尾が/で終わる。 /// ユニット番号は持たない。 /// 高速化のため、ホスト側の名称にはベースパス部分も含む。 // //=========================================================================== /** @note ほとんどのHuman68kのアプリは、ファイルの更新等によってディレクトリエ ントリに変更が生じた際、親ディレクトリのタイムスタンプは変化しないも のと想定して作られている。 ところがホスト側のファイルシステムでは親ディレクトリのタイムスタンプ も変化してしまうものが主流となってしまっている。 このため、ディレクトリのコピー等において、アプリ側は正確にタイムスタ ンプ情報などを設定しているにもかかわらず、実行結果では時刻情報が上書 きされてしまうという惨劇が起きてしまう。 そこでディレクトリキャッシュ内にFATタイムスタンプのエミュレーション 機能を実装した。ホスト側のファイルシステムの更新時にタイムスタンプ情 報を復元することでHuman68k側の期待する結果と一致させる。 */ class CHostPath: public CRing { /// メモリ管理用 struct ring_t { CRing r; ///< 円環 CHostFilename f; ///< 実体 }; public: /// 検索用バッファ struct find_t { DWORD count; ///< 検索実行回数 + 1 (0のときは以下の値は無効) DWORD id; ///< 次回検索を続行するパスのエントリ識別ID const ring_t* pos; ///< 次回検索を続行する位置 (識別ID一致時) Human68k::dirent_t entry; ///< 次回検索を続行するエントリ内容 void Clear() { count = 0; } ///< 初期化 }; // 基本ファンクション CHostPath(); ///< デフォルトコンストラクタ ~CHostPath(); ///< デストラクタ final void Clean(); ///< 再利用のための初期化 void SetHuman(const BYTE* szHuman); ///< Human68k側の名称を直接指定する void SetHost(const TCHAR* szHost); ///< ホスト側の名称を直接指定する BOOL isSameHuman(const BYTE* szHuman) const; ///< Human68k側の名称を比較する BOOL isSameChild(const BYTE* szHuman) const; ///< Human68k側の名称を比較する const TCHAR* GetHost() const { return m_szHost; } ///< ホスト側の名称の獲得 const CHostFilename* FindFilename(const BYTE* szHuman, DWORD nHumanAttribute = Human68k::AT_ALL) const; ///< ファイル名を検索 const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const; ///< ファイル名を検索 (ワイルドカード対応) BOOL isRefresh(); ///< ファイル変更が行なわれたか確認 void Refresh(); ///< ファイル再構成 void Backup(); /// ホスト側のタイムスタンプを保存 void Restore() const; /// ホスト側のタイムスタンプを復元 void Release(); ///< 更新 // CHostEntryが利用する外部API static void InitId() { g_nId = 0; } ///< 識別ID生成用カウンタ初期化 private: static ring_t* Alloc(size_t nLength); ///< ファイル名領域確保 static void Free(ring_t* pRing); ///< ファイル名領域解放 static int Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFirst, const BYTE* pBufLast); ///< 文字列比較 (ワイルドカード対応) CRing m_cRing; ///< CHostFilename連結用 time_t m_tBackup; ///< 時刻復元用 BOOL m_bRefresh; ///< 更新フラグ DWORD m_nId; ///< 識別ID (値が変化した場合は更新を意味する) BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< 該当エントリのHuman68k内部名 TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 static DWORD g_nId; ///< 識別ID生成用カウンタ }; //=========================================================================== // /// ファイル検索処理 /// /// Human68k側のファイル名を内部Unicodeで処理するのは正直キツい。と /// いうわけで、全てBYTEに変換して処理する。変換処理はディレクトリエ /// ントリキャッシュが一手に担い、WINDRV側はすべてシフトJISのみで扱 /// えるようにする。 /// また、Human68k側名称は、完全にベースパス指定から独立させる。 /// /// ファイルを扱う直前に、ディレクトリエントリのキャッシュを生成する。 /// ディレクトリエントリの生成処理は高コストのため、一度生成したエントリは /// 可能な限り維持して使い回す。 /// /// ファイル検索は3方式。すべてCHostFiles::Find()で処理する。 /// 1. パス名のみ検索 属性はディレクトリのみ _CHKDIR _CREATE /// 2. パス名+ファイル名+属性の検索 _OPEN /// 3. パス名+ワイルドカード+属性の検索 _FILES _NFILES /// 検索結果は、ディレクトリエントリ情報として保持しておく。 // //=========================================================================== class CHostFiles { public: // 基本ファンクション CHostFiles() { SetKey(0); Init(); } ///< デフォルトコンストラクタ void Init(); ///< 初期化 void SetKey(DWORD nKey) { m_nKey = nKey; } ///< 検索キー設定 BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< 検索キー比較 void SetPath(const Human68k::namests_t* pNamests); ///< パス名・ファイル名を内部で生成 BOOL isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< ルートディレクトリ判定 void SetPathWildcard() { m_nHumanWildcard = 1; } ///< ワイルドカードによるファイル検索を有効化 void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< パス名のみを有効化 BOOL isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< パス名のみ設定か判定 void SetAttribute(DWORD nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } ///< 検索属性を設定 BOOL Find(DWORD nUnit, class CHostEntry* pEntry); ///< Human68k側でファイルを検索しホスト側の情報を生成 const CHostFilename* Find(CHostPath* pPath); ///< ファイル名検索 void SetEntry(const CHostFilename* pFilename); ///< Human68k側の検索結果保存 void SetResult(const TCHAR* szPath); ///< ホスト側の名称を設定 void AddResult(const TCHAR* szPath); ///< ホスト側の名称にファイル名を追加 void AddFilename(); ///< ホスト側の名称にHuman68kの新規ファイル名を追加 const TCHAR* GetPath() const { return m_szHostResult; } ///< ホスト側の名称を取得 const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; }///< Human68kディレクトリエントリを取得 DWORD GetAttribute() const { return m_dirHuman.attr; } ///< Human68k属性を取得 WORD GetDate() const { return m_dirHuman.date; } ///< Human68k日付を取得 WORD GetTime() const { return m_dirHuman.time; } ///< Human68k時刻を取得 DWORD GetSize() const { return m_dirHuman.size; } ///< Human68kファイルサイズを取得 const BYTE* GetHumanFilename() const { return m_szHumanFilename; }///< Human68kファイル名を取得 const BYTE* GetHumanResult() const { return m_szHumanResult; } ///< Human68kファイル名検索結果を取得 const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Human68kパス名を取得 private: DWORD m_nKey; ///< Human68kのFILESバッファアドレス 0なら未使用 DWORD m_nHumanWildcard; ///< Human68kのワイルドカード情報 DWORD m_nHumanAttribute; ///< Human68kの検索属性 CHostPath::find_t m_findNext; ///< 次回検索位置情報 Human68k::dirent_t m_dirHuman; ///< 検索結果 Human68kファイル情報 BYTE m_szHumanFilename[24]; ///< Human68kのファイル名 BYTE m_szHumanResult[24]; ///< 検索結果 Human68kファイル名 BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; ///< Human68kのパス名 TCHAR m_szHostResult[FILEPATH_MAX]; ///< 検索結果 ホスト側のフルパス名 }; //=========================================================================== // /// ファイル検索領域 マネージャ // //=========================================================================== class CHostFilesManager { public: #ifdef _DEBUG // 基本ファンクション ~CHostFilesManager(); ///< デストラクタ final #endif // _DEBUG void Init(); ///< 初期化 (ドライバ組込み時) void Clean(); ///< 解放 (起動・リセット時) CHostFiles* Alloc(DWORD nKey); ///< 確保 CHostFiles* Search(DWORD nKey); ///< 検索 void Free(CHostFiles* pFiles); ///< 解放 private: /// メモリ管理用 struct ring_t { CRing r; ///< 円環 CHostFiles f; ///< 実体 }; CRing m_cRing; ///< CHostFiles連結用 }; //=========================================================================== // /// FCB処理 // //=========================================================================== class CHostFcb { public: // 基本ファンクション CHostFcb() { SetKey(0); Init(); } ///< デフォルトコンストラクタ ~CHostFcb() { Close(); } ///< デストラクタ final void Init(); ///< 初期化 void SetKey(DWORD nKey) { m_nKey = nKey; } ///< 検索キー設定 BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< 検索キー比較 void SetUpdate() { m_bUpdate = TRUE; } ///< 更新 BOOL isUpdate() const { return m_bUpdate; } ///< 更新状態取得 BOOL SetMode(DWORD nHumanMode); ///< ファイルオープンモードを設定 void SetFilename(const TCHAR* szFilename); ///< ファイル名を設定 void SetHumanPath(const BYTE* szHumanPath); ///< Human68kパス名を設定 const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Human68kパス名を取得 BOOL Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); ///< ファイル作成 BOOL Open(); ///< ファイルオープン BOOL Rewind(DWORD nOffset); ///< ファイルシーク DWORD Read(BYTE* pBuffer, DWORD nSize); ///< ファイル読み込み DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< ファイル書き込み BOOL Truncate(); ///< ファイル切り詰め DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< ファイルシーク BOOL TimeStamp(DWORD nHumanTime); ///< ファイル時刻設定 BOOL Close(); ///< ファイルクローズ private: DWORD m_nKey; ///< Human68kのFCBバッファアドレス (0なら未使用) BOOL m_bUpdate; ///< 更新フラグ FILE* m_pFile; ///< ホスト側のファイルオブジェクト const char* m_pszMode; ///< ホスト側のファイルオープンモード bool m_bFlag; ///< ホスト側のファイルオープンフラグ BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; ///< Human68kのパス名 TCHAR m_szFilename[FILEPATH_MAX]; ///< ホスト側のファイル名 }; //=========================================================================== // /// FCB処理 マネージャ // //=========================================================================== class CHostFcbManager { public: #ifdef _DEBUG // 基本ファンクション ~CHostFcbManager(); ///< デストラクタ final #endif // _DEBUG void Init(); ///< 初期化 (ドライバ組込み時) void Clean(); ///< 解放 (起動・リセット時) CHostFcb* Alloc(DWORD nKey); ///< 確保 CHostFcb* Search(DWORD nKey); ///< 検索 void Free(CHostFcb* p); ///< 解放 private: /// メモリ管理用 struct ring_t { CRing r; ///< 円環 CHostFcb f; ///< 実体 }; CRing m_cRing; ///< CHostFcb連結用 }; //=========================================================================== // /// ホスト側ドライブ /// /// ドライブ毎に必要な情報の保持に専念し、管理はCHostEntryで行なう。 // //=========================================================================== class CHostDrv { public: // 基本ファンクション CHostDrv(); ///< デフォルトコンストラクタ ~CHostDrv(); ///< デストラクタ final void Init(const TCHAR* szBase, DWORD nFlag); ///< 初期化 (デバイス起動とロード) BOOL isWriteProtect() const { return m_bWriteProtect; } ///< 書き込み禁止か? BOOL isEnable() const { return m_bEnable; } ///< アクセス可能か? BOOL isMediaOffline(); ///< メディアチェック BYTE GetMediaByte() const; ///< メディアバイトの取得 DWORD GetStatus() const; ///< ドライブ状態の取得 void SetEnable(BOOL bEnable); ///< メディア状態設定 BOOL CheckMedia(); ///< メディア交換チェック void Update(); ///< メディア状態更新 void Eject(); ///< イジェクト void GetVolume(TCHAR* szLabel); ///< ボリュームラベルの取得 BOOL GetVolumeCache(TCHAR* szLabel) const; ///< キャッシュからボリュームラベルを取得 DWORD GetCapacity(Human68k::capacity_t* pCapacity); ///< 容量の取得 BOOL GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< キャッシュから容量を取得 // キャッシュ操作 void CleanCache(); ///< 全てのキャッシュを更新する void CleanCache(const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを更新する void CleanCacheChild(const BYTE* szHumanPath); ///< 指定されたパス以下のキャッシュを全て更新する void DeleteCache(const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを削除する CHostPath* FindCache(const BYTE* szHuman); ///< 指定されたパスがキャッシュされているか検索する CHostPath* CopyCache(CHostFiles* pFiles); ///< キャッシュ情報を元に、ホスト側の名称を獲得する CHostPath* MakeCache(CHostFiles* pFiles); ///< ホスト側の名称の構築に必要な情報をすべて取得する BOOL Find(CHostFiles* pFiles); ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) private: // パス名操作 static const BYTE* SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer); ///< Human68kフルパス名から先頭の要素を分離・コピー // 排他制御 void Lock() {} void Unlock() {} /// メモリ管理用 struct ring_t { CRing r; ///< 円環 CHostPath f; ///< 実体 }; BOOL m_bWriteProtect; ///< 書き込み禁止ならTRUE BOOL m_bEnable; ///< メディアが利用可能ならTRUE DWORD m_nRing; ///< パス名保持数 CRing m_cRing; ///< CHostPath連結用 Human68k::capacity_t m_capCache; ///< セクタ情報キャッシュ sectors == 0 なら未キャッシュ BOOL m_bVolumeCache; ///< ボリュームラベル読み込み済みならTRUE TCHAR m_szVolumeCache[24]; ///< ボリュームラベルキャッシュ TCHAR m_szBase[FILEPATH_MAX]; ///< ベースパス }; //=========================================================================== // /// ディレクトリエントリ管理 // //=========================================================================== class CHostEntry { public: // 基本ファンクション CHostEntry(); ///< デフォルトコンストラクタ ~CHostEntry(); ///< デストラクタ final void Init(); ///< 初期化 (ドライバ組込み時) void Clean(); ///< 解放 (起動・リセット時) // キャッシュ操作 void CleanCache(); ///< 全てのキャッシュを更新する void CleanCache(DWORD nUnit); ///< 指定されたユニットのキャッシュを更新する void CleanCache(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを更新する void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパス以下のキャッシュを全て更新する void DeleteCache(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを削除する BOOL Find(DWORD nUnit, CHostFiles* pFiles); ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) void ShellNotify(DWORD nEvent, const TCHAR* szPath); ///< ホスト側ファイルシステム状態変化通知 // ドライブオブジェクト操作 void SetDrv(DWORD nUnit, CHostDrv* pDrv); ///< ドライブ設定 BOOL isWriteProtect(DWORD nUnit) const; ///< 書き込み禁止か? BOOL isEnable(DWORD nUnit) const; ///< アクセス可能か? BOOL isMediaOffline(DWORD nUnit); ///< メディアチェック BYTE GetMediaByte(DWORD nUnit) const; ///< メディアバイトの取得 DWORD GetStatus(DWORD nUnit) const; ///< ドライブ状態の取得 BOOL CheckMedia(DWORD nUnit); ///< メディア交換チェック void Eject(DWORD nUnit); ///< イジェクト void GetVolume(DWORD nUnit, TCHAR* szLabel); ///< ボリュームラベルの取得 BOOL GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; ///< キャッシュからボリュームラベルを取得 DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< 容量の取得 BOOL GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const; ///< キャッシュからクラスタサイズを取得 /// 定数 enum { DriveMax = 10 ///< ドライブ最大候補数 }; private: CHostDrv* m_pDrv[DriveMax]; ///< ホスト側ドライブオブジェクト DWORD m_nTimeout; ///< 最後にタイムアウトチェックを行なった時刻 }; //=========================================================================== // /// ホスト側ファイルシステム // //=========================================================================== /** @note 現在の見解。 XM6の設計思想とは反するが、class Windrvまたはclass CWindrvに直接 class CFileSysへのポインタを持たせる方法を模索するべきである。 これにより、以下のメリットが得られる。 メリットその1。 コマンドハンドラの大量のメソッド群を一ヶ所で集中管理できる。 コマンドハンドラはホスト側の仕様変更などの要因によって激しく変化する 可能性が高いため、メソッドの追加削除や引数の変更などのメンテナンスが 大幅に楽になる。 メリットその2。 仮想関数のテーブル生成・参照処理に関する処理コードを駆逐できる。 XM6では複数のファイルシステムオブジェクトを同時に使うような実装は ありえない。つまりファイルシステムオブジェクトにポリモーフィズムは まったく必要ないどころか、ただクロックの無駄となっているだけである。 試しに変えてみた。実際効率上がった。 windrv.h内のFILESYS_FAST_STRUCTUREの値を変えてコンパイラの吐くソース を比較すれば一目瞭然。何故私がこんな長文を書こうと思ったのかを理解で きるはず。 一方ロシアはclass CWindrv内にclass CFileSysを直接設置した。 (本当はclass CHostを廃止して直接置きたい……良い方法はないものか……) */ class CFileSys { public: // 基本ファンクション CFileSys(); ///< デフォルトコンストラクタ virtual ~CFileSys() {}; ///< デストラクタ // 初期化・終了 void Reset(); ///< リセット (全クローズ) void Init(); ///< 初期化 (デバイス起動とロード) // コマンドハンドラ DWORD InitDevice(const Human68k::argument_t* pArgument); ///< $40 - デバイス起動 int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $41 - ディレクトリチェック int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $42 - ディレクトリ作成 int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $43 - ディレクトリ削除 int Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew); ///< $44 - ファイル名変更 int Delete(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $45 - ファイル削除 int Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute); ///< $46 - ファイル属性取得/設定 int Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles); ///< $47 - ファイル検索 int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); ///< $48 - ファイル次検索 int Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); ///< $49 - ファイル作成 int Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb); ///< $4A - ファイルオープン int Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb); ///< $4B - ファイルクローズ int Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, DWORD nSize); ///< $4C - ファイル読み込み int Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, DWORD nSize); ///< $4D - ファイル書き込み int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); ///< $4E - ファイルシーク DWORD TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime); ///< $4F - ファイル時刻取得/設定 int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< $50 - 容量取得 int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive); ///< $51 - ドライブ状態検査/制御 int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb); ///< $52 - DPB取得 int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); ///< $53 - セクタ読み込み int DiskWrite(DWORD nUnit); ///< $54 - セクタ書き込み int Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl); ///< $55 - IOCTRL int Flush(DWORD nUnit); ///< $56 - フラッシュ int CheckMedia(DWORD nUnit); ///< $57 - メディア交換チェック int Lock(DWORD nUnit); ///< $58 - 排他制御 void SetOption(DWORD nOption); ///< オプション設定 DWORD GetOption() const { return m_nOption; } ///< オプション取得 DWORD GetDefault() const { return m_nOptionDefault; } ///< デフォルトオプション取得 static DWORD GetFileOption() { return g_nOption; } ///< ファイル名変換オプション取得 void ShellNotify(DWORD nEvent, const TCHAR* szPath) { m_cEntry.ShellNotify(nEvent, szPath); } ///< ホスト側ファイルシステム状態変化通知 /// 定数 enum { DriveMax = CHostEntry::DriveMax ///< ドライブ最大候補数 }; private: // 内部補助用 void InitOption(const Human68k::argument_t* pArgument); ///< オプション初期化 BOOL FilesVolume(DWORD nUnit, Human68k::files_t* pFiles); ///< ボリュームラベル取得 DWORD m_nUnits; ///< 現在のドライブオブジェクト数 (レジューム毎に変化) DWORD m_nOption; ///< 現在の動作フラグ DWORD m_nOptionDefault; ///< リセット時の動作フラグ DWORD m_nDrives; ///< ベースパス状態復元用の候補数 (0なら毎回スキャン) DWORD m_nKernel; ///< カーネルチェック用カウンタ DWORD m_nKernelSearch; ///< NULデバイスの先頭アドレス DWORD m_nHostSectorCount; ///< 擬似セクタ番号 CHostFilesManager m_cFiles; ///< ファイル検索領域 CHostFcbManager m_cFcb; ///< FCB操作領域 CHostEntry m_cEntry; ///< ドライブオブジェクトとディレクトリエントリ DWORD m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX]; ///< 擬似セクタの指すファイル実体 DWORD m_nFlag[DriveMax]; ///< ベースパス状態復元用の動作フラグ候補 TCHAR m_szBase[DriveMax][FILEPATH_MAX]; ///< ベースパス状態復元用の候補 static DWORD g_nOption; ///< ファイル名変換フラグ };