2
#include "Math/MathUtility.h"
6
// Choose the size so it is a power of 2. Example (size-1)= 11111111.
7
const t_int NWindowsSerialFileReader::sBufferSize = 1024;
9
NWindowsSerialFileReader::NWindowsSerialFileReader(HANDLE InHandle, NOutputDevice& InError)
10
: m_FileHandle (InHandle)
16
m_Buffer = new BYTE[sBufferSize];
17
m_FileSize = GetFileSize();
19
NWindowsSerialFileReader::~NWindowsSerialFileReader()
21
INL_SAFE_DELETE_ARRAY(m_Buffer);
28
bool NWindowsSerialFileReader::Precache(t_int PrecacheOffset, t_int PrecacheSize)
30
// Only pre-cache at current position and avoid work if pre-caching same offset twice.
31
if((m_FilePos == PrecacheOffset) && (!m_BufferBase || !m_BufferCount || m_BufferBase != m_FilePos))
33
m_BufferBase = m_FilePos;
34
// (sBufferSize - 1) contains only '1', i.e 1111111111.
35
// So (m_FilePos & (sBufferSize-1)) is equal to m_FilePos if m_FilePos <= (sBufferSize-1).
36
m_BufferCount = Min<t_s64>(Min<t_s64>(PrecacheSize, (t_int)(sBufferSize - (m_FilePos & (sBufferSize-1)))), m_FileSize - m_FilePos);
38
//GTotalBytesReadViaFileManager += m_BufferCount;
39
::ReadFile(m_FileHandle, m_Buffer, m_BufferCount, INL_REINTERPRET_CAST(DWORD*, &Count), NULL);
40
if(Count != m_BufferCount)
43
m_Error.LogFunction(TEXT("ReadFile failed: Count=%i BufferCount=%i Error=%s"), Count, m_BufferCount, inlGetSystemErrorMessage());
49
t_s64 NWindowsSerialFileReader::Seek(t_s64 InPos, NSerializer::SeekPos seekpos)
51
nuxAssert(InPos >= 0);
52
nuxAssert(InPos <= m_FileSize);
55
// Because we precache our reads, we must perform Seek accordingly.
58
pos.QuadPart = m_FilePos;
59
LARGE_INTEGER filepos;
62
// Set the file pointer to m_FilePos.
63
if(::SetFilePointerEx(m_FileHandle, pos, &filepos, FILE_BEGIN) == 0)
66
m_Error.LogFunction(TEXT("SetFilePointer Failed %i/%i: %i %s"), InPos, m_FileSize, m_FilePos, inlGetSystemErrorMessage());
69
// Now the file pointer is current with what we have read so far.
72
if(::SetFilePointerEx(m_FileHandle, pos, &filepos, (seekpos == SeekStart) ? FILE_BEGIN : (seekpos == SeekCurrent) ? FILE_CURRENT : FILE_END) == 0)
75
m_Error.LogFunction(TEXT("SetFilePointer Failed %i/%i: %i %s"), InPos, m_FileSize, m_FilePos, inlGetSystemErrorMessage());
77
m_FilePos = filepos.QuadPart;
80
Precache(m_FilePos, sBufferSize);
81
return filepos.QuadPart;
84
t_s64 NWindowsSerialFileReader::Tell()
88
// LARGE_INTEGER filepos;
90
// ::SetFilePointerEx(m_FileHandle, pos, &filepos, FILE_CURRENT);
91
// return filepos.QuadPart;
96
t_s64 NWindowsSerialFileReader::GetFileSize()
98
nuxAssert(m_FileHandle);
99
if(m_FileHandle == NULL)
103
if(::GetFileSizeEx(m_FileHandle, INL_REINTERPRET_CAST(PLARGE_INTEGER, &Size)) == 0)
107
m_FileSize = Size > 0 ? Size : 0;
111
bool NWindowsSerialFileReader::Close()
115
CloseHandle(m_FileHandle);
121
void NWindowsSerialFileReader::SerializeFinal(void* Dest, t_u64 Length)
126
t_int DataSize = Min<t_s64>(Length, m_BufferBase + m_BufferCount - m_FilePos);
129
if(Length >= sBufferSize)
132
//GTotalBytesReadViaFileManager += Length;
133
ReadFile(m_FileHandle, Dest, Length, (DWORD*)&Count, NULL);
137
m_Error.LogFunction(TEXT("ReadFile failed: Count=%i Length=%i Error=%s"), Count, Length, inlGetSystemErrorMessage());
140
m_BufferBase += Length;
143
Precache(m_FilePos, t_s32_max);
144
DataSize = Min<t_s64>(Length, m_BufferBase + m_BufferCount - m_FilePos);
148
m_Error.LogFunction(TEXT("ReadFile beyond EOF %i+%i/%i"), m_FilePos, Length, m_FileSize);
153
Memcpy(Dest, m_Buffer + m_FilePos - m_BufferBase, DataSize);
154
m_FilePos += DataSize;
156
Dest = (BYTE*)Dest + DataSize;
159
//////////////////////////////////////////////////////////////////////////
160
// Choose the size so it is a power of 2. Example (size-1)= 11111111.
161
const t_int NWindowsSerialFileWriter::sBufferSize = 32;
162
//NCriticalSection NWindowsSerialFileWriter::m_CriticalSection;
164
NWindowsSerialFileWriter::NWindowsSerialFileWriter(HANDLE InHandle, NOutputDevice& InError)
165
: m_FileHandle (InHandle)
170
m_Buffer = new BYTE[sBufferSize];
173
NWindowsSerialFileWriter::~NWindowsSerialFileWriter()
175
INL_SAFE_DELETE_ARRAY(m_Buffer);
181
t_s64 NWindowsSerialFileWriter::Seek(t_s64 InPos, NSerializer::SeekPos seekpos)
183
NScopeLock Scope(&m_CriticalSection);
184
nuxAssert(m_FileHandle);
185
if(m_FileHandle == NULL)
190
pos.QuadPart = InPos;
191
LARGE_INTEGER filepos;
192
filepos.QuadPart = 0;
193
if(::SetFilePointerEx(m_FileHandle, pos, &filepos, (seekpos == SeekStart) ? FILE_BEGIN : (seekpos == SeekCurrent) ? FILE_CURRENT : FILE_END) == 0)
196
m_Error.LogFunction(TEXT("SeekFailed"));
198
m_Pos = filepos.QuadPart;
200
return filepos.QuadPart;
203
t_s64 NWindowsSerialFileWriter::Tell()
205
NScopeLock Scope(&m_CriticalSection);
206
nuxAssert(m_FileHandle);
207
if(m_FileHandle == NULL)
212
LARGE_INTEGER filepos;
213
filepos.QuadPart = 0;
215
::SetFilePointerEx(m_FileHandle, pos, &filepos, FILE_CURRENT);
216
return filepos.QuadPart;
219
bool NWindowsSerialFileWriter::Close()
221
NScopeLock Scope(&m_CriticalSection);
222
nuxAssert(m_FileHandle);
223
if(m_FileHandle == NULL)
227
if(m_FileHandle && !CloseHandle(m_FileHandle))
230
m_Error.LogFunction(TEXT("WriteFailed"));
236
t_s64 NWindowsSerialFileWriter::GetFileSize()
238
nuxAssert(m_FileHandle);
239
if(m_FileHandle == NULL)
243
if(::GetFileSizeEx(m_FileHandle, INL_REINTERPRET_CAST(PLARGE_INTEGER, &Size)) == 0)
250
void NWindowsSerialFileWriter::SerializeFinal(void* V, t_u64 Length)
252
// This method is not re-entrant by itself. It relies on m_Buffer and other variables
253
// that belong to this object. Therefore, it is not thread safe. We add a critical section
254
// to make it thread safe.
256
NScopeLock Scope(&m_CriticalSection);
257
nuxAssert(m_FileHandle);
260
INL_RETURN_IF_NULL(m_FileHandle);
264
while(Length > (FreeSpace = sBufferSize - m_BufferCount))
266
// m_Buffer is Full. Write it to the file.
267
Memcpy(m_Buffer + m_BufferCount, V, FreeSpace);
268
m_BufferCount += FreeSpace;
270
V = (BYTE*)V + FreeSpace;
275
Memcpy(m_Buffer + m_BufferCount, V, Length);
276
m_BufferCount += Length; // Count the number of Characters stored in m_Buffer.
280
void NWindowsSerialFileWriter::Flush()
282
NScopeLock Scope(&m_CriticalSection);
283
nuxAssert(m_FileHandle);
284
if(m_FileHandle == NULL)
289
void NWindowsSerialFileWriter::_Flush()
291
//GTotalBytesWrittenViaFileManager += m_BufferCount;
295
if(!WriteFile(m_FileHandle, m_Buffer, m_BufferCount, (DWORD*)&Result, NULL))
298
m_Error.LogFunction(TEXT("[NWindowsSerialFileWriter::_Flush] Write failed"));
304
//////////////////////////////////////////////////////////////////////////
305
INL_IMPLEMENT_GLOBAL_OBJECT(NFileManagerWindows);
307
void NFileManagerWindows::Constructor()
311
void NFileManagerWindows::Destructor()
315
HANDLE NFileManagerWindows::CreateReadFileHandle(const TCHAR* Filename, DWORD Flags)
317
DWORD Access = GENERIC_READ;
318
DWORD Create = OPEN_EXISTING;
320
DWORD SharedModeFlags = 0;
321
SharedModeFlags |= (Flags & NSerializer::Read) ? FILE_SHARE_READ : 0;
322
SharedModeFlags |= (Flags & NSerializer::Write) ? FILE_SHARE_WRITE : 0;
324
HANDLE Handle = ::CreateFile(Filename, Access, SharedModeFlags, NULL, Create, FILE_ATTRIBUTE_NORMAL, NULL);
325
if(Handle == INVALID_HANDLE_VALUE)
327
if(Flags & NSerializer::OutputErrorIfFail)
328
nuxError(TEXT("Failed to read file: %s"), Filename);
333
NSerializer* NFileManagerWindows::CreateFileReader(const TCHAR* Filename, DWORD Flags, NOutputDevice& Error)
335
HANDLE Handle = CreateReadFileHandle(Filename, Flags);
336
if(Handle == INVALID_HANDLE_VALUE)
340
return new NWindowsSerialFileReader(Handle, Error);
343
HANDLE NFileManagerWindows::CreateWriteFileHandle(const TCHAR* Filename, DWORD Flags)
345
if(Flags & NSerializer::OverWriteReadOnly)
347
::SetFileAttributes(Filename, 0);
350
DWORD Access = GENERIC_WRITE;
351
DWORD SharedModeFlags = 0;
352
SharedModeFlags |= (Flags & NSerializer::Read) ? FILE_SHARE_READ : 0;
353
SharedModeFlags |= (Flags & NSerializer::Write) ? FILE_SHARE_WRITE : 0;
356
Create |= (Flags & NSerializer::Append) ? OPEN_ALWAYS : 0;
357
Create |= (Flags & NSerializer::NoOverWrite) ? CREATE_NEW /*fail if the file already exist*/: CREATE_ALWAYS /*create the file if it does not exist*/;
358
HANDLE Handle = ::CreateFile(Filename, Access, SharedModeFlags, NULL, Create, FILE_ATTRIBUTE_NORMAL, NULL);
361
if(Handle == INVALID_HANDLE_VALUE)
363
if(Flags & NSerializer::OutputErrorIfFail)
365
nuxError(TEXT("[NFileManagerWindows::CreateFileWriter] Failed to create file %s (GetLastError: %d)"), Filename, ::GetLastError());
368
if((Flags & NSerializer::Append) && (Handle != INVALID_HANDLE_VALUE))
370
Pos = ::SetFilePointer(Handle, 0, 0, FILE_END);
375
NSerializer* NFileManagerWindows::CreateFileWriter(const TCHAR* Filename,
377
NOutputDevice& Error)
379
HANDLE Handle = CreateWriteFileHandle(Filename, Flags);
380
if(Handle == INVALID_HANDLE_VALUE)
384
return new NWindowsSerialFileWriter(Handle, Error);
388
@return Size of the File. Return -1 if an error occurs.
390
t_s64 NFileManagerWindows::FileSize(const TCHAR* Filename)
392
HANDLE Handle = ::CreateFile(Filename, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
393
if(Handle == INVALID_HANDLE_VALUE)
396
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
399
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
404
nuxDebugMsg(TEXT("[NFileManagerWindows::FileSize] Failed to open file %s for attribute read (GetLastError: %d - %s)"), Filename, ::GetLastError(), lpMsgBuf);
406
::LocalFree(lpMsgBuf);
410
if(::GetFileSizeEx(Handle, INL_REINTERPRET_CAST(PLARGE_INTEGER, &Size)) == 0)
418
bool NFileManagerWindows::FileExist(const TCHAR* Filename)
420
WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
421
if(::GetFileAttributesEx(Filename, GetFileExInfoStandard, INL_STATIC_CAST(void*, &FileAttrData)))
428
int NFileManagerWindows::Copy(const TCHAR* DestFile,
429
const TCHAR* SrcFile,
430
bool OverWriteExisting,
431
bool OverWriteReadOnly,
432
NFileTransferMonitor* Monitor)
434
// In case file exist and OverWriteReadOnly is true, Remove the ReadOnly attribute from the file.
435
if(OverWriteReadOnly)
437
::SetFileAttributes(DestFile, FILE_ATTRIBUTE_NORMAL);
439
DWORD Flags = (OverWriteExisting ? 0 : COPY_FILE_FAIL_IF_EXISTS);
440
BOOL* pCancel = NULL;
442
pCancel = &(Monitor->m_bCancel);
443
if(::CopyFileEx(SrcFile, DestFile, NFileTransferMonitor::CopyProgressRoutine, INL_REINTERPRET_CAST(void*, Monitor), pCancel, Flags) == 0)
446
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
449
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
454
nuxDebugMsg(TEXT("[NFileManagerWindows::Copy] Error copying file from '%s' to '%s'(GetLastError: %d - %s)"), SrcFile, DestFile, ::GetLastError(), lpMsgBuf);
455
::LocalFree(lpMsgBuf);
458
::SetFileAttributes(DestFile, FILE_ATTRIBUTE_NORMAL);
462
bool NFileManagerWindows::Delete(const TCHAR* Filename, bool OverWriteReadOnly)
464
if(OverWriteReadOnly)
466
::SetFileAttributes(Filename, FILE_ATTRIBUTE_NORMAL);
468
if((::DeleteFile(Filename) == 0) && (GetLastError() != ERROR_FILE_NOT_FOUND))
471
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
474
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
479
nuxDebugMsg(TEXT("[NFileManagerWindows::Delete] Error deleting file '%s' (GetLastError: %d - %s)"), Filename, ::GetLastError(), lpMsgBuf);
480
::LocalFree(lpMsgBuf);
486
bool NFileManagerWindows::IsReadOnly(const TCHAR* Filename)
488
WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
489
if(::GetFileAttributesEx(Filename, GetFileExInfoStandard, INL_STATIC_CAST(void*, &FileAttrData)))
491
return((FileAttrData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
495
nuxDebugMsg(TEXT("[NFileManagerWindows::IsReadOnly]: Error reading attributes for file '%s'"), Filename);
500
bool NFileManagerWindows::IsDirectory(const TCHAR* DirectoryName)
502
WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
503
if(::GetFileAttributesEx(DirectoryName, GetFileExInfoStandard, INL_STATIC_CAST(void*, &FileAttrData)))
505
return ((FileAttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
509
nuxDebugMsg(TEXT("[NFileManagerWindows::IsDirectory]: Error reading attributes for directory '%s'"), DirectoryName);
514
bool NFileManagerWindows::IsHidden(const TCHAR* Filename)
516
WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
517
if(::GetFileAttributesEx(Filename, GetFileExInfoStandard, INL_STATIC_CAST(void*, &FileAttrData)))
519
return ((FileAttrData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0);
523
nuxDebugMsg(TEXT("[NFileManagerWindows::IsHidden]: Error reading attributes for file '%s'"), Filename);
529
@return TRUE is the file exist.
531
bool NFileManagerWindows::GetFileAttribute(const TCHAR* Filename, bool& isDirectory, bool& isReadOnly, bool& isHidden, t_s64& Size)
537
WIN32_FILE_ATTRIBUTE_DATA FileAttrData;
538
if(::GetFileAttributesEx(Filename, GetFileExInfoStandard, (void*) &FileAttrData))
540
isDirectory = ((FileAttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
541
isReadOnly = ((FileAttrData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
542
isHidden = ((FileAttrData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0);
543
Size = FileAttrData.nFileSizeLow | ((t_s64)(FileAttrData.nFileSizeHigh) << 32);
547
nuxDebugMsg(TEXT("[NFileManagerWindows::GetFileAttribute]: Error reading attributes for file '%s'"), Filename);
553
bool NFileManagerWindows::Move(const TCHAR* Dest,
557
NFileTransferMonitor* Monitor)
559
DWORD Flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_FAIL_IF_NOT_TRACKABLE;
560
Flags |= (EvenIfReadOnly ? MOVEFILE_REPLACE_EXISTING : 0);
562
BOOL* pCancel = NULL;
564
pCancel = &(Monitor->m_bCancel);
565
if(::MoveFileWithProgress(Src, Dest, NFileTransferMonitor::CopyProgressRoutine, INL_REINTERPRET_CAST(void*, Monitor), Flags) != 0)
567
nuxDebugMsg(TEXT("[NFileManagerWindows::Move] Error moving file '%s' to '%s' (GetLastError: %d)"), Src, Dest, ::GetLastError());
573
bool NFileManagerWindows::MakeDirectory(const TCHAR* Path, bool CreateCompletePath)
575
if(CreateCompletePath)
577
return NFileManagerGeneric::MakeDirectory(Path, CreateCompletePath);
579
if((::CreateDirectory(Path, NULL) == 0) && (::GetLastError() != ERROR_ALREADY_EXISTS))
581
nuxDebugMsg(TEXT("[NFileManagerWindows::MakeDirectory] Error creating directory '%s' (GetLastError: %d)"), Path, ::GetLastError());
587
bool NFileManagerWindows::DeleteDirectory(const TCHAR* Path, bool DeleteContentFirst)
589
if(DeleteContentFirst)
591
return NFileManagerGeneric::DeleteDirectory(Path, DeleteContentFirst);
593
if((::RemoveDirectory(Path) == 0) && (::GetLastError() != ERROR_FILE_NOT_FOUND))
595
nuxDebugMsg(TEXT("[NFileManagerWindows::DeleteDirectory] Error deleting directory '%s' (GetLastError: %d)"), Path, ::GetLastError());
601
void NFileManagerWindows::FindFiles(std::vector<NString>& Result, const TCHAR* Filename, bool Files, bool Directories)
603
HANDLE Handle=INVALID_HANDLE_VALUE;
604
WIN32_FIND_DATA SearchData;
605
Handle = ::FindFirstFile(Filename, &SearchData);
606
if(Handle != INVALID_HANDLE_VALUE)
610
if(Stricmp(SearchData.cFileName, TEXT(".")) &&
611
Stricmp(SearchData.cFileName, TEXT("..")) &&
612
!(SearchData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) &&
613
!(SearchData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
616
if((SearchData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? Directories : Files)
617
Result.push_back(NString(SearchData.cFileName));
619
} while(::FindNextFile(Handle, &SearchData));
621
if(Handle != INVALID_HANDLE_VALUE)
625
void NFileManagerWindows::ListFilesInDirectory(std::vector<NString>& Result, const TCHAR* DirName)
627
WIN32_FIND_DATA SearchData;
628
HANDLE Handle = INVALID_HANDLE_VALUE;
629
NString DirectoryName = DirName;
630
DirectoryName += TEXT("\\*");
631
Handle = ::FindFirstFile(DirectoryName.GetTCharPtr(), &SearchData);
632
if (Handle != INVALID_HANDLE_VALUE)
634
// List all the other files in the directory.
637
if(Stricmp(SearchData.cFileName,TEXT(".")) &&
638
Stricmp(SearchData.cFileName,TEXT("..")) &&
639
!(SearchData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) &&
640
!(SearchData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
642
Result.push_back(NString(SearchData.cFileName));
644
} while(::FindNextFile(Handle, &SearchData));
646
if(Handle != INVALID_HANDLE_VALUE)
650
double NFileManagerWindows::GetFileAgeSeconds(const TCHAR* Filename)
652
struct _stat FileInfo;
653
if(_tstat(Filename, &FileInfo) == 0)
657
FileTime = FileInfo.st_mtime;
660
return difftime(CurrentTime, FileTime);
665
time_t NFileManagerWindows::GetFileLastModified(const TCHAR* Filename)
667
struct _stat FileInfo;
668
if(_tstat(Filename, &FileInfo) == 0)
671
FileTime = FileInfo.st_mtime;
678
// bool GetFileLastModified(const TCHAR* Filename, SYSTEMTIME& sysTime, bool bLocalTime)
680
// ZeroMemory(&sysTime, sizeof(SYSTEMTIME));
682
// DWORD dwAttr = ::GetFileAttributes(Filename);
685
// if (dwAttr == 0xFFFFFFFF)
688
// WIN32_FIND_DATA findFileData;
689
// HANDLE hFind = ::FindFirstFile((LPTSTR)Filename, &findFileData);
691
// if (hFind == INVALID_HANDLE_VALUE)
694
// ::FindClose(hFind);
696
// FILETIME ft = findFileData.ftLastWriteTime;
699
// ::FileTimeToLocalFileTime(&findFileData.ftLastWriteTime, &ft);
701
// ::FileTimeToSystemTime(&ft, &sysTime);
705
bool NFileManagerWindows::SetDefaultDirectory()
707
return CALL_OS_TCHAR_FUNCTION(SetCurrentDirectoryW(GetProgramDirectory()),SetCurrentDirectoryA(TCHAR_TO_ANSI(GetProgramDirectory().GetTCharPtr())))!=0;
710
NString NFileManagerWindows::GetCurrentDirectory()
713
TCHAR Buffer[1024]=TEXT("");
714
::GetCurrentDirectoryW(INL_ARRAY_COUNT(Buffer),Buffer);
715
return NString(Buffer);
717
ANSICHAR Buffer[1024]="";
718
::GetCurrentDirectoryA(INL_ARRAY_COUNT(Buffer),Buffer);
719
return NString(Buffer);
723
bool NFileManagerWindows::GetTimeStamp(const TCHAR* Filename, FileTimeStamp& Timestamp)
725
Memzero(&Timestamp, sizeof(Timestamp));
726
struct _stat FileInfo;
727
if(_tstat(Filename, &FileInfo) == 0)
731
// FileTime represents seconds elapsed since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC).
732
FileTime = FileInfo.st_mtime;
734
// _gmtime64_s can express time up to 23:59:59, December 31, 3000, UTC
735
_gmtime64_s(&pTime, &FileTime);
737
Timestamp.Day = pTime.tm_mday;
738
Timestamp.DayOfWeek = pTime.tm_wday;
739
Timestamp.DayOfYear = pTime.tm_yday;
740
Timestamp.Hour = pTime.tm_hour;
741
Timestamp.Minute = pTime.tm_min;
742
Timestamp.Second = pTime.tm_sec;
743
Timestamp.Year = pTime.tm_year + 1900;
746
// FileTime represents seconds elapsed since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC).
747
FileTime = FileInfo.st_mtime;
748
// gmtime can express time up to 03:14:07 January 19, 2038, UTC
749
tm* pTime = gmtime(&FileTime);
751
Timestamp.Day = pTime->tm_mday;
752
Timestamp.DayOfWeek = pTime->tm_wday;
753
Timestamp.DayOfYear = pTime->tm_yday;
754
Timestamp.Hour = pTime->tm_hour;
755
Timestamp.Minute = pTime->tm_min;
756
Timestamp.Second = pTime->tm_sec;
757
Timestamp.Year = pTime->tm_year + 1900;