ezEngine  Milestone 7
OSFile_win.h
1 #pragma once
2 
3 // Defined in Timestamp_win.h
4 ezInt64 FileTimeToEpoch(FILETIME fileTime);
5 
6 static ezUInt64 HighLowToUInt64(ezUInt32 uiHigh32, ezUInt32 uiLow32)
7 {
8  ezUInt64 uiHigh64 = uiHigh32;
9  ezUInt64 uiLow64 = uiLow32;
10 
11  return (uiHigh64 << 32) | uiLow64;
12 }
13 
14 #if EZ_DISABLED(EZ_USE_POSIX_FILE_API)
15 
16 ezResult ezOSFile::InternalOpen(const char* szFile, ezFileMode::Enum OpenMode)
17 {
18  ezStringWChar s = szFile;
19 
20  switch (OpenMode)
21  {
22  case ezFileMode::Read:
23  m_FileData.m_pFileHandle = CreateFileW(s.GetData(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
24  break;
25  case ezFileMode::Write:
26  m_FileData.m_pFileHandle = CreateFileW(s.GetData(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
27  break;
28  case ezFileMode::Append:
29  m_FileData.m_pFileHandle = CreateFileW(s.GetData(), FILE_APPEND_DATA, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
30 
31  // in append mode we need to set the file pointer to the end explicitly, otherwise GetFilePosition might return 0 the first time
32  if ((m_FileData.m_pFileHandle != nullptr) && (m_FileData.m_pFileHandle != INVALID_HANDLE_VALUE))
33  InternalSetFilePosition(0, ezFilePos::FromEnd);
34 
35  break;
36  }
37 
38  return ((m_FileData.m_pFileHandle != nullptr) && (m_FileData.m_pFileHandle != INVALID_HANDLE_VALUE)) ? EZ_SUCCESS : EZ_FAILURE;
39 }
40 
41 void ezOSFile::InternalClose()
42 {
43  CloseHandle(m_FileData.m_pFileHandle);
44  m_FileData.m_pFileHandle = INVALID_HANDLE_VALUE;
45 }
46 
47 ezResult ezOSFile::InternalWrite(const void* pBuffer, ezUInt64 uiBytes)
48 {
49  const ezUInt32 uiBatchBytes = 1024 * 1024 * 1024; // 1 GB
50 
51  // first write out all the data in 1GB batches
52  while (uiBytes > uiBatchBytes)
53  {
54  DWORD uiBytesWritten = 0;
55  if ((!WriteFile(m_FileData.m_pFileHandle, pBuffer, uiBatchBytes, &uiBytesWritten, nullptr)) || (uiBytesWritten != uiBatchBytes))
56  return EZ_FAILURE;
57 
58  uiBytes -= uiBatchBytes;
59  pBuffer = ezMemoryUtils::AddByteOffsetConst(pBuffer, uiBatchBytes);
60  }
61 
62  if (uiBytes > 0)
63  {
64  const ezUInt32 uiBytes32 = static_cast<ezUInt32>(uiBytes);
65 
66  DWORD uiBytesWritten = 0;
67  if ((!WriteFile(m_FileData.m_pFileHandle, pBuffer, uiBytes32, &uiBytesWritten, nullptr)) || (uiBytesWritten != uiBytes32))
68  return EZ_FAILURE;
69  }
70 
71  return EZ_SUCCESS;
72 }
73 
74 ezUInt64 ezOSFile::InternalRead(void* pBuffer, ezUInt64 uiBytes)
75 {
76  ezUInt64 uiBytesRead = 0;
77 
78  const ezUInt32 uiBatchBytes = 1024 * 1024 * 1024; // 1 GB
79 
80  // first write out all the data in 1GB batches
81  while (uiBytes > uiBatchBytes)
82  {
83  DWORD uiBytesReadThisTime = 0;
84  if (!ReadFile(m_FileData.m_pFileHandle, pBuffer, uiBatchBytes, &uiBytesReadThisTime, nullptr))
85  return uiBytesRead + uiBytesReadThisTime;
86 
87  uiBytesRead += uiBytesReadThisTime;
88 
89  if (uiBytesReadThisTime != uiBatchBytes)
90  return uiBytesRead;
91 
92  uiBytes -= uiBatchBytes;
93  pBuffer = ezMemoryUtils::AddByteOffset(pBuffer, uiBatchBytes);
94  }
95 
96  if (uiBytes > 0)
97  {
98  const ezUInt32 uiBytes32 = static_cast<ezUInt32>(uiBytes);
99 
100  DWORD uiBytesReadThisTime = 0;
101  if (!ReadFile(m_FileData.m_pFileHandle, pBuffer, uiBytes32, &uiBytesReadThisTime, nullptr))
102  return uiBytesRead + uiBytesReadThisTime;
103 
104  uiBytesRead += uiBytesReadThisTime;
105  }
106 
107  return uiBytesRead;
108 }
109 
110 ezUInt64 ezOSFile::InternalGetFilePosition() const
111 {
112  long int uiHigh32 = 0;
113  ezUInt32 uiLow32 = SetFilePointer(m_FileData.m_pFileHandle, 0, &uiHigh32, FILE_CURRENT);
114 
115  return HighLowToUInt64(uiHigh32, uiLow32);
116 }
117 
118 void ezOSFile::InternalSetFilePosition(ezInt64 iDistance, ezFilePos::Enum Pos) const
119 {
120  LARGE_INTEGER pos;
121  LARGE_INTEGER newpos;
122  pos.QuadPart = static_cast<LONGLONG>(iDistance);
123 
124  switch (Pos)
125  {
127  EZ_VERIFY(SetFilePointerEx(m_FileData.m_pFileHandle, pos, &newpos, FILE_BEGIN), "Seek Failed.");
128  break;
129  case ezFilePos::FromEnd:
130  EZ_VERIFY(SetFilePointerEx(m_FileData.m_pFileHandle, pos, &newpos, FILE_END), "Seek Failed.");
131  break;
133  EZ_VERIFY(SetFilePointerEx(m_FileData.m_pFileHandle, pos, &newpos, FILE_CURRENT), "Seek Failed.");
134  break;
135  }
136 }
137 
138 bool ezOSFile::InternalExistsFile(const char* szFile)
139 {
140  ezStringWChar sPath(szFile);
141  DWORD dwAttrib = GetFileAttributesW(sPath.GetData());
142 
143  return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0));
144 }
145 
146 bool ezOSFile::InternalExistsDirectory(const char* szDirectory)
147 {
148  ezStringWChar sPath(szDirectory);
149  DWORD dwAttrib = GetFileAttributesW(sPath.GetData());
150 
151  return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0));
152 }
153 
154 #endif // not EZ_USE_POSIX_FILE_API
155 
156 ezResult ezOSFile::InternalDeleteFile(const char* szFile)
157 {
158  ezStringWChar s = szFile;
159  if (DeleteFileW(s.GetData()) == FALSE)
160  {
161  if (GetLastError() == ERROR_FILE_NOT_FOUND)
162  return EZ_SUCCESS;
163 
164  return EZ_FAILURE;
165  }
166 
167  return EZ_SUCCESS;
168 }
169 
170 ezResult ezOSFile::InternalCreateDirectory(const char* szDirectory)
171 {
172  // handle drive letters as always successful
173  if (ezStringUtils::GetCharacterCount(szDirectory) <= 3) // 'C:\'
174  return EZ_SUCCESS;
175 
176  ezStringWChar s = szDirectory;
177  if (CreateDirectoryW(s.GetData(), nullptr) == FALSE)
178  {
179  DWORD uiError = GetLastError();
180  if (uiError == ERROR_ALREADY_EXISTS)
181  return EZ_SUCCESS;
182 
183  return EZ_FAILURE;
184  }
185 
186  return EZ_SUCCESS;
187 }
188 
189 ezResult ezOSFile::InternalGetFileStats(const char* szFileOrFolder, ezFileStats& out_Stats)
190 {
191  ezStringBuilder s = szFileOrFolder;
192 
193  // FindFirstFile does not like paths that end with a separator, so remove them all
194  s.Trim(nullptr, "/\\");
195 
196  // handle the case that this query is done on the 'device part' of a path
197  if (s.GetCharacterCount() <= 2) // 'C:', 'D:', 'E' etc.
198  {
199  s.ToUpper();
200 
201  out_Stats.m_uiFileSize = 0;
202  out_Stats.m_bIsDirectory = true;
203  out_Stats.m_sFileName = s;
205  return EZ_SUCCESS;
206  }
207 
208  WIN32_FIND_DATAW data;
209  HANDLE hSearch = FindFirstFileW(ezStringWChar(s.GetData()).GetData(), &data);
210 
211  if ((hSearch == nullptr) || (hSearch == INVALID_HANDLE_VALUE))
212  return EZ_FAILURE;
213 
214  out_Stats.m_uiFileSize = HighLowToUInt64(data.nFileSizeHigh, data.nFileSizeLow);
215  out_Stats.m_bIsDirectory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
216  out_Stats.m_sFileName = data.cFileName;
217  out_Stats.m_LastModificationTime.SetInt64(FileTimeToEpoch(data.ftLastWriteTime), ezSIUnitOfTime::Microsecond);
218 
219  FindClose(hSearch);
220  return EZ_SUCCESS;
221 }
222 
223 ezFileSystemIterator::ezFileSystemIterator()
224 {
225 }
226 
227 ezFileSystemIterator::~ezFileSystemIterator()
228 {
229  while (!m_Data.m_Handles.IsEmpty())
230  {
231  FindClose(m_Data.m_Handles.PeekBack());
232  m_Data.m_Handles.PopBack();
233  }
234 }
235 
236 ezResult ezFileSystemIterator::StartSearch(const char* szSearchStart, bool bRecursive, bool bReportFolders)
237 {
238  EZ_ASSERT_DEV(m_Data.m_Handles.IsEmpty(), "Cannot start another search.");
239 
240  ezStringBuilder sSearch = szSearchStart;
241  sSearch.MakeCleanPath();
242 
243  m_sCurPath = sSearch.GetFileDirectory();
244 
245  EZ_ASSERT_DEV(sSearch.IsAbsolutePath(), "The path '%s' is not absolute.", m_sCurPath.GetData());
246 
247  m_bRecursive = bRecursive;
248  m_bReportFolders = bReportFolders;
249 
250  WIN32_FIND_DATAW data;
251  HANDLE hSearch = FindFirstFileW(ezStringWChar(sSearch.GetData()).GetData(), &data);
252 
253  if ((hSearch == nullptr) || (hSearch == INVALID_HANDLE_VALUE))
254  return EZ_FAILURE;
255 
256  m_CurFile.m_uiFileSize = HighLowToUInt64(data.nFileSizeHigh, data.nFileSizeLow);
257  m_CurFile.m_bIsDirectory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
258  m_CurFile.m_sFileName = data.cFileName;
259  m_CurFile.m_LastModificationTime.SetInt64(FileTimeToEpoch(data.ftLastWriteTime), ezSIUnitOfTime::Microsecond);
260 
261  m_Data.m_Handles.PushBack(hSearch);
262 
263  if ((m_CurFile.m_sFileName == "..") || (m_CurFile.m_sFileName == "."))
264  return Next(); // will search for the next file or folder that is not ".." or "." ; might return false though
265 
267  return Next();
268 
269  return EZ_SUCCESS;
270 }
271 
273 {
274  if (m_Data.m_Handles.IsEmpty())
275  return EZ_FAILURE;
276 
278  {
280 
281  ezStringBuilder sNewSearch = m_sCurPath;
282  sNewSearch.AppendPath("*");
283 
284  WIN32_FIND_DATAW data;
285  HANDLE hSearch = FindFirstFileW(ezStringWChar(sNewSearch.GetData()).GetData(), &data);
286 
287  if ((hSearch != nullptr) && (hSearch != INVALID_HANDLE_VALUE))
288  {
289  m_CurFile.m_uiFileSize = HighLowToUInt64(data.nFileSizeHigh, data.nFileSizeLow);
290  m_CurFile.m_bIsDirectory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
291  m_CurFile.m_sFileName = data.cFileName;
292  m_CurFile.m_LastModificationTime.SetInt64(FileTimeToEpoch(data.ftLastWriteTime), ezSIUnitOfTime::Microsecond);
293 
294  m_Data.m_Handles.PushBack(hSearch);
295 
296  if ((m_CurFile.m_sFileName == "..") || (m_CurFile.m_sFileName == "."))
297  return Next(); // will search for the next file or folder that is not ".." or "." ; might return false though
298 
300  return Next();
301 
302  return EZ_SUCCESS;
303  }
304 
305  // if the recursion did not work, just iterate in this folder further
306  }
307 
308  WIN32_FIND_DATAW data;
309  if (!FindNextFileW(m_Data.m_Handles.PeekBack(), &data))
310  {
311  // nothing found in this directory anymore
312  FindClose(m_Data.m_Handles.PeekBack());
313  m_Data.m_Handles.PopBack();
314 
315  if (m_Data.m_Handles.IsEmpty())
316  return EZ_FAILURE;
317 
319 
320  return Next();
321  }
322 
323  m_CurFile.m_uiFileSize = HighLowToUInt64(data.nFileSizeHigh, data.nFileSizeLow);
324  m_CurFile.m_bIsDirectory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
325  m_CurFile.m_sFileName = data.cFileName;
326  m_CurFile.m_LastModificationTime.SetInt64(FileTimeToEpoch(data.ftLastWriteTime), ezSIUnitOfTime::Microsecond);
327 
328  if ((m_CurFile.m_sFileName == "..") || (m_CurFile.m_sFileName == "."))
329  return Next();
330 
332  return Next();
333 
334  return EZ_SUCCESS;
335 }
336 
338 {
339  EZ_ASSERT_DEV(m_bRecursive, "SkipFolder has no meaning when the iterator is not set to be recursive.");
340  EZ_ASSERT_DEV(m_CurFile.m_bIsDirectory, "SkipFolder can only be called when the current object is a folder.");
341 
342  m_bRecursive = false;
343 
344  const ezResult bRet = Next();
345 
346  m_bRecursive = true;
347 
348  return bRet;
349 }
350 
352 {
354  {
355  wchar_t szFilename[256];
356  GetModuleFileNameW(nullptr, szFilename, 256);
357 
358  ezStringBuilder sPath = ezStringUtf8(szFilename).GetData();
360  }
361 
362  return s_ApplicationPath.GetData();
363 }
364 
365