mirror of
https://github.com/holub/mame
synced 2025-04-30 11:50:30 +03:00
588 lines
12 KiB
C++
588 lines
12 KiB
C++
// Windows/FileDir.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#ifndef _UNICODE
|
|
#include "../Common/StringConvert.h"
|
|
#endif
|
|
|
|
#include "FileDir.h"
|
|
#include "FileFind.h"
|
|
#include "FileName.h"
|
|
|
|
#ifndef _UNICODE
|
|
extern bool g_IsNT;
|
|
#endif
|
|
|
|
namespace NWindows {
|
|
namespace NFile {
|
|
|
|
// SetCurrentDirectory doesn't support \\?\ prefix
|
|
|
|
#ifdef WIN_LONG_PATH
|
|
bool GetLongPathBase(CFSTR fileName, UString &res);
|
|
bool GetLongPath(CFSTR fileName, UString &res);
|
|
#endif
|
|
|
|
namespace NDirectory {
|
|
|
|
#ifndef UNDER_CE
|
|
|
|
bool MyGetWindowsDirectory(FString &path)
|
|
{
|
|
UINT needLength;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetWindowsDirectory(s, MAX_PATH + 1);
|
|
path = fas2fs(s);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
WCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1);
|
|
path = us2fs(s);
|
|
}
|
|
return (needLength > 0 && needLength <= MAX_PATH);
|
|
}
|
|
|
|
|
|
bool MyGetSystemDirectory(FString &path)
|
|
{
|
|
UINT needLength;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetSystemDirectory(s, MAX_PATH + 1);
|
|
path = fas2fs(s);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
WCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1);
|
|
path = us2fs(s);
|
|
}
|
|
return (needLength > 0 && needLength <= MAX_PATH);
|
|
}
|
|
#endif
|
|
|
|
bool SetDirTime(CFSTR fileName, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return false;
|
|
}
|
|
#endif
|
|
HANDLE hDir = ::CreateFileW(fs2us(fileName), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
#ifdef WIN_LONG_PATH
|
|
if (hDir == INVALID_HANDLE_VALUE)
|
|
{
|
|
UString longPath;
|
|
if (GetLongPath(fileName, longPath))
|
|
hDir = ::CreateFileW(longPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
}
|
|
#endif
|
|
|
|
bool res = false;
|
|
if (hDir != INVALID_HANDLE_VALUE)
|
|
{
|
|
res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime));
|
|
::CloseHandle(hDir);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifdef WIN_LONG_PATH
|
|
bool GetLongPaths(CFSTR s1, CFSTR s2, UString &d1, UString &d2)
|
|
{
|
|
if (!GetLongPathBase(s1, d1) ||
|
|
!GetLongPathBase(s2, d2))
|
|
return false;
|
|
if (d1.IsEmpty() && d2.IsEmpty())
|
|
return false;
|
|
if (d1.IsEmpty()) d1 = fs2us(s1);
|
|
if (d2.IsEmpty()) d2 = fs2us(s2);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool MySetFileAttributes(CFSTR fileName, DWORD fileAttributes)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::SetFileAttributes(fs2fas(fileName), fileAttributes))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (::SetFileAttributesW(fs2us(fileName), fileAttributes))
|
|
return true;
|
|
#ifdef WIN_LONG_PATH
|
|
UString longPath;
|
|
if (GetLongPath(fileName, longPath))
|
|
return BOOLToBool(::SetFileAttributesW(longPath, fileAttributes));
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MyRemoveDirectory(CFSTR path)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::RemoveDirectory(fs2fas(path)))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (::RemoveDirectoryW(fs2us(path)))
|
|
return true;
|
|
#ifdef WIN_LONG_PATH
|
|
UString longPath;
|
|
if (GetLongPath(path, longPath))
|
|
return BOOLToBool(::RemoveDirectoryW(longPath));
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MyMoveFile(CFSTR existFileName, CFSTR newFileName)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::MoveFile(fs2fas(existFileName), fs2fas(newFileName)))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (::MoveFileW(fs2us(existFileName), fs2us(newFileName)))
|
|
return true;
|
|
#ifdef WIN_LONG_PATH
|
|
UString d1, d2;
|
|
if (GetLongPaths(existFileName, newFileName, d1, d2))
|
|
return BOOLToBool(::MoveFileW(d1, d2));
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MyCreateDirectory(CFSTR path)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::CreateDirectory(fs2fas(path), NULL))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (::CreateDirectoryW(fs2us(path), NULL))
|
|
return true;
|
|
#ifdef WIN_LONG_PATH
|
|
if (::GetLastError() != ERROR_ALREADY_EXISTS)
|
|
{
|
|
UString longPath;
|
|
if (GetLongPath(path, longPath))
|
|
return BOOLToBool(::CreateDirectoryW(longPath, NULL));
|
|
}
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CreateComplexDirectory(CFSTR _aPathName)
|
|
{
|
|
FString pathName = _aPathName;
|
|
int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
|
|
if (pos > 0 && pos == pathName.Length() - 1)
|
|
{
|
|
if (pathName.Length() == 3 && pathName[1] == L':')
|
|
return true; // Disk folder;
|
|
pathName.Delete(pos);
|
|
}
|
|
FString pathName2 = pathName;
|
|
pos = pathName.Length();
|
|
for (;;)
|
|
{
|
|
if (MyCreateDirectory(pathName))
|
|
break;
|
|
if (::GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
NFind::CFileInfo fileInfo;
|
|
if (!fileInfo.Find(pathName)) // For network folders
|
|
return true;
|
|
if (!fileInfo.IsDir())
|
|
return false;
|
|
break;
|
|
}
|
|
pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
|
|
if (pos < 0 || pos == 0)
|
|
return false;
|
|
if (pathName[pos - 1] == L':')
|
|
return false;
|
|
pathName = pathName.Left(pos);
|
|
}
|
|
pathName = pathName2;
|
|
while (pos < pathName.Length())
|
|
{
|
|
pos = pathName.Find(FCHAR_PATH_SEPARATOR, pos + 1);
|
|
if (pos < 0)
|
|
pos = pathName.Length();
|
|
if (!MyCreateDirectory(pathName.Left(pos)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DeleteFileAlways(CFSTR name)
|
|
{
|
|
if (!MySetFileAttributes(name, 0))
|
|
return false;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::DeleteFile(fs2fas(name)))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (::DeleteFileW(fs2us(name)))
|
|
return true;
|
|
#ifdef WIN_LONG_PATH
|
|
UString longPath;
|
|
if (GetLongPath(name, longPath))
|
|
return BOOLToBool(::DeleteFileW(longPath));
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool RemoveDirectorySubItems2(const FString pathPrefix, const NFind::CFileInfo &fileInfo)
|
|
{
|
|
if (fileInfo.IsDir())
|
|
return RemoveDirectoryWithSubItems(pathPrefix + fileInfo.Name);
|
|
return DeleteFileAlways(pathPrefix + fileInfo.Name);
|
|
}
|
|
bool RemoveDirectoryWithSubItems(const FString &path)
|
|
{
|
|
NFind::CFileInfo fileInfo;
|
|
FString pathPrefix = path + FCHAR_PATH_SEPARATOR;
|
|
{
|
|
NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK);
|
|
while (enumerator.Next(fileInfo))
|
|
if (!RemoveDirectorySubItems2(pathPrefix, fileInfo))
|
|
return false;
|
|
}
|
|
if (!MySetFileAttributes(path, 0))
|
|
return false;
|
|
return MyRemoveDirectory(path);
|
|
}
|
|
|
|
#ifdef UNDER_CE
|
|
|
|
bool MyGetFullPathName(CFSTR fileName, FString &resFullPath)
|
|
{
|
|
resFullPath = fileName;
|
|
return true;
|
|
}
|
|
|
|
#else
|
|
|
|
#ifdef WIN_LONG_PATH
|
|
|
|
static FString GetLastPart(CFSTR path)
|
|
{
|
|
int i = MyStringLen(path);
|
|
for (; i > 0; i--)
|
|
{
|
|
FChar c = path[i - 1];
|
|
if (c == FCHAR_PATH_SEPARATOR || c == '/')
|
|
break;
|
|
}
|
|
return path + i;
|
|
}
|
|
|
|
static void AddTrailingDots(CFSTR oldPath, FString &newPath)
|
|
{
|
|
int len = MyStringLen(oldPath);
|
|
int i;
|
|
for (i = len; i > 0 && oldPath[i - 1] == '.'; i--);
|
|
if (i == 0 || i == len)
|
|
return;
|
|
FString oldName = GetLastPart(oldPath);
|
|
FString newName = GetLastPart(newPath);
|
|
int nonDotsLen = oldName.Length() - (len - i);
|
|
if (nonDotsLen == 0 || newName.CompareNoCase(oldName.Left(nonDotsLen)) != 0)
|
|
return;
|
|
for (; i != len; i++)
|
|
newPath += '.';
|
|
}
|
|
|
|
#endif
|
|
|
|
bool MyGetFullPathName(CFSTR fileName, FString &resFullPath)
|
|
{
|
|
resFullPath.Empty();
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
LPTSTR fileNamePointer = 0;
|
|
DWORD needLength = ::GetFullPathName(fs2fas(fileName), MAX_PATH + 1, s, &fileNamePointer);
|
|
if (needLength == 0 || needLength > MAX_PATH)
|
|
return false;
|
|
resFullPath = fas2fs(s);
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
LPWSTR fileNamePointer = 0;
|
|
WCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
DWORD needLength = ::GetFullPathNameW(fs2us(fileName), MAX_PATH + 1, s, &fileNamePointer);
|
|
if (needLength == 0)
|
|
return false;
|
|
if (needLength <= MAX_PATH)
|
|
{
|
|
resFullPath = us2fs(s);
|
|
return true;
|
|
}
|
|
#ifdef WIN_LONG_PATH
|
|
needLength++;
|
|
UString temp;
|
|
LPWSTR buffer = temp.GetBuffer(needLength + 1);
|
|
buffer[0] = 0;
|
|
DWORD needLength2 = ::GetFullPathNameW(fs2us(fileName), needLength, buffer, &fileNamePointer);
|
|
temp.ReleaseBuffer();
|
|
if (needLength2 > 0 && needLength2 <= needLength)
|
|
{
|
|
resFullPath = us2fs(temp);
|
|
AddTrailingDots(fileName, resFullPath);
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool MySetCurrentDirectory(CFSTR path)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
|
|
}
|
|
}
|
|
|
|
bool MyGetCurrentDirectory(FString &path)
|
|
{
|
|
path.Empty();
|
|
DWORD needLength;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetCurrentDirectory(MAX_PATH + 1, s);
|
|
path = fas2fs(s);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
WCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s);
|
|
path = us2fs(s);
|
|
}
|
|
return (needLength > 0 && needLength <= MAX_PATH);
|
|
}
|
|
|
|
#endif
|
|
|
|
bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
|
|
{
|
|
bool res = MyGetFullPathName(path, resFileName);
|
|
if (!res)
|
|
resFileName = path;
|
|
int pos = resFileName.ReverseFind(FCHAR_PATH_SEPARATOR);
|
|
if (pos >= 0)
|
|
{
|
|
resDirPrefix = resFileName.Left(pos + 1);
|
|
resFileName = resFileName.Mid(pos + 1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
|
|
{
|
|
FString resFileName;
|
|
return GetFullPathAndSplit(path, resDirPrefix, resFileName);
|
|
}
|
|
|
|
bool MyGetTempPath(FString &path)
|
|
{
|
|
path.Empty();
|
|
DWORD needLength;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetTempPath(MAX_PATH + 1, s);
|
|
path = fas2fs(s);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
WCHAR s[MAX_PATH + 2];
|
|
s[0] = 0;
|
|
needLength = ::GetTempPathW(MAX_PATH + 1, s);;
|
|
path = us2fs(s);
|
|
}
|
|
return (needLength > 0 && needLength <= MAX_PATH);
|
|
}
|
|
|
|
static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile)
|
|
{
|
|
UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
|
|
for (unsigned i = 0; i < 100; i++)
|
|
{
|
|
path = prefix;
|
|
if (addRandom)
|
|
{
|
|
FChar s[16];
|
|
UInt32 value = d;
|
|
unsigned k;
|
|
for (k = 0; k < 8; k++)
|
|
{
|
|
unsigned t = value & 0xF;
|
|
value >>= 4;
|
|
s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
|
|
}
|
|
s[k] = '\0';
|
|
if (outFile)
|
|
path += FChar('.');
|
|
path += s;
|
|
UInt32 step = GetTickCount() + 2;
|
|
if (step == 0)
|
|
step = 1;
|
|
d += step;
|
|
}
|
|
addRandom = true;
|
|
if (outFile)
|
|
path += FTEXT(".tmp");
|
|
if (NFind::DoesFileOrDirExist(path))
|
|
{
|
|
SetLastError(ERROR_ALREADY_EXISTS);
|
|
continue;
|
|
}
|
|
if (outFile)
|
|
{
|
|
if (outFile->Create(path, false))
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (MyCreateDirectory(path))
|
|
return true;
|
|
}
|
|
DWORD error = GetLastError();
|
|
if (error != ERROR_FILE_EXISTS &&
|
|
error != ERROR_ALREADY_EXISTS)
|
|
break;
|
|
}
|
|
path.Empty();
|
|
return false;
|
|
}
|
|
|
|
bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
|
|
{
|
|
if (!Remove())
|
|
return false;
|
|
if (!CreateTempFile(prefix, false, _path, outFile))
|
|
return false;
|
|
_mustBeDeleted = true;
|
|
return true;
|
|
}
|
|
|
|
bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
|
|
{
|
|
if (!Remove())
|
|
return false;
|
|
FString tempPath;
|
|
if (!MyGetTempPath(tempPath))
|
|
return false;
|
|
if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile))
|
|
return false;
|
|
_mustBeDeleted = true;
|
|
return true;
|
|
}
|
|
|
|
bool CTempFile::Remove()
|
|
{
|
|
if (!_mustBeDeleted)
|
|
return true;
|
|
_mustBeDeleted = !DeleteFileAlways(_path);
|
|
return !_mustBeDeleted;
|
|
}
|
|
|
|
bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
|
|
{
|
|
if (deleteDestBefore)
|
|
if (NFind::DoesFileExist(name))
|
|
if (!DeleteFileAlways(name))
|
|
return false;
|
|
DisableDeleting();
|
|
return MyMoveFile(_path, name);
|
|
}
|
|
|
|
bool CTempDir::Create(CFSTR prefix)
|
|
{
|
|
if (!Remove())
|
|
return false;
|
|
FString tempPath;
|
|
if (!MyGetTempPath(tempPath))
|
|
return false;
|
|
if (!CreateTempFile(tempPath + prefix, true, _path, NULL))
|
|
return false;
|
|
_mustBeDeleted = true;
|
|
return true;
|
|
}
|
|
|
|
bool CTempDir::Remove()
|
|
{
|
|
if (!_mustBeDeleted)
|
|
return true;
|
|
_mustBeDeleted = !RemoveDirectoryWithSubItems(_path);
|
|
return !_mustBeDeleted;
|
|
}
|
|
|
|
}}}
|