diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..fea56615 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "BurnOutSharp/External/LessIO"] + path = BurnOutSharp/External/LessIO + url = https://github.com/activescott/LessIO.git diff --git a/BurnOutSharp/BurnOutSharp.csproj b/BurnOutSharp/BurnOutSharp.csproj index 8172eed4..0edd8f6e 100644 --- a/BurnOutSharp/BurnOutSharp.csproj +++ b/BurnOutSharp/BurnOutSharp.csproj @@ -1,7 +1,7 @@  - net48;netcoreapp3.1;net5.0-windows + net48;netcoreapp3.1;net5.0 x86 BurnOutSharp BurnOutSharp @@ -28,6 +28,11 @@ + + + $(DefaultItemExcludes);**\AssemblyInfo.cs;External\LessIO\src\LessIO.Tests\**\* + + diff --git a/BurnOutSharp/External/LessIO b/BurnOutSharp/External/LessIO new file mode 160000 index 00000000..1def7d19 --- /dev/null +++ b/BurnOutSharp/External/LessIO @@ -0,0 +1 @@ +Subproject commit 1def7d19cc8b2c0a2b5bdaed28b272cd2c32c54f diff --git a/BurnOutSharp/External/LessIO.bak/FileAttributes.cs b/BurnOutSharp/External/LessIO.bak/FileAttributes.cs deleted file mode 100644 index b9cdaa11..00000000 --- a/BurnOutSharp/External/LessIO.bak/FileAttributes.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; - -namespace LessIO -{ - //TODO: These are the samea s Win32. Consider whether we should expose all of these? - /// - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117%28v=vs.85%29.aspx - /// - /// - /// These have the same values as they can generally be casted between these enums (this Enum has more values than System.IO though). - /// Defined in C:\Program Files (x86)\Windows Kits\8.1\Include\um\winnt.h - /// - [Flags] - public enum FileAttributes - { - //#define FILE_ATTRIBUTE_READONLY 0x00000001 - ReadOnly = 0x00000001, - //#define FILE_ATTRIBUTE_HIDDEN 0x00000002 - Hidden = 0x00000002, - //#define FILE_ATTRIBUTE_SYSTEM 0x00000004 - System = 0x00000004, - //#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 - Directory = 0x00000010, - //#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 - Archive = 0x00000020, - //#define FILE_ATTRIBUTE_DEVICE 0x00000040 - Device = 0x00000040, - //#define FILE_ATTRIBUTE_NORMAL 0x00000080 - Normal = 0x00000080, - //#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 - Temporary = 0x00000100, - //#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 - SparseFile = 0x00000200, - //#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 - ReparsePoint = 0x00000400, - //#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 - Compressed = 0x00000800, - //#define FILE_ATTRIBUTE_OFFLINE 0x00001000 - Offline = 0x00001000, - //#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 - NotContentIndexed = 0x00002000, - //#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 - Encrypted = 0x00004000, - //#define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 - IntegrityStream = 0x00008000, - //#define FILE_ATTRIBUTE_VIRTUAL 0x00010000 - Virtual = 0x00010000, - //#define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 - NoScrubData = 0x00020000, - /* EA is not documented - #define FILE_ATTRIBUTE_EA 0x00040000 - EA = 0x00040000 - */ - } -} diff --git a/BurnOutSharp/External/LessIO.bak/FileSystem.cs b/BurnOutSharp/External/LessIO.bak/FileSystem.cs deleted file mode 100644 index 86089c60..00000000 --- a/BurnOutSharp/External/LessIO.bak/FileSystem.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using LessIO.Strategies; -using LessIO.Strategies.Win32; -using BadPath = System.IO.Path; -using System.Collections.Generic; - -namespace LessIO -{ - /// - /// Provides various file system operations for the current platform. - /// - public static class FileSystem - { - private static readonly Lazy LazyStrategy = new Lazy(() => new Win32FileSystemStrategy()); - - private static FileSystemStrategy Strategy - { - get { return LazyStrategy.Value; } - } - - /// - /// Sets the date and time that the specified file was last written to. - /// - /// The path of the file to set the file time on. - /// - /// The date to set for the last write date and time of specified file. - /// Expressed in local time. - /// - public static void SetLastWriteTime(Path path, DateTime lastWriteTime) - { - Strategy.SetLastWriteTime(path, lastWriteTime); - } - - public static void SetAttributes(Path path, LessIO.FileAttributes fileAttributes) - { - Strategy.SetAttributes(path, fileAttributes); - } - - public static LessIO.FileAttributes GetAttributes(Path path) - { - return Strategy.GetAttributes(path); - } - - /// - /// Returns true if a file or directory exists at the specified path. - /// - public static bool Exists(Path path) - { - return Strategy.Exists(path); - } - - /// - /// Creates the specified directory. - /// - /// - /// Creates parent directories as needed. - /// - public static void CreateDirectory(Path path) - { - Strategy.CreateDirectory(path); - } - - /// - /// Removes/deletes an existing empty directory. - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365488%28v=vs.85%29.aspx - /// - /// - public static void RemoveDirectory(Path path) - { - RemoveDirectory(path, false); - } - - /// - /// Removes/deletes an existing directory. - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365488%28v=vs.85%29.aspx - /// - /// The path to the directory to remove. - /// - /// True to remove the directory and all of its contents recursively. - /// False will remove the directory only if it is empty. - /// - /// Recursively implies removing contained files forcefully. - public static void RemoveDirectory(Path path, bool recursively) - { - Strategy.RemoveDirectory(path, recursively); - } - - /// - /// Removes/deletes an existing file. - /// To remove a directory see . - /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363915%28v=vs.85%29.aspx - /// - /// The file to remove. - /// True to remove the file even if it is read-only. - public static void RemoveFile(Path path, bool forcefully) - { - Strategy.RemoveFile(path, forcefully); - } - - /// - /// Removes/deletes an existing file. - /// To remove a directory see . - /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363915%28v=vs.85%29.aspx - /// - /// The file to remove. - public static void RemoveFile(Path path) - { - Strategy.RemoveFile(path, false); - } - - /// - /// Copies the specified existing file to a new location. - /// Will throw an exception if the destination file already exists. - /// - public static void Copy(Path source, Path dest) - { - if (!Strategy.Exists(source)) - throw new Exception(string.Format("The file \"{0}\" does not exist.", source)); - Strategy.Copy(source, dest); - } - - /// - /// Returns true if the specified path is a directory. - /// - internal static bool IsDirectory(Path path) - { - return Strategy.IsDirectory(path); - } - - /// - /// Creates or overwrites the file at the specified path. - /// - /// The path and name of the file to create. Supports long file paths. - /// A that provides read/write access to the file specified in path. - public static System.IO.Stream CreateFile(Path path) - { - return Strategy.CreateFile(path); - } - - /// - /// Returns the contents of the specified directory. - /// - /// The path to the directory to get the contents of. - public static IEnumerable ListContents(Path directory) - { - return Strategy.ListContents(directory); - } - - /// - /// Returns the contents of the specified directory. - /// - /// The path to the directory to get the contents of. - /// True to list the contents of any child directories. - /// If the specified directory is not actually a directory then an empty set is returned. - public static IEnumerable ListContents(Path directory, bool recursive) - { - return Strategy.ListContents(directory, recursive); - } - } -} diff --git a/BurnOutSharp/External/LessIO.bak/Path.cs b/BurnOutSharp/External/LessIO.bak/Path.cs deleted file mode 100644 index e7ac1f1a..00000000 --- a/BurnOutSharp/External/LessIO.bak/Path.cs +++ /dev/null @@ -1,399 +0,0 @@ -using System; -using System.Linq; -using BadPath = System.IO.Path; - -namespace LessIO -{ - /// - /// Represents a file system path. - /// - public struct Path : IEquatable - { - private readonly string _path; - private static readonly string _pathEmpty = string.Empty; - public static readonly Path Empty = new Path(); - - /// - /// This is the special prefix to prepend to paths to support up to 32,767 character paths. - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath - /// - private static readonly string Win32LongPathPrefix = @"\\?\"; - /// - /// This is the special prefix to prepend to paths to support long paths for UNC paths. - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath - /// - private static readonly string Win32LongPathPrefixUNC = @"\\?\UNC\"; - - private static readonly string UNCPrefix = @"\\"; - - // TODO: Add validation using a strategy? Or just use it as a strongly typed path to force caller to be explicit? - - public Path(string path) - { - //TODO: Consider doing a FileSystem.Normalize and FileSystem.Validate to allow the strategy to Normalize & Validate path - //To maintain sanity NEVER let the Path object store the long path prefixes. That is a hack for Win32 that should only ever be used just before calling the Win32 API and stripped out of any paths coming out of the Win32 API. - path = StripWin32PathPrefix(path); - path = StripDirectorySeperatorPostfix(path); - path = RemoveDoubleSeperators(path); - _path = path; - } - - private static string RemoveDoubleSeperators(string path) - { - /* - "\\" is legit for UNC paths and as a prefix. - So don't remove "\\" if it is in the root. - */ - string root = GetPathRoot(path); - string remainder = path.Length > root.Length ? path.Substring(root.Length) : ""; - - Array.ForEach(DirectorySeperatorChars, sep => remainder = remainder.Replace(new string(new char[] { sep, sep }), new string(new char[] { sep }))); - return root + remainder; - } - - private static string StripDirectorySeperatorPostfix(string path) - { - /* Here we want to trim any trailing directory seperator charactars EXCEPT - in one case: When the path is a fully qualified root dir such as "x:\". See GetPathRoot and System.IO.Path.GetPathRoot - */ - - // "X:/"(path specified an absolute path on a given drive). - if (path.Length == 3 && path[1] == ':' && IsDirectorySeparator(path[2])) - return path; - else - return path.TrimEnd(DirectorySeperatorChars); - } - - private static string StripWin32PathPrefix(string pathString) - { - if (pathString.StartsWith(Win32LongPathPrefixUNC)) - return UNCPrefix + pathString.Substring(Win32LongPathPrefixUNC.Length); - - if (pathString.StartsWith(Win32LongPathPrefix)) - return pathString.Substring(Win32LongPathPrefix.Length); - - return pathString; - } - - /// - /// Returns the directory seperator characers. - /// - internal static char[] DirectorySeperatorChars - { - get - { - return new char[] { BadPath.DirectorySeparatorChar, BadPath.AltDirectorySeparatorChar }; - } - } - - internal static bool IsDirectorySeparator(char ch) - { - return Array.Exists(DirectorySeperatorChars, c => c == ch); - } - - /// - /// Gets the normalized path string. May be rooted or may be relative. - /// For a rooted/qualified path use - /// - public string PathString - { - get - { - return _path != null ? _path : _pathEmpty; - } - } - - /// - /// Returns the absolute path for the current path. - /// Compatible with . - /// - public Path FullPath - { - get - { - return new Path(this.FullPathString); - } - } - - /// - /// Returns the absolute path for the current path. - /// Compatible with . - /// - public string FullPathString - { - get - { - var pathString = this.PathString; - var pathRoot = this.PathRoot; - if (pathRoot == "") - { // relative - return Combine(WorkingDirectory, pathString).PathString; - } - else if (pathRoot == @"\" || pathRoot == @"/") - { // use the working directory's drive/root only. - pathString = pathString.TrimStart(DirectorySeperatorChars);//otherwise Combine will ignore the root - string workingRoot = new Path(WorkingDirectory).PathRoot; - return Combine(workingRoot, pathString).PathString; - } - else - { - return pathString; - } - } - } - - private static string WorkingDirectory - { - get { - //TODO: There is a Win32 native equivelent for this: - return System.IO.Directory.GetCurrentDirectory(); - } - } - - public bool IsEmpty - { - get { return Equals(Path.Empty); } - } - - /// - /// Indicates if the two paths are equivelent and point to the same file or directory. - /// - private static bool PathEquals(string pathA, string pathB) - { - /* Now we never let the Win32 long path prefix get into a Path instance: - pathA = StripWin32PathPrefix(pathA); - pathB = StripWin32PathPrefix(pathB); - */ - pathA = pathA.TrimEnd(DirectorySeperatorChars); - pathB = pathB.TrimEnd(DirectorySeperatorChars); - var partsA = pathA.Split(DirectorySeperatorChars); - var partsB = pathB.Split(DirectorySeperatorChars); - if (partsA.Length != partsB.Length) - return false; - - for (var i = 0; i < partsA.Length; i++) - { - var areEqual = string.Equals(partsA[i], partsB[i], StringComparison.InvariantCultureIgnoreCase); - if (!areEqual) - return false; - } - return true; - } - - public static bool operator ==(Path a, Path b) - { - return Path.Equals(a, b); - } - - public static bool operator !=(Path a, Path b) - { - return !Path.Equals(a, b); - } - - public override bool Equals(object obj) - { - if (obj == null || GetType() != obj.GetType()) - return false; - return Equals((Path)obj); - } - - public bool Equals(Path other) - { - return Path.PathEquals(this.PathString, other.PathString); - } - - internal static bool Equals(Path a, Path b) - { - return PathEquals(a.PathString, b.PathString); - } - - /// - /// Long-form filenames are not supported by the .NET system libraries, so we do win32 calls. - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#maxpath - /// - /// - /// The object will never store the Win32 long path prefix. Instead use this method to add it back when necessary (i.e. when making direct calls into Win32 APIs). - /// - public string WithWin32LongPathPrefix() - { - if (!PathString.StartsWith(Win32LongPathPrefix)) // More consistent to deal with if we just add it to all of them: if (!path.StartsWith(LongPathPrefix) && path.Length >= MAX_PATH) - { - if (PathString.StartsWith(UNCPrefix)) - return Win32LongPathPrefixUNC + this.PathString.Substring(UNCPrefix.Length); - else - return Win32LongPathPrefix + this.PathString; - } - else - { - //NOTE that Win32LongPathPrefixUNC is a superset of Win32LongPathPrefix we just assume the right pathprefix is already there. - return this.PathString; - } - } - - public override int GetHashCode() - { - return PathString.GetHashCode(); - } - - public override string ToString() - { - return PathString.ToString(); - } - - /// - /// Gets the root directory information of the specified path. - /// - public string PathRoot - { - get - { - return GetPathRoot(this.PathString); - } - } - - /// - /// Modeled after but supports long path names. - /// - /// - /// - /// - /// See https://msdn.microsoft.com/en-us/library/system.io.path.getpathroot%28v=vs.110%29.aspx - /// Possible patterns for the string returned by this method are as follows: - /// An empty string (path specified a relative path on the current drive or volume). - /// "/"(path specified an absolute path on the current drive). - /// "X:"(path specified a relative path on a drive, where X represents a drive or volume letter). - /// "X:/"(path specified an absolute path on a given drive). - /// "\\ComputerName\SharedFolder"(a UNC path). - /// - internal static string GetPathRoot(string path) - { - // "X:/"(path specified an absolute path on a given drive). - if (path.Length >= 3 && path[1] == ':' && IsDirectorySeparator(path[2])) - return path.Substring(0, 3); - // "X:"(path specified a relative path on a drive, where X represents a drive or volume letter). - if (path.Length >= 2 && path[1] == ':') - { - return path.Substring(0, 2); - } - // "\\ComputerName\SharedFolder"(a UNC path). - // NOTE: UNC Path "root" includes the server/host AND have the root share folder too. - if (path.Length > 2 - && IsDirectorySeparator(path[0]) - && IsDirectorySeparator(path[1]) - && path.IndexOfAny(DirectorySeperatorChars, 2) > 2) - { - var beginShareName = path.IndexOfAny(DirectorySeperatorChars, 2); - var endShareName = path.IndexOfAny(DirectorySeperatorChars, beginShareName + 1); - if (endShareName < 0) - endShareName = path.Length; - if (beginShareName > 2 && endShareName > beginShareName) - return path.Substring(0, endShareName); - } - // "/"(path specified an absolute path on the current drive). - if (path.Length >= 1 && IsDirectorySeparator(path[0])) - { - return path.Substring(0, 1); - } - // path specified a relative path on the current drive or volume? - return ""; - } - - public static Path Combine(Path path1, params string[] pathParts) - { - if (path1.IsEmpty) - throw new ArgumentNullException("path1"); - if (pathParts == null || pathParts.Length == 0) - throw new ArgumentNullException("pathParts"); - - string[] allStrings = new string[pathParts.Length + 1]; - allStrings[0] = path1.PathString; - Array.Copy(pathParts, 0, allStrings, 1, pathParts.Length); - return Combine(allStrings); - } - - public static Path Combine(params Path[] pathParts) - { - if (pathParts == null) - throw new ArgumentNullException(); - var strs = pathParts.Select(p => p.PathString); - return Combine(strs.ToArray()); - } - - public static Path Combine(params string[] pathParts) - { - if (pathParts == null) - throw new ArgumentNullException(); - if (pathParts.Length < 2) - throw new ArgumentException("Expected at least two parts to combine."); - var output = BadPath.Combine(pathParts[0], pathParts[1]); - for (var i = 2; i < pathParts.Length; i++) - { - output = BadPath.Combine(output, pathParts[i]); - } - return new Path(output); - } - - public Path Parent - { - get - { - var path = this.PathString; - path = path.TrimEnd(Path.DirectorySeperatorChars); - var parentEnd = path.LastIndexOfAny(Path.DirectorySeperatorChars); - if (parentEnd >= 0 && parentEnd > GetPathRoot(path).Length) - { - var result = path.Substring(0, parentEnd); - return new Path(result); - } - else - return Path.Empty; - } - } - - /// - /// Indicates if the file or directory at the specified path exists. - /// For code compatibility with . - /// - public bool Exists - { - get { return FileSystem.Exists(this); } - } - - /// - /// True if the path is a rooted/fully qualified path. Otherwise returns false if it is a relative path. - /// Compatible with . - /// - public bool IsPathRooted - { - get - { - /* The IsPathRooted method returns true if the first character is a directory separator character such as "\", or if the path starts with a drive letter and colon (:). - * For example, it returns true for path strings such as "\\MyDir\\MyFile.txt", "C:\\MyDir", or "C: MyDir". It returns false for path strings such as "MyDir". - * - https://msdn.microsoft.com/en-us/library/system.io.path.ispathrooted%28v=vs.110%29.aspx - */ - var pathString = this.PathString; - bool rooted = - DirectorySeperatorChars.Any(c => c == pathString[0]) - || pathString.Length >= 2 && pathString[1] == ':'; - return rooted; - } - } - - /// - /// For code compatibility with - /// - public System.IO.StreamWriter CreateText() - { - var stream = FileSystem.CreateFile(this); - return new System.IO.StreamWriter(stream, System.Text.Encoding.UTF8); - } - - /// - /// For code compatibility with - /// - public static string GetFileName(string path) - { - return BadPath.GetFileName(path); - } - } -} diff --git a/BurnOutSharp/External/LessIO.bak/Strategies/FileSystemStrategy.cs b/BurnOutSharp/External/LessIO.bak/Strategies/FileSystemStrategy.cs deleted file mode 100644 index 2f36677e..00000000 --- a/BurnOutSharp/External/LessIO.bak/Strategies/FileSystemStrategy.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace LessIO.Strategies -{ - /// - /// See for documentation of each method of this class. - /// - internal abstract class FileSystemStrategy - { - public abstract void SetLastWriteTime(Path path, DateTime lastWriteTime); - public abstract void SetAttributes(Path path, FileAttributes fileAttributes); - public abstract FileAttributes GetAttributes(Path path); - public abstract bool Exists(Path path); - public abstract void CreateDirectory(Path path); - public abstract void Copy(Path source, Path dest); - public abstract void RemoveDirectory(Path path, bool recursively); - public abstract void RemoveFile(Path path, bool force); - public abstract System.IO.Stream CreateFile(Path path); - public abstract IEnumerable ListContents(Path directory); - - public virtual IEnumerable ListContents(Path directory, bool recursive) - { - IEnumerable children = ListContents(directory); - if (recursive) - { - IEnumerable grandChildren = children.SelectMany( - p => ListContents(p, recursive) - ); - return Enumerable.Concat(children, grandChildren); - } - else - { - return children; - } - } - - public virtual bool IsDirectory(Path path) - { - FileAttributes attributes = GetAttributes(path); - return (attributes & FileAttributes.Directory) == FileAttributes.Directory; - } - - public virtual bool IsReadOnly(Path path) - { - FileAttributes attributes = GetAttributes(path); - return (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly; - } - - public virtual void SetReadOnly(Path path, bool readOnly) - { - FileAttributes attributes = GetAttributes(path); - if (readOnly) - attributes = attributes | FileAttributes.ReadOnly; - else - attributes = attributes & ~FileAttributes.ReadOnly; - SetAttributes(path, attributes); - } - } -} diff --git a/BurnOutSharp/External/LessIO.bak/Strategies/Win32/NativeMethods.cs b/BurnOutSharp/External/LessIO.bak/Strategies/Win32/NativeMethods.cs deleted file mode 100644 index 933c640c..00000000 --- a/BurnOutSharp/External/LessIO.bak/Strategies/Win32/NativeMethods.cs +++ /dev/null @@ -1,211 +0,0 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Runtime.InteropServices; -using FileAttributes = System.IO.FileAttributes; - -namespace LessIO.Strategies.Win32 -{ - /// - /// Native/Win32 methods for accessing file system. Primarily to get around . - /// Good references: - /// * https://blogs.msdn.microsoft.com/bclteam/2007/03/26/long-paths-in-net-part-2-of-3-long-path-workarounds-kim-hamilton/ - /// - internal class NativeMethods - { - /// - /// Specified in Windows Headers for default maximum path. To go beyond this length you must prepend to the path. - /// - internal const int MAX_PATH = 260; - /// - /// This is the special prefix to prepend to paths to support up to 32,767 character paths. - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath - /// - internal static readonly string LongPathPrefix = @"\\?\"; - - /// - /// This is the special prefix to prepend to UNC paths to support up to 32,767 character paths. - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath - /// - internal static readonly string LongPathPrefixUNC = @"\\?\UNC\"; - - internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - - [StructLayout(LayoutKind.Sequential)] - internal class SECURITY_ATTRIBUTES - { - //internal unsafe byte* pSecurityDescriptor = (byte*)null; - internal IntPtr pSecurityDescriptor; - internal int nLength; - internal int bInheritHandle; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FILETIME - { - internal uint dwLowDateTime; - internal uint dwHighDateTime; - }; - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct WIN32_FIND_DATA - { - internal EFileAttributes dwFileAttributes; - internal FILETIME ftCreationTime; - internal FILETIME ftLastAccessTime; - internal FILETIME ftLastWriteTime; - internal int nFileSizeHigh; - internal int nFileSizeLow; - internal int dwReserved0; - internal int dwReserved1; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] - internal string cFileName; - // not using this - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] - internal string cAlternate; - } - - [Flags] - internal enum EFileAccess : uint - { - FILE_READ_ATTRIBUTES = 0x00000080, - FILE_WRITE_ATTRIBUTES = 0x00000100, - GenericRead = 0x80000000, - GenericWrite = 0x40000000, - GenericExecute = 0x20000000, - GenericAll = 0x10000000 - } - - [Flags] - internal enum EFileShare : uint - { - None = 0x00000000, - Read = 0x00000001, - Write = 0x00000002, - Delete = 0x00000004 - } - - internal enum ECreationDisposition : uint - { - New = 1, - CreateAlways = 2, - OpenExisting = 3, - OpenAlways = 4, - TruncateExisting = 5 - } - - [Flags] - internal enum EFileAttributes : uint - { - None = 0x0, - Readonly = 0x00000001, - Hidden = 0x00000002, - System = 0x00000004, - Directory = 0x00000010, - Archive = 0x00000020, - Device = 0x00000040, - Normal = 0x00000080, - Temporary = 0x00000100, - SparseFile = 0x00000200, - ReparsePoint = 0x00000400, - Compressed = 0x00000800, - Offline = 0x00001000, - NotContentIndexed = 0x00002000, - Encrypted = 0x00004000, - Write_Through = 0x80000000, - Overlapped = 0x40000000, - NoBuffering = 0x20000000, - RandomAccess = 0x10000000, - SequentialScan = 0x08000000, - DeleteOnClose = 0x04000000, - BackupSemantics = 0x02000000, - PosixSemantics = 0x01000000, - OpenReparsePoint = 0x00200000, - OpenNoRecall = 0x00100000, - FirstPipeInstance = 0x00080000 - } - - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855%28v=vs.85%29.aspx - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)] - internal static extern bool CreateDirectory(string path, SECURITY_ATTRIBUTES lpSecurityAttributes); - - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - internal static extern IntPtr FindFirstFile(string lpFileName, out - WIN32_FIND_DATA lpFindFileData); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - internal static extern bool FindNextFile(IntPtr hFindFile, out - WIN32_FIND_DATA lpFindFileData); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool FindClose(IntPtr hFindFile); - - - - - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365535%28v=vs.85%29.aspx - /// - [DllImport("kernel32.dll", EntryPoint = "SetFileAttributes", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)] - internal static extern bool SetFileAttributes(string lpFileName, uint dwFileAttributes); - - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364944%28v=vs.85%29.aspx - /// - /// - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - internal static extern uint GetFileAttributes(string lpFileName); - - // Invalid is from C:\Program Files (x86)\Windows Kits\8.1\Include\um\fileapi.h - //#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) - internal static readonly uint INVALID_FILE_ATTRIBUTES = 0xffffffff; - - - [DllImport("kernel32.dll", EntryPoint = "RemoveDirectory", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)] - internal static extern bool RemoveDirectory(string lpPathName); - - [DllImport("kernel32.dll", EntryPoint = "DeleteFile", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)] - internal static extern bool DeleteFile(string path); - - /// - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx - /// - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern SafeFileHandle CreateFile(string lpFileName, - EFileAccess dwDesiredAccess, - EFileShare dwShareMode, - IntPtr lpSecurityAttributes, - ECreationDisposition dwCreationDisposition, - EFileAttributes dwFlagsAndAttributes, - IntPtr hTemplateFile); - - // This binding only allows setting creation and last write times. - // The last access time parameter must be zero; that time is not - // modified. - [DllImport("kernel32.dll", SetLastError = true)] - internal static extern bool SetFileTime( - IntPtr hFile, - ref long lpCreationTime, - IntPtr lpLastAccessTime, - ref long lpLastWriteTime); - - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363851%28v=vs.85%29.aspx - /// - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - internal static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists); - - - internal const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; - - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351%28v=vs.85%29.aspx - /// - [DllImport("kernel32.dll")] - internal static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, [Out] System.Text.StringBuilder lpBuffer, uint nSize, IntPtr Arguments); - } -} diff --git a/BurnOutSharp/External/LessIO.bak/Strategies/Win32/Win32FileSystemStrategy.cs b/BurnOutSharp/External/LessIO.bak/Strategies/Win32/Win32FileSystemStrategy.cs deleted file mode 100644 index 5c8c8eb9..00000000 --- a/BurnOutSharp/External/LessIO.bak/Strategies/Win32/Win32FileSystemStrategy.cs +++ /dev/null @@ -1,230 +0,0 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using BadPath = System.IO.Path; - -namespace LessIO.Strategies.Win32 -{ - internal sealed class Win32FileSystemStrategy : FileSystemStrategy - { - public Win32FileSystemStrategy() - { - } - - private static Exception CreateWin32LastErrorException(string userMessage, params object[] args) - { - uint lastError = (uint)Marshal.GetLastWin32Error(); - - const int bufferByteCapacity = 1024 * 32; - StringBuilder buffer = new StringBuilder(bufferByteCapacity);// NOTE: capacity here will be interpreted by StringBuilder as chars not bytes, but that's ok since it is definitely bigger in byte terms. - uint bufferCharLength = NativeMethods.FormatMessage(NativeMethods.FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, lastError, 0, buffer, bufferByteCapacity, IntPtr.Zero); - - string systemErrorMessage; - if (bufferCharLength == 0) - { //FormatMessage failed: - systemErrorMessage = string.Format("Error code=0x{1:x8}", lastError); - } - else - { - Debug.Assert(bufferCharLength < buffer.Capacity, "unexpected result capacity"); - char[] systemChars = new char[bufferCharLength]; - buffer.CopyTo(0, systemChars, 0, (int)bufferCharLength); - systemErrorMessage = new string(systemChars); - } - - var formattedUserMessage = string.Format(userMessage, args); - return new Exception(formattedUserMessage + " System error information:'" + systemErrorMessage + "'"); - } - - public override void SetLastWriteTime(Path path, DateTime lastWriteTime) - { - SafeFileHandle h = NativeMethods.CreateFile( - path.WithWin32LongPathPrefix(), - NativeMethods.EFileAccess.FILE_READ_ATTRIBUTES | NativeMethods.EFileAccess.FILE_WRITE_ATTRIBUTES, - NativeMethods.EFileShare.Read | NativeMethods.EFileShare.Write| NativeMethods.EFileShare.Delete, - IntPtr.Zero,// See https://github.com/activescott/LessIO/issues/4 before changing these flags. - NativeMethods.ECreationDisposition.OpenExisting, - NativeMethods.EFileAttributes.None, - IntPtr.Zero); - using (h) - { - if (h.IsInvalid) - throw CreateWin32LastErrorException("Error opening file {0}.", path); - var modifiedTime = lastWriteTime.ToFileTime(); - if (!NativeMethods.SetFileTime( - h.DangerousGetHandle(), - ref modifiedTime, IntPtr.Zero, ref modifiedTime)) - throw CreateWin32LastErrorException("Error setting times for {0}.", path); - } - } - - public override void SetAttributes(Path path, FileAttributes fileAttributes) - { - var result = NativeMethods.SetFileAttributes(path.WithWin32LongPathPrefix(), (uint)fileAttributes); - if (!result) - { - throw CreateWin32LastErrorException("Error setting file attributes on '{0}'.", path); - } - } - - public override FileAttributes GetAttributes(Path path) - { - var result = NativeMethods.GetFileAttributes(path.WithWin32LongPathPrefix()); - if (result == NativeMethods.INVALID_FILE_ATTRIBUTES) - { - throw CreateWin32LastErrorException("Error getting file attributes for '{0}'.", path); - } - return (FileAttributes)result; - } - - public override void CreateDirectory(Path path) - { - // Since System.IO.Directory.Create() creates all neecessary directories, we emulate here: - string pathString = path.PathString; - var dirsToCreate = new List(); - int lengthRoot = path.PathRoot.Length; - - var firstNonRootPathIndex = pathString.IndexOfAny(Path.DirectorySeperatorChars, lengthRoot); - if (firstNonRootPathIndex == -1) - { - // this is a directory directly off of the root (because no, non-root directory seperators exist) - firstNonRootPathIndex = pathString.Length - 1; // set it to the whole path so that the loop below will create the root dir - } - var i = firstNonRootPathIndex; - while (i < pathString.Length) - { - if (Path.IsDirectorySeparator(pathString[i]) || i == pathString.Length - 1) - { - Path currentPath = new Path(pathString.Substring(0, i + 1)); - if (!Exists(currentPath)) - { - bool succeeded = NativeMethods.CreateDirectory(currentPath.WithWin32LongPathPrefix(), null); - if (!succeeded) - throw CreateWin32LastErrorException("Error creating directory '{0}'.", currentPath); - } - Debug.Assert(Exists(currentPath), "path should always exists at this point!"); - } - i++; - } - } - - public override bool Exists(Path path) - { - var result = NativeMethods.GetFileAttributes(path.WithWin32LongPathPrefix()); - return (result != NativeMethods.INVALID_FILE_ATTRIBUTES); - } - - public override void RemoveDirectory(Path path, bool recursively) - { - if (recursively) - { // first gather contents and remove all content - RemoveDirectoryRecursively(path); - } - else - { - var succeeded = NativeMethods.RemoveDirectory(path.WithWin32LongPathPrefix()); - if (!succeeded) - throw CreateWin32LastErrorException("Error removing directory '{0}'.", path); - } - } - - private void RemoveDirectoryRecursively(Path dirName) - { - var list = ListContents(dirName, true).ToArray(); - /* - We need to delete leaf nodes in the file hierarchy first to make sure all directories are empty. - We identify leaf nodes by their depth (greatest number of path parts) - */ - Func pathPartCount = (Path p) => p.PathString.Split(Path.DirectorySeperatorChars).Length; - Array.Sort(list, (a,b) => pathPartCount(b) - pathPartCount(a)); - Array.ForEach(list - , p => { - if (FileSystem.IsDirectory(p)) - FileSystem.RemoveDirectory(p); - else - FileSystem.RemoveFile(p, true); - Debug.Assert(FileSystem.Exists(p) == false, string.Format("Files/directory still exits:'{0}'", p)); - } - ); - Debug.Assert(list.All(p => FileSystem.Exists(p) == false), "Some files/directories still exits?"); - FileSystem.RemoveDirectory(dirName, false); - } - - public override void RemoveFile(Path path, bool force) - { - if (IsReadOnly(path) && force) - SetReadOnly(path, false); - - var succeeded = NativeMethods.DeleteFile(path.WithWin32LongPathPrefix()); - if (!succeeded) - throw CreateWin32LastErrorException("Error deleting file '{0}'.", path); - } - - public override void Copy(Path source, Path dest) - { - var succeeded = NativeMethods.CopyFile(source.WithWin32LongPathPrefix(), dest.WithWin32LongPathPrefix(), true); - if (!succeeded) - throw CreateWin32LastErrorException("Error copying file '{0}' to '{1}'.", source, dest); - } - - /// - /// Creates or overwrites the file at the specified path. - /// - /// The path and name of the file to create. Supports long file paths. - /// A that provides read/write access to the file specified in path. - public override System.IO.Stream CreateFile(Path path) - { - if (path.IsEmpty) - throw new ArgumentNullException("path"); - - NativeMethods.EFileAccess fileAccess = NativeMethods.EFileAccess.GenericWrite | NativeMethods.EFileAccess.GenericRead; - NativeMethods.EFileShare fileShareMode = NativeMethods.EFileShare.None;//exclusive - NativeMethods.ECreationDisposition creationDisposition = NativeMethods.ECreationDisposition.CreateAlways; - SafeFileHandle hFile = NativeMethods.CreateFile(path.WithWin32LongPathPrefix(), fileAccess, fileShareMode, IntPtr.Zero, creationDisposition, NativeMethods.EFileAttributes.Normal, IntPtr.Zero); - if (hFile.IsInvalid) - throw CreateWin32LastErrorException("Error creating file at path '{0}'.", path); - return new System.IO.FileStream(hFile, System.IO.FileAccess.ReadWrite); - } - - public override IEnumerable ListContents(Path directory) - { - //NOTE: An important part of our contract is that if directory is not a directory, we return an empty set: - if (!Exists(directory) || !IsDirectory(directory)) - yield break; - - // normalize dirName so we can assume it doesn't have a slash on the end: - string dirName = directory.PathString; - dirName = dirName.TrimEnd(Path.DirectorySeperatorChars); - dirName = new Path(dirName).WithWin32LongPathPrefix(); - - NativeMethods.WIN32_FIND_DATA findData; - IntPtr findHandle = NativeMethods.FindFirstFile(dirName + @"\*", out findData); - if (findHandle != NativeMethods.INVALID_HANDLE_VALUE) - { - try - { - bool found; - do - { - string currentFileName = findData.cFileName; - if (currentFileName != "." && currentFileName != "..") - { - //NOTE: Instantiating the Path here will strip off the Win32 prefix that is added here as needed: - yield return Path.Combine(dirName, currentFileName); - } - // find next - found = NativeMethods.FindNextFile(findHandle, out findData); - } while (found); - } - finally - { - NativeMethods.FindClose(findHandle); - } - } - } - } -} diff --git a/Test/Test.csproj b/Test/Test.csproj index 47a8c403..c233b188 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -1,7 +1,7 @@  - net48;netcoreapp3.1;net5.0-windows + net48;netcoreapp3.1;net5.0 x86 false Exe diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..b7ac8f2e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,50 @@ +# version format +version: 1.5.1-{build} + +# pull request template +pull_requests: + do_not_increment_build_number: true + +# vm template +image: Visual Studio 2019 + +# environment variables +environment: + EnableNuGetPackageRestore: true + +# msbuild configuration +platform: +- Any CPU +configuration: +- Debug + +# install dependencies +install: +- ps: appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe +- cd %APPVEYOR_BUILD_FOLDER% +- git submodule update --init --recursive + +# pre-build script +before_build: +- nuget restore + +# build step +build: + verbosity: minimal + project: BurnOutSharp.sln + +# post-build step +after_build: +- cd Test\bin\Debug +- 7z a BurnOutSharp_net48.zip net48\* +- 7z a BurnOutSharp_netcoreapp3.1.zip netcoreapp3.1\* +- 7z a BurnOutSharp_net5.0.zip net5.0\* + +# artifact linking +artifacts: +- path: Test\bin\Debug\BurnOutSharp_net48.zip + name: BurnOutSharp (.NET Framework 4.8) +- path: Test\bin\Debug\BurnOutSharp_netcoreapp3.1.zip + name: BurnOutSharp (.NET Core 3.1) +- path: Test\bin\Debug\BurnOutSharp_net5.0.zip + name: BurnOutSharp (.NET 5.0) \ No newline at end of file