mirror of
https://github.com/Ed94/HandmadeHero.git
synced 2024-12-21 22:14:43 -08:00
Added win32 missing files.
This commit is contained in:
parent
42dc75900f
commit
80996709fe
4
.gitignore
vendored
4
.gitignore
vendored
@ -8,10 +8,6 @@
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
187
project/platform/win32/win32.hpp
Normal file
187
project/platform/win32/win32.hpp
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
Windows dependency header
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable: 5105 )
|
||||
#pragma warning( disable: 4820 )
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
#include <mmeapi.h>
|
||||
#include <dsound.h>
|
||||
#include <timeapi.h>
|
||||
#pragma warning( pop )
|
||||
|
||||
// #include "windows/windows_base.h"
|
||||
// #include "windows/window.h"
|
||||
|
||||
// #include "windows/file.h"
|
||||
// #include "windows/io.h"
|
||||
|
||||
// #if Build_Debug
|
||||
// # include "windows/dbghelp.h"
|
||||
// #endif
|
||||
|
||||
#if Build_DLL
|
||||
# define WIN_LIB_API extern "C" __declspec(dllexport)
|
||||
#else
|
||||
# define WIN_LIB_API extern "C"
|
||||
#endif
|
||||
|
||||
// #ifndef CONST
|
||||
// # define CONST const
|
||||
// #endif
|
||||
|
||||
// SAL BS
|
||||
#ifndef _In_
|
||||
# define _In_
|
||||
#endif
|
||||
|
||||
#define NS_WIN32_BEGIN namespace win32 {
|
||||
#define NS_WIN32_END }
|
||||
|
||||
NS_WIN32_BEGIN
|
||||
|
||||
enum LWA : DWORD
|
||||
{
|
||||
LWA_Alpha = 0x00000002,
|
||||
LWA_ColorKey = 0x00000001,
|
||||
};
|
||||
|
||||
enum BI : DWORD
|
||||
{
|
||||
BI_RGB_Uncompressed = 0L,
|
||||
BI_RunLength_Encoded_8bpp = 1L,
|
||||
BI_RunLength_Encoded_4bpp = 2L,
|
||||
};
|
||||
|
||||
enum CS : UINT
|
||||
{
|
||||
CS_Own_Device_Context = CS_OWNDC,
|
||||
CS_Horizontal_Redraw = CS_HREDRAW,
|
||||
CS_Vertical_Redraw = CS_VREDRAW,
|
||||
};
|
||||
|
||||
enum CW : s32
|
||||
{
|
||||
CW_Use_Default = CW_USEDEFAULT,
|
||||
};
|
||||
|
||||
enum DIB : UINT
|
||||
{
|
||||
DIB_ColorTable_RGB = 0,
|
||||
DIB_ColorTable_Palette = 1
|
||||
|
||||
};
|
||||
|
||||
enum MB : UINT
|
||||
{
|
||||
MB_Ok_Btn = MB_OK,
|
||||
MB_Icon_Information = MB_ICONINFORMATION,
|
||||
};
|
||||
|
||||
enum Mem : DWORD
|
||||
{
|
||||
MEM_Commit_Zeroed = MEM_COMMIT,
|
||||
MEM_Reserve = MEM_RESERVE,
|
||||
MEM_Release = MEM_RELEASE,
|
||||
MEM_Use_Large_pages = MEM_LARGE_PAGES,
|
||||
};
|
||||
|
||||
enum Page : DWORD
|
||||
{
|
||||
Page_Read_Write = PAGE_READWRITE,
|
||||
};
|
||||
|
||||
enum PM : UINT
|
||||
{
|
||||
PM_Remove_Messages_From_Queue = PM_REMOVE,
|
||||
};
|
||||
|
||||
enum RasterOps : DWORD
|
||||
{
|
||||
RO_Source_To_Dest = (DWORD)0x00CC0020,
|
||||
RO_Blackness = (DWORD)0x00000042,
|
||||
RO_Whiteness = (DWORD)0x00FF0062,
|
||||
};
|
||||
|
||||
#define WM_ACTIVATEAPP 0x001C
|
||||
|
||||
enum WS : UINT
|
||||
{
|
||||
WS_Overlapped_Window = WS_OVERLAPPEDWINDOW,
|
||||
WS_Initially_Visible = WS_VISIBLE,
|
||||
};
|
||||
|
||||
enum XI_State : DWORD
|
||||
{
|
||||
XI_PluggedIn = ERROR_SUCCESS,
|
||||
};
|
||||
|
||||
|
||||
template< typename ProcSignature >
|
||||
ProcSignature* get_procedure_from_library( HMODULE library_module, char const* symbol )
|
||||
{
|
||||
void* address = rcast( void*, GetProcAddress( library_module, symbol ) );
|
||||
return rcast( ProcSignature*, address );
|
||||
}
|
||||
|
||||
#pragma region XInput
|
||||
WIN_LIB_API DWORD WINAPI XInputGetState
|
||||
(
|
||||
DWORD dwUserIndex, // Index of the gamer associated with the device
|
||||
XINPUT_STATE* pState // Receives the current state
|
||||
);
|
||||
|
||||
WIN_LIB_API DWORD WINAPI XInputSetState
|
||||
(
|
||||
DWORD dwUserIndex, // Index of the gamer associated with the device
|
||||
XINPUT_VIBRATION* pVibration // The vibration information to send to the controller
|
||||
);
|
||||
|
||||
DWORD WINAPI xinput_get_state_stub( DWORD dwUserIndex, XINPUT_STATE* pVibration ) {
|
||||
do_once_start
|
||||
OutputDebugStringA( "xinput_get_state stubbed!\n");
|
||||
do_once_end
|
||||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
DWORD WINAPI xinput_set_state_stub( DWORD dwUserIndex, XINPUT_VIBRATION* pVibration ) {
|
||||
do_once_start
|
||||
OutputDebugStringA( "xinput_set_state stubbed!\n");
|
||||
do_once_end
|
||||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
using XInputGetStateFn = DWORD WINAPI( DWORD dwUserIndex, XINPUT_STATE* pVibration );
|
||||
using XInputSetStateFn = DWORD WINAPI( DWORD dwUserIndex, XINPUT_VIBRATION* pVibration );
|
||||
|
||||
global XInputGetStateFn* xinput_get_state = xinput_get_state_stub;
|
||||
global XInputSetStateFn* xinput_set_state = xinput_set_state_stub;
|
||||
|
||||
internal void
|
||||
xinput_load_library_bindings()
|
||||
{
|
||||
HMODULE xinput_lib = LoadLibraryA( XINPUT_DLL_A );
|
||||
|
||||
XInputGetStateFn* get_state = get_procedure_from_library< XInputGetStateFn >( xinput_lib, "XInputGetState" );
|
||||
XInputSetStateFn* set_state = get_procedure_from_library< XInputSetStateFn >( xinput_lib, "XInputSetState" );
|
||||
|
||||
if ( get_state )
|
||||
xinput_get_state = get_state;
|
||||
|
||||
if ( set_state )
|
||||
xinput_set_state = set_state;
|
||||
}
|
||||
#pragma endregion XInput
|
||||
|
||||
NS_WIN32_END
|
||||
#undef _SAL_nop_impl_
|
||||
#undef _SAL2_Source_
|
||||
#undef _Deref_post2_impl_
|
||||
#undef _Outptr_result_bytebuffer_
|
||||
#undef _At_
|
||||
#undef _When_
|
||||
#undef GDI_DIBSIZE
|
453
project/platform/win32/win32_audio.cpp
Normal file
453
project/platform/win32/win32_audio.cpp
Normal file
@ -0,0 +1,453 @@
|
||||
#include "platform/platform.hpp"
|
||||
#include "win32.hpp"
|
||||
|
||||
NS_PLATFORM_BEGIN
|
||||
|
||||
// TODO : This will def need to be looked over.
|
||||
struct DirectSoundBuffer
|
||||
{
|
||||
LPDIRECTSOUNDBUFFER secondary_buffer;
|
||||
s16* samples;
|
||||
u32 secondary_buffer_size;
|
||||
u32 samples_per_second;
|
||||
u32 bytes_per_sample;
|
||||
|
||||
u32 bytes_per_second;
|
||||
u32 guard_sample_bytes;
|
||||
|
||||
DWORD is_playing;
|
||||
u32 running_sample_index;
|
||||
|
||||
u32 latency_sample_count;
|
||||
};
|
||||
|
||||
struct AudioTimeMarker
|
||||
{
|
||||
DWORD output_play_cursor;
|
||||
DWORD output_write_cursor;
|
||||
DWORD output_location;
|
||||
DWORD output_byte_count;
|
||||
|
||||
DWORD flip_play_curosr;
|
||||
DWORD flip_write_cursor;
|
||||
|
||||
DWORD expected_flip_cursor;
|
||||
};
|
||||
|
||||
using DirectSoundCreateFn = HRESULT WINAPI (LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN pUnkOuter );
|
||||
global DirectSoundCreateFn* direct_sound_create;
|
||||
|
||||
#if Build_Development
|
||||
internal void
|
||||
debug_draw_vertical_line( s32 x_pos, s32 top, s32 bottom, s32 color )
|
||||
{
|
||||
if ( top <= 0 )
|
||||
{
|
||||
top = 0;
|
||||
}
|
||||
|
||||
if ( bottom > Surface_Back_Buffer.height )
|
||||
{
|
||||
bottom = Surface_Back_Buffer.height;
|
||||
}
|
||||
|
||||
if ( x_pos >= 0 && x_pos < Surface_Back_Buffer.width )
|
||||
{
|
||||
u8*
|
||||
pixel_byte = rcast(u8*, Surface_Back_Buffer.memory );
|
||||
pixel_byte += x_pos * Surface_Back_Buffer.bytes_per_pixel;
|
||||
pixel_byte += top * Surface_Back_Buffer.pitch;
|
||||
|
||||
for ( s32 y = top; y < bottom; ++ y )
|
||||
{
|
||||
s32* pixel = rcast(s32*, pixel_byte);
|
||||
*pixel = color;
|
||||
|
||||
pixel_byte += Surface_Back_Buffer.pitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
debug_draw_sound_buffer_marker( DirectSoundBuffer* sound_buffer, f32 ratio
|
||||
, u32 pad_x, u32 pad_y
|
||||
, u32 top, u32 bottom
|
||||
, DWORD value, u32 color )
|
||||
{
|
||||
// assert( value < sound_buffer->SecondaryBufferSize );
|
||||
u32 x = pad_x + scast(u32, ratio * scast(f32, value ));
|
||||
debug_draw_vertical_line( x, top, bottom, color );
|
||||
}
|
||||
|
||||
internal void
|
||||
debug_sync_display( DirectSoundBuffer* sound_buffer
|
||||
, u32 num_markers, AudioTimeMarker* markers
|
||||
, u32 current_marker
|
||||
, f32 ms_per_frame )
|
||||
{
|
||||
u32 pad_x = 32;
|
||||
u32 pad_y = 16;
|
||||
f32 buffers_ratio = scast(f32, Surface_Back_Buffer.width) / (scast(f32, sound_buffer->secondary_buffer_size) * 1);
|
||||
|
||||
u32 line_height = 64;
|
||||
for ( u32 marker_index = 0; marker_index < num_markers; ++ marker_index )
|
||||
{
|
||||
AudioTimeMarker* marker = & markers[marker_index];
|
||||
assert( marker->output_play_cursor < sound_buffer->secondary_buffer_size );
|
||||
assert( marker->output_write_cursor < sound_buffer->secondary_buffer_size );
|
||||
assert( marker->output_location < sound_buffer->secondary_buffer_size );
|
||||
assert( marker->output_byte_count < sound_buffer->secondary_buffer_size );
|
||||
assert( marker->flip_play_curosr < sound_buffer->secondary_buffer_size );
|
||||
assert( marker->flip_write_cursor < sound_buffer->secondary_buffer_size );
|
||||
|
||||
DWORD play_color = 0x88888888;
|
||||
DWORD write_color = 0x88800000;
|
||||
DWORD expected_flip_color = 0xFFFFF000;
|
||||
DWORD play_window_color = 0xFFFF00FF;
|
||||
|
||||
u32 top = pad_y;
|
||||
u32 bottom = pad_y + line_height;
|
||||
if ( marker_index == current_marker )
|
||||
{
|
||||
play_color = 0xFFFFFFFF;
|
||||
write_color = 0xFFFF0000;
|
||||
|
||||
top += pad_y + line_height;
|
||||
bottom += pad_y + line_height;
|
||||
|
||||
u32 row_2_top = top;
|
||||
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_play_cursor, play_color );
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_write_cursor, write_color );
|
||||
|
||||
play_color = 0xFFFFFFFF;
|
||||
write_color = 0xFFFF0000;
|
||||
|
||||
top += pad_y + line_height;
|
||||
bottom += pad_y + line_height;
|
||||
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_location, play_color );
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_location + marker->output_byte_count, write_color );
|
||||
|
||||
play_color = 0xFFFFFFFF;
|
||||
write_color = 0xFFFF0000;
|
||||
|
||||
top += pad_y + line_height;
|
||||
bottom += pad_y + line_height;
|
||||
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, row_2_top, bottom, marker->expected_flip_cursor, expected_flip_color );
|
||||
}
|
||||
|
||||
DWORD play_window = marker->flip_play_curosr + 480 * sound_buffer->bytes_per_sample;
|
||||
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->flip_play_curosr, play_color );
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, play_window, play_window_color );
|
||||
debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->flip_write_cursor, write_color );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal void
|
||||
init_sound(HWND window_handle, DirectSoundBuffer* sound_buffer )
|
||||
{
|
||||
// Load library
|
||||
HMODULE sound_library = LoadLibraryA( "dsound.dll" );
|
||||
if ( ! ensure(sound_library, "Failed to load direct sound library" ) )
|
||||
{
|
||||
// TOOD : Diagnostic
|
||||
return;
|
||||
}
|
||||
|
||||
// Get direct sound object
|
||||
direct_sound_create = get_procedure_from_library< DirectSoundCreateFn >( sound_library, "DirectSoundCreate" );
|
||||
if ( ! ensure( direct_sound_create, "Failed to get direct_sound_create_procedure" ) )
|
||||
{
|
||||
// TOOD : Diagnostic
|
||||
return;
|
||||
}
|
||||
|
||||
LPDIRECTSOUND direct_sound;
|
||||
if ( ! SUCCEEDED(direct_sound_create( 0, & direct_sound, 0 )) )
|
||||
{
|
||||
// TODO : Diagnostic
|
||||
}
|
||||
if ( ! SUCCEEDED( direct_sound->SetCooperativeLevel(window_handle, DSSCL_PRIORITY) ) )
|
||||
{
|
||||
// TODO : Diagnostic
|
||||
}
|
||||
|
||||
WAVEFORMATEX
|
||||
wave_format {};
|
||||
wave_format.wFormatTag = WAVE_FORMAT_PCM; /* format type */
|
||||
wave_format.nChannels = 2; /* number of channels (i.e. mono, stereo...) */
|
||||
wave_format.nSamplesPerSec = scast(u32, sound_buffer->samples_per_second); /* sample rate */
|
||||
wave_format.wBitsPerSample = 16; /* number of bits per sample of mono data */
|
||||
wave_format.nBlockAlign = wave_format.nChannels * wave_format.wBitsPerSample / 8 ; /* block size of data */
|
||||
wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign; /* for buffer estimation */
|
||||
wave_format.cbSize = 0; /* the count in bytes of the size of */
|
||||
|
||||
LPDIRECTSOUNDBUFFER primary_buffer;
|
||||
{
|
||||
DSBUFFERDESC
|
||||
buffer_description { sizeof(buffer_description) };
|
||||
buffer_description.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
buffer_description.dwBufferBytes = 0;
|
||||
|
||||
if ( ! SUCCEEDED( direct_sound->CreateSoundBuffer( & buffer_description, & primary_buffer, 0 ) ))
|
||||
{
|
||||
// TODO : Diagnostic
|
||||
}
|
||||
if ( ! SUCCEEDED( primary_buffer->SetFormat( & wave_format ) ) )
|
||||
{
|
||||
// TODO : Diagnostic
|
||||
}
|
||||
}
|
||||
|
||||
DSBUFFERDESC
|
||||
buffer_description { sizeof(buffer_description) };
|
||||
buffer_description.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
||||
buffer_description.dwBufferBytes = sound_buffer->secondary_buffer_size;
|
||||
buffer_description.lpwfxFormat = & wave_format;
|
||||
|
||||
if ( ! SUCCEEDED( direct_sound->CreateSoundBuffer( & buffer_description, & sound_buffer->secondary_buffer, 0 ) ))
|
||||
{
|
||||
// TODO : Diagnostic
|
||||
}
|
||||
if ( ! SUCCEEDED( sound_buffer->secondary_buffer->SetFormat( & wave_format ) ) )
|
||||
{
|
||||
// TODO : Diagnostic
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
ds_clear_sound_buffer( DirectSoundBuffer* sound_buffer )
|
||||
{
|
||||
LPVOID region_1;
|
||||
DWORD region_1_size;
|
||||
LPVOID region_2;
|
||||
DWORD region_2_size;
|
||||
|
||||
HRESULT ds_lock_result = sound_buffer->secondary_buffer->Lock( 0, sound_buffer->secondary_buffer_size
|
||||
, & region_1, & region_1_size
|
||||
, & region_2, & region_2_size
|
||||
, 0 );
|
||||
if ( ! SUCCEEDED( ds_lock_result ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
u8* sample_out = rcast( u8*, region_1 );
|
||||
for ( DWORD byte_index = 0; byte_index < region_1_size; ++ byte_index )
|
||||
{
|
||||
*sample_out = 0;
|
||||
++ sample_out;
|
||||
}
|
||||
|
||||
sample_out = rcast( u8*, region_2 );
|
||||
for ( DWORD byte_index = 0; byte_index < region_2_size; ++ byte_index )
|
||||
{
|
||||
*sample_out = 0;
|
||||
++ sample_out;
|
||||
}
|
||||
|
||||
if ( ! SUCCEEDED( sound_buffer->secondary_buffer->Unlock( region_1, region_1_size, region_2, region_2_size ) ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
ds_fill_sound_buffer( DirectSoundBuffer* sound_buffer, DWORD byte_to_lock, DWORD bytes_to_write )
|
||||
{
|
||||
LPVOID region_1;
|
||||
DWORD region_1_size;
|
||||
LPVOID region_2;
|
||||
DWORD region_2_size;
|
||||
|
||||
HRESULT ds_lock_result = sound_buffer->secondary_buffer->Lock( byte_to_lock, bytes_to_write
|
||||
, & region_1, & region_1_size
|
||||
, & region_2, & region_2_size
|
||||
, 0 );
|
||||
if ( ! SUCCEEDED( ds_lock_result ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO : Assert that region sizes are valid
|
||||
|
||||
DWORD region_1_sample_count = region_1_size / sound_buffer->bytes_per_sample;
|
||||
s16* sample_out = rcast( s16*, region_1 );
|
||||
s16* sample_in = sound_buffer->samples;
|
||||
for ( DWORD sample_index = 0; sample_index < region_1_sample_count; ++ sample_index )
|
||||
{
|
||||
*sample_out = *sample_in;
|
||||
++ sample_out;
|
||||
++ sample_in;
|
||||
|
||||
*sample_out = *sample_in;
|
||||
++ sample_out;
|
||||
++ sample_in;
|
||||
|
||||
++ sound_buffer->running_sample_index;
|
||||
}
|
||||
|
||||
DWORD region_2_sample_count = region_2_size / sound_buffer->bytes_per_sample;
|
||||
sample_out = rcast( s16*, region_2 );
|
||||
for ( DWORD sample_index = 0; sample_index < region_2_sample_count; ++ sample_index )
|
||||
{
|
||||
*sample_out = *sample_in;
|
||||
++ sample_out;
|
||||
++ sample_in;
|
||||
|
||||
*sample_out = *sample_in;
|
||||
++ sample_out;
|
||||
++ sample_in;
|
||||
|
||||
++ sound_buffer->running_sample_index;
|
||||
}
|
||||
|
||||
if ( ! SUCCEEDED( sound_buffer->secondary_buffer->Unlock( region_1, region_1_size, region_2, region_2_size ) ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal
|
||||
void process_audio_frame( DirectSoundBuffer& ds_sound_buffer, DWORD& ds_play_cursor, DWORD& ds_write_cursor, f32& ds_latency_ms
|
||||
, b32& sound_is_valid
|
||||
, AudioTimeMarker* audio_time_markers, u32 audio_marker_index
|
||||
, f32 flip_to_audio_ms, u64 last_frame_clock
|
||||
, engine::ModuleAPI& engine_api, engine::Memory* engine_memory, ModuleAPI* platform_api, engine::ThreadContext* thread_context_placeholder)
|
||||
{
|
||||
/*
|
||||
Audio Processing:
|
||||
There is a sync boundary value, that is the number of samples that the engine's frame-time may vary by
|
||||
(ex: approx 2ms of variance between frame-times).
|
||||
|
||||
On wakeup : Check play cursor position and forcast ahead where the cursor will be for the next sync boundary.
|
||||
Based on that, check the write cursor position, if its (at least) before the synch boundary, the target write position is
|
||||
the frame boundary plus one frame. (Low latency)
|
||||
|
||||
If its after (sync boundary), we cannot sync audio.
|
||||
Write a frame's worth of audio plus some number of "guard" samples. (High Latency)
|
||||
*/
|
||||
if ( ! SUCCEEDED( ds_sound_buffer.secondary_buffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) ))
|
||||
{
|
||||
sound_is_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! sound_is_valid )
|
||||
{
|
||||
ds_sound_buffer.running_sample_index = ds_write_cursor / ds_sound_buffer.bytes_per_sample;
|
||||
sound_is_valid = true;
|
||||
}
|
||||
|
||||
DWORD byte_to_lock = 0;
|
||||
DWORD target_cursor = 0;
|
||||
DWORD bytes_to_write = 0;
|
||||
|
||||
byte_to_lock = (ds_sound_buffer.running_sample_index * ds_sound_buffer.bytes_per_sample) % ds_sound_buffer.secondary_buffer_size;
|
||||
|
||||
DWORD bytes_per_second = ds_sound_buffer.bytes_per_sample * ds_sound_buffer.samples_per_second;
|
||||
|
||||
DWORD expected_samplebytes_per_frame = bytes_per_second / Engine_Refresh_Hz;
|
||||
|
||||
f32 left_until_flip_ms = Engine_Frame_Target_MS - flip_to_audio_ms;
|
||||
DWORD expected_bytes_until_flip = scast(DWORD, (left_until_flip_ms / Engine_Frame_Target_MS) * scast(f32, expected_samplebytes_per_frame));
|
||||
|
||||
DWORD expected_sync_boundary_byte = ds_play_cursor + expected_bytes_until_flip;
|
||||
|
||||
DWORD sync_write_cursor = ds_write_cursor;
|
||||
if ( sync_write_cursor < ds_play_cursor )
|
||||
{
|
||||
// unwrap the cursor so its ahead of the play curosr linearly.
|
||||
sync_write_cursor += ds_sound_buffer.secondary_buffer_size;
|
||||
}
|
||||
assert( sync_write_cursor >= ds_play_cursor );
|
||||
|
||||
sync_write_cursor += ds_sound_buffer.guard_sample_bytes;
|
||||
|
||||
b32 audio_interface_is_low_latency = sync_write_cursor < expected_sync_boundary_byte;
|
||||
if ( audio_interface_is_low_latency )
|
||||
{
|
||||
target_cursor = ( expected_sync_boundary_byte + expected_samplebytes_per_frame );
|
||||
}
|
||||
else
|
||||
{
|
||||
target_cursor = (ds_write_cursor + expected_samplebytes_per_frame + ds_sound_buffer.guard_sample_bytes);
|
||||
}
|
||||
target_cursor %= ds_sound_buffer.secondary_buffer_size;
|
||||
|
||||
if ( byte_to_lock > target_cursor)
|
||||
{
|
||||
// Infront of play cursor |--play--byte_to_write-->--|
|
||||
bytes_to_write = ds_sound_buffer.secondary_buffer_size - byte_to_lock;
|
||||
bytes_to_write += target_cursor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Behind play cursor |--byte_to_write-->--play--|
|
||||
bytes_to_write = target_cursor - byte_to_lock;
|
||||
}
|
||||
|
||||
// Engine Sound
|
||||
// f32 delta_time = timing_get_seconds_elapsed( last_frame_clock, timing_get_wall_clock() );
|
||||
|
||||
// s16 samples[ 48000 * 2 ];
|
||||
engine::AudioBuffer sound_buffer {};
|
||||
sound_buffer.num_samples = bytes_to_write / ds_sound_buffer.bytes_per_sample;
|
||||
sound_buffer.running_sample_index = ds_sound_buffer.running_sample_index;
|
||||
sound_buffer.samples_per_second = ds_sound_buffer.samples_per_second;
|
||||
sound_buffer.samples = ds_sound_buffer.samples;
|
||||
engine_api.update_audio( 0.f, & sound_buffer, engine_memory, platform_api, thread_context_placeholder );
|
||||
|
||||
AudioTimeMarker* marker = & audio_time_markers[ audio_marker_index ];
|
||||
marker->output_play_cursor = ds_play_cursor;
|
||||
marker->output_write_cursor = ds_write_cursor;
|
||||
marker->output_location = byte_to_lock;
|
||||
marker->output_byte_count = bytes_to_write;
|
||||
marker->expected_flip_cursor = expected_sync_boundary_byte;
|
||||
|
||||
// Update audio buffer
|
||||
if ( ! sound_is_valid )
|
||||
return;
|
||||
|
||||
#if Build_Development && 0
|
||||
#if 0
|
||||
DWORD play_cursor;
|
||||
DWORD write_cursor;
|
||||
ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor );
|
||||
#endif
|
||||
DWORD unwrapped_write_cursor = ds_write_cursor;
|
||||
if ( unwrapped_write_cursor < ds_play_cursor )
|
||||
{
|
||||
unwrapped_write_cursor += ds_sound_buffer.SecondaryBufferSize;
|
||||
}
|
||||
ds_cursor_byte_delta = unwrapped_write_cursor - ds_play_cursor;
|
||||
|
||||
constexpr f32 to_milliseconds = 1000.f;
|
||||
f32 sample_delta = scast(f32, ds_cursor_byte_delta) / scast(f32, ds_sound_buffer.BytesPerSample);
|
||||
f32 ds_latency_s = sample_delta / scast(f32, ds_sound_buffer.SamplesPerSecond);
|
||||
ds_latency_ms = ds_latency_s * to_milliseconds;
|
||||
|
||||
char text_buffer[256];
|
||||
sprintf_s( text_buffer, sizeof(text_buffer), "BTL:%u TC:%u BTW:%u - PC:%u WC:%u DELTA:%u bytes %f ms\n"
|
||||
, (u32)byte_to_lock, (u32)target_cursor, (u32)bytes_to_write
|
||||
, (u32)play_cursor, (u32)write_cursor, (u32)ds_cursor_byte_delta, ds_latency_ms );
|
||||
OutputDebugStringA( text_buffer );
|
||||
#endif
|
||||
ds_fill_sound_buffer( & ds_sound_buffer, byte_to_lock, bytes_to_write );
|
||||
|
||||
DWORD ds_status = 0;
|
||||
if ( SUCCEEDED( ds_sound_buffer.secondary_buffer->GetStatus( & ds_status ) ) )
|
||||
{
|
||||
ds_sound_buffer.is_playing = ds_status & DSBSTATUS_PLAYING;
|
||||
}
|
||||
if ( ds_sound_buffer.is_playing )
|
||||
return;
|
||||
|
||||
ds_sound_buffer.secondary_buffer->Play( 0, 0, DSBPLAY_LOOPING );
|
||||
}
|
||||
|
||||
NS_PLATFORM_END
|
227
project/platform/win32/win32_input.cpp
Normal file
227
project/platform/win32/win32_input.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
#include "platform/platform.hpp"
|
||||
#include "platform/jsl.hpp"
|
||||
#include "win32.hpp"
|
||||
|
||||
NS_PLATFORM_BEGIN
|
||||
|
||||
// Max controllers for the platform layer and thus for all other layers is 4. (Sanity and xinput limit)
|
||||
constexpr u32 Max_Controllers = 4;
|
||||
|
||||
using JSL_DeviceHandle = int;
|
||||
using EngineXInputPadStates = engine::XInputPadState[ Max_Controllers ];
|
||||
using EngineDSPadStates = engine::DualsensePadState[Max_Controllers];
|
||||
|
||||
internal void
|
||||
input_process_digital_btn( engine::DigitalBtn* old_state, engine::DigitalBtn* new_state, u32 raw_btns, u32 btn_flag )
|
||||
{
|
||||
#define had_transition() ( old_state->ended_down != new_state->ended_down )
|
||||
new_state->ended_down = (raw_btns & btn_flag) > 0;
|
||||
if ( had_transition() )
|
||||
new_state->half_transitions += 1;
|
||||
else
|
||||
new_state->half_transitions = 0;
|
||||
#undef had_transition
|
||||
}
|
||||
|
||||
internal f32
|
||||
jsl_input_process_axis_value( f32 value, f32 deadzone_threshold )
|
||||
{
|
||||
f32 result = 0;
|
||||
if ( value < -deadzone_threshold )
|
||||
{
|
||||
result = (value + deadzone_threshold ) / (1.0f - deadzone_threshold );
|
||||
|
||||
if (result < -1.0f)
|
||||
result = -1.0f; // Clamp to ensure it doesn't go below -1
|
||||
}
|
||||
else if ( value > deadzone_threshold )
|
||||
{
|
||||
result = (value - deadzone_threshold ) / (1.0f - deadzone_threshold );
|
||||
|
||||
if (result > 1.0f)
|
||||
result = 1.0f; // Clamp to ensure it doesn't exceed 1
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal f32
|
||||
xinput_process_axis_value( s16 value, s16 deadzone_threshold )
|
||||
{
|
||||
f32 result = 0;
|
||||
if ( value < -deadzone_threshold )
|
||||
{
|
||||
result = scast(f32, value + deadzone_threshold) / (32768.0f - scast(f32, deadzone_threshold));
|
||||
}
|
||||
else if ( value > deadzone_threshold )
|
||||
{
|
||||
result = scast(f32, value + deadzone_threshold) / (32767.0f - scast(f32, deadzone_threshold));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
poll_input( HWND window_handle, engine::InputState* input, u32 jsl_num_devices, JSL_DeviceHandle* jsl_device_handles
|
||||
, engine::KeyboardState* old_keyboard, engine::KeyboardState* new_keyboard
|
||||
, engine::MousesState* old_mouse, engine::MousesState* new_mouse
|
||||
, EngineXInputPadStates* old_xpads, EngineXInputPadStates* new_xpads
|
||||
, EngineDSPadStates* old_ds_pads, EngineDSPadStates* new_ds_pads )
|
||||
{
|
||||
// Keyboard Polling
|
||||
// Keyboards are unified for now.
|
||||
{
|
||||
constexpr u32 is_down = 0x80000000;
|
||||
input_process_digital_btn( & old_keyboard->_1, & new_keyboard->_1, GetAsyncKeyState( '1' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->_2, & new_keyboard->_2, GetAsyncKeyState( '2' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->_3, & new_keyboard->_3, GetAsyncKeyState( '3' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->_4, & new_keyboard->_4, GetAsyncKeyState( '4' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->Q, & new_keyboard->Q, GetAsyncKeyState( 'Q' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->E, & new_keyboard->E, GetAsyncKeyState( 'E' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->W, & new_keyboard->W, GetAsyncKeyState( 'W' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->A, & new_keyboard->A, GetAsyncKeyState( 'A' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->S, & new_keyboard->S, GetAsyncKeyState( 'S' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->D, & new_keyboard->D, GetAsyncKeyState( 'D' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->K, & new_keyboard->K, GetAsyncKeyState( 'K' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->L, & new_keyboard->L, GetAsyncKeyState( 'L' ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->escape, & new_keyboard->escape, GetAsyncKeyState( VK_ESCAPE ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->backspace, & new_keyboard->backspace, GetAsyncKeyState( VK_BACK ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->up, & new_keyboard->up, GetAsyncKeyState( VK_UP ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->down, & new_keyboard->down, GetAsyncKeyState( VK_DOWN ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->left, & new_keyboard->left, GetAsyncKeyState( VK_LEFT ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->right, & new_keyboard->right, GetAsyncKeyState( VK_RIGHT ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->space, & new_keyboard->space, GetAsyncKeyState( VK_SPACE ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->pause, & new_keyboard->pause, GetAsyncKeyState( VK_PAUSE ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->left_alt, & new_keyboard->left_alt, GetAsyncKeyState( VK_LMENU ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->right_alt, & new_keyboard->right_alt, GetAsyncKeyState( VK_RMENU ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->left_shift, & new_keyboard->left_shift, GetAsyncKeyState( VK_LSHIFT ), is_down );
|
||||
input_process_digital_btn( & old_keyboard->right_shift, & new_keyboard->right_shift, GetAsyncKeyState( VK_RSHIFT ), is_down );
|
||||
|
||||
input->controllers[0].keyboard = new_keyboard;
|
||||
}
|
||||
|
||||
// Mouse polling
|
||||
{
|
||||
// input->Controllers[0].Mouse = {};
|
||||
|
||||
constexpr u32 is_down = 0x80000000;
|
||||
input_process_digital_btn( & old_mouse->left, & new_mouse->left, GetAsyncKeyState( VK_LBUTTON ), is_down );
|
||||
input_process_digital_btn( & old_mouse->middle, & new_mouse->middle, GetAsyncKeyState( VK_MBUTTON ), is_down );
|
||||
input_process_digital_btn( & old_mouse->right, & new_mouse->right, GetAsyncKeyState( VK_RBUTTON ), is_down );
|
||||
|
||||
POINT mouse_pos;
|
||||
GetCursorPos( & mouse_pos );
|
||||
ScreenToClient( window_handle, & mouse_pos );
|
||||
|
||||
new_mouse->vertical_wheel = {};
|
||||
new_mouse->horizontal_wheel = {};
|
||||
|
||||
new_mouse->X.end = (f32)mouse_pos.x;
|
||||
new_mouse->Y.end = (f32)mouse_pos.y;
|
||||
|
||||
input->controllers[0].mouse = new_mouse;
|
||||
}
|
||||
|
||||
// XInput Polling
|
||||
// TODO(Ed) : Should we poll this more frequently?
|
||||
for ( DWORD controller_index = 0; controller_index < Max_Controllers; ++ controller_index )
|
||||
{
|
||||
XINPUT_STATE controller_state;
|
||||
b32 xinput_detected = xinput_get_state( controller_index, & controller_state ) == XI_PluggedIn;
|
||||
if ( xinput_detected )
|
||||
{
|
||||
XINPUT_GAMEPAD* xpad = & controller_state.Gamepad;
|
||||
engine::XInputPadState* old_xpad = old_xpads[ controller_index ];
|
||||
engine::XInputPadState* new_xpad = new_xpads[ controller_index ];
|
||||
input_process_digital_btn( & old_xpad->dpad.up, & new_xpad->dpad.up, xpad->wButtons, XINPUT_GAMEPAD_DPAD_UP );
|
||||
input_process_digital_btn( & old_xpad->dpad.down, & new_xpad->dpad.down, xpad->wButtons, XINPUT_GAMEPAD_DPAD_DOWN );
|
||||
input_process_digital_btn( & old_xpad->dpad.left, & new_xpad->dpad.left, xpad->wButtons, XINPUT_GAMEPAD_DPAD_LEFT );
|
||||
input_process_digital_btn( & old_xpad->dpad.right, & new_xpad->dpad.right, xpad->wButtons, XINPUT_GAMEPAD_DPAD_RIGHT );
|
||||
|
||||
input_process_digital_btn( & old_xpad->Y, & new_xpad->Y, xpad->wButtons, XINPUT_GAMEPAD_Y );
|
||||
input_process_digital_btn( & old_xpad->A, & new_xpad->A, xpad->wButtons, XINPUT_GAMEPAD_A );
|
||||
input_process_digital_btn( & old_xpad->B, & new_xpad->B, xpad->wButtons, XINPUT_GAMEPAD_B );
|
||||
input_process_digital_btn( & old_xpad->X, & new_xpad->X, xpad->wButtons, XINPUT_GAMEPAD_X );
|
||||
|
||||
input_process_digital_btn( & old_xpad->back, & new_xpad->back, xpad->wButtons, XINPUT_GAMEPAD_BACK );
|
||||
input_process_digital_btn( & old_xpad->start, & new_xpad->start, xpad->wButtons, XINPUT_GAMEPAD_START );
|
||||
|
||||
input_process_digital_btn( & old_xpad->left_shoulder, & new_xpad->left_shoulder, xpad->wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER );
|
||||
input_process_digital_btn( & old_xpad->right_shoulder, & new_xpad->right_shoulder, xpad->wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER );
|
||||
|
||||
new_xpad->stick.left.X.start = old_xpad->stick.left.X.end;
|
||||
new_xpad->stick.left.Y.start = old_xpad->stick.left.Y.end;
|
||||
|
||||
f32 left_x = xinput_process_axis_value( xpad->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE );
|
||||
f32 left_y = xinput_process_axis_value( xpad->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE );
|
||||
|
||||
// TODO(Ed) : Min/Max macros!!!
|
||||
new_xpad->stick.left.X.min = new_xpad->stick.left.X.max = new_xpad->stick.left.X.end = left_x;
|
||||
new_xpad->stick.left.Y.min = new_xpad->stick.left.Y.max = new_xpad->stick.left.Y.end = left_y;
|
||||
|
||||
// TODO(Ed): Make this actually an average for later
|
||||
new_xpad->stick.left.X.average = left_x;
|
||||
new_xpad->stick.left.Y.average = left_y;
|
||||
|
||||
input->controllers[ controller_index ].xpad = new_xpad;
|
||||
}
|
||||
else
|
||||
{
|
||||
input->controllers[ controller_index ].xpad = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// JSL Input Polling
|
||||
for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index )
|
||||
{
|
||||
if ( ! JslStillConnected( jsl_device_handles[ jsl_device_index ] ) )
|
||||
{
|
||||
OutputDebugStringA( "Error: JSLStillConnected returned false\n" );
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO : Won't support more than 4 for now... (or prob ever)
|
||||
if ( jsl_device_index > 4 )
|
||||
break;
|
||||
|
||||
JOY_SHOCK_STATE state = JslGetSimpleState( jsl_device_handles[ jsl_device_index ] );
|
||||
|
||||
// For now we're assuming anything that is detected via JSL is a dualsense pad.
|
||||
// We'll eventually add support possibly for the nintendo pro controller.
|
||||
engine::DualsensePadState* old_ds_pad = old_ds_pads[ jsl_device_index ];
|
||||
engine::DualsensePadState* new_ds_pad = new_ds_pads[ jsl_device_index ];
|
||||
|
||||
input_process_digital_btn( & old_ds_pad->dpad.up, & new_ds_pad->dpad.up, state.buttons, JSMASK_UP );
|
||||
input_process_digital_btn( & old_ds_pad->dpad.down, & new_ds_pad->dpad.down, state.buttons, JSMASK_DOWN );
|
||||
input_process_digital_btn( & old_ds_pad->dpad.left, & new_ds_pad->dpad.left, state.buttons, JSMASK_LEFT );
|
||||
input_process_digital_btn( & old_ds_pad->dpad.right, & new_ds_pad->dpad.right, state.buttons, JSMASK_RIGHT );
|
||||
|
||||
input_process_digital_btn( & old_ds_pad->triangle, & new_ds_pad->triangle, state.buttons, JSMASK_N );
|
||||
input_process_digital_btn( & old_ds_pad->cross, & new_ds_pad->cross, state.buttons, JSMASK_S );
|
||||
input_process_digital_btn( & old_ds_pad->square, & new_ds_pad->square, state.buttons, JSMASK_W );
|
||||
input_process_digital_btn( & old_ds_pad->circle, & new_ds_pad->circle, state.buttons, JSMASK_E );
|
||||
|
||||
input_process_digital_btn( & old_ds_pad->share, & new_ds_pad->share, state.buttons, JSMASK_SHARE );
|
||||
input_process_digital_btn( & old_ds_pad->options, & new_ds_pad->options, state.buttons, JSMASK_OPTIONS );
|
||||
|
||||
input_process_digital_btn( & old_ds_pad->L1, & new_ds_pad->L1, state.buttons, JSMASK_L );
|
||||
input_process_digital_btn( & old_ds_pad->R1, & new_ds_pad->R1, state.buttons, JSMASK_R );
|
||||
|
||||
new_ds_pad->stick.left.X.start = old_ds_pad->stick.left.X.end;
|
||||
new_ds_pad->stick.left.Y.start = old_ds_pad->stick.left.Y.end;
|
||||
|
||||
// Joyshock abstracts the sticks to a float value already for us of -1.f to 1.f.
|
||||
// We'll assume a deadzone of 10% for now.
|
||||
f32 left_x = jsl_input_process_axis_value( state.stickLX, 0.1f );
|
||||
f32 left_y = jsl_input_process_axis_value( state.stickLY, 0.1f );
|
||||
|
||||
new_ds_pad->stick.left.X.min = new_ds_pad->stick.left.X.max = new_ds_pad->stick.left.X.end = left_x;
|
||||
new_ds_pad->stick.left.Y.min = new_ds_pad->stick.left.Y.max = new_ds_pad->stick.left.Y.end = left_y;
|
||||
|
||||
// TODO(Ed): Make this actually an average for later
|
||||
new_ds_pad->stick.left.X.average = left_x;
|
||||
new_ds_pad->stick.left.Y.average = left_y;
|
||||
|
||||
input->controllers[ jsl_device_index ].ds_pad = new_ds_pad;
|
||||
}
|
||||
}
|
||||
|
||||
NS_PLATFORM_END
|
1006
project/platform/win32/win32_platform.cpp
Normal file
1006
project/platform/win32/win32_platform.cpp
Normal file
File diff suppressed because it is too large
Load Diff
261
project/platform/win32/win32_platform_api.cpp
Normal file
261
project/platform/win32/win32_platform_api.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
#include "platform/platform.hpp"
|
||||
#include "platform/jsl.hpp"
|
||||
#include "win32.hpp"
|
||||
|
||||
NS_PLATFORM_BEGIN
|
||||
|
||||
global b32 Pause_Rendering = false;
|
||||
|
||||
#pragma region Platfom API
|
||||
#if Build_Development
|
||||
void congrats_impl( char const* message )
|
||||
{
|
||||
JslSetLightColour( 0, (255 << 16) | (215 << 8) );
|
||||
MessageBoxA( 0, message, "Congratulations!", MB_OK | MB_ICONEXCLAMATION );
|
||||
JslSetLightColour( 0, (255 << 8 ) );
|
||||
}
|
||||
|
||||
bool impl_ensure( bool condition, char const* message )
|
||||
{
|
||||
if ( ! condition ) {
|
||||
JslSetLightColour( 0, (255 << 16) );
|
||||
MessageBoxA( 0, message, "Ensure Failure", MB_OK | MB_ICONASTERISK );
|
||||
JslSetLightColour( 0, ( 255 << 8 ) );
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
void impl_fatal( char const* message )
|
||||
{
|
||||
JslSetLightColour( 0, (255 << 16) );
|
||||
MessageBoxA( 0, message, "Fatal Error", MB_OK | MB_ICONERROR );
|
||||
JslSetLightColour( 0, (255 << 8 ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if Build_Development
|
||||
void debug_set_pause_rendering( b32 value )
|
||||
{
|
||||
Pause_Rendering = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
b32 file_check_exists( Str path )
|
||||
{
|
||||
HANDLE file_handle = CreateFileA( path
|
||||
, GENERIC_READ, FILE_SHARE_READ, 0
|
||||
, OPEN_EXISTING, 0, 0
|
||||
);
|
||||
if ( file_handle != INVALID_HANDLE_VALUE )
|
||||
{
|
||||
CloseHandle( file_handle );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void file_close( File* file )
|
||||
{
|
||||
HANDLE handle = pcast(HANDLE, file->opaque_handle);
|
||||
|
||||
if ( handle == INVALID_HANDLE_VALUE )
|
||||
return;
|
||||
|
||||
CloseHandle( handle );
|
||||
|
||||
if ( file->data )
|
||||
{
|
||||
// TODO(Ed): This should use our persistent memory block.
|
||||
VirtualFree( file->data, 0, MEM_Release);
|
||||
}
|
||||
*file = {};
|
||||
}
|
||||
|
||||
b32 file_delete( Str path )
|
||||
{
|
||||
return DeleteFileA( path );
|
||||
}
|
||||
|
||||
b32 file_read_stream( File* file, u32 content_size, void* content_memory )
|
||||
{
|
||||
HANDLE file_handle;
|
||||
if ( file->opaque_handle == nullptr )
|
||||
{
|
||||
file_handle = CreateFileA( file->path
|
||||
, GENERIC_READ, FILE_SHARE_READ, 0
|
||||
, OPEN_EXISTING, 0, 0
|
||||
);
|
||||
if ( file_handle == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
// TODO : Logging
|
||||
return {};
|
||||
}
|
||||
|
||||
file->opaque_handle = file_handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
file_handle = pcast(HANDLE, file->opaque_handle );
|
||||
}
|
||||
|
||||
u32 bytes_read;
|
||||
if ( ReadFile( file_handle, content_memory, content_size, rcast(LPDWORD, &bytes_read), 0 ) == false )
|
||||
{
|
||||
// TODO : Logging
|
||||
return {};
|
||||
}
|
||||
|
||||
if ( bytes_read != content_size )
|
||||
{
|
||||
// TODO : Logging
|
||||
return {};
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
b32 file_read_content( File* file )
|
||||
{
|
||||
HANDLE file_handle = CreateFileA( file->path
|
||||
, GENERIC_READ, FILE_SHARE_READ, 0
|
||||
, OPEN_EXISTING, 0, 0
|
||||
);
|
||||
if ( file_handle == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
// TODO(Ed) : Logging
|
||||
return {};
|
||||
}
|
||||
|
||||
u32 size;
|
||||
GetFileSizeEx( file_handle, rcast(LARGE_INTEGER*, &size) );
|
||||
if ( size == 0 )
|
||||
{
|
||||
// TODO(Ed) : Logging
|
||||
CloseHandle( file_handle );
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO(Ed) : This should use our memory block.
|
||||
file->data = rcast(HANDLE*, VirtualAlloc( 0, sizeof(HANDLE) + size, MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write ));
|
||||
file->size = size;
|
||||
file->opaque_handle = file_handle;
|
||||
|
||||
u32 bytes_read;
|
||||
if ( ReadFile( file_handle, file->data, file->size, rcast(LPDWORD, &bytes_read), 0 ) == false )
|
||||
{
|
||||
// TODO(Ed) : Logging
|
||||
CloseHandle( file_handle );
|
||||
return {};
|
||||
}
|
||||
|
||||
if ( bytes_read != file->size )
|
||||
{
|
||||
// TODO : Logging
|
||||
CloseHandle( file_handle );
|
||||
return {};
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
void file_rewind( File* file )
|
||||
{
|
||||
HANDLE file_handle = pcast(HANDLE, file->opaque_handle );
|
||||
if ( file_handle == INVALID_HANDLE_VALUE )
|
||||
return;
|
||||
|
||||
SetFilePointer(file_handle, 0, NULL, FILE_BEGIN);
|
||||
}
|
||||
|
||||
u32 file_write_stream( File* file, u32 content_size, void* content_memory )
|
||||
{
|
||||
HANDLE file_handle;
|
||||
if ( file->opaque_handle == nullptr )
|
||||
{
|
||||
file_handle = CreateFileA( file->path
|
||||
,GENERIC_WRITE, 0, 0
|
||||
, OPEN_ALWAYS, 0, 0
|
||||
);
|
||||
if ( file_handle == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
// TODO(Ed) : Logging
|
||||
return {};
|
||||
}
|
||||
|
||||
file->opaque_handle = file_handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
file_handle = pcast(HANDLE, file->opaque_handle );
|
||||
}
|
||||
|
||||
DWORD bytes_written;
|
||||
if ( WriteFile( file_handle, content_memory, content_size, & bytes_written, 0 ) == false )
|
||||
{
|
||||
// TODO : Logging
|
||||
return false;
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
u32 file_write_content( File* file, u32 content_size, void* content_memory )
|
||||
{
|
||||
HANDLE file_handle = CreateFileA( file->path
|
||||
, GENERIC_WRITE, 0, 0
|
||||
, CREATE_ALWAYS, 0, 0
|
||||
);
|
||||
if ( file_handle == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
// TODO : Logging
|
||||
return false;
|
||||
}
|
||||
file->opaque_handle = file_handle;
|
||||
|
||||
DWORD bytes_written;
|
||||
if ( WriteFile( file_handle, content_memory, content_size, & bytes_written, 0 ) == false )
|
||||
{
|
||||
// TODO : Logging
|
||||
return false;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
u32 get_monitor_refresh_rate()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void set_monitor_refresh_rate( u32 refresh_rate )
|
||||
{
|
||||
}
|
||||
u32 get_engine_refresh_rate()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void set_engine_refresh_rate( u32 refresh_rate )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BinaryModule load_binary_module( char const* module_path )
|
||||
{
|
||||
HMODULE lib = LoadLibraryA( module_path );
|
||||
return BinaryModule { scast(void*, lib) };
|
||||
}
|
||||
|
||||
void unload_binary_module( BinaryModule* module )
|
||||
{
|
||||
FreeLibrary( scast(HMODULE, module->opaque_handle) );
|
||||
*module = {};
|
||||
}
|
||||
|
||||
void* get_binary_module_symbol( BinaryModule module, char const* symbol_name )
|
||||
{
|
||||
return rcast(void*, GetProcAddress( scast(HMODULE, module.opaque_handle), symbol_name ));
|
||||
}
|
||||
|
||||
void memory_copy( void* dest, u64 src_size, void* src )
|
||||
{
|
||||
CopyMemory( dest, src, src_size );
|
||||
}
|
||||
#pragma endregion Platform API
|
||||
|
||||
NS_PLATFORM_END
|
Loading…
Reference in New Issue
Block a user