607 lines
22 KiB
C#
607 lines
22 KiB
C#
/******************************************************
|
|
* ROMVault2 is written by Gordon J. *
|
|
* Contact gordon@romvault.com *
|
|
* Copyright 2014 *
|
|
******************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using Microsoft.Win32.SafeHandles;
|
|
|
|
namespace ROMVault2.IO
|
|
{
|
|
[Flags]
|
|
[ComVisible(true)]
|
|
[Serializable]
|
|
public enum FileAttributes
|
|
{
|
|
ReadOnly = 1,
|
|
Hidden = 2,
|
|
System = 4,
|
|
Directory = 16,
|
|
Archive = 32,
|
|
Device = 64,
|
|
Normal = 128,
|
|
Temporary = 256,
|
|
SparseFile = 512,
|
|
ReparsePoint = 1024,
|
|
Compressed = 2048,
|
|
Offline = 4096,
|
|
NotContentIndexed = 8192,
|
|
Encrypted = 16384,
|
|
}
|
|
public static class Error
|
|
{
|
|
public static int GetLastError()
|
|
{
|
|
return Marshal.GetLastWin32Error();
|
|
}
|
|
}
|
|
|
|
public class FileInfo
|
|
{
|
|
|
|
public string Name;
|
|
public string FullName;
|
|
public long LastWriteTime;
|
|
public long Length;
|
|
public int fileAttributes;
|
|
|
|
public bool isHidden
|
|
{
|
|
get { return (fileAttributes & Win32Native.FILE_ATTRIBUTE_HIDDEN) != 0; }
|
|
}
|
|
|
|
public FileInfo()
|
|
{ }
|
|
|
|
public FileInfo(string path)
|
|
{
|
|
FullName = path;
|
|
Name = Path.GetFileName(path);
|
|
|
|
string fileName = NameFix.AddLongPathPrefix(path);
|
|
Win32Native.WIN32_FILE_ATTRIBUTE_DATA wIn32FileAttributeData = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
|
|
|
|
bool b = Win32Native.GetFileAttributesEx(fileName, 0, ref wIn32FileAttributeData);
|
|
|
|
if (!b || (wIn32FileAttributeData.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0) return;
|
|
|
|
Length = Convert.Length(wIn32FileAttributeData.fileSizeHigh, wIn32FileAttributeData.fileSizeLow);
|
|
LastWriteTime = Convert.Time(wIn32FileAttributeData.ftLastWriteTimeHigh, wIn32FileAttributeData.ftLastWriteTimeLow);
|
|
fileAttributes = wIn32FileAttributeData.fileAttributes;
|
|
}
|
|
|
|
}
|
|
|
|
public class DirectoryInfo
|
|
{
|
|
public string Name;
|
|
public string FullName;
|
|
public long LastWriteTime;
|
|
public int fileAttributes;
|
|
|
|
public bool isHidden
|
|
{
|
|
get { return (fileAttributes & Win32Native.FILE_ATTRIBUTE_HIDDEN) != 0; }
|
|
}
|
|
|
|
public DirectoryInfo()
|
|
{ }
|
|
public DirectoryInfo(string path)
|
|
{
|
|
FullName = path;
|
|
Name = Path.GetFileName(path);
|
|
|
|
string fileName = NameFix.AddLongPathPrefix(path);
|
|
Win32Native.WIN32_FILE_ATTRIBUTE_DATA wIn32FileAttributeData = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
|
|
|
|
bool b = Win32Native.GetFileAttributesEx(fileName, 0, ref wIn32FileAttributeData);
|
|
|
|
if (!b || (wIn32FileAttributeData.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) == 0) return;
|
|
LastWriteTime = Convert.Time(wIn32FileAttributeData.ftLastWriteTimeHigh, wIn32FileAttributeData.ftLastWriteTimeLow);
|
|
fileAttributes = wIn32FileAttributeData.fileAttributes;
|
|
}
|
|
|
|
|
|
|
|
public DirectoryInfo[] GetDirectories(bool includeHidden = true)
|
|
{
|
|
return GetDirectories("*", includeHidden);
|
|
}
|
|
public DirectoryInfo[] GetDirectories(string SearchPattern, bool includeHidden = true)
|
|
{
|
|
List<DirectoryInfo> dirs = new List<DirectoryInfo>();
|
|
|
|
string dirName = NameFix.AddLongPathPrefix(FullName);
|
|
|
|
Win32Native.WIN32_FIND_DATA findData = new Win32Native.WIN32_FIND_DATA();
|
|
SafeFindHandle findHandle = Win32Native.FindFirstFile(dirName + @"\" + SearchPattern, findData);
|
|
|
|
if (!findHandle.IsInvalid)
|
|
{
|
|
do
|
|
{
|
|
string currentFileName = findData.cFileName;
|
|
|
|
// if this is a directory, find its contents
|
|
if ((findData.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) == 0) continue;
|
|
if (currentFileName == "." || currentFileName == "..") continue;
|
|
if (!includeHidden && (findData.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_HIDDEN) != 0) continue;
|
|
|
|
DirectoryInfo di = new DirectoryInfo
|
|
{
|
|
Name = currentFileName,
|
|
FullName = Path.Combine(FullName, currentFileName),
|
|
LastWriteTime = Convert.Time(findData.ftLastWriteTimeHigh, findData.ftLastWriteTimeLow),
|
|
fileAttributes = findData.dwFileAttributes
|
|
};
|
|
dirs.Add(di);
|
|
}
|
|
while (Win32Native.FindNextFile(findHandle, findData));
|
|
}
|
|
|
|
// close the find handle
|
|
findHandle.Dispose();
|
|
|
|
return dirs.ToArray();
|
|
}
|
|
|
|
public FileInfo[] GetFiles()
|
|
{
|
|
return GetFiles("*");
|
|
}
|
|
public FileInfo[] GetFiles(string SearchPattern, bool includeHidden = true)
|
|
{
|
|
List<FileInfo> files = new List<FileInfo>();
|
|
|
|
string dirName = NameFix.AddLongPathPrefix(FullName);
|
|
|
|
Win32Native.WIN32_FIND_DATA findData = new Win32Native.WIN32_FIND_DATA();
|
|
SafeFindHandle findHandle = Win32Native.FindFirstFile(dirName + @"\" + SearchPattern, findData);
|
|
|
|
if (!findHandle.IsInvalid)
|
|
{
|
|
do
|
|
{
|
|
string currentFileName = findData.cFileName;
|
|
|
|
// if this is a directory, find its contents
|
|
if ((findData.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0) continue;
|
|
if (!includeHidden && (findData.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_HIDDEN) != 0) continue;
|
|
|
|
FileInfo fi = new FileInfo
|
|
{
|
|
Name = currentFileName,
|
|
FullName = Path.Combine(FullName, currentFileName),
|
|
Length = Convert.Length(findData.nFileSizeHigh, findData.nFileSizeLow),
|
|
LastWriteTime = Convert.Time(findData.ftLastWriteTimeHigh, findData.ftLastWriteTimeLow),
|
|
fileAttributes = findData.dwFileAttributes
|
|
};
|
|
files.Add(fi);
|
|
}
|
|
while (Win32Native.FindNextFile(findHandle, findData));
|
|
}
|
|
|
|
// close the find handle
|
|
findHandle.Dispose();
|
|
|
|
return files.ToArray();
|
|
}
|
|
}
|
|
|
|
public static class Directory
|
|
{
|
|
public static bool Exists(string path)
|
|
{
|
|
string fixPath = NameFix.AddLongPathPrefix(path);
|
|
|
|
Win32Native.WIN32_FILE_ATTRIBUTE_DATA wIn32FileAttributeData = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
|
|
|
|
bool b = Win32Native.GetFileAttributesEx(fixPath, 0, ref wIn32FileAttributeData);
|
|
return b && (wIn32FileAttributeData.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
}
|
|
public static void Move(String sourceDirName, String destDirName)
|
|
{
|
|
if (sourceDirName == null)
|
|
throw new ArgumentNullException("sourceDirName");
|
|
if (sourceDirName.Length == 0)
|
|
throw new ArgumentException("Argument_EmptyFileName", "sourceDirName");
|
|
|
|
if (destDirName == null)
|
|
throw new ArgumentNullException("destDirName");
|
|
if (destDirName.Length == 0)
|
|
throw new ArgumentException("Argument_EmptyFileName", "destDirName");
|
|
|
|
String fullsourceDirName = NameFix.AddLongPathPrefix(sourceDirName);
|
|
|
|
String fulldestDirName = NameFix.AddLongPathPrefix(destDirName);
|
|
|
|
if (!Win32Native.MoveFile(fullsourceDirName, fulldestDirName))
|
|
{
|
|
int hr = Marshal.GetLastWin32Error();
|
|
if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // Source dir not found
|
|
{
|
|
throw new Exception("ERROR_PATH_NOT_FOUND " + fullsourceDirName);
|
|
}
|
|
if (hr == Win32Native.ERROR_ACCESS_DENIED) // WinNT throws IOException. This check is for Win9x. We can't change it for backcomp.
|
|
{
|
|
throw new Exception("UnauthorizedAccess_IODenied_Path" + sourceDirName);
|
|
}
|
|
}
|
|
}
|
|
public static void Delete(String path)
|
|
{
|
|
String fullPath = NameFix.AddLongPathPrefix(path);
|
|
|
|
Win32Native.RemoveDirectory(fullPath);
|
|
}
|
|
|
|
public static void CreateDirectory(String path)
|
|
{
|
|
if (path == null)
|
|
throw new ArgumentNullException("path");
|
|
if (path.Length == 0)
|
|
throw new ArgumentException("Argument_PathEmpty");
|
|
|
|
String fullPath = NameFix.AddLongPathPrefix(path);
|
|
|
|
Win32Native.CreateDirectory(fullPath, IntPtr.Zero);
|
|
}
|
|
}
|
|
|
|
public static class File
|
|
{
|
|
public static bool Exists(string path)
|
|
{
|
|
string fixPath = NameFix.AddLongPathPrefix(path);
|
|
|
|
Win32Native.WIN32_FILE_ATTRIBUTE_DATA wIn32FileAttributeData = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
|
|
|
|
bool b = Win32Native.GetFileAttributesEx(fixPath, 0, ref wIn32FileAttributeData);
|
|
return b && (wIn32FileAttributeData.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) == 0;
|
|
}
|
|
public static void Copy(String sourceFileName, string destfileName)
|
|
{
|
|
Copy(sourceFileName, destfileName, true);
|
|
}
|
|
public static void Copy(String sourceFileName, String destFileName, bool overwrite)
|
|
{
|
|
if (sourceFileName == null || destFileName == null)
|
|
throw new ArgumentNullException((sourceFileName == null ? "sourceFileName" : "destFileName"), "ArgumentNull_FileName");
|
|
if (sourceFileName.Length == 0 || destFileName.Length == 0)
|
|
throw new ArgumentException("Argument_EmptyFileName", (sourceFileName.Length == 0 ? "sourceFileName" : "destFileName"));
|
|
|
|
String fullSourceFileName = NameFix.AddLongPathPrefix(sourceFileName);
|
|
String fullDestFileName = NameFix.AddLongPathPrefix(destFileName);
|
|
|
|
bool r = Win32Native.CopyFile(fullSourceFileName, fullDestFileName, !overwrite);
|
|
if (!r)
|
|
{
|
|
// Save Win32 error because subsequent checks will overwrite this HRESULT.
|
|
int errorCode = Marshal.GetLastWin32Error();
|
|
String fileName = destFileName;
|
|
|
|
/*
|
|
if (errorCode != Win32Native.ERROR_FILE_EXISTS)
|
|
{
|
|
// For a number of error codes (sharing violation, path
|
|
// not found, etc) we don't know if the problem was with
|
|
// the source or dest file. Try reading the source file.
|
|
using (SafeFileHandle handle = Win32Native.UnsafeCreateFile(fullSourceFileName, FileStream.GENERIC_READ, FileShare.Read, null, FileMode.Open, 0, IntPtr.Zero))
|
|
{
|
|
if (handle.IsInvalid)
|
|
fileName = sourceFileName;
|
|
}
|
|
|
|
if (errorCode == Win32Native.ERROR_ACCESS_DENIED)
|
|
{
|
|
if (Directory.InternalExists(fullDestFileName))
|
|
throw new IOException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Arg_FileIsDirectory_Name"), destFileName), Win32Native.ERROR_ACCESS_DENIED, fullDestFileName);
|
|
}
|
|
}
|
|
|
|
__Error.WinIOError(errorCode, fileName);
|
|
|
|
*/
|
|
}
|
|
}
|
|
public static void Move(String sourceFileName, String destFileName)
|
|
{
|
|
if (sourceFileName == null || destFileName == null)
|
|
throw new ArgumentNullException((sourceFileName == null ? "sourceFileName" : "destFileName"), "ArgumentNull_FileName");
|
|
if (sourceFileName.Length == 0 || destFileName.Length == 0)
|
|
throw new ArgumentException("Argument_EmptyFileName", (sourceFileName.Length == 0 ? "sourceFileName" : "destFileName"));
|
|
|
|
String fullSourceFileName = NameFix.AddLongPathPrefix(sourceFileName);
|
|
String fullDestFileName = NameFix.AddLongPathPrefix(destFileName);
|
|
|
|
if (!Exists(fullSourceFileName))
|
|
throw new Exception("ERROR_FILE_NOT_FOUND" + fullSourceFileName);
|
|
|
|
if (!Win32Native.MoveFile(fullSourceFileName, fullDestFileName))
|
|
{
|
|
int hr = Marshal.GetLastWin32Error();
|
|
throw new Exception("ERROR_MOVING_FILE. Error Code ("+hr+")");
|
|
}
|
|
}
|
|
public static void Delete(String path)
|
|
{
|
|
string fixPath = NameFix.AddLongPathPrefix(path);
|
|
|
|
if (!Win32Native.DeleteFile(fixPath))
|
|
{
|
|
int hr = Marshal.GetLastWin32Error();
|
|
if (hr != Win32Native.ERROR_FILE_NOT_FOUND)
|
|
throw new Exception("Error while deleting file :" + path);
|
|
}
|
|
}
|
|
|
|
public static bool SetAttributes(String path, FileAttributes fileAttributes)
|
|
{
|
|
String fullPath = NameFix.AddLongPathPrefix(path);
|
|
return Win32Native.SetFileAttributes(fullPath, (int)fileAttributes);
|
|
}
|
|
|
|
|
|
private const int ERROR_INVALID_PARAMETER = 87;
|
|
private const int ERROR_ACCESS_DENIED = 0x5;
|
|
}
|
|
|
|
public static class Path
|
|
{
|
|
public static readonly char DirectorySeparatorChar = '\\';
|
|
public static readonly char AltDirectorySeparatorChar = '/';
|
|
public static readonly char VolumeSeparatorChar = ':';
|
|
|
|
public static string GetExtension(String path)
|
|
{
|
|
return System.IO.Path.GetExtension(path);
|
|
}
|
|
public static string Combine(string path1, string path2)
|
|
{
|
|
if (path1 == null || path2 == null)
|
|
throw new ArgumentNullException((path1 == null) ? "path1" : "path2");
|
|
//CheckInvalidPathChars(path1);
|
|
//CheckInvalidPathChars(path2);
|
|
|
|
if (path2.Length == 0)
|
|
return path1;
|
|
|
|
if (path1.Length == 0)
|
|
return path2;
|
|
|
|
if (IsPathRooted(path2))
|
|
return path2;
|
|
|
|
char ch = path1[path1.Length - 1];
|
|
if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar)
|
|
return path1 + DirectorySeparatorChar + path2;
|
|
return path1 + path2;
|
|
}
|
|
private static bool IsPathRooted(String path)
|
|
{
|
|
if (path != null)
|
|
{
|
|
//CheckInvalidPathChars(path);
|
|
|
|
int length = path.Length;
|
|
if (
|
|
(length >= 1 && (path[0] == DirectorySeparatorChar ||
|
|
path[0] == AltDirectorySeparatorChar)) ||
|
|
(length >= 2 && path[1] == VolumeSeparatorChar)
|
|
) return true;
|
|
}
|
|
return false;
|
|
}
|
|
/*
|
|
private static void CheckInvalidPathChars(string path)
|
|
{
|
|
for (int index = 0; index < path.Length; ++index)
|
|
{
|
|
int num = path[index];
|
|
switch (num)
|
|
{
|
|
case 34:
|
|
case 60:
|
|
case 62:
|
|
case 124:
|
|
ReportError.SendErrorMessage("Invalid Character " + num + " in filename " + path);
|
|
continue;
|
|
default:
|
|
if (num >= 32)
|
|
continue;
|
|
|
|
goto case 34;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
public static string GetFileNameWithoutExtension(string path)
|
|
{
|
|
return System.IO.Path.GetFileNameWithoutExtension(path);
|
|
}
|
|
|
|
public static string GetFileName(String path)
|
|
{
|
|
return System.IO.Path.GetFileName(path);
|
|
}
|
|
public static String GetDirectoryName(String path)
|
|
{
|
|
if (path != null)
|
|
{
|
|
int root = GetRootLength(path);
|
|
int i = path.Length;
|
|
if (i > root)
|
|
{
|
|
i = path.Length;
|
|
if (i == root) return null;
|
|
while (i > root && path[--i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar) ;
|
|
return path.Substring(0, i);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static int GetRootLength(String path)
|
|
{
|
|
int i = 0;
|
|
int length = path.Length;
|
|
|
|
if (length >= 1 && (IsDirectorySeparator(path[0])))
|
|
{
|
|
// handles UNC names and directories off current drive's root.
|
|
i = 1;
|
|
if (length >= 2 && (IsDirectorySeparator(path[1])))
|
|
{
|
|
i = 2;
|
|
int n = 2;
|
|
while (i < length && ((path[i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar) || --n > 0)) i++;
|
|
}
|
|
}
|
|
else if (length >= 2 && path[1] == VolumeSeparatorChar)
|
|
{
|
|
// handles A:\foo.
|
|
i = 2;
|
|
if (length >= 3 && (IsDirectorySeparator(path[2]))) i++;
|
|
}
|
|
return i;
|
|
}
|
|
private static bool IsDirectorySeparator(char c)
|
|
{
|
|
return (c == DirectorySeparatorChar || c == AltDirectorySeparatorChar);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public static class FileStream
|
|
{
|
|
private const uint GENERIC_READ = 0x80000000;
|
|
private const uint GENERIC_WRITE = 0x40000000;
|
|
|
|
private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
|
|
|
|
// errorMessage = new Win32Exception(errorCode).Message;
|
|
|
|
public static int OpenFileRead(string path, out System.IO.Stream stream)
|
|
{
|
|
|
|
string filename = NameFix.AddLongPathPrefix(path);
|
|
SafeFileHandle hFile = Win32Native.CreateFile(filename,
|
|
GENERIC_READ,
|
|
System.IO.FileShare.Read,
|
|
IntPtr.Zero,
|
|
System.IO.FileMode.Open,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
IntPtr.Zero);
|
|
|
|
if (hFile.IsInvalid)
|
|
{
|
|
stream = null;
|
|
return Marshal.GetLastWin32Error();
|
|
}
|
|
stream = new System.IO.FileStream(hFile, System.IO.FileAccess.Read);
|
|
return 0;
|
|
}
|
|
|
|
public static int OpenFileWrite(string path, out System.IO.Stream stream)
|
|
{
|
|
string filename = NameFix.AddLongPathPrefix(path);
|
|
SafeFileHandle hFile = Win32Native.CreateFile(filename,
|
|
GENERIC_WRITE,
|
|
System.IO.FileShare.None,
|
|
IntPtr.Zero,
|
|
System.IO.FileMode.Create,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
IntPtr.Zero);
|
|
|
|
if (hFile.IsInvalid)
|
|
{
|
|
stream = null;
|
|
return Marshal.GetLastWin32Error();
|
|
}
|
|
|
|
stream = new System.IO.FileStream(hFile, System.IO.FileAccess.Write);
|
|
return 0;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public static class NameFix
|
|
{
|
|
public static string GetShortPath(string path)
|
|
{
|
|
int remove = 0;
|
|
string retPath;
|
|
if (path.StartsWith(@"\\"))
|
|
{
|
|
retPath = @"\\?\UNC\" + path.Substring(2);
|
|
remove = 8;
|
|
}
|
|
else
|
|
{
|
|
retPath = path;
|
|
if (path.Substring(1, 1) != ":")
|
|
retPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), retPath);
|
|
|
|
retPath = cleandots(retPath);
|
|
retPath = @"\\?\" + retPath;
|
|
remove = 4;
|
|
}
|
|
|
|
|
|
const int MAX_PATH = 300;
|
|
var shortPath = new StringBuilder(MAX_PATH);
|
|
Win32Native.GetShortPathName(retPath, shortPath, MAX_PATH);
|
|
retPath = shortPath.ToString();
|
|
|
|
retPath = retPath.Substring(remove);
|
|
if (remove == 8) retPath = "\\" + retPath;
|
|
|
|
return retPath;
|
|
}
|
|
|
|
|
|
|
|
internal static string AddLongPathPrefix(string path)
|
|
{
|
|
if (string.IsNullOrEmpty(path) || path.StartsWith(@"\\?\"))
|
|
return path;
|
|
|
|
if (path.StartsWith(@"\\"))
|
|
return @"\\?\UNC\" + path.Substring(2);
|
|
|
|
string retPath = path;
|
|
if (path.Substring(1, 1) != ":")
|
|
retPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), retPath);
|
|
|
|
retPath = cleandots(retPath);
|
|
|
|
return @"\\?\" + retPath;
|
|
|
|
}
|
|
|
|
private static string cleandots(string path)
|
|
{
|
|
string retPath = path;
|
|
while (retPath.Contains(@"\..\"))
|
|
{
|
|
int index = retPath.IndexOf(@"\..\");
|
|
string path1 = retPath.Substring(0, index);
|
|
string path2 = retPath.Substring(index + 4);
|
|
|
|
int path1Back = path1.LastIndexOf(@"\");
|
|
|
|
retPath = path1.Substring(0, path1Back + 1) + path2;
|
|
}
|
|
return retPath;
|
|
|
|
}
|
|
}
|
|
}
|