OS_Handle FS_FileOpen(Str8 path, FS_AccessFlags access) { OS_Handle result = { 0 }; M_TempScope(0, 0) { Str8 wpath = Win32_WideStr(temp.arena, path); DWORD dwAccess = 0; DWORD dwShare = FILE_SHARE_READ; DWORD dwCreate = OPEN_EXISTING; if (access & FS_ACCESS_WRITE) { dwCreate = CREATE_ALWAYS; dwAccess = GENERIC_WRITE; } if (access & FS_ACCESS_READ) { dwAccess |= GENERIC_READ; } HANDLE hFile = CreateFileW((LPCWSTR) wpath.data, dwAccess, dwShare, 0, dwCreate, 0, 0); result.v[0] = (hFile == INVALID_HANDLE_VALUE) ? 0 : ((U64) hFile); } return result; } void FS_FileClose(OS_Handle file) { HANDLE hFile = cast(HANDLE) file.v[0]; if (hFile) { CloseHandle(hFile); } } void FS_FileRead(OS_Handle file, void *ptr, U64 size, U64 offset) { HANDLE hFile = cast(HANDLE) file.v[0]; if (hFile) { U8 *buffer = ptr; U64 current = offset; U64 remainder = size; while (remainder != 0) { OVERLAPPED overlapped = { 0 }; overlapped.Offset = cast(DWORD) (current & 0xFFFFFFFF); overlapped.OffsetHigh = cast(DWORD) ((current >> 32) & 0xFFFFFFFF); DWORD toread = (remainder > U32_MAX) ? U32_MAX : (DWORD) remainder; DWORD nread = 0; if (!ReadFile(hFile, buffer, toread, &nread, &overlapped)) { break; } buffer += nread; current += nread; remainder -= nread; } } } void FS_FileWrite(OS_Handle file, void *ptr, U64 size, U64 offset) { HANDLE hFile = cast(HANDLE) file.v[0]; if (hFile) { U8 *buffer = ptr; U64 current = offset; U64 remainder = size; while (remainder != 0) { OVERLAPPED overlapped = { 0 }; overlapped.Offset = cast(DWORD) (current & 0xFFFFFFFF); overlapped.OffsetHigh = cast(DWORD) ((current >> 32) & 0xFFFFFFFF); DWORD towrite = (remainder > U32_MAX) ? U32_MAX : (DWORD) remainder; DWORD nwritten = 0; if (!WriteFile(hFile, buffer, towrite, &nwritten, &overlapped)) { break; } buffer += nwritten; current += nwritten; remainder -= nwritten; } } } U64 FS_FileSize(OS_Handle file) { U64 result = 0; HANDLE hFile = cast(HANDLE) file.v[0]; if (hFile) { DWORD dwLow, dwHigh; dwLow = GetFileSize(hFile, &dwHigh); result = ((U64) dwHigh << 32) | ((U64) dwLow); } return result; } FS_List FS_PathList(M_Arena *arena, Str8 path) { FS_List result = { 0 }; M_TempScope(1, &arena) { Str8 wpath = Win32_WideStr(temp.arena, Sf(temp.arena, "%.*s\\*", Sv(path))); WIN32_FIND_DATAW find = { 0 }; HANDLE hFind = FindFirstFileW((LPCWSTR) wpath.data, &find); while (hFind != INVALID_HANDLE_VALUE) { if (find.cFileName[0] != L'.') { FS_Entry *entry = M_ArenaPush(arena, FS_Entry); entry->type = (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FS_ENTRY_TYPE_DIR : FS_ENTRY_TYPE_FILE; entry->basename = Win32_MultiByteStr(arena, find.cFileName); entry->path = Sf(arena, "%.*s/%.*s", Sv(path), Sv(entry->basename)); FILETIME *wt = &find.ftLastWriteTime; entry->time = ((U64) wt->dwHighDateTime << 32) | ((U64) wt->dwLowDateTime); entry->size = ((U64) find.nFileSizeHigh << 32) | ((U64) find.nFileSizeLow); SLL_Enqueue(result.first, result.last, entry); result.count += 1; } if (!FindNextFileW(hFind, &find)) { break; } } } return result; } Str8 FS_SystemPath(M_Arena *arena, FS_SystemPathType path) { Str8 result = { 0 }; M_TempScope(1, &arena) { switch (path) { case FS_SYSTEM_PATH_EXE: { U64 offset = M_ArenaOffset(temp.arena); DWORD err, nchars; DWORD nSize = 1024; LPWSTR lpFilename = M_ArenaPush(temp.arena, WCHAR, .count = nSize, .flags = M_ARENA_NO_ZERO); for (;;) { nchars = GetModuleFileNameW(0, lpFilename, nSize); err = GetLastError(); if (err != ERROR_INSUFFICIENT_BUFFER) { break; } M_ArenaPop(temp.arena, offset); nSize *= 2; lpFilename = M_ArenaPush(temp.arena, WCHAR, .count = nSize, .flags = M_ARENA_NO_ZERO); } if (err == ERROR_SUCCESS) { while (lpFilename[nchars] != '\\') { nchars -= 1; } lpFilename[nchars] = 0; result = Win32_MultiByteStr(arena, lpFilename); } } break; case FS_SYSTEM_PATH_WORKING: { DWORD nSize = GetCurrentDirectoryW(0, 0); if (nSize != 0) { LPWSTR lpBuffer = M_ArenaPush(temp.arena, WCHAR, .count = nSize, .flags = M_ARENA_NO_ZERO); GetCurrentDirectoryW(nSize, lpBuffer); result = Win32_MultiByteStr(arena, lpBuffer); } } break; case FS_SYSTEM_PATH_USER: { LPWSTR wpath; HRESULT hResult = SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, 0, &wpath); if (hResult == S_OK) { result = Win32_MultiByteStr(arena, wpath); CoTaskMemFree(wpath); } } break; case FS_SYSTEM_PATH_TEMP: { DWORD nSize = MAX_PATH + 1; LPWSTR lpBuffer = M_ArenaPush(temp.arena, WCHAR, .count = nSize); DWORD nchars = GetTempPathW(nSize, lpBuffer); if (nchars != 0) { lpBuffer[nchars - 1] = 0; // Strip trailing backslash result = Win32_MultiByteStr(arena, lpBuffer); } } break; } } return result; }