Files
ed 3e064f6443 progress on sanity_tests.c, adding back md_str8_styllize
Eventually a heavy reduction of base & OS should be provided via libgen or done here to reduce the overall surface area of the library for the user.
Base and os were dragged from raddbg and the original metadesk didn't need as many definitions.
2025-02-13 09:53:46 -05:00

1658 lines
50 KiB
C

#ifdef INTELLISENSE_DIRECTIVES
# include "os_win32.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
MD_API md_global MD_OS_W32_State md_os_w32_state = {0};
////////////////////////////////
//~ rjf: Modern Windows SDK Functions
//
// (We must dynamically link to them, since they can be missing in older SDKs)
typedef HRESULT W32_SetThreadDescription_Type(HANDLE hThread, PCWSTR lpThreadDescription);
MD_API md_global W32_SetThreadDescription_Type* w32_SetThreadDescription_func = 0;
////////////////////////////////
//~ rjf: Time Conversion Helpers
MD_U32
md_os_w32_sleep_ms_from_endt_us(MD_U64 endt_us)
{
MD_U32 sleep_ms = 0;
if (endt_us == MD_MAX_U64) {
sleep_ms = INFINITE;
}
else
{
MD_U64 begint = md_os_now_microseconds();
if (begint < endt_us) {
MD_U64 sleep_us = endt_us - begint;
sleep_ms = (MD_U32)((sleep_us + 999)/1000);
}
}
return sleep_ms;
}
////////////////////////////////
//~ rjf: Entity Functions
MD_OS_W32_Entity*
md_os_w32_entity_alloc(MD_OS_W32_EntityKind kind)
{
MD_OS_W32_Entity *result = 0;
EnterCriticalSection(&md_os_w32_state.entity_mutex);
{
result = md_os_w32_state.entity_free;
if(result)
{
md_sll_stack_pop(md_os_w32_state.entity_free);
}
else
{
result = md_push_array__no_zero(md_os_w32_state.entity_arena, MD_OS_W32_Entity, 1);
}
md_memory_zero_struct(result);
}
LeaveCriticalSection(&md_os_w32_state.entity_mutex);
result->kind = kind;
return result;
}
void
md_os_w32_entity_release(MD_OS_W32_Entity *entity)
{
entity->kind = MD_OS_W32_EntityKind_Null;
EnterCriticalSection(&md_os_w32_state.entity_mutex);
md_sll_stack_push(md_os_w32_state.entity_free, entity);
LeaveCriticalSection(&md_os_w32_state.entity_mutex);
}
////////////////////////////////
//~ rjf: Thread Entry Point
DWORD
md_os_w32_thread_entry_point(void *ptr)
{
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)ptr;
MD_OS_ThreadFunctionType* func = entity->thread.func;
void* thread_ptr = entity->thread.ptr;
MD_TCTX md_tctx_;
md_tctx_init_and_equip(&md_tctx_);
func(thread_ptr);
md_tctx_release();
return 0;
}
////////////////////////////////
//~ rjf: @md_os_hooks System/Process Info (Implemented Per-OS)
MD_OS_SystemInfo*
md_os_get_system_info(void) {
return &md_os_w32_state.system_info;
}
MD_OS_ProcessInfo*
md_os_get_process_info(void) {
return &md_os_w32_state.process_info;
}
////////////////////////////////
//~ rjf: @md_os_hooks Thread Info (Implemented Per-OS)
void
md_os_set_thread_name(MD_String8 name)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
// rjf: windows 10 style
if (w32_SetThreadDescription_func)
{
MD_String16 name16 = md_str16_from(scratch.arena, name);
HRESULT hr = w32_SetThreadDescription_func(GetCurrentThread(), (WCHAR*)name16.str);
}
// rjf: raise-exception style
{
MD_String8 name_copy = md_str8_copy(scratch.arena, name);
#pragma pack(push,8)
typedef struct THREADNAME_INFO THREADNAME_INFO;
struct THREADNAME_INFO
{
MD_U32 dwType; // Must be 0x1000.
char* szName; // Pointer to name (in user addr space).
MD_U32 dwThreadID; // Thread ID (-1=caller thread).
MD_U32 dwFlags; // Reserved for future use, must be zero.
};
#pragma pack(pop)
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = (char *)name_copy.str;
info.dwThreadID = md_os_tid();
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
__try
{
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
#pragma warning(pop)
}
scratch_end(scratch);
}
////////////////////////////////
//~ rjf: @md_os_hooks Aborting (Implemented Per-OS)
void md_os_abort(MD_S32 exit_code) { ExitProcess(exit_code); }
////////////////////////////////
//~ rjf: @md_os_hooks File System (Implemented Per-OS)
//- rjf: files
MD_OS_Handle
md_os_file_open(MD_OS_AccessFlags flags, MD_String8 path)
{
MD_OS_Handle result = {0};
MD_TempArena scratch = md_scratch_begin(0, 0);
{
MD_String16 path16 = md_str16_from(scratch.arena, path);
DWORD access_flags = 0;
DWORD share_mode = 0;
DWORD creation_disposition = OPEN_EXISTING;
if (flags & MD_OS_AccessFlag_Read) { access_flags |= GENERIC_READ; }
if (flags & MD_OS_AccessFlag_Write) { access_flags |= GENERIC_WRITE; }
if (flags & MD_OS_AccessFlag_Execute) { access_flags |= GENERIC_EXECUTE; }
if (flags & MD_OS_AccessFlag_ShareRead) { share_mode |= FILE_SHARE_READ; }
if (flags & MD_OS_AccessFlag_ShareWrite) { share_mode |= FILE_SHARE_WRITE | FILE_SHARE_DELETE; }
if (flags & MD_OS_AccessFlag_Write) { creation_disposition = CREATE_ALWAYS; }
if (flags & MD_OS_AccessFlag_Append) { creation_disposition = OPEN_ALWAYS; }
HANDLE file = CreateFileW((WCHAR*)path16.str, access_flags, share_mode, 0, creation_disposition, FILE_ATTRIBUTE_NORMAL, 0);
if (file != INVALID_HANDLE_VALUE) {
result.u64[0] = (MD_U64)file;
}
}
scratch_end(scratch);
return result;
}
void
md_os_file_close(MD_OS_Handle file) {
if (md_os_handle_match(file, md_os_handle_zero())) { return; }
HANDLE handle = (HANDLE)file.u64[0];
BOOL result = CloseHandle(handle);
(void)result;
}
MD_U64
md_os_file_read(MD_OS_Handle file, MD_Rng1U64 rng, void *out_data)
{
if (md_os_handle_match(file, md_os_handle_zero())) { return 0; }
HANDLE handle = (HANDLE)file.u64[0];
// rjf: md_clamp range by file size
MD_U64 size = 0;
GetFileSizeEx(handle, (LARGE_INTEGER *)&size);
MD_Rng1U64 rng_clamped = md_r1u64(md_clamp_top(rng.md_min, size), md_clamp_top(rng.md_max, size));
MD_U64 total_read_size = 0;
// rjf: read loop
{
MD_U64 to_read = md_dim_1u64(rng_clamped);
for(MD_U64 off = rng.md_min; total_read_size < to_read;)
{
MD_U64 amt64 = to_read - total_read_size;
MD_U32 amt32 = md_u32_from_u64_saturate(amt64);
DWORD read_size = 0;
OVERLAPPED overlapped = {0};
overlapped.Offset = (off&0x00000000ffffffffull);
overlapped.OffsetHigh = (off&0xffffffff00000000ull) >> 32;
ReadFile(handle, (MD_U8 *)out_data + total_read_size, amt32, &read_size, &overlapped);
off += read_size;
total_read_size += read_size;
if (read_size != amt32) {
break;
}
}
}
return total_read_size;
}
MD_U64
md_os_file_write(MD_OS_Handle file, MD_Rng1U64 rng, void *data)
{
if(md_os_handle_match(file, md_os_handle_zero())) { return 0; }
HANDLE win_handle = (HANDLE)file.u64[0];
MD_U64 src_off = 0;
MD_U64 dst_off = rng.md_min;
MD_U64 bytes_to_write_total = rng.md_max-rng.md_min;
MD_U64 total_bytes_written = 0;
for (;src_off < bytes_to_write_total;)
{
void* bytes_src = (void*)((MD_U8*)data + src_off);
MD_U64 bytes_to_write_64 = (bytes_to_write_total-src_off);
MD_U32 bytes_to_write_32 = md_u32_from_u64_saturate(bytes_to_write_64);
MD_U32 bytes_written = 0;
OVERLAPPED overlapped = {0};
overlapped.Offset = (dst_off&0x00000000ffffffffull);
overlapped.OffsetHigh = (dst_off&0xffffffff00000000ull) >> 32;
BOOL success = WriteFile(win_handle, bytes_src, bytes_to_write_32, (DWORD *)&bytes_written, &overlapped);
if (success == 0) {
break;
}
src_off += bytes_written;
dst_off += bytes_written;
total_bytes_written += bytes_written;
}
return total_bytes_written;
}
MD_B32
md_os_file_set_times(MD_OS_Handle file, MD_DateTime time)
{
if(md_os_handle_match(file, md_os_handle_zero())) { return 0; }
MD_B32 result = 0;
{
HANDLE handle = (HANDLE)file.u64[0];
SYSTEMTIME system_time = {0};
md_os_w32_system_time_from_date_time(&system_time, &time);
FILETIME file_time = {0};
result = (SystemTimeToFileTime(&system_time, &file_time) && SetFileTime(handle, &file_time, &file_time, &file_time));
}
return result;
}
MD_FileProperties
md_os_properties_from_file(MD_OS_Handle file)
{
if (md_os_handle_match(file, md_os_handle_zero())) { MD_FileProperties r = {0}; return r; }
MD_FileProperties props = {0};
HANDLE handle = (HANDLE)file.u64[0];
BY_HANDLE_FILE_INFORMATION info;
BOOL info_good = GetFileInformationByHandle(handle, &info);
if (info_good)
{
MD_U32 size_lo = info.nFileSizeLow;
MD_U32 size_hi = info.nFileSizeHigh;
props.size = (MD_U64)size_lo | (((MD_U64)size_hi)<<32);
md_os_w32_dense_time_from_file_time(&props.modified, &info.ftLastWriteTime);
md_os_w32_dense_time_from_file_time(&props.created, &info.ftCreationTime);
props.flags = md_os_w32_file_property_flags_from_dwFileAttributes(info.dwFileAttributes);
}
return props;
}
MD_OS_FileID
md_os_id_from_file(MD_OS_Handle file)
{
if(md_os_handle_match(file, md_os_handle_zero())) { MD_OS_FileID r = {0}; return r; }
MD_OS_FileID result = {0};
HANDLE handle = (HANDLE)file.u64[0];
BY_HANDLE_FILE_INFORMATION info;
BOOL is_ok = GetFileInformationByHandle(handle, &info);
if (is_ok)
{
result.v[0] = info.dwVolumeSerialNumber;
result.v[1] = info.nFileIndexLow;
result.v[2] = info.nFileIndexHigh;
}
return result;
}
MD_B32
md_os_delete_file_at_path(MD_String8 path)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 path16 = md_str16_from(scratch.arena, path);
MD_B32 result = DeleteFileW((WCHAR*)path16.str);
scratch_end(scratch);
return result;
}
MD_B32
md_os_copy_file_path(MD_String8 dst, MD_String8 src)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 dst16 = md_str16_from(scratch.arena, dst);
MD_String16 src16 = md_str16_from(scratch.arena, src);
MD_B32 result = CopyFileW((WCHAR*)src16.str, (WCHAR*)dst16.str, 0);
scratch_end(scratch);
return result;
}
MD_String8
md_os_full_path_from_path__ainfo(MD_AllocatorInfo ainfo, MD_String8 path)
{
MD_TempArena scratch = md_scratch_begin(ainfo);
DWORD buffer_size = MAX_PATH + 1;
MD_U16* buffer = md_push_array__no_zero(scratch.arena, MD_U16, buffer_size);
MD_String16 path16 = md_str16_from(scratch.arena, path);
DWORD path16_size = GetFullPathNameW((WCHAR*)path16.str, buffer_size, (WCHAR*)buffer, NULL);
MD_String8 full_path = md_str8_from(ainfo, md_str16(buffer, path16_size));
scratch_end(scratch);
return full_path;
}
MD_B32
md_os_file_path_exists(MD_String8 path)
{
MD_TempArena scratch = md_scratch_begin(0,0);
MD_String16 path16 = md_str16_from(scratch.arena, path);
DWORD attributes = GetFileAttributesW((WCHAR *)path16.str);
MD_B32 exists = (attributes != INVALID_FILE_ATTRIBUTES) && !!(~attributes & FILE_ATTRIBUTE_DIRECTORY);
scratch_end(scratch);
return exists;
}
MD_FileProperties
md_os_properties_from_file_path(MD_String8 path)
{
MD_FileProperties props = {0};
WIN32_FIND_DATAW find_data = {0};
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 path16 = md_str16_from(scratch.arena, path);
HANDLE handle = FindFirstFileW((WCHAR *)path16.str, &find_data);
if (handle != INVALID_HANDLE_VALUE)
{
props.size = md_compose_64bit(find_data.nFileSizeHigh, find_data.nFileSizeLow);
md_os_w32_dense_time_from_file_time(&props.created, &find_data.ftCreationTime);
md_os_w32_dense_time_from_file_time(&props.modified, &find_data.ftLastWriteTime);
props.flags = md_os_w32_file_property_flags_from_dwFileAttributes(find_data.dwFileAttributes);
}
FindClose(handle);
scratch_end(scratch);
return props;
}
//- rjf: file maps
MD_OS_Handle
md_os_file_map_open(MD_OS_AccessFlags flags, MD_OS_Handle file)
{
MD_OS_Handle map = {0};
{
HANDLE file_handle = (HANDLE)file.u64[0];
DWORD protect_flags = 0;
{
switch(flags)
{
default: {} break;
case MD_OS_AccessFlag_Read:
{ protect_flags = PAGE_READONLY; }
break;
case MD_OS_AccessFlag_Write:
case MD_OS_AccessFlag_Read|MD_OS_AccessFlag_Write:
{ protect_flags = PAGE_READWRITE; }
break;
case MD_OS_AccessFlag_Execute:
case MD_OS_AccessFlag_Read|MD_OS_AccessFlag_Execute:
{ protect_flags = PAGE_EXECUTE_READ; }
break;
case MD_OS_AccessFlag_Execute|MD_OS_AccessFlag_Write|MD_OS_AccessFlag_Read:
case MD_OS_AccessFlag_Execute|MD_OS_AccessFlag_Write:
{ protect_flags = PAGE_EXECUTE_READWRITE; }
break;
}
}
HANDLE map_handle = CreateFileMappingA(file_handle, 0, protect_flags, 0, 0, 0);
map.u64[0] = (MD_U64)map_handle;
}
return map;
}
void
md_os_file_map_close(MD_OS_Handle map)
{
HANDLE handle = (HANDLE)map.u64[0];
BOOL result = CloseHandle(handle);
(void)result;
}
void*
md_os_file_map_view_open(MD_OS_Handle map, MD_OS_AccessFlags flags, MD_Rng1U64 range)
{
HANDLE handle = (HANDLE)map.u64[0];
MD_U32 off_lo = (MD_U32)((range.md_min&0x00000000ffffffffull)>>0);
MD_U32 off_hi = (MD_U32)((range.md_min&0xffffffff00000000ull)>>32);
MD_U64 size = md_dim_1u64(range);
DWORD access_flags = 0;
{
switch(flags)
{
default:{}break;
case MD_OS_AccessFlag_Read: { access_flags = FILE_MAP_READ; } break;
case MD_OS_AccessFlag_Write: { access_flags = FILE_MAP_WRITE; } break;
case MD_OS_AccessFlag_Read | MD_OS_AccessFlag_Write: { access_flags = FILE_MAP_ALL_ACCESS; } break;
case MD_OS_AccessFlag_Execute:
case MD_OS_AccessFlag_Read | MD_OS_AccessFlag_Execute:
case MD_OS_AccessFlag_Write| MD_OS_AccessFlag_Execute:
case MD_OS_AccessFlag_Read | MD_OS_AccessFlag_Write | MD_OS_AccessFlag_Execute:
{
access_flags = FILE_MAP_ALL_ACCESS|FILE_MAP_EXECUTE;
}
break;
}
}
void* result = MapViewOfFile(handle, access_flags, off_hi, off_lo, size);
return result;
}
void
md_os_file_map_view_close(MD_OS_Handle map, void* ptr, MD_Rng1U64 range) {
BOOL result = UnmapViewOfFile(ptr);
(void)result;
}
//- rjf: directory iteration
MD_OS_FileIter*
md_os_file_iter_begin__ainfo(MD_AllocatorInfo ainfo, MD_String8 path, MD_OS_FileIterFlags flags)
{
MD_TempArena scratch = md_scratch_begin(ainfo);
MD_String8 path_with_wildcard = md_str8_cat(scratch.arena, path, md_str8_lit("\\*"));
MD_String16 path16 = md_str16_from(scratch.arena, path_with_wildcard);
MD_OS_FileIter*
iter = md_alloc_array(ainfo, MD_OS_FileIter, 1);
iter->flags = flags;
MD_OS_W32_FileIter* w32_iter = (MD_OS_W32_FileIter*)iter->memory;
if (path.size == 0)
{
w32_iter->is_volume_iter = 1;
WCHAR buffer[512] = {0};
DWORD length = GetLogicalDriveStringsW(sizeof(buffer), buffer);
MD_String8List drive_strings = {0};
for(MD_U64 off = 0; off < (MD_U64)length;)
{
MD_String16 next_drive_string_16 = md_str16_cstring((MD_U16*)buffer + off);
off += next_drive_string_16.size + 1;
MD_String8 next_drive_string = md_str8_from(ainfo, next_drive_string_16);
next_drive_string = md_str8_chop_last_slash(next_drive_string);
md_str8_list_push(scratch.arena, &drive_strings, next_drive_string);
}
w32_iter->drive_strings = md_str8_array_from_list(ainfo, &drive_strings);
w32_iter->drive_strings_iter_idx = 0;
}
else
{
w32_iter->handle = FindFirstFileW((WCHAR*)path16.str, &w32_iter->find_data);
}
scratch_end(scratch);
return iter;
}
MD_B32
md_os_file_iter_next__ainfo(MD_AllocatorInfo ainfo, MD_OS_FileIter* iter, MD_OS_FileInfo* info_out)
{
MD_B32 result = 0;
MD_OS_FileIterFlags flags = iter->flags;
MD_OS_W32_FileIter* w32_iter = (MD_OS_W32_FileIter*)iter->memory;
switch (w32_iter->is_volume_iter)
{
//- rjf: file iteration
default:
case 0:
{
if (!(flags & MD_OS_FileIterFlag_Done) && w32_iter->handle != INVALID_HANDLE_VALUE)
{
do
{
// check is usable
MD_B32 usable_file = 1;
WCHAR* file_name = w32_iter->find_data.cFileName;
DWORD attributes = w32_iter->find_data.dwFileAttributes;
if (file_name[0] == '.')
{
if (flags & MD_OS_FileIterFlag_SkipHiddenFiles) {
usable_file = 0;
}
else if (file_name[1] == 0){
usable_file = 0;
}
else if (file_name[1] == '.' && file_name[2] == 0) {
usable_file = 0;
}
}
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (flags & MD_OS_FileIterFlag_SkipFolders) {
usable_file = 0;
}
}
else
{
if (flags & MD_OS_FileIterFlag_SkipFiles) {
usable_file = 0;
}
}
// emit if usable
if (usable_file)
{
info_out->name = md_str8_from(ainfo, md_str16_cstring((MD_U16*)file_name));
info_out->props.size = (MD_U64)w32_iter->find_data.nFileSizeLow | (((MD_U64)w32_iter->find_data.nFileSizeHigh) << 32);
md_os_w32_dense_time_from_file_time(&info_out->props.created, &w32_iter->find_data.ftCreationTime);
md_os_w32_dense_time_from_file_time(&info_out->props.modified, &w32_iter->find_data.ftLastWriteTime);
info_out->props.flags = md_os_w32_file_property_flags_from_dwFileAttributes(attributes);
result = 1;
if (!FindNextFileW(w32_iter->handle, &w32_iter->find_data)) {
iter->flags |= MD_OS_FileIterFlag_Done;
}
break;
}
}
while (FindNextFileW(w32_iter->handle, &w32_iter->find_data));
}
}
break;
//- rjf: volume iteration
case 1:
{
result = w32_iter->drive_strings_iter_idx < w32_iter->drive_strings.count;
if (result != 0)
{
md_memory_zero_struct(info_out);
info_out->name = w32_iter->drive_strings.v[w32_iter->drive_strings_iter_idx];
info_out->props.flags |= MD_FilePropertyFlag_IsFolder;
w32_iter->drive_strings_iter_idx += 1;
}
}
break;
}
if (!result)
{
iter->flags |= MD_OS_FileIterFlag_Done;
}
return result;
}
void
md_os_file_iter_end(MD_OS_FileIter* iter) {
MD_OS_W32_FileIter* w32_iter = (MD_OS_W32_FileIter*)iter->memory;
FindClose(w32_iter->handle);
}
//- rjf: directory creation
MD_B32
md_os_make_directory(MD_String8 path)
{
MD_B32 result = 0;
MD_TempArena scratch = md_scratch_begin(0, 0);
{
MD_String16 name16 = md_str16_from(scratch.arena, path);
WIN32_FILE_ATTRIBUTE_DATA attributes = {0};
GetFileAttributesExW((WCHAR*)name16.str, GetFileExInfoStandard, &attributes);
if (attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
result = 1;
}
else if (CreateDirectoryW((WCHAR*)name16.str, 0)) {
result = 1;
}
}
scratch_end(scratch);
return(result);
}
////////////////////////////////
//~ rjf: @md_os_hooks Shared Memory (Implemented Per-OS)
MD_OS_Handle
md_os_shared_memory_alloc(MD_U64 size, MD_String8 name)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 name16 = md_str16_from(scratch.arena, name);
HANDLE file = CreateFileMappingW(INVALID_HANDLE_VALUE,
0,
PAGE_READWRITE,
(MD_U32)((size & 0xffffffff00000000) >> 32),
(MD_U32)((size & 0x00000000ffffffff)),
(WCHAR *)name16.str
);
scratch_end(scratch);
MD_OS_Handle result = {(MD_U64)file};
return result;
}
MD_OS_Handle
md_os_shared_memory_open(MD_String8 name)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 name16 = md_str16_from(scratch.arena, name);
HANDLE file = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, (WCHAR *)name16.str);
scratch_end(scratch);
MD_OS_Handle result = {(MD_U64)file};
return result;
}
void
md_os_shared_memory_close(MD_OS_Handle handle) {
HANDLE file = (HANDLE)(handle.u64[0]);
CloseHandle(file);
}
void*
md_os_shared_memory_view_open(MD_OS_Handle handle, MD_Rng1U64 range)
{
HANDLE file = (HANDLE)(handle.u64[0]);
MD_U64 offset = range.md_min;
MD_U64 size = range.md_max-range.md_min;
void* ptr = MapViewOfFile(file, FILE_MAP_ALL_ACCESS,
(MD_U32)((offset & 0xffffffff00000000) >> 32),
(MD_U32)((offset & 0x00000000ffffffff)),
size
);
return ptr;
}
void
md_os_shared_memory_view_close(MD_OS_Handle handle, void *ptr, MD_Rng1U64 range) {
UnmapViewOfFile(ptr);
}
////////////////////////////////
//~ rjf: @md_os_hooks Time (Implemented Per-OS)
MD_U64
md_os_now_microseconds(void) {
MD_U64 result = 0;
LARGE_INTEGER large_int_counter;
if (QueryPerformanceCounter(&large_int_counter)) {
result = (large_int_counter.QuadPart * md_million(1)) / md_os_w32_state.microsecond_resolution;
}
return result;
}
MD_U32
md_os_now_unix(void) {
FILETIME file_time;
GetSystemTimeAsFileTime(&file_time);
MD_U64 win32_time = ((MD_U64)file_time.dwHighDateTime << 32) | file_time.dwLowDateTime;
MD_U64 unix_time64 = ((win32_time - 0x19DB1DED53E8000ULL) / 10000000);
MD_U32 unix_time32 = (MD_U32)unix_time64;
return unix_time32;
}
MD_DateTime
md_os_now_universal_time(void) {
SYSTEMTIME systime = {0};
GetSystemTime(&systime);
MD_DateTime result = {0};
md_os_w32_date_time_from_system_time(&result, &systime);
return result;
}
MD_DateTime
md_os_universal_time_from_local(MD_DateTime* date_time)
{
SYSTEMTIME systime = {0};
md_os_w32_system_time_from_date_time(&systime, date_time);
FILETIME ftime = {0};
SystemTimeToFileTime(&systime, &ftime);
FILETIME ftime_local = {0};
LocalFileTimeToFileTime(&ftime, &ftime_local);
FileTimeToSystemTime(&ftime_local, &systime);
MD_DateTime result = {0};
md_os_w32_date_time_from_system_time(&result, &systime);
return result;
}
MD_DateTime
md_os_local_time_from_universal(MD_DateTime* date_time)
{
SYSTEMTIME systime = {0};
md_os_w32_system_time_from_date_time(&systime, date_time);
FILETIME ftime = {0};
SystemTimeToFileTime(&systime, &ftime);
FILETIME ftime_local = {0};
FileTimeToLocalFileTime(&ftime, &ftime_local);
FileTimeToSystemTime(&ftime_local, &systime);
MD_DateTime result = {0};
md_os_w32_date_time_from_system_time(&result, &systime);
return result;
}
void
md_os_sleep_milliseconds(MD_U32 msec) {
Sleep(msec);
}
////////////////////////////////
//~ rjf: @md_os_hooks Child Processes (Implemented Per-OS)
MD_OS_Handle
md_os_process_launch(MD_OS_ProcessLaunchParams* params)
{
MD_OS_Handle result = {0};
MD_TempArena scratch = md_scratch_begin(0, 0);
//- rjf: form full command string
MD_String8 cmd = {0};
{
MD_StringJoin
join_params = {0};
join_params.pre = md_str8_lit("\"");
join_params.sep = md_str8_lit("\" \"");
join_params.post = md_str8_lit("\"");
cmd = md_str8_list_join(scratch.arena, &params->cmd_line, &join_params);
}
//- rjf: form environment
MD_B32 use_null_env_arg = 0;
MD_String8 env = {0};
{
MD_StringJoin
join_params2 = {0};
join_params2.sep = md_str8_lit("\0");
join_params2.post = md_str8_lit("\0");
MD_String8List all_opts = params->env;
if (params->inherit_env != 0)
{
if (all_opts.node_count != 0)
{
md_memory_zero_struct(&all_opts);
for (MD_String8Node *n = params->env.first; n != 0; n = n->next) {
md_str8_list_push(scratch.arena, &all_opts, n->string);
}
for (MD_String8Node *n = md_os_w32_state.process_info.environment.first; n != 0; n = n->next) {
md_str8_list_push(scratch.arena, &all_opts, n->string);
}
}
else {
use_null_env_arg = 1;
}
}
if (use_null_env_arg == 0) {
env = md_str8_list_join(scratch.arena, &all_opts, &join_params2);
}
}
//- rjf: utf-8 -> utf-16
MD_String16 cmd16 = md_str16_from(scratch.arena, cmd);
MD_String16 dir16 = md_str16_from(scratch.arena, params->path);
MD_String16 env16 = {0};
if (use_null_env_arg == 0) {
env16 = md_str16_from(scratch.arena, env);
}
//- rjf: determine creation flags
DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT;
if (params->consoleless) {
creation_flags |= CREATE_NO_WINDOW;
}
//- rjf: launch
STARTUPINFOW startup_info = {sizeof(startup_info)};
PROCESS_INFORMATION process_info = {0};
if (CreateProcessW(0, (WCHAR*)cmd16.str, 0, 0, 0, creation_flags, use_null_env_arg ? 0 : (WCHAR*)env16.str, (WCHAR*)dir16.str, &startup_info, &process_info))
{
result.u64[0] = (MD_U64)process_info.hProcess;
CloseHandle(process_info.hThread);
}
scratch_end(scratch);
return result;
}
MD_B32
md_os_process_join(MD_OS_Handle handle, MD_U64 endt_us) {
HANDLE process = (HANDLE)(handle.u64[0]);
DWORD sleep_ms = md_os_w32_sleep_ms_from_endt_us(endt_us);
DWORD result = WaitForSingleObject(process, sleep_ms);
return (result == WAIT_OBJECT_0);
}
void
md_os_process_detach(MD_OS_Handle handle) {
HANDLE process = (HANDLE)(handle.u64[0]);
CloseHandle(process);
}
////////////////////////////////
//~ rjf: @md_os_hooks Threads (Implemented Per-OS)
MD_OS_Handle
md_os_thread_launch(MD_OS_ThreadFunctionType* func, void* ptr, void* params) {
MD_OS_W32_Entity* entity = md_os_w32_entity_alloc(MD_OS_W32_EntityKind_Thread);
entity->thread.func = func;
entity->thread.ptr = ptr;
entity->thread.handle = CreateThread(0, 0, md_os_w32_thread_entry_point, entity, 0, &entity->thread.tid);
MD_OS_Handle result = {md_int_from_ptr(entity)};
return result;
}
MD_B32
md_os_thread_join(MD_OS_Handle handle, MD_U64 endt_us)
{
DWORD sleep_ms = md_os_w32_sleep_ms_from_endt_us(endt_us);
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(handle.u64[0]);
DWORD wait_result = WAIT_OBJECT_0;
if (entity != 0) {
wait_result = WaitForSingleObject(entity->thread.handle, sleep_ms);
}
md_os_w32_entity_release(entity);
return (wait_result == WAIT_OBJECT_0);
}
void
md_os_thread_detach(MD_OS_Handle thread) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(thread.u64[0]);
md_os_w32_entity_release(entity);
}
////////////////////////////////
//~ rjf: @md_os_hooks Synchronization Primitives (Implemented Per-OS)
//- rjf: mutexes
MD_OS_Handle
md_os_mutex_alloc(void) {
MD_OS_W32_Entity* entity = md_os_w32_entity_alloc(MD_OS_W32_EntityKind_Mutex);
InitializeCriticalSection(&entity->mutex);
MD_OS_Handle result = {md_int_from_ptr(entity)};
return result;
}
void
md_os_mutex_release(MD_OS_Handle mutex) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(mutex.u64[0]);
md_os_w32_entity_release(entity);
}
void
md_os_mutex_take(MD_OS_Handle mutex) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(mutex.u64[0]);
EnterCriticalSection(&entity->mutex);
}
void
md_os_mutex_drop(MD_OS_Handle mutex)
{
MD_OS_W32_Entity *entity = (MD_OS_W32_Entity*)md_ptr_from_int(mutex.u64[0]);
LeaveCriticalSection(&entity->mutex);
}
//- rjf: reader/writer mutexes
MD_OS_Handle
md_os_rw_mutex_alloc(void) {
MD_OS_W32_Entity* entity = md_os_w32_entity_alloc(MD_OS_W32_EntityKind_RWMutex);
InitializeSRWLock(&entity->rw_mutex);
MD_OS_Handle result = {md_int_from_ptr(entity)};
return result;
}
void
md_os_rw_mutex_release(MD_OS_Handle rw_mutex) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(rw_mutex.u64[0]);
md_os_w32_entity_release(entity);
}
void
md_os_rw_mutex_take_r(MD_OS_Handle rw_mutex) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(rw_mutex.u64[0]);
AcquireSRWLockShared(&entity->rw_mutex);
}
void
md_os_rw_mutex_drop_r(MD_OS_Handle rw_mutex) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(rw_mutex.u64[0]);
ReleaseSRWLockShared(&entity->rw_mutex);
}
void
md_os_rw_mutex_take_w(MD_OS_Handle rw_mutex) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(rw_mutex.u64[0]);
AcquireSRWLockExclusive(&entity->rw_mutex);
}
void
md_os_rw_mutex_drop_w(MD_OS_Handle rw_mutex) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(rw_mutex.u64[0]);
ReleaseSRWLockExclusive(&entity->rw_mutex);
}
//- rjf: condition variables
MD_OS_Handle
md_os_condition_variable_alloc(void) {
MD_OS_W32_Entity* entity = md_os_w32_entity_alloc(MD_OS_W32_EntityKind_ConditionVariable);
InitializeConditionVariable(&entity->cv);
MD_OS_Handle result = {md_int_from_ptr(entity)};
return result;
}
void
md_os_condition_variable_release(MD_OS_Handle cv) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(cv.u64[0]);
md_os_w32_entity_release(entity);
}
MD_B32
md_os_condition_variable_wait(MD_OS_Handle cv, MD_OS_Handle mutex, MD_U64 endt_us) {
MD_U32 sleep_ms = md_os_w32_sleep_ms_from_endt_us(endt_us);
BOOL result = 0;
if (sleep_ms > 0) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(cv.u64[0]);
MD_OS_W32_Entity* mutex_entity = (MD_OS_W32_Entity*)md_ptr_from_int(mutex.u64[0]);
result = SleepConditionVariableCS(&entity->cv, &mutex_entity->mutex, sleep_ms);
}
return result;
}
MD_B32
md_os_condition_variable_wait_rw_r(MD_OS_Handle cv, MD_OS_Handle mutex_rw, MD_U64 endt_us) {
MD_U32 sleep_ms = md_os_w32_sleep_ms_from_endt_us(endt_us);
BOOL result = 0;
if (sleep_ms > 0) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(cv.u64[0]);
MD_OS_W32_Entity* mutex_entity = (MD_OS_W32_Entity*)md_ptr_from_int(mutex_rw.u64[0]);
result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, CONDITION_VARIABLE_LOCKMODE_SHARED);
}
return result;
}
MD_B32
md_os_condition_variable_wait_rw_w(MD_OS_Handle cv, MD_OS_Handle mutex_rw, MD_U64 endt_us) {
MD_U32 sleep_ms = md_os_w32_sleep_ms_from_endt_us(endt_us);
BOOL result = 0;
if(sleep_ms > 0)
{
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(cv.u64[0]);
MD_OS_W32_Entity* mutex_entity = (MD_OS_W32_Entity*)md_ptr_from_int(mutex_rw.u64[0]);
result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, 0);
}
return result;
}
void
md_os_condition_variable_signal(MD_OS_Handle cv) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(cv.u64[0]);
WakeConditionVariable(&entity->cv);
}
void
md_os_condition_variable_broadcast(MD_OS_Handle cv) {
MD_OS_W32_Entity* entity = (MD_OS_W32_Entity*)md_ptr_from_int(cv.u64[0]);
WakeAllConditionVariable(&entity->cv);
}
//- rjf: cross-process semaphores
MD_OS_Handle
md_os_semaphore_alloc(MD_U32 initial_count, MD_U32 max_count, MD_String8 name) {
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 name16 = md_str16_from(scratch.arena, name);
HANDLE handle = CreateSemaphoreW(0, initial_count, max_count, (WCHAR *)name16.str);
MD_OS_Handle result = {(MD_U64)handle};
scratch_end(scratch);
return result;
}
void
md_os_semaphore_release(MD_OS_Handle semaphore) {
HANDLE handle = (HANDLE)semaphore.u64[0];
CloseHandle(handle);
}
MD_OS_Handle
md_os_semaphore_open(MD_String8 name) {
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 name16 = md_str16_from(scratch.arena, name);
HANDLE handle = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS , 0, (WCHAR *)name16.str);
MD_OS_Handle result = {(MD_U64)handle};
scratch_end(scratch);
return result;
}
void
md_os_semaphore_close(MD_OS_Handle semaphore) {
HANDLE handle = (HANDLE)semaphore.u64[0];
CloseHandle(handle);
}
MD_B32
md_os_semaphore_take(MD_OS_Handle semaphore, MD_U64 endt_us) {
MD_U32 sleep_ms = md_os_w32_sleep_ms_from_endt_us(endt_us);
HANDLE handle = (HANDLE)semaphore.u64[0];
DWORD wait_result = WaitForSingleObject(handle, sleep_ms);
MD_B32 result = (wait_result == WAIT_OBJECT_0);
return result;
}
void
md_os_semaphore_drop(MD_OS_Handle semaphore) {
HANDLE handle = (HANDLE)semaphore.u64[0];
ReleaseSemaphore(handle, 1, 0);
}
////////////////////////////////
//~ rjf: @md_os_hooks Dynamically-Loaded Libraries (Implemented Per-OS)
MD_OS_Handle
md_os_library_open(MD_String8 path)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String16 path16 = md_str16_from(scratch.arena, path);
HMODULE mod = LoadLibraryW((LPCWSTR)path16.str);
MD_OS_Handle result = { (MD_U64)mod };
scratch_end(scratch);
return result;
}
MD_VoidProc*
md_os_library_load_proc(MD_OS_Handle lib, MD_String8 name)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
HMODULE mod = (HMODULE)lib.u64[0];
name = md_str8_copy(scratch.arena, name);
MD_VoidProc* result = (MD_VoidProc*)GetProcAddress(mod, (LPCSTR)name.str);
scratch_end(scratch);
return result;
}
void
md_os_library_close(MD_OS_Handle lib) {
HMODULE mod = (HMODULE)lib.u64[0];
FreeLibrary(mod);
}
////////////////////////////////
//~ rjf: @md_os_hooks Safe Calls (Implemented Per-OS)
void
md_os_safe_call(MD_OS_ThreadFunctionType *func, MD_OS_ThreadFunctionType *fail_handler, void *ptr)
{
__try {
func(ptr);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
if (fail_handler != 0)
{
fail_handler(ptr);
}
ExitProcess(1);
}
}
////////////////////////////////
//~ rjf: @md_os_hooks GUIDs (Implemented Per-OS)
MD_OS_Guid
md_os_make_guid(void)
{
MD_OS_Guid result; md_memory_zero_struct(&result);
UUID uuid;
RPC_STATUS rpc_status = UuidCreate(&uuid);
if (rpc_status == RPC_S_OK)
{
result.data1 = uuid.Data1;
result.data2 = uuid.Data2;
result.data3 = uuid.Data3;
md_memory_copy_array(result.data4, uuid.Data4);
}
return result;
}
////////////////////////////////
//~ rjf: @md_os_hooks Entry Points (Implemented Per-OS)
#if MD_BUILD_ENTRY_DEFINING_UNIT
#include <dbghelp.h>
#undef MD_OS_WINDOWS // shlwapi uses its own MD_OS_WINDOWS include inside
#include <shlwapi.h>
MD_B32 win32_g_is_quiet = 0;
HRESULT WINAPI
win32_dialog_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LONG_PTR data)
{
if (msg == TDN_HYPERLINK_CLICKED)
{
ShellExecuteW(NULL, L"open", (LPWSTR)lparam, NULL, NULL, SW_SHOWNORMAL);
}
return S_OK;
}
LONG WINAPI
win32_exception_filter(EXCEPTION_POINTERS* exception_ptrs)
{
if (win32_g_is_quiet)
{
ExitProcess(1);
}
static volatile LONG first = 0;
if (InterlockedCompareExchange(&first, 1, 0) != 0)
{
// prevent failures in other threads to popup same message box
// this handler just shows first thread that crashes
// we are terminating afterwards anyway
for (;;) Sleep(1000);
}
WCHAR buffer[4096] = {0};
int buflen = 0;
DWORD exception_code = exception_ptrs->ExceptionRecord->ExceptionCode;
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L"A fatal exception (code 0x%x) occurred. The process is terminating.\n", exception_code);
// load dbghelp dynamically just in case if it is missing
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
if(dbghelp)
{
DWORD (WINAPI * dbg_SymSetOptions )(DWORD SymOptions);
BOOL (WINAPI * dbg_SymInitializeW)(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess);
BOOL (WINAPI * dbg_StackWalk64)(
DWORD MachineType,
HANDLE hProcess,
HANDLE hThread,
LPSTACKFRAME64 StackFrame,
PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
PVOID (WINAPI * dbg_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase);
DWORD64 (WINAPI * dbg_SymGetModuleBase64 )(HANDLE hProcess, DWORD64 qwAddr);
BOOL (WINAPI * dbg_SymFromAddrW )(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFOW Symbol);
BOOL (WINAPI * dbg_SymGetLineFromAddrW64 )(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINEW64 Line);
BOOL (WINAPI * dbg_SymGetModuleInfoW64 )(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULEW64 ModuleInfo);
* (FARPROC*) &dbg_SymSetOptions = GetProcAddress(dbghelp, "SymSetOptions");
* (FARPROC*) &dbg_SymInitializeW = GetProcAddress(dbghelp, "SymInitializeW");
* (FARPROC*) &dbg_StackWalk64 = GetProcAddress(dbghelp, "StackWalk64");
* (FARPROC*) &dbg_SymFunctionTableAccess64 = GetProcAddress(dbghelp, "SymFunctionTableAccess64");
* (FARPROC*) &dbg_SymGetModuleBase64 = GetProcAddress(dbghelp, "SymGetModuleBase64");
* (FARPROC*) &dbg_SymFromAddrW = GetProcAddress(dbghelp, "SymFromAddrW");
* (FARPROC*) &dbg_SymGetLineFromAddrW64 = GetProcAddress(dbghelp, "SymGetLineFromAddrW64");
* (FARPROC*) &dbg_SymGetModuleInfoW64 = GetProcAddress(dbghelp, "SymGetModuleInfoW64");
if(dbg_SymSetOptions && dbg_SymInitializeW && dbg_StackWalk64 && dbg_SymFunctionTableAccess64 && dbg_SymGetModuleBase64 && dbg_SymFromAddrW && dbg_SymGetLineFromAddrW64 && dbg_SymGetModuleInfoW64)
{
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
CONTEXT* context = exception_ptrs->ContextRecord;
dbg_SymSetOptions(SYMOPT_EXACT_SYMBOLS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
if(dbg_SymInitializeW(process, L"", TRUE))
{
// check that raddbg.pdb file is good
MD_B32 raddbg_pdb_valid = 0;
{
IMAGEHLP_MODULEW64
module = {0};
module.SizeOfStruct = sizeof(module);
if(dbg_SymGetModuleInfoW64(process, (DWORD64)&win32_exception_filter, &module)) {
raddbg_pdb_valid = (module.SymType == SymPdb);
}
}
if(!raddbg_pdb_valid)
{
buflen += wnsprintfW(buffer + buflen, sizeof(buffer) - buflen,
L"\nThe PDB debug information file for this executable is not valid or was not found. Please rebuild binary to get the call stack.\n"
);
}
else
{
STACKFRAME64 frame = {0};
DWORD image_type;
#if defined(_M_AMD64)
image_type = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context->Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context->Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context->Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif defined(_M_ARM64)
image_type = IMAGE_FILE_MACHINE_ARM64;
frame.AddrPC.Offset = context->Pc;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context->Fp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context->Sp;
frame.AddrStack.Mode = AddrModeFlat;
#else
# error Architecture not supported!
#endif
for(MD_U32 idx=0; ;idx++)
{
const MD_U32 max_frames = 32;
if(idx == max_frames)
{
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L"...");
break;
}
if(!dbg_StackWalk64(image_type, process, thread, &frame, context, 0, dbg_SymFunctionTableAccess64, dbg_SymGetModuleBase64, 0))
{
break;
}
MD_U64 address = frame.AddrPC.Offset;
if(address == 0)
{
break;
}
if(idx==0)
{
#if MD_BUILD_CONSOLE_INTERFACE
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L"\nCreate a new issue with this report at %S.\n\n", MD_BUILD_ISSUES_LINK_STRING_LITERAL);
#else
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen,
L"\nPress Ctrl+C to copy this text to clipboard, then create a new issue at\n"
L"<a href=\"%S\">%S</a>\n\n", MD_BUILD_ISSUES_LINK_STRING_LITERAL, MD_BUILD_ISSUES_LINK_STRING_LITERAL);
#endif
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L"Call stack:\n");
}
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L"%u. [0x%I64x]", idx + 1, address);
struct
{
SYMBOL_INFOW info;
WCHAR name[MAX_SYM_NAME];
}
symbol = {0};
symbol.info.SizeOfStruct = sizeof(symbol.info);
symbol.info.MaxNameLen = MAX_SYM_NAME;
DWORD64 displacement = 0;
if (dbg_SymFromAddrW(process, address, &displacement, &symbol.info))
{
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L" %s +%u", symbol.info.Name, (DWORD)displacement);
IMAGEHLP_LINEW64
line = {0};
line.SizeOfStruct = sizeof(line);
DWORD line_displacement = 0;
if(dbg_SymGetLineFromAddrW64(process, address, &line_displacement, &line)) {
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L", %s line %u", PathFindFileNameW(line.FileName), line.LineNumber);
}
}
else
{
IMAGEHLP_MODULEW64
module = {0};
module.SizeOfStruct = sizeof(module);
if (dbg_SymGetModuleInfoW64(process, address, &module)) {
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L" %s", module.ModuleName);
}
}
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L"\n");
}
}
}
}
}
buflen += wnsprintfW(buffer + buflen, md_array_count(buffer) - buflen, L"\nVersion: %S%S", MD_BUILD_VERSION_STRING_LITERAL, MD_BUILD_GIT_HASH_STRING_LITERAL_APPEND);
#if MD_BUILD_CONSOLE_INTERFACE
fwprintf(stderr, L"\n--- Fatal Exception ---\n");
fwprintf(stderr, L"%s\n\n", buffer);
#else
TASKDIALOGCONFIG dialog = {0};
dialog.cbSize = sizeof(dialog);
dialog.dwFlags = TDF_SIZE_TO_CONTENT | TDF_ENABLE_HYPERLINKS | TDF_ALLOW_DIALOG_CANCELLATION;
dialog.pszMainIcon = TD_ERROR_ICON;
dialog.dwCommonButtons = TDCBF_CLOSE_BUTTON;
dialog.pszWindowTitle = L"Fatal Exception";
dialog.pszContent = buffer;
dialog.pfCallback = &win32_dialog_callback;
TaskDialogIndirect(&dialog, 0, 0, 0);
#endif
ExitProcess(1);
}
#undef MD_OS_WINDOWS // shlwapi uses its own MD_OS_WINDOWS include inside
#define MD_OS_WINDOWS 1
void
w32_entry_point_caller(int argc, WCHAR** wargv)
{
SetUnhandledExceptionFilter(&win32_exception_filter);
//- rjf: do OS layer initialization
{
// rjf: dynamically load windows functions which are not guaranteed
// in all SDKs
{
HMODULE module = LoadLibraryA("kernel32.dll");
w32_SetThreadDescription_func = (W32_SetThreadDescription_Type *)GetProcAddress(module, "SetThreadDescription");
FreeLibrary(module);
}
// rjf: try to enable large pages if we can
{
HANDLE token;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
{
LUID luid;
if (LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &luid))
{
TOKEN_PRIVILEGES priv;
priv.PrivilegeCount = 1;
priv.Privileges[0].Luid = luid;
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, 0, &priv, sizeof(priv), 0, 0);
}
CloseHandle(token);
}
}
// rjf: get system info
SYSTEM_INFO sysinfo = {0};
GetSystemInfo(&sysinfo);
// rjf: set up non-dynamically-md_alloc'd state
//
// (we need to set up some basics before this layer can supply
// memory allocation primitives)
{
md_os_w32_state.microsecond_resolution = 1;
LARGE_INTEGER large_int_resolution;
if (QueryPerformanceFrequency(&large_int_resolution))
{
md_os_w32_state.microsecond_resolution = large_int_resolution.QuadPart;
}
}
{
MD_OS_SystemInfo* info = &md_os_w32_state.system_info;
info->logical_processor_count = (MD_U64)sysinfo.dwNumberOfProcessors;
info->page_size = sysinfo.dwPageSize;
info->large_page_size = GetLargePageMinimum();
info->allocation_granularity = sysinfo.dwAllocationGranularity;
}
{
MD_OS_ProcessInfo* info = &md_os_w32_state.process_info;
info->pid = GetCurrentProcessId();
}
// rjf: set up thread context
md_local_persist MD_TCTX md_tctx;
md_tctx_init_and_equip(&md_tctx);
// rjf: set up dynamically-md_alloc'd state
MD_Arena* arena = md_arena_alloc(.backing = md_varena_alloc(0));
{
md_os_w32_state.arena = arena;
{
MD_OS_SystemInfo* info = &md_os_w32_state.system_info;
MD_U8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = {0};
DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
if(GetComputerNameA((char*)buffer, &size))
{
info->machine_name = md_push_str8_copy(arena, md_str8(buffer, size));
}
}
}
{
MD_OS_ProcessInfo* info = &md_os_w32_state.process_info;
{
MD_TempArena scratch = md_scratch_begin(0, 0);
DWORD size = MD_KB(32);
MD_U16* buffer = md_push_array__no_zero(scratch.arena, MD_U16, size);
DWORD length = GetModuleFileNameW(0, (WCHAR*)buffer, size);
MD_String8 name8 = md_str8_from_16(scratch.arena, md_str16(buffer, length));
MD_String8 name_chopped = md_str8_chop_last_slash(name8);
info->binary_path = md_push_str8_copy(arena, name_chopped);
scratch_end(scratch);
}
info->initial_path = md_os_get_current_path(arena);
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_U64 size = MD_KB(32);
MD_U16* buffer = md_push_array__no_zero(scratch.arena, MD_U16, size);
if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, (WCHAR*)buffer))) {
info->user_program_data_path = md_str8_from_16(arena, md_str16_cstring(buffer));
}
scratch_end(scratch);
}
{
WCHAR* this_proc_env = GetEnvironmentStringsW();
MD_U64 start_idx = 0;
for (MD_U64 idx = 0;; idx += 1)
{
if (this_proc_env[idx] == 0)
{
if (start_idx == idx)
{
break;
}
else
{
MD_String16 string16 = md_str16((MD_U16 *)this_proc_env + start_idx, idx - start_idx);
MD_String8 string = md_str8_from_16(arena, string16);
md_str8_list_push(arena, &info->environment, string);
start_idx = idx+1;
}
}
}
}
}
// rjf: set up entity storage
InitializeCriticalSection(&md_os_w32_state.entity_mutex);
md_os_w32_state.entity_arena = md_arena_alloc(.backing = md_varena_alloc(0));
}
//- rjf: extract arguments
MD_Arena* args_arena = md_arena_alloc(.backing = md_varena_alloc(.reserve_size = MD_MB(1), .commit_size = MD_KB(32)));
char** argv = md_push_array(args_arena, char*, argc);
for (int i = 0; i < argc; i += 1)
{
MD_String16 arg16 = md_str16_cstring((MD_U16 *)wargv[i]);
MD_String8 arg8 = md_str8_from_16(args_arena, arg16);
if (md_str8_match(arg8, md_str8_lit("--quiet"), MD_StringMatchFlag_CaseInsensitive))
{
win32_g_is_quiet = 1;
}
argv[i] = (char *)arg8.str;
}
//- rjf: call into "real" entry point
md_main_thread_base_entry_point(md_entry_point, argv, (MD_U64)argc);
}
#if MD_BUILD_CONSOLE_INTERFACE
int wmain(int argc, WCHAR** argv)
{
w32_entry_point_caller(argc, argv);
return 0;
}
#else
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
w32_entry_point_caller(__argc, __wargv);
return 0;
}
#endif
// MD_BUILD_ENTRY_POINT
#endif
////////////////////////////////
//~ Ed: Manual OS Bootstrap
void md_os_init(MD_OS_Context* ctx, MD_TCTX* thread_ctx)
{
// rjf: dynamically load windows functions which are not guaranteed
// in all SDKs
{
HMODULE module = LoadLibraryA("kernel32.dll");
w32_SetThreadDescription_func = (W32_SetThreadDescription_Type *)GetProcAddress(module, "SetThreadDescription");
FreeLibrary(module);
}
// rjf: try to enable large pages if we can
if (ctx->enable_large_pages)
{
HANDLE token;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
{
LUID luid;
if (LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &luid))
{
TOKEN_PRIVILEGES priv;
priv.PrivilegeCount = 1;
priv.Privileges[0].Luid = luid;
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, 0, &priv, sizeof(priv), 0, 0);
}
CloseHandle(token);
}
}
SYSTEM_INFO sysinfo = {0};
GetSystemInfo(&sysinfo);
// rjf: set up non-dynamically-md_alloc'd state
//
// (we need to set up some basics before this layer can supply
// memory allocation primitives)
{
md_os_w32_state.microsecond_resolution = 1;
LARGE_INTEGER large_int_resolution;
if (QueryPerformanceFrequency(&large_int_resolution))
{
md_os_w32_state.microsecond_resolution = large_int_resolution.QuadPart;
}
}
{
MD_OS_SystemInfo* info = &md_os_w32_state.system_info;
info->logical_processor_count = (MD_U64)sysinfo.dwNumberOfProcessors;
info->page_size = sysinfo.dwPageSize;
info->large_page_size = GetLargePageMinimum();
info->allocation_granularity = sysinfo.dwAllocationGranularity;
}
{
MD_OS_ProcessInfo* info = &md_os_w32_state.process_info;
info->pid = GetCurrentProcessId();
}
// rjf: set up thread context
md_tctx_init_and_equip(thread_ctx);
// rjf: set up dynamically-md_alloc'd state
MD_Arena* state_arena = ctx->entity_arena;
if (state_arena == md_nullptr) {
state_arena = md_arena_alloc(.backing = md_varena_allocator(md_varena_alloc(0)));
ctx->state_arena = state_arena;
}
{
md_os_w32_state.arena = state_arena;
{
MD_OS_SystemInfo* info = &md_os_w32_state.system_info;
MD_U8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = {0};
DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
if(GetComputerNameA((char*)buffer, &size))
{
info->machine_name = md_str8_copy(state_arena, md_str8(buffer, size));
}
}
}
{
MD_OS_ProcessInfo* info = &md_os_w32_state.process_info;
{
MD_TempArena scratch = md_scratch_begin(0, 0);
DWORD size = MD_KB(32);
MD_U16* buffer = md_push_array__no_zero(scratch.arena, MD_U16, size);
DWORD length = GetModuleFileNameW(0, (WCHAR*)buffer, size);
MD_String8 name8 = md_str8_from(scratch.arena, md_str16(buffer, length));
MD_String8 name_chopped = md_str8_chop_last_slash(name8);
info->binary_path = md_str8_copy(state_arena, name_chopped);
scratch_end(scratch);
}
info->initial_path = md_os_get_current_path(state_arena);
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_U64 size = MD_KB(32);
MD_U16* buffer = md_push_array__no_zero(scratch.arena, MD_U16, size);
if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, (WCHAR*)buffer))) {
info->user_program_data_path = md_str8_from(state_arena, md_str16_cstring(buffer));
}
scratch_end(scratch);
}
{
WCHAR* this_proc_env = GetEnvironmentStringsW();
MD_U64 start_idx = 0;
for (MD_U64 idx = 0;; idx += 1)
{
if (this_proc_env[idx] == 0)
{
if (start_idx == idx)
{
break;
}
else
{
MD_String16 string16 = md_str16((MD_U16 *)this_proc_env + start_idx, idx - start_idx);
MD_String8 string = md_str8_from(state_arena, string16);
md_str8_list_push(state_arena, &info->environment, string);
start_idx = idx+1;
}
}
}
}
}
// rjf: set up entity storage
InitializeCriticalSection(&md_os_w32_state.entity_mutex);
if (ctx->entity_arena == md_nullptr) {
ctx->entity_arena = md_arena_alloc(.backing = md_varena_allocator(md_varena_alloc(0)));
md_os_w32_state.entity_arena = ctx->entity_arena;
}
}