BsWin32FileSystem.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsFileSystem.h"
  4. #include "BsException.h"
  5. #include "BsDataStream.h"
  6. #include "BsDebug.h"
  7. #include <windows.h>
  8. namespace BansheeEngine
  9. {
  10. void win32_handleError(DWORD error, const WString& path)
  11. {
  12. switch (error)
  13. {
  14. case ERROR_FILE_NOT_FOUND:
  15. LOGERR("File at path: \"" + toString(path) + "\" not found.");
  16. break;
  17. case ERROR_PATH_NOT_FOUND:
  18. case ERROR_BAD_NETPATH:
  19. case ERROR_CANT_RESOLVE_FILENAME:
  20. case ERROR_INVALID_DRIVE:
  21. LOGERR("Path \"" + toString(path) + "\" not found.");
  22. break;
  23. case ERROR_ACCESS_DENIED:
  24. LOGERR("Access to path \"" + toString(path) + "\" denied.");
  25. break;
  26. case ERROR_ALREADY_EXISTS:
  27. case ERROR_FILE_EXISTS:
  28. LOGERR("File/folder at path \"" + toString(path) + "\" already exists.");
  29. break;
  30. case ERROR_INVALID_NAME:
  31. case ERROR_DIRECTORY:
  32. case ERROR_FILENAME_EXCED_RANGE:
  33. case ERROR_BAD_PATHNAME:
  34. LOGERR("Invalid path string: \"" + toString(path) + "\".");
  35. break;
  36. case ERROR_FILE_READ_ONLY:
  37. LOGERR("File at path \"" + toString(path) + "\" is read only.");
  38. break;
  39. case ERROR_CANNOT_MAKE:
  40. LOGERR("Cannot create file/folder at path: \"" + toString(path) + "\".");
  41. break;
  42. case ERROR_DIR_NOT_EMPTY:
  43. LOGERR("Directory at path \"" + toString(path) + "\" not empty.");
  44. break;
  45. case ERROR_WRITE_FAULT:
  46. LOGERR("Error while writing a file at path \"" + toString(path) + "\".");
  47. break;
  48. case ERROR_READ_FAULT:
  49. LOGERR("Error while reading a file at path \"" + toString(path) + "\".");
  50. break;
  51. case ERROR_SHARING_VIOLATION:
  52. LOGERR("Sharing violation at path \"" + toString(path) + "\".");
  53. break;
  54. case ERROR_LOCK_VIOLATION:
  55. LOGERR("Lock violation at path \"" + toString(path) + "\".");
  56. break;
  57. case ERROR_HANDLE_EOF:
  58. LOGERR("End of file reached for file at path \"" + toString(path) + "\".");
  59. break;
  60. case ERROR_HANDLE_DISK_FULL:
  61. case ERROR_DISK_FULL:
  62. LOGERR("Disk full.");
  63. break;
  64. case ERROR_NEGATIVE_SEEK:
  65. LOGERR("Negative seek.");
  66. break;
  67. default:
  68. LOGERR("Undefined file system exception: " + toString((UINT32)error));
  69. break;
  70. }
  71. }
  72. WString win32_getCurrentDirectory()
  73. {
  74. DWORD len = GetCurrentDirectoryW(0, NULL);
  75. if (len > 0)
  76. {
  77. wchar_t* buffer = (wchar_t*)bs_alloc(len * sizeof(wchar_t));
  78. DWORD n = GetCurrentDirectoryW(len, buffer);
  79. if (n > 0 && n <= len)
  80. {
  81. WString result(buffer);
  82. if (result[result.size() - 1] != '\\')
  83. result.append(L"\\");
  84. bs_free(buffer);
  85. return result;
  86. }
  87. bs_free(buffer);
  88. }
  89. return StringUtil::WBLANK;
  90. }
  91. WString win32_getTempDirectory()
  92. {
  93. DWORD len = GetTempPathW(0, NULL);
  94. if (len > 0)
  95. {
  96. wchar_t* buffer = (wchar_t*)bs_alloc(len * sizeof(wchar_t));
  97. DWORD n = GetTempPathW(len, buffer);
  98. if (n > 0 && n <= len)
  99. {
  100. WString result(buffer);
  101. if (result[result.size() - 1] != '\\')
  102. result.append(L"\\");
  103. bs_free(buffer);
  104. return result;
  105. }
  106. bs_free(buffer);
  107. }
  108. return StringUtil::WBLANK;
  109. }
  110. bool win32_pathExists(const WString& path)
  111. {
  112. DWORD attr = GetFileAttributesW(path.c_str());
  113. if (attr == 0xFFFFFFFF)
  114. {
  115. switch (GetLastError())
  116. {
  117. case ERROR_FILE_NOT_FOUND:
  118. case ERROR_PATH_NOT_FOUND:
  119. case ERROR_NOT_READY:
  120. case ERROR_INVALID_DRIVE:
  121. return false;
  122. default:
  123. win32_handleError(GetLastError(), path);
  124. }
  125. }
  126. return true;
  127. }
  128. bool win32_isDirectory(const WString& path)
  129. {
  130. DWORD attr = GetFileAttributesW(path.c_str());
  131. if (attr == 0xFFFFFFFF)
  132. win32_handleError(GetLastError(), path);
  133. return (attr & FILE_ATTRIBUTE_DIRECTORY) != FALSE;
  134. }
  135. bool win32_isDevice(const WString& path)
  136. {
  137. WString ucPath = path;
  138. StringUtil::toUpperCase(ucPath);
  139. return
  140. ucPath.compare(0, 4, L"\\\\.\\") == 0 ||
  141. ucPath.compare(L"CON") == 0 ||
  142. ucPath.compare(L"PRN") == 0 ||
  143. ucPath.compare(L"AUX") == 0 ||
  144. ucPath.compare(L"NUL") == 0 ||
  145. ucPath.compare(L"LPT1") == 0 ||
  146. ucPath.compare(L"LPT2") == 0 ||
  147. ucPath.compare(L"LPT3") == 0 ||
  148. ucPath.compare(L"LPT4") == 0 ||
  149. ucPath.compare(L"LPT5") == 0 ||
  150. ucPath.compare(L"LPT6") == 0 ||
  151. ucPath.compare(L"LPT7") == 0 ||
  152. ucPath.compare(L"LPT8") == 0 ||
  153. ucPath.compare(L"LPT9") == 0 ||
  154. ucPath.compare(L"COM1") == 0 ||
  155. ucPath.compare(L"COM2") == 0 ||
  156. ucPath.compare(L"COM3") == 0 ||
  157. ucPath.compare(L"COM4") == 0 ||
  158. ucPath.compare(L"COM5") == 0 ||
  159. ucPath.compare(L"COM6") == 0 ||
  160. ucPath.compare(L"COM7") == 0 ||
  161. ucPath.compare(L"COM8") == 0 ||
  162. ucPath.compare(L"COM9") == 0;
  163. }
  164. bool win32_isFile(const WString& path)
  165. {
  166. return !win32_isDirectory(path) && !win32_isDevice(path);
  167. }
  168. bool win32_createFile(const WString& path)
  169. {
  170. HANDLE hFile = CreateFileW(path.c_str(), GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);
  171. if (hFile != INVALID_HANDLE_VALUE)
  172. {
  173. CloseHandle(hFile);
  174. return true;
  175. }
  176. else if (GetLastError() == ERROR_FILE_EXISTS)
  177. return false;
  178. else
  179. win32_handleError(GetLastError(), path);
  180. return false;
  181. }
  182. bool win32_createDirectory(const WString& path)
  183. {
  184. if (win32_pathExists(path) && win32_isDirectory(path))
  185. return false;
  186. if (CreateDirectoryW(path.c_str(), 0) == FALSE)
  187. win32_handleError(GetLastError(), path);
  188. return true;
  189. }
  190. void FileSystem::removeFile(const Path& path)
  191. {
  192. WString pathStr = path.toWString();
  193. if (win32_isDirectory(pathStr))
  194. {
  195. if (RemoveDirectoryW(pathStr.c_str()) == 0)
  196. win32_handleError(GetLastError(), pathStr);
  197. }
  198. else
  199. {
  200. if (DeleteFileW(pathStr.c_str()) == 0)
  201. win32_handleError(GetLastError(), pathStr);
  202. }
  203. }
  204. void FileSystem::copyFile(const Path& from, const Path& to)
  205. {
  206. if (CopyFileW(from.toWString().c_str(), to.toWString().c_str(), FALSE) == FALSE)
  207. win32_handleError(GetLastError(), from);
  208. }
  209. void FileSystem::moveFile(const Path& oldPath, const Path& newPath)
  210. {
  211. WString oldPathStr = oldPath.toWString();
  212. WString newPathStr = newPath.toWString();
  213. if (MoveFileW(oldPath.c_str(), newPath.c_str()) == 0)
  214. win32_handleError(GetLastError(), oldPath);
  215. }
  216. UINT64 win32_getFileSize(const WString& path)
  217. {
  218. WIN32_FILE_ATTRIBUTE_DATA attrData;
  219. if (GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attrData) == FALSE)
  220. win32_handleError(GetLastError(), path);
  221. LARGE_INTEGER li;
  222. li.LowPart = attrData.nFileSizeLow;
  223. li.HighPart = attrData.nFileSizeHigh;
  224. return (UINT64)li.QuadPart;
  225. }
  226. std::time_t win32_getLastModifiedTime(const WString& path)
  227. {
  228. WIN32_FILE_ATTRIBUTE_DATA fad;
  229. if (GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &fad) == 0)
  230. win32_handleError(GetLastError(), path);
  231. ULARGE_INTEGER ull;
  232. ull.LowPart = fad.ftLastWriteTime.dwLowDateTime;
  233. ull.HighPart = fad.ftLastWriteTime.dwHighDateTime;
  234. return (std::time_t) ((ull.QuadPart / 10000000ULL) - 11644473600ULL);
  235. }
  236. SPtr<DataStream> FileSystem::openFile(const Path& fullPath, bool readOnly)
  237. {
  238. WString pathWString = fullPath.toWString();
  239. const wchar_t* pathString = pathWString.c_str();
  240. if (!win32_pathExists(pathString) || !win32_isFile(pathString))
  241. {
  242. LOGWRN("Attempting to open a file that doesn't exist: " + fullPath.toString());
  243. return nullptr;
  244. }
  245. DataStream::AccessMode accessMode = DataStream::READ;
  246. if (!readOnly)
  247. accessMode = (DataStream::AccessMode)(accessMode | (UINT32)DataStream::WRITE);
  248. return bs_shared_ptr_new<FileDataStream>(fullPath, accessMode, true);
  249. }
  250. SPtr<DataStream> FileSystem::createAndOpenFile(const Path& fullPath)
  251. {
  252. return bs_shared_ptr_new<FileDataStream>(fullPath, DataStream::AccessMode::WRITE, true);
  253. }
  254. UINT64 FileSystem::getFileSize(const Path& fullPath)
  255. {
  256. return win32_getFileSize(fullPath.toWString());
  257. }
  258. void FileSystem::move(const Path& oldPath, const Path& newPath, bool overwriteExisting)
  259. {
  260. WString oldPathStr = oldPath.toWString();
  261. WString newPathStr = newPath.toWString();
  262. if (win32_pathExists(newPathStr))
  263. {
  264. if (overwriteExisting)
  265. FileSystem::removeFile(newPath);
  266. else
  267. {
  268. LOGWRN("Move operation failed because another file already exists at the new path: \"" + toString(newPathStr) + "\"");
  269. return;
  270. }
  271. }
  272. win32_rename(oldPathStr, newPathStr);
  273. }
  274. bool FileSystem::exists(const Path& fullPath)
  275. {
  276. return win32_pathExists(fullPath.toWString());
  277. }
  278. bool FileSystem::isFile(const Path& fullPath)
  279. {
  280. WString pathStr = fullPath.toWString();
  281. return win32_pathExists(pathStr) && win32_isFile(pathStr);
  282. }
  283. bool FileSystem::isDirectory(const Path& fullPath)
  284. {
  285. WString pathStr = fullPath.toWString();
  286. return win32_pathExists(pathStr) && win32_isDirectory(pathStr);
  287. }
  288. void FileSystem::createDir(const Path& fullPath)
  289. {
  290. Path parentPath = fullPath;
  291. while (!exists(parentPath) && parentPath.getNumDirectories() > 0)
  292. {
  293. parentPath = parentPath.getParent();
  294. }
  295. for (UINT32 i = parentPath.getNumDirectories(); i < fullPath.getNumDirectories(); i++)
  296. {
  297. parentPath.append(fullPath[i]);
  298. win32_createDirectory(parentPath.toWString());
  299. }
  300. if (fullPath.isFile())
  301. win32_createDirectory(fullPath.toWString());
  302. }
  303. void FileSystem::getChildren(const Path& dirPath, Vector<Path>& files, Vector<Path>& directories)
  304. {
  305. WString findPath = dirPath.toWString();
  306. if (win32_isFile(findPath))
  307. return;
  308. if(dirPath.isFile()) // Assuming the file is a folder, just improperly formatted in Path
  309. findPath.append(L"\\*");
  310. else
  311. findPath.append(L"*");
  312. WIN32_FIND_DATAW findData;
  313. HANDLE fileHandle = FindFirstFileW(findPath.c_str(), &findData);
  314. if(fileHandle == INVALID_HANDLE_VALUE)
  315. {
  316. win32_handleError(GetLastError(), findPath);
  317. return;
  318. }
  319. WString tempName;
  320. do
  321. {
  322. tempName = findData.cFileName;
  323. if (tempName != L"." && tempName != L"..")
  324. {
  325. Path fullPath = dirPath;
  326. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
  327. directories.push_back(fullPath.append(tempName + L"/"));
  328. else
  329. files.push_back(fullPath.append(tempName));
  330. }
  331. if(FindNextFileW(fileHandle, &findData) == FALSE)
  332. {
  333. if (GetLastError() != ERROR_NO_MORE_FILES)
  334. win32_handleError(GetLastError(), findPath);
  335. break;
  336. }
  337. } while (true);
  338. FindClose(fileHandle);
  339. }
  340. bool FileSystem::iterate(const Path& dirPath, std::function<bool(const Path&)> fileCallback,
  341. std::function<bool(const Path&)> dirCallback, bool recursive)
  342. {
  343. WString findPath = dirPath.toWString();
  344. if (win32_isFile(findPath))
  345. return false;
  346. if (dirPath.isFile()) // Assuming the file is a folder, just improperly formatted in Path
  347. findPath.append(L"\\*");
  348. else
  349. findPath.append(L"*");
  350. WIN32_FIND_DATAW findData;
  351. HANDLE fileHandle = FindFirstFileW(findPath.c_str(), &findData);
  352. if (fileHandle == INVALID_HANDLE_VALUE)
  353. {
  354. win32_handleError(GetLastError(), findPath);
  355. return false;
  356. }
  357. WString tempName;
  358. do
  359. {
  360. tempName = findData.cFileName;
  361. if (tempName != L"." && tempName != L"..")
  362. {
  363. Path fullPath = dirPath;
  364. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
  365. {
  366. Path childDir = fullPath.append(tempName + L"/");
  367. if (dirCallback != nullptr)
  368. {
  369. if (!dirCallback(childDir))
  370. {
  371. FindClose(fileHandle);
  372. return false;
  373. }
  374. }
  375. if (recursive)
  376. {
  377. if (!iterate(childDir, fileCallback, dirCallback, recursive))
  378. {
  379. FindClose(fileHandle);
  380. return false;
  381. }
  382. }
  383. }
  384. else
  385. {
  386. Path filePath = fullPath.append(tempName);
  387. if (fileCallback != nullptr)
  388. {
  389. if (!fileCallback(filePath))
  390. {
  391. FindClose(fileHandle);
  392. return false;
  393. }
  394. }
  395. }
  396. }
  397. if(FindNextFileW(fileHandle, &findData) == FALSE)
  398. {
  399. if (GetLastError() != ERROR_NO_MORE_FILES)
  400. win32_handleError(GetLastError(), findPath);
  401. break;
  402. }
  403. } while (true);
  404. FindClose(fileHandle);
  405. return true;
  406. }
  407. std::time_t FileSystem::getLastModifiedTime(const Path& fullPath)
  408. {
  409. return win32_getLastModifiedTime(fullPath.toWString().c_str());
  410. }
  411. Path FileSystem::getWorkingDirectoryPath()
  412. {
  413. return Path(win32_getCurrentDirectory());
  414. }
  415. Path FileSystem::getTempDirectoryPath()
  416. {
  417. return Path(win32_getTempDirectory());
  418. }
  419. }