diff --git a/Tests/VfsControl.pas b/Tests/VfsControl.pas index 845a50f..2df74da 100644 --- a/Tests/VfsControl.pas +++ b/Tests/VfsControl.pas @@ -8,8 +8,17 @@ unit VfsControl; uses Windows, SysUtils, - Utils, WinUtils, - VfsBase, VfsUtils, VfsHooks; + Utils, WinUtils, TypeWrappers, DataLib, + Files, StrLib, + VfsBase, VfsUtils, VfsHooks, DlgMes; + +type + (* Import *) + TWideString = TypeWrappers.TWideString; + +const + (* Flag forces to skip directory names, starting with '#' *) + SKIP_HASHTAGGED_MODS = 1; (* Runs all VFS subsystems, unless VFS is already running *) @@ -44,4 +53,87 @@ begin end; // .with end; // function RunVfs +function ValidateModName (const ModName: WideString): boolean; +const + DISALLOWED_CHARS = ['<', '>', '"', '?', '*', '\', '/', '|', ':', #0]; + +var + StrLen: integer; + i: integer; + +begin + StrLen := Length(ModName); + i := 1; + + while (i <= StrLen) and ((ord(ModName[i]) > 255) or not (AnsiChar(ModName[i]) in DISALLOWED_CHARS)) do begin + Inc(i); + end; + + result := (i > StrLen) and (ModName <> '') and (ModName <> '.') and (ModName <> '..'); +end; + +function LoadModList (const ModListFilePath: WideString): {O} DataLib.TList {of (O) TWideString}; +var + AbsFilePath: WideString; + FileContents: string; + Lines: Utils.TArrayOfStr; + ModNameUtf8: string; + ModName: WideString; + i: integer; + +begin + result := DataLib.NewList(Utils.OWNS_ITEMS); + // * * * * * // + AbsFilePath := VfsUtils.NormalizePath(ModListFilePath); + + if (AbsFilePath <> '') and (Files.ReadFileContents(AbsFilePath, FileContents)) then begin + Lines := StrLib.Explode(FileContents, #10); + + for i := 0 to High(Lines) do begin + ModNameUtf8 := Lines[i]; + ModName := StrLib.Utf8ToWide(ModNameUtf8); + + if ValidateModName(ModName) then begin + result.Add(TWideString.Create(ModName)); + end; + end; + end; +end; // .function LoadModList + +// function MapModsDir (const RootDir, ModsDir: WideString; Flags: integer = 0); +// var +// AbsRootDir: WideString; +// AbsModsDir: WideString; +// FileInfo: VfsUtils.TNativeFileInfo; +// ModName: WideString; + + +// begin +// AbsRootDir := VfsUtils.NormalizePath(RootDir); +// AbsModsDir := VfsUtils.NormalizePath(ModsDir); +// result := (AbsRootDir <> '') and (AbsModsDir <> '') and VfsUtils.GetFileInfo(AbsRootDir, FileInfo); +// result := result and Utils.HasFlag(Windows.FILE_ATTRIBUTE_DIRECTORY, FileInfo.Base.FileAttributes); +// result := result and VfsUtils.GetFileInfo(AbsModsDir, FileInfo); +// result := result and Utils.HasFlag(Windows.FILE_ATTRIBUTE_DIRECTORY, FileInfo.Base.FileAttributes); + +// if result then begin +// with VfsUtils.SysScanDir(AbsModsDir, '*') do begin +// while IterNext(ModName, @FileInfo.Base) do begin +// if (ModName <> '.') and (ModName <> '..') and Utils.HasFlag(Windows.FILE_ATTRIBUTE_DIRECTORY, FileInfo.Base.FileAttributes) then begin + +// end; +// end; +// end; +// end; +// end; + +var +L: TList; +i: integer; + +begin + // L := LoadModList('D:\Heroes 3\Mods\list.txt'); + // for i := 0 to L.Count- 1 do begin + // VarDump([TWideString(L[i]).Value]); + // end; end. \ No newline at end of file diff --git a/Tests/VfsIntegratedTest.pas b/Tests/VfsIntegratedTest.pas index edb17ff..fa0113b 100644 --- a/Tests/VfsIntegratedTest.pas +++ b/Tests/VfsIntegratedTest.pas @@ -61,7 +61,7 @@ end; procedure TestIntegrated.TearDown; begin VfsBase.ResetVfs(); - VfsDebug.SetLoggingProc(nil); + //VfsDebug.SetLoggingProc(nil); end; procedure TestIntegrated.TestGetFileAttributes; @@ -88,12 +88,14 @@ var end; // .function HasValidAttrs begin + VfsDebug.WriteLog('TestGetFileAttributes', 'Started'); RootDir := VfsTestHelper.GetTestsRootDir; - Check(not HasValidAttrs(VfsUtils.MakePath([RootDir, '\non-existing.non'])), '{1}'); - Check(HasValidAttrs(VfsUtils.MakePath([RootDir, '\Hobbots\mms.cfg']), 0, Windows.FILE_ATTRIBUTE_DIRECTORY), '{2}'); - Check(HasValidAttrs(VfsUtils.MakePath([RootDir, '\503.html']), 0, Windows.FILE_ATTRIBUTE_DIRECTORY), '{3}'); - Check(HasValidAttrs(VfsUtils.MakePath([RootDir, '\Hobbots\']), Windows.FILE_ATTRIBUTE_DIRECTORY), '{4}'); - Check(HasValidAttrs(VfsUtils.MakePath([RootDir, '\Mods']), Windows.FILE_ATTRIBUTE_DIRECTORY), '{5}'); + Check(not HasValidAttrs(VfsUtils.MakePath([RootDir, 'non-existing.non'])), '{1}'); + Check(HasValidAttrs(VfsUtils.MakePath([RootDir, 'Hobbots\mms.cfg']), 0, Windows.FILE_ATTRIBUTE_DIRECTORY), '{2}'); + Check(HasValidAttrs(VfsUtils.MakePath([RootDir, '503.html']), 0, Windows.FILE_ATTRIBUTE_DIRECTORY), '{3}'); + Check(HasValidAttrs(VfsUtils.MakePath([RootDir, 'Hobbots\']), Windows.FILE_ATTRIBUTE_DIRECTORY), '{4}'); + Check(HasValidAttrs(VfsUtils.MakePath([RootDir, 'Mods']), Windows.FILE_ATTRIBUTE_DIRECTORY), '{5}'); + VfsDebug.WriteLog('TestGetFileAttributes', 'Ended'); end; // .procedure TestIntegrated.TestGetFileAttributes; procedure TestIntegrated.TestGetFileAttributesEx; @@ -113,11 +115,13 @@ var end; begin + VfsDebug.WriteLog('TestGetFileAttributesEx', 'Started'); RootDir := VfsTestHelper.GetTestsRootDir; - CheckEquals(-1, GetFileSize(VfsUtils.MakePath([RootDir, '\non-existing.non'])), '{1}'); - CheckEquals(42, GetFileSize(VfsUtils.MakePath([RootDir, '\Hobbots\mms.cfg'])), '{2}'); - CheckEquals(22, GetFileSize(VfsUtils.MakePath([RootDir, '\503.html'])), '{3}'); - CheckEquals(318, GetFileSize(VfsUtils.MakePath([RootDir, '\default'])), '{4}'); + CheckEquals(-1, GetFileSize(VfsUtils.MakePath([RootDir, 'non-existing.non'])), '{1}'); + CheckEquals(42, GetFileSize(VfsUtils.MakePath([RootDir, 'Hobbots\mms.cfg'])), '{2}'); + CheckEquals(22, GetFileSize(VfsUtils.MakePath([RootDir, '503.html'])), '{3}'); + CheckEquals(318, GetFileSize(VfsUtils.MakePath([RootDir, 'default'])), '{4}'); + VfsDebug.WriteLog('TestGetFileAttributesEx', 'Ended'); end; // .procedure TestIntegrated.TestGetFileAttributesEx; procedure TestIntegrated.TestFilesOpenClose; @@ -137,6 +141,7 @@ begin RootDir := VfsTestHelper.GetTestsRootDir; try + VfsDebug.WriteLog('TestFilesOpenClose', 'Started'); Check(WinUtils.SetCurrentDirW(RootDir), 'Setting current directory to real path must succeed. Path: ' + RootDir); Check(OpenFile(VfsUtils.MakePath([RootDir, 'non-existing.non'])) <= 0, 'Opening non-existing file must fail'); @@ -165,6 +170,8 @@ begin finally WinUtils.SetCurrentDirW(CurrDir); end; // .try + + VfsDebug.WriteLog('TestFilesOpenClose', 'Ended'); end; // .procedure TestIntegrated.TestFilesOpenClose; procedure TestIntegrated.TestDirectoryListing; @@ -223,6 +230,7 @@ begin RootDir := VfsTestHelper.GetTestsRootDir; try + VfsDebug.WriteLog('TestDirectoryListing', 'Started'); FileList := GetDirListing(VfsUtils.MakePath([RootDir, '*'])); DirContents := FileList.ToText(#13#10); CheckEquals(VALID_ROOT_DIR_LISTING, DirContents); @@ -242,6 +250,8 @@ begin SysUtils.FreeAndNil(FileList); SysUtils.FreeAndNil(DirListing); end; // .try + + VfsDebug.WriteLog('TestDirectoryListing', 'Ended'); end; // .procedure TestIntegrated.TestDirectoryListing; begin diff --git a/VfsBase.pas b/VfsBase.pas index 8ec0407..16978d9 100644 --- a/VfsBase.pas +++ b/VfsBase.pas @@ -331,7 +331,10 @@ end; function IsVfsActive: boolean; begin result := EnterVfs; - LeaveVfs; + + if result then begin + LeaveVfs; + end; end; (* Returns real path for vfs item by its absolute virtual path or empty string. Optionally returns file info structure *) diff --git a/VfsHooks.pas b/VfsHooks.pas index 59c7919..215126f 100644 --- a/VfsHooks.pas +++ b/VfsHooks.pas @@ -83,11 +83,12 @@ end; // .function GetFileObjectPath function Hook_NtQueryAttributesFile (OrigFunc: WinNative.TNtQueryAttributesFile; ObjectAttributes: POBJECT_ATTRIBUTES; FileInformation: PFILE_BASIC_INFORMATION): NTSTATUS; stdcall; var - ExpandedPath: WideString; - RedirectedPath: WideString; - ReplacedObjAttrs: WinNative.TObjectAttributes; - FileInfo: TNativeFileInfo; - HadTrailingDelim: boolean; + ExpandedPath: WideString; + RedirectedPath: WideString; + ReplacedObjAttrs: WinNative.TObjectAttributes; + FileInfo: TNativeFileInfo; + HadTrailingDelim_: array [0..3] of byte; // Fix Delphi bug: HadTrailingDelim causes stack 4-bytes misalignment + HadTrailingDelim: boolean absolute HadTrailingDelim_; begin if VfsDebug.LoggingEnabled then begin @@ -140,11 +141,12 @@ end; // .function Hook_NtQueryAttributesFile function Hook_NtQueryFullAttributesFile (OrigFunc: WinNative.TNtQueryFullAttributesFile; ObjectAttributes: POBJECT_ATTRIBUTES; FileInformation: PFILE_NETWORK_OPEN_INFORMATION): NTSTATUS; stdcall; var - ExpandedPath: WideString; - RedirectedPath: WideString; - ReplacedObjAttrs: WinNative.TObjectAttributes; - FileInfo: TNativeFileInfo; - HadTrailingDelim: boolean; + ExpandedPath: WideString; + RedirectedPath: WideString; + ReplacedObjAttrs: WinNative.TObjectAttributes; + FileInfo: TNativeFileInfo; + HadTrailingDelim_: array [0..3] of byte; // Fix Delphi bug: HadTrailingDelim causes stack 4-bytes misalignment + HadTrailingDelim: boolean absolute HadTrailingDelim_; begin if VfsDebug.LoggingEnabled then begin @@ -211,10 +213,11 @@ end; function Hook_NtCreateFile (OrigFunc: WinNative.TNtCreateFile; FileHandle: PHANDLE; DesiredAccess: ACCESS_MASK; ObjectAttributes: POBJECT_ATTRIBUTES; IoStatusBlock: PIO_STATUS_BLOCK; AllocationSize: PLARGE_INTEGER; FileAttributes: ULONG; ShareAccess: ULONG; CreateDisposition: ULONG; CreateOptions: ULONG; EaBuffer: PVOID; EaLength: ULONG): NTSTATUS; stdcall; var - ExpandedPath: WideString; - RedirectedPath: WideString; - ReplacedObjAttrs: WinNative.TObjectAttributes; - HadTrailingDelim: boolean; + ExpandedPath: WideString; + RedirectedPath: WideString; + ReplacedObjAttrs: WinNative.TObjectAttributes; + HadTrailingDelim_: array [0..3] of byte; // Fix Delphi bug: HadTrailingDelim causes stack 4-bytes misalignment + HadTrailingDelim: boolean absolute HadTrailingDelim_; begin if VfsDebug.LoggingEnabled then begin diff --git a/VfsOpenFiles.pas b/VfsOpenFiles.pas index 692a84d..586ac73 100644 --- a/VfsOpenFiles.pas +++ b/VfsOpenFiles.pas @@ -96,7 +96,7 @@ begin ExcludedItems := DataLib.NewDict(not Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE); if VfsItemFound then begin - while DirListing.GetNextItem(DirItem) do begin + while Self.DirListing.GetNextItem(DirItem) do begin ExcludedItems[WideStrToCaselessKey(DirItem.Data.FileName)] := Ptr(1); end; diff --git a/VfsTest.dproj b/VfsTest.dproj index d304ba3..33381f2 100644 --- a/VfsTest.dproj +++ b/VfsTest.dproj @@ -24,7 +24,6 @@ ..\Lib\B2;.\Tests ..\Lib\B2;.\Tests 3 - False Delphi.Personality @@ -58,6 +57,14 @@ + + + + + + + + diff --git a/_TODO_.txt b/_TODO_.txt index bf44dfa..f0e80a8 100644 --- a/_TODO_.txt +++ b/_TODO_.txt @@ -1 +1,10 @@ -UTF-8 Logging \ No newline at end of file +UTF-8 Logging + +Editor/Development mode: +Separate thread with FindFirstChangeNotificationA/FindNextChangeNotification on mods root dir + WaitForSingleObject ++ ResetVfs + BlockVfs + MapDir for each cached mapped directory + RunVfs. +We will need some API like RefreshVfs. + +Add ANSI versions of exported API. + +Write VfsImport.pas nad vfs_import.c \ No newline at end of file