2025-10-04 11:52:39 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 21:42:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
FS_List FS_PathList(M_Arena *arena, Str8 path) {
|
|
|
|
|
FS_List result = { 0 };
|
2025-10-04 11:52:39 +01:00
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2025-10-05 00:24:51 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|