diff --git a/.gitignore b/.gitignore
index c29ce1e..c46cc39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,5 @@ build
**/*.dll
data/test.out
+data/handmade_engine.symbols
+data/handmade_win32.exe
diff --git a/.vscode/launch.json b/.vscode/launch.json
index f2191e2..30f2117 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,7 +8,7 @@
"type":"cppvsdbg",
"request": "launch",
"name" : "Debug handmade win32 msvc",
- "program": "${workspaceFolder}/build/handmade_win32.exe",
+ "program": "${workspaceFolder}/data/handmade_win32.exe",
"args": [],
"cwd": "${workspaceFolder}/data",
"visualizerFile": "${workspaceFolder}/scripts/handmade.natvis"
diff --git a/HandmadeHero.10x b/HandmadeHero.10x
index b21fa9d..7e5a044 100644
--- a/HandmadeHero.10x
+++ b/HandmadeHero.10x
@@ -8,16 +8,16 @@
true
false
false
- pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/build.ps1 msvc dev optimized
-
+ pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/build.ps1 msvc dev debug engine
+ pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/build.ps1 msvc dev debug platform
pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/clean.ps1
- $(WorkspaceDirectory)/build/handmade_win32.exe
+ $(WorkspaceDirectory)/data/handmade_win32.exe
$(WorkspaceDirectory)/data
- $(WorkspaceDirectory)/build/handmade_win32.exe
- $(WorkspaceDirectory)/build/handmade_win32.exe
+ $(WorkspaceDirectory)/data/handmade_win32.exe
+ $(WorkspaceDirectory)/data/handmade_win32.exe
true
diff --git a/HandmadeHero.vcxproj b/HandmadeHero.vcxproj
index f8703d3..13e3e2c 100644
--- a/HandmadeHero.vcxproj
+++ b/HandmadeHero.vcxproj
@@ -17,28 +17,43 @@
$(ProjectDir)project;$(IncludePath)
$(ProjectDir)data;$(windir)System32;$(LibraryPath)
- pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(ProjectDir)scripts\build.ps1 msvc dev
+ pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(ProjectDir)scripts\build.ps1 msvc dev engine
pwsh ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(ProjectDir)scripts\clean.ps1
GEN_TIME;Build_Development;Build_Debug;$(NMakePreprocessorDefinitions)
$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HandmadeHero.vcxproj.user b/HandmadeHero.vcxproj.user
index 556ee01..977a45d 100644
--- a/HandmadeHero.vcxproj.user
+++ b/HandmadeHero.vcxproj.user
@@ -6,6 +6,6 @@
$(ProjectDir)data
WindowsLocalDebugger
- $(ProjectDir)build\handmade_win32.exe
+ $(ProjectDir)data\handmade_win32.exe
\ No newline at end of file
diff --git a/docs/Day 021.md b/docs/Day 021.md
new file mode 100644
index 0000000..ef9b6a7
--- /dev/null
+++ b/docs/Day 021.md
@@ -0,0 +1,15 @@
+# Day 21
+
+So I learned today the good reason why he doesn't use static variables; So that when he makes this library dynamically loaded we don't lose the state of the game on reload.
+
+This day was extremely gratifying to get working.
+
+I went the extra mile than what Casey did and allow for name mangled symbols (for both clang and msvc).
+It took a few more steps than his solution but I get to keep the eronomics of namespaces.
+
+After the linker finishes emitting, I use the build script to parse the .map file and extract the decorated symbols I need to load in the platform layer. Those are exported to a file called `handmade_engine.symbols` and then in the platform layer I load them up using an enum as the lookup table for the line the symbol will be in the file. From there its just loading up the symbol with GetProcAddress!
+
+![img](https://files.catbox.moe/sdebtc.png)
+![img](https://files.catbox.moe/ufkacl.png)
+![img](https://files.catbox.moe/zr52au.png)
+![img](https://files.catbox.moe/g3f1vv.png)
diff --git a/project/engine.cpp b/project/engine/engine.cpp
similarity index 77%
rename from project/engine.cpp
rename to project/engine/engine.cpp
index eb297e8..d8a1e58 100644
--- a/project/engine.cpp
+++ b/project/engine/engine.cpp
@@ -1,15 +1,21 @@
-#include "engine.hpp"
//#include "win32.h"
+#include "engine.hpp"
+#include "platform/platform_engine_api.hpp"
+#include "handmade.hpp"
NS_ENGINE_BEGIN
struct EngineState
{
- s32 WaveSwitch;
s32 WaveToneHz;
s32 ToneVolume;
s32 XOffset;
s32 YOffset;
+
+ b32 RendererPaused;
+
+ f32 SampleWaveSineTime;
+ b32 SampleWaveSwitch;
};
using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer );
@@ -28,7 +34,7 @@ square_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
internal s16
sine_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
{
- local_persist f32 time = 0.f;
+ f32& time = state->SampleWaveSineTime;
s32 wave_period = sound_buffer->SamplesPerSecond / state->WaveToneHz;
@@ -104,7 +110,6 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset )
u8 red = 0;
#endif
-
*pixel++ = u32(red << 16) | u32(green << 8) | blue;
}
wildcard += 0.5375f;
@@ -112,66 +117,55 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset )
}
}
-b32 input_using_analog()
+Engine_API
+void on_module_reload( Memory* memory, platform::ModuleAPI* platfom_api )
{
- return false;
+
}
-void startup()
+Engine_API
+void startup( Memory* memory, platform::ModuleAPI* platform_api )
{
-}
-
-void shutdown()
-{
-}
-
-// TODO : I rather expose the back_buffer and sound_buffer using getters for access in any function.
-void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory )
-{
- // Graphics & Input Test
- local_persist u32 x_offset = 0;
- local_persist u32 y_offset = 0;
-
- // Wave Sound Test
- local_persist bool wave_switch = false;
-
-#if 0
- if ( input_using_analog() )
- {
- // TODO(Ed) : Use analog movement tuning
- }
- else
- {
- // TODO(Ed) : Use digital movement tuning
- }
-#endif
-
EngineState* state = rcast( EngineState*, memory->Persistent );
+ assert( sizeof(EngineState) <= memory->PersistentSize );
- do_once_start
- assert( sizeof(EngineState) <= memory->PersistentSize );
+ state->ToneVolume = 1000;
- state->ToneVolume = 1000;
- state->WaveToneHz = 120;
+ state->XOffset = 0;
+ state->YOffset = 0;
- state->XOffset = 0;
- state->YOffset = 0;
- state->WaveSwitch = false;
+ state->SampleWaveSwitch = false;
+ state->WaveToneHz = 120;
+ state->SampleWaveSineTime = 0.f;
- #if Build_Debug && 0
+ state->RendererPaused = false;
+
+ #if Build_Debug && 0
+ {
+ using namespace platform;
+
+ char const* file_path = __FILE__;
+ Debug_FileContent file_content = platform_api->debug_file_read_content( file_path );
+ if ( file_content.Size )
{
- using namespace platform;
-
- char const* file_path = __FILE__;
- Debug_FileContent file_content = debug_file_read_content( file_path );
- if ( file_content.Size )
- {
- debug_file_write_content( "test.out", file_content.Size, file_content.Data );
- debug_file_free_content( & file_content );
- }
+ platform_api->debug_file_write_content( "test.out", file_content.Size, file_content.Data );
+ platform_api->debug_file_free_content( & file_content );
}
- #endif
- do_once_end
+ }
+ #endif
+}
+
+Engine_API
+void shutdown( Memory* memory, platform::ModuleAPI* platform_api )
+{
+}
+
+Engine_API
+// TODO : I rather expose the back_buffer and sound_buffer using getters for access in any function.
+void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api )
+{
+ EngineState* state = rcast( EngineState*, memory->Persistent );
+ assert( sizeof(EngineState) <= memory->PersistentSize );
ControllerState* controller = & input->Controllers[0];
@@ -195,7 +189,6 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory*
b32 toggle_wave_tone = false;
b32 pause_renderer = false;
- local_persist b32 renderer_paused = false;
f32 analog_threshold = 0.5f;
@@ -255,10 +248,10 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory*
toggle_wave_tone |= pressed( keyboard->Space );
}
- x_offset += 3 * move_right;
- x_offset -= 3 * move_left;
- y_offset += 3 * move_down;
- y_offset -= 3 * move_up;
+ state->XOffset += 3 * move_right;
+ state->XOffset -= 3 * move_left;
+ state->YOffset += 3 * move_down;
+ state->YOffset -= 3 * move_up;
if ( raise_volume )
{
@@ -284,26 +277,27 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory*
if ( toggle_wave_tone )
{
- state->WaveSwitch ^= true;
+ state->SampleWaveSwitch ^= true;
}
- render_weird_graident( back_buffer, x_offset, y_offset );
+ render_weird_graident( back_buffer, state->XOffset, state->YOffset );
if ( pause_renderer )
{
- if ( renderer_paused )
+ if ( state->RendererPaused )
{
- platform::set_pause_rendering(false);
- renderer_paused = false;
+ platform_api->debug_set_pause_rendering(false);
+ state->RendererPaused = false;
}
else
{
- platform::set_pause_rendering(true);
- renderer_paused = true;
+ platform_api->debug_set_pause_rendering(true);
+ state->RendererPaused = true;
}
}
}
-void update_audio( AudioBuffer* audio_buffer, Memory* memory )
+Engine_API
+void update_audio( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api )
{
EngineState* state = rcast( EngineState*, memory->Persistent );
do_once_start
@@ -311,11 +305,10 @@ void update_audio( AudioBuffer* audio_buffer, Memory* memory )
do_once_end
// TODO(Ed) : Allow sample offsets here for more robust platform options
- if ( ! state->WaveSwitch )
+ if ( ! state->SampleWaveSwitch )
output_sound( state, audio_buffer, sine_wave_sample_value );
else
output_sound( state, audio_buffer, square_wave_sample_value );
}
NS_ENGINE_END
-
diff --git a/project/engine.hpp b/project/engine/engine.hpp
similarity index 76%
rename from project/engine.hpp
rename to project/engine/engine.hpp
index eff148a..17e6d2d 100644
--- a/project/engine.hpp
+++ b/project/engine/engine.hpp
@@ -197,56 +197,3 @@ void input_mode_pop( InputMode* mode );
void input_mode_pop( InputMode* mode );
NS_ENGINE_END
-
-// TODO(Ed) : Move this to handmade game layer later.
-
-#define NS_HANDMADE_BEGIN namespace handmade {
-#define NS_HANDMADE_END }
-
-NS_HANDMADE_BEGIN
-
-// We want a 'binding' to have multiple binds to active it (most likely)
-struct Actionable
-{
- char const* Name;
- engine::InputBindCallback* Binds;
- s32 NumBinds;
- char _PAD_[4];
-};
-
-struct ActionableMode
-{
-
-};
-
-/*
- Platform Layer:
-
- Controller : Keyboard & Mouse, XPad, DSPad
-
- ---VV---
-
- Engine Layer:
-
- InputBinding callbacks (per-game-logic frame basis)
- Push/Pop input modes (binding sets)
-
- ---VV---
-
- Game Layer:
-
- Actionables : Binding Sets where a raw input, or input interpretation leads to an player action.
- ActionSet : Actionables.Push/Pop -> Input.Push/Pop ?
- Player : Controller, Actionables, ActionSets
-*/
-
-struct Player
-{
- // So far just has an assigned controller.
- engine::ControllerState* Controller;
-
- // Possilby some other stuff in the future.
-};
-
-NS_HANDMADE_END
-
diff --git a/project/engine/win32_engine.cpp b/project/engine/win32_engine.cpp
new file mode 100644
index 0000000..3f7f7a7
--- /dev/null
+++ b/project/engine/win32_engine.cpp
@@ -0,0 +1,11 @@
+#include "platform/win32.hpp"
+#include "engine.hpp"
+
+b32 WINAPI DllMain(
+ HINSTANCE instance,
+ DWORD reason_for_call,
+ LPVOID reserved
+)
+{
+ return true;
+}
diff --git a/project/handmade.cpp b/project/handmade.cpp
index 5ae0d3b..9bcbd36 100644
--- a/project/handmade.cpp
+++ b/project/handmade.cpp
@@ -4,5 +4,5 @@
#pragma once
-#include "engine.hpp"
+#include "engine/engine.hpp"
diff --git a/project/handmade.hpp b/project/handmade.hpp
new file mode 100644
index 0000000..f0bd3b6
--- /dev/null
+++ b/project/handmade.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "engine/engine.hpp"
+
+#define NS_HANDMADE_BEGIN namespace handmade {
+#define NS_HANDMADE_END }
+
+NS_HANDMADE_BEGIN
+
+struct Memory
+{
+ // Subscection of engine memory for the game to use.
+
+ void* Persistent;
+ u64 PersistentSize;
+
+ // void* Frame;
+ // u64 FrameSize;
+
+ void* Transient;
+ u64 TransientSize;
+};
+
+// We want a 'binding' to have multiple binds to active it (most likely)
+struct Actionable
+{
+ char const* Name;
+ engine::InputBindCallback* Binds;
+ s32 NumBinds;
+ char _PAD_[4];
+};
+
+struct ActionableMode
+{
+
+};
+
+/*
+ Platform Layer:
+
+ Controller : Keyboard & Mouse, XPad, DSPad
+
+ ---VV---
+
+ Engine Layer:
+
+ InputBinding callbacks (per-game-logic frame basis)
+ Push/Pop input modes (binding sets)
+
+ ---VV---
+
+ Game Layer:
+
+ Actionables : Binding Sets where a raw input, or input interpretation leads to an player action.
+ ActionSet : Actionables.Push/Pop -> Input.Push/Pop ?
+ Player : Controller, Actionables, ActionSets
+*/
+
+struct Player
+{
+ // So far just has an assigned controller.
+ engine::ControllerState* Controller;
+
+ // Possilby some other stuff in the future.
+};
+
+NS_HANDMADE_END
diff --git a/project/handmade_engine.cpp b/project/handmade_engine.cpp
new file mode 100644
index 0000000..419e824
--- /dev/null
+++ b/project/handmade_engine.cpp
@@ -0,0 +1,19 @@
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-const-variable"
+#pragma clang diagnostic ignored "-Wswitch"
+#pragma clang diagnostic ignored "-Wunused-variable"
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wvarargs"
+#pragma clang diagnostic ignored "-Wunused-function"
+#pragma clang diagnostic ignored "-Wunused-but-set-variable"
+#endif
+
+#include "platform/grime.hpp"
+
+#if Build_Unity
+# include "engine/engine.cpp"
+# if SYSTEM_WINDOWS
+# include "engine/win32_engine.cpp"
+# endif
+#endif
diff --git a/project/handmade_win32.cpp b/project/handmade_win32.cpp
index c2d2599..5a56c90 100644
--- a/project/handmade_win32.cpp
+++ b/project/handmade_win32.cpp
@@ -10,7 +10,5 @@
#endif
#if Build_Unity
-#include "handmade.cpp"
-#include "engine.cpp"
#include "platform/win32_platform.cpp"
#endif
diff --git a/project/platform/macros.hpp b/project/platform/macros.hpp
index fe72e29..2223008 100644
--- a/project/platform/macros.hpp
+++ b/project/platform/macros.hpp
@@ -61,6 +61,12 @@
# define assert( expression )
#endif
+#ifdef COMPILER_CLANG
+# define compiler_decorated_func_name __PRETTY_NAME__
+#elif defined(COMPILER_MSVC)
+# define compiler_decorated_func_name __FUNCDNAME__
+#endif
+
// TODO(Ed) : Add this sauce later
#if 0
#define congrats( message )
diff --git a/project/platform/platform.hpp b/project/platform/platform.hpp
index fc5d094..92ac416 100644
--- a/project/platform/platform.hpp
+++ b/project/platform/platform.hpp
@@ -32,7 +32,11 @@
NS_PLATFORM_BEGIN
-#if Build_Debug
+// On-Demand platform interface.
+// Everything exposed here should be based on a feature a game may want to provide a user
+// (Example: Letting the user change the refresh-rate of the monitor or the engine's target frame-rate)
+
+#if Build_Development
/*
IMPORTANT : These are not for shipping code - they are blocking and the write isn't protected.
*/
@@ -44,34 +48,42 @@ struct Debug_FileContent
char _PAD_[4];
};
-void debug_file_free_content ( Debug_FileContent* file_content );
-Debug_FileContent debug_file_read_content ( char const* file_path );
-b32 debug_file_write_content( char const* file_path, u32 content_size, void* content_memory );
-
-// Allows the engine or game to pause the renderering of any next frames.
-// ( Prevents blipping of the black buffer )
-void set_pause_rendering( b32 value );
+using DebugFileFreeContentFn = void ( Debug_FileContent* file_content );
+using DebugFileReadContentFn = Debug_FileContent ( char const* file_path );
+using DebugFileWriteContentFn = b32 ( char const* file_path, u32 content_size, void* content_memory );
+using DebugSetPauseRenderingFn = void (b32 value);
#endif
-// On-Demand platform interface.
-// Everything exposed here should be based on a feature a game may want to provide a user
-// (Example: Letting the user change the refresh-rate of the monitor or the engine's target frame-rate)
-
// TODO(Ed) : Implement this later when settings UI is setup.
#pragma region Settings Exposure
// Exposing specific properties for user configuration in settings
// Returns the current monitor refresh rate.
-u32 get_monitor_refresh_rate();
-
+using GetMonitorRefreshRateFn = u32();
// Sets the monitor refresh rate
-// Must be of the compatiable listing for the monitor the window surface is presenting to.
-void set_monitor_refresh_rate( u32 rate_in_hz );
+// Must be of the compatiable listing for the monitor the window surface is presenting to
+using SetMonitorRefreshRateFn = void ( u32 rate_in_hz );
-u32 get_engine_frame_target();
+using GetEngineFrameTargetFn = u32 ();
+using SetEngineFrameTargetFn = void ( u32 rate_in_hz );
-void set_engine_frame_target( u32 rate_in_hz );
+struct ModuleAPI
+{
+#if Build_Development
+ DebugFileFreeContentFn* debug_file_free_content;
+ DebugFileReadContentFn* debug_file_read_content;
+ DebugFileWriteContentFn* debug_file_write_content;
+
+ DebugSetPauseRenderingFn* debug_set_pause_rendering;
+#endif
+
+ GetMonitorRefreshRateFn* get_monitor_refresh_rate;
+ SetMonitorRefreshRateFn* set_monitor_refresh_rate;
+
+ GetEngineFrameTargetFn* get_engine_frame_target;
+ SetEngineFrameTargetFn* set_engine_frame_target;
+};
#pragma endregion Settings Exposure
diff --git a/project/platform/platform_engine_api.hpp b/project/platform/platform_engine_api.hpp
index 8e67c46..0a0cd41 100644
--- a/project/platform/platform_engine_api.hpp
+++ b/project/platform/platform_engine_api.hpp
@@ -2,20 +2,47 @@
This represents the API only accessible to the platform layer to fullfill for the engine layer.
*/
#pragma once
-#include "engine.hpp"
+#include "engine/engine.hpp"
+
+#ifndef Engine_API
+# define Engine_API
+#endif
NS_ENGINE_BEGIN
-void startup();
-void shutdown();
+using OnModuleRelaodFn = void( Memory* memory, platform::ModuleAPI* platform_api );
+using StartupFn = void( Memory* memory, platform::ModuleAPI* platform_api );
+using ShutdownFn = void( Memory* memory, platform::ModuleAPI* platform_api );
// Needs a contextual reference to four things:
// Timing, Input, Bitmap Buffer
-void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory );
+using UpdateAndRenderFn = void ( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api );
// Audio timing is complicated, processing samples must be done at a different period from the rest of the engine's usual update.
// IMPORTANT: This has very tight timing, and cannot be more than a millisecond in execution.
// TODO(Ed) : Reduce timing pressure on performance by measuring it or pinging its time.
-void update_audio( AudioBuffer* audio_buffer, Memory* memory );
+using UpdateAudioFn = void ( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api );
+
+struct ModuleAPI
+{
+ enum : u32
+ {
+ Sym_OnModuleReload,
+ Sym_Startup,
+ Sym_Shutdown,
+ Sym_UpdateAndRender,
+ Sym_UpdateAudio,
+ };
+
+ OnModuleRelaodFn* on_module_reload;
+ StartupFn* startup;
+ ShutdownFn* shutdown;
+
+ UpdateAndRenderFn* update_and_render;
+ UpdateAudioFn* update_audio;
+
+ b32 IsValid;
+ char _PAD_[4];
+};
NS_ENGINE_END
diff --git a/project/platform/platform_game_api.hpp b/project/platform/platform_game_api.hpp
new file mode 100644
index 0000000..9186eab
--- /dev/null
+++ b/project/platform/platform_game_api.hpp
@@ -0,0 +1,3 @@
+/*
+ This represents the API only accessible to the platform layer to fullfill for the game layer.
+*/
diff --git a/project/platform/win32.hpp b/project/platform/win32.hpp
index 197b3c7..813be72 100644
--- a/project/platform/win32.hpp
+++ b/project/platform/win32.hpp
@@ -113,6 +113,14 @@ 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
(
@@ -151,11 +159,14 @@ xinput_load_library_bindings()
{
HMODULE xinput_lib = LoadLibraryA( XINPUT_DLL_A );
-#pragma warning( push )
-#pragma warning( disable: 4191 )
- xinput_get_state = rcast( XInputGetStateFn*, GetProcAddress( xinput_lib, "XInputGetState" ));
- xinput_set_state = rcast( XInputSetStateFn*, GetProcAddress( xinput_lib, "XInputSetState" ));
-#pragma warning( pop )
+ 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
diff --git a/project/platform/win32_platform.cpp b/project/platform/win32_platform.cpp
index b13ee38..dd8f8a1 100644
--- a/project/platform/win32_platform.cpp
+++ b/project/platform/win32_platform.cpp
@@ -23,7 +23,7 @@
#include "win32.hpp"
// Engine layer headers
-#include "engine.hpp"
+#include "engine/engine.hpp"
#include "platform_engine_api.hpp"
// Standard-Library stand-ins
@@ -216,7 +216,7 @@ b32 debug_file_write_content( char const* file_path, u32 content_size, void* con
return true;
}
-void set_pause_rendering( b32 value )
+void debug_set_pause_rendering( b32 value )
{
Pause_Rendering = value;
}
@@ -425,15 +425,12 @@ init_sound(HWND window_handle, DirectSoundBuffer* sound_buffer )
}
// Get direct sound object
-#pragma warning( push )
-#pragma warning( disable: 4191 )
- direct_sound_create = rcast( DirectSoundCreateFn*, GetProcAddress( sound_library, "DirectSoundCreate" ));
+ 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;
}
-#pragma warning( pop )
LPDIRECTSOUND direct_sound;
if ( ! SUCCEEDED(direct_sound_create( 0, & direct_sound, 0 )) )
@@ -751,6 +748,131 @@ process_pending_window_messages( engine::KeyboardState* keyboard )
}
}
}
+
+#pragma region Platfom API
+u32 get_monitor_refresh_rate();
+
+#pragma endregion Platform API
+
+// TODO(Ed): This also assumes the symbol name is always within size of the provided buffer, needs to fail if not.
+void get_symbol_from_module_table( Debug_FileContent symbol_table, u32 symbol_ID, char* symbol_name )
+{
+ struct Token
+ {
+ char const* Ptr;
+ u32 Len;
+ char _PAD_[4];
+ };
+
+ Token tokens[256] = {};
+ s32 idx = 0;
+
+ char const* scanner = rcast( char const*, symbol_table.Data );
+ u32 left = symbol_table.Size;
+ while ( left )
+ {
+ if ( *scanner == '\n' || *scanner == '\r' )
+ {
+ ++ scanner;
+ -- left;
+ }
+ else
+ {
+ tokens[idx].Ptr = scanner;
+ while ( left && *scanner != '\r' && *scanner != '\n' )
+ {
+ -- left;
+ ++ scanner;
+ ++ tokens[idx].Len;
+ }
+ ++ idx;
+ }
+ }
+
+ Token& token = tokens[symbol_ID];
+ while ( token.Len -- )
+ {
+ *symbol_name = *token.Ptr;
+ ++ symbol_name;
+ ++ token.Ptr;
+ }
+ *symbol_name = '\0';
+}
+
+// Right now they are just the data directory
+#define Path_To_Symbol_Tables
+
+global HMODULE Lib_Handmade_Engine = nullptr;
+
+engine::ModuleAPI load_engine_module_api()
+{
+ using ModuleAPI = engine::ModuleAPI;
+
+ // TODO(Ed) : Need proper paything to the dll (not assume is in the base directory).
+
+ CopyFileA( "handmade_engine.dll", "handmade_engine_temp.dll", FALSE );
+
+ // Engine
+ Lib_Handmade_Engine = LoadLibraryA( "handmade_engine_temp.dll" );
+ if ( ! Lib_Handmade_Engine )
+ {
+ return {};
+ }
+
+ constexpr char const*
+ handmade_engine_symbols = Path_To_Symbol_Tables "handmade_engine.symbols";
+
+ Debug_FileContent symbol_table = debug_file_read_content( handmade_engine_symbols );
+ if ( symbol_table.Size == 0 )
+ {
+ fatal( "Failed to laod symbol table for handmade engine module!" );
+ return {};
+ }
+
+ // TODO(Ed) : Clean this up later when Casey makes strings. (If he doesn't we'll do it)
+ char symbol_on_module_reload[256];
+ char symboL_startup[256];
+ char symboL_shutdown[256];
+ char symboL_update_and_render[256];
+ char symbol_update_audio[256];
+ get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_OnModuleReload, symbol_on_module_reload );
+ get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_Startup, symboL_startup );
+ get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_Shutdown, symboL_shutdown );
+ get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_UpdateAndRender, symboL_update_and_render );
+ get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_UpdateAudio, symbol_update_audio );
+
+ debug_file_free_content( & symbol_table );
+
+ engine::ModuleAPI engine_api {};
+ engine_api.on_module_reload = get_procedure_from_library< engine::OnModuleRelaodFn > ( Lib_Handmade_Engine, symbol_on_module_reload );
+ engine_api.startup = get_procedure_from_library< engine::StartupFn > ( Lib_Handmade_Engine, symboL_startup );
+ engine_api.shutdown = get_procedure_from_library< engine::ShutdownFn > ( Lib_Handmade_Engine, symboL_shutdown );
+ engine_api.update_and_render = get_procedure_from_library< engine::UpdateAndRenderFn >( Lib_Handmade_Engine, symboL_update_and_render );
+ engine_api.update_audio = get_procedure_from_library< engine::UpdateAudioFn > ( Lib_Handmade_Engine, symbol_update_audio );
+
+ engine_api.IsValid =
+ engine_api.on_module_reload
+ && engine_api.startup
+ && engine_api.shutdown
+ && engine_api.update_and_render
+ && engine_api.update_audio;
+ if ( engine_api.IsValid )
+ {
+ OutputDebugStringA( "Loaded engine module API\n" );
+ }
+ return engine_api;
+}
+
+void unload_engine_module_api( engine::ModuleAPI* engine_api )
+{
+ if ( engine_api->IsValid )
+ {
+ FreeLibrary( Lib_Handmade_Engine );
+ *engine_api = {};
+ OutputDebugStringA( "Unloaded engine module API\n" );
+ }
+}
+
NS_PLATFORM_END
int CALLBACK
@@ -774,6 +896,22 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & Performance_Counter_Frequency) );
+ // Prepare platform API
+ ModuleAPI platform_api {};
+ {
+ #if Build_Development
+ platform_api.debug_file_free_content = & debug_file_free_content;
+ platform_api.debug_file_read_content = & debug_file_read_content;
+ platform_api.debug_file_write_content = & debug_file_write_content;
+
+ platform_api.debug_set_pause_rendering = & debug_set_pause_rendering;
+ #endif
+ platform_api.get_monitor_refresh_rate = nullptr;
+ platform_api.set_monitor_refresh_rate = nullptr;
+ platform_api.get_engine_frame_target = nullptr;
+ platform_api.set_engine_frame_target = nullptr;
+ }
+
// Memory
engine::Memory engine_memory {};
{
@@ -802,6 +940,9 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
}
}
+ // Load engine module
+ engine::ModuleAPI engine_api = load_engine_module_api();
+
WNDCLASSW window_class {};
HWND window_handle = nullptr;
{
@@ -935,6 +1076,8 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
}
}
+ engine_api.startup( & engine_memory, & platform_api );
+
u64 last_frame_clock = timing_get_wall_clock();
u64 last_frame_cycle = __rdtsc();
u64 flip_wall_clock = last_frame_clock;
@@ -942,8 +1085,14 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
#if Build_Development
u64 startup_cycles = last_frame_cycle - launch_cycle;
f32 startup_ms = timing_get_ms_elapsed( launch_clock, last_frame_clock );
+
+ char text_buffer[256];
+ sprintf_s( text_buffer, sizeof(text_buffer), "Startup MS: %f\n", startup_ms );
+ OutputDebugStringA( text_buffer );
#endif
+ u64 module_reload_counter = 0;
+
Running = true;
#if 0
// This tests the play & write cursor update frequency.
@@ -960,6 +1109,14 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
#endif
while( Running )
{
+ if ( module_reload_counter > 120 )
+ {
+ unload_engine_module_api( & engine_api );
+ engine_api = load_engine_module_api();
+ module_reload_counter = 0;
+ }
+ ++ module_reload_counter;
+
process_pending_window_messages( new_keyboard );
// TODO(Ed): Offload polling to these functions later.
@@ -1107,7 +1264,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
}
// Engine's logical iteration and rendering process
- engine::update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & engine_memory );
+ engine_api.update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & engine_memory, & platform_api );
u64 audio_frame_start = timing_get_wall_clock();
f32 flip_to_audio_ms = timing_get_ms_elapsed( flip_wall_clock, audio_frame_start );
@@ -1194,7 +1351,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
sound_buffer.RunningSampleIndex = ds_sound_buffer.RunningSampleIndex;
sound_buffer.SamplesPerSecond = ds_sound_buffer.SamplesPerSecond;
sound_buffer.Samples = ds_sound_buffer.Samples;
- engine::update_audio( & sound_buffer, & engine_memory );
+ engine_api.update_audio( & sound_buffer, & engine_memory, & platform_api );
DebugTimeMarker* marker = & debug_markers[ debug_marker_index ];
marker->OutputPlayCusror = ds_play_cursor;
@@ -1331,7 +1488,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
#endif
}
- engine::shutdown();
+ engine_api.shutdown( & engine_memory, & platform_api );
if ( jsl_num_devices > 0 )
{
diff --git a/scripts/build.ps1 b/scripts/build.ps1
index d0e33e7..2187381 100644
--- a/scripts/build.ps1
+++ b/scripts/build.ps1
@@ -14,6 +14,9 @@ Push-Location $path_root
$debug = $null
$analysis = $false
$dev = $false
+ $platform = $null
+ $engine = $null
+ $game = $null
[array] $vendors = @( "clang", "msvc" )
@@ -26,11 +29,14 @@ if ( $args ) { $args | ForEach-Object {
"debug" { $debug = $true }
"analysis" { $analysis = $true }
"dev" { $dev = $true }
+ "platform" { $platform = $true }
+ "engine" { $engine = $true }
+ "game" { $game = $true }
}
}}
#endregion Argument
-#region Configuration
+#region Toolchain Configuration
if ($IsWindows) {
# This HandmadeHero implementation is only designed for 64-bit systems
& $devshell -arch amd64
@@ -45,7 +51,7 @@ write-host "Building HandmadeHero with $vendor"
if ( $dev ) {
if ( $debug -eq $null ) {
- $debug = $true
+ # $debug = $true
}
if ( $optimize -eq $null ) {
@@ -118,42 +124,52 @@ function run-linker
if ( $vendor -match "clang" )
{
# https://clang.llvm.org/docs/ClangCommandLineReference.html
- $flag_all_c = '/TC'
- $flag_all_cpp = '/TP'
- $flag_compile = '-c'
- $flag_color_diagnostics = '-fcolor-diagnostics'
- $flag_no_color_diagnostics = '-fno-color-diagnostics'
- $flag_debug = '-g'
- $flag_debug_codeview = '-gcodeview'
- $flag_define = '-D'
- $flag_exceptions_disabled = '-fno-exceptions'
- $flag_preprocess = '-E'
- $flag_include = '-I'
- $flag_library = '-l'
- $flag_library_path = '-L'
- $flag_linker = '-Wl,'
- $flag_link_mapfile = '-Map'
- $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
- $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
- $flag_link_win_machine_32 = '/MACHINE:X86'
- $flag_link_win_machine_64 = '/MACHINE:X64'
- $flag_link_win_debug = '/DEBUG'
- $flag_link_win_pdb = '/PDB:'
- $flag_link_win_path_output = '/OUT:'
- $flag_no_optimization = '-O0'
- $flag_optimize_fast = '-O2'
- $flag_optimize_size = '-O1'
- $flag_optimize_intrinsics = '-Oi'
- $flag_path_output = '-o'
- $flag_preprocess_non_intergrated = '-no-integrated-cpp'
- $flag_profiling_debug = '-fdebug-info-for-profiling'
- $flag_set_stack_size = '-stack='
- $flag_syntax_only = '-fsyntax-only'
- $flag_target_arch = '-target'
- $flag_wall = '-Wall'
- $flag_warning = '-W'
- $flag_warnings_as_errors = '-Werror'
- $flag_win_nologo = '/nologo'
+ $flag_all_c = '/TC'
+ $flag_all_cpp = '/TP'
+ $flag_compile = '-c'
+ $flag_color_diagnostics = '-fcolor-diagnostics'
+ $flag_no_color_diagnostics = '-fno-color-diagnostics'
+ $flag_debug = '-g'
+ $flag_debug_codeview = '-gcodeview'
+ $flag_define = '-D'
+ $flag_exceptions_disabled = '-fno-exceptions'
+ $flag_preprocess = '-E'
+ $flag_include = '-I'
+ $flag_section_data = '-fdata-sections'
+ $flag_section_functions = '-ffunction-sections'
+ $flag_library = '-l'
+ $flag_library_path = '-L'
+ $flag_linker = '-Wl,'
+ if ( $IsWindows ) {
+ $flag_link_dll = '/DLL'
+ $flag_link_mapfile = '/MAP:'
+ $flag_link_optimize_references = '/OPT:REF'
+ }
+ if ( $IsLinux ) {
+ $flag_link_mapfile = '--Map='
+ $flag_link_optimize_references = '--gc-sections'
+ }
+ $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
+ $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
+ $flag_link_win_machine_32 = '/MACHINE:X86'
+ $flag_link_win_machine_64 = '/MACHINE:X64'
+ $flag_link_win_debug = '/DEBUG'
+ $flag_link_win_pdb = '/PDB:'
+ $flag_link_win_path_output = '/OUT:'
+ $flag_no_optimization = '-O0'
+ $flag_optimize_fast = '-O2'
+ $flag_optimize_size = '-O1'
+ $flag_optimize_intrinsics = '-Oi'
+ $flag_path_output = '-o'
+ $flag_preprocess_non_intergrated = '-no-integrated-cpp'
+ $flag_profiling_debug = '-fdebug-info-for-profiling'
+ $flag_set_stack_size = '-stack='
+ $flag_syntax_only = '-fsyntax-only'
+ $flag_target_arch = '-target'
+ $flag_wall = '-Wall'
+ $flag_warning = '-W'
+ $flag_warnings_as_errors = '-Werror'
+ $flag_win_nologo = '/nologo'
$ignore_warning_ms_include = 'no-microsoft-include'
$ignore_warning_return_type_c_linkage = 'no-return-type-c-linkage'
@@ -175,12 +191,12 @@ if ( $vendor -match "clang" )
function build-simple
{
- param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$executable )
+ param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
Write-Host "build-simple: clang"
- $object = $executable -replace '\.exe', '.obj'
- $pdb = $executable -replace '\.exe', '.pdb'
- $map = $executable -replace '\.exe', '.map'
+ $object = $binary -replace '\.exe', '.obj'
+ $pdb = $binary -replace '\.exe', '.pdb'
+ $map = $binary -replace '\.exe', '.map'
$compiler_args += @(
$flag_no_color_diagnostics,
@@ -188,6 +204,8 @@ if ( $vendor -match "clang" )
$flag_target_arch, $target_arch,
$flag_wall,
$flag_preprocess_non_intergrated,
+ $flag_section_data,
+ $flag_section_functions,
( $flag_path_output + $object )
)
if ( $optimized ) {
@@ -214,12 +232,12 @@ if ( $vendor -match "clang" )
$linker_args += @(
$flag_link_win_machine_64,
- $( $flag_link_win_path_output + $executable )
+ $( $flag_link_win_path_output + $binary )
)
if ( $debug ) {
$linker_args += $flag_link_win_debug
$linker_args += $flag_link_win_pdb + $pdb
- # $linker_args += $flag_link_mapfile + $map
+ $linker_args += $flag_link_mapfile + $map
}
$libraries | ForEach-Object {
@@ -227,7 +245,7 @@ if ( $vendor -match "clang" )
}
$linker_args += $object
- run-linker $linker $executable $linker_args
+ run-linker $linker $binary $linker_args
# $compiler_args += $unit
# $linker_args | ForEach-Object {
@@ -256,6 +274,7 @@ if ( $vendor -match "msvc" )
$flag_dll = '/LD'
$flag_dll_debug = '/LDd'
$flag_linker = '/link'
+ $flag_link_dll = '/DLL'
$flag_link_mapfile = '/MAP:'
$flag_link_optimize_references = '/OPT:REF'
$flag_link_win_debug = '/DEBUG'
@@ -287,12 +306,12 @@ if ( $vendor -match "msvc" )
# This works because this project uses a single unit to build
function build-simple
{
- param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$executable )
+ param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
Write-Host "build-simple: msvc"
- $object = $executable -replace '\.exe', '.obj'
- $pdb = $executable -replace '\.exe', '.pdb'
- $map = $executable -replace '\.exe', '.map'
+ $object = $binary -replace '\.(exe|dll)$', '.obj'
+ $pdb = $binary -replace '\.(exe|dll)$', '.pdb'
+ $map = $binary -replace '\.(exe|dll)$', '.map'
$compiler_args += @(
$flag_nologo,
@@ -336,18 +355,18 @@ if ( $vendor -match "msvc" )
$linker_args += @(
$flag_nologo,
$flag_link_win_machine_64,
- ( $flag_link_win_path_output + $executable )
+ ( $flag_link_win_path_output + $binary )
)
if ( $debug ) {
$linker_args += $flag_link_win_debug
$linker_args += $flag_link_win_pdb + $pdb
- # $linker_args += $flag_link_mapfile + $map
+ $linker_args += $flag_link_mapfile + $map
}
else {
}
$linker_args += $object
- run-linker $linker $executable $linker_args
+ run-linker $linker $binary $linker_args
# $compiler_args += $unit
# $compiler_args += $flag_linker
@@ -360,11 +379,14 @@ if ( $vendor -match "msvc" )
}
#endregion Configuration
+#region Building
$path_project = Join-Path $path_root 'project'
$path_build = Join-Path $path_root 'build'
+$path_data = Join-Path $path_root 'data'
$path_deps = Join-Path $path_project 'dependencies'
$path_gen = Join-Path $path_project 'gen'
$path_platform = Join-Path $path_project 'platform'
+$path_engine = Join-Path $path_project 'engine'
$update_deps = Join-Path $PSScriptRoot 'update_deps.ps1'
@@ -376,21 +398,21 @@ if ( (Test-Path $path_deps) -eq $false ) {
& $update_deps
}
-$includes = @(
- $path_project,
- $path_gen,
- # $path_deps,
- $path_platform
-)
-$compiler_args = @()
-$compiler_args += ( $flag_define + 'GEN_TIME' )
-
-$linker_args = @(
- $flag_link_win_subsystem_console
-)
-
#region Handmade Generate
if ( $false ) {
+ $includes = @(
+ $path_project,
+ $path_gen,
+ # $path_deps,
+ $path_platform
+ )
+ $compiler_args = @()
+ $compiler_args += ( $flag_define + 'GEN_TIME' )
+
+ $linker_args = @(
+ $flag_link_win_subsystem_console
+ )
+
$unit = Join-Path $path_gen 'handmade_gen.cpp'
$executable = Join-Path $path_build 'handmade_gen.exe'
@@ -408,10 +430,7 @@ if ( $false ) {
#region Handmade Runtime
$includes = @(
- $path_project,
- $path_gen,
- $path_deps,
- $path_platform
+ $path_project
)
# Microsoft
@@ -423,9 +442,6 @@ $lib_winmm = 'Winmm.lib'
# Github
$lib_jsl = Join-Path $path_deps 'JoyShockLibrary/x64/JoyShockLibrary.lib'
-$unit = Join-Path $path_project 'handmade_win32.cpp'
-$executable = Join-Path $path_build 'handmade_win32.exe'
-
$stack_size = 1024 * 1024 * 4
$compiler_args = @(
@@ -436,9 +452,6 @@ $compiler_args = @(
$flag_warnings_as_errors
$flag_optimize_intrinsics
- ($flag_define + 'Build_DLL=0' )
-
- # For now this script only supports unity builds... (for the full binary)
($flag_define + 'Build_Unity=1' )
)
@@ -449,19 +462,108 @@ else {
$compiler_args += ( $flag_define + 'Build_Development=0' )
}
-$linker_args = @(
- $lib_gdi32,
- # $lib_xinput,
- $lib_user32,
- $lib_winmm,
+if ( $engine )
+{
+ $engine_compiler_args = $compiler_args
+ $engine_compiler_args += ($flag_define + 'Build_DLL=1' )
- $lib_jsl,
+ if ( $vendor -eq 'msvc' )
+ {
+ $engine_compiler_args += ($flag_define + 'Engine_API=__declspec(dllexport)')
+ }
+ if ( $vendor -eq 'clang' )
+ {
+ $engine_compiler_args += ($flag_define + 'Engine_API=__attribute__((visibility("default")))')
+ }
- $flag_link_win_subsystem_windows
- $flag_link_optimize_references
-)
+ $linker_args = @(
+ $flag_link_dll,
+ $flag_link_optimize_references
+ )
-build-simple $includes $compiler_args $linker_args $unit $executable
+ $unit = Join-Path $path_project 'handmade_engine.cpp'
+ $dynamic_library = Join-Path $path_build 'handmade_engine.dll'
+
+ build-simple $includes $engine_compiler_args $linker_args $unit $dynamic_library
+
+ if ( Test-Path $dynamic_library )
+ {
+ $data_path = Join-Path $path_data 'handmade_engine.dll'
+ move-item $dynamic_library $data_path -Force
+
+ # We need to generate the symbol table so that we can lookup the symbols we need when loading/reloading the library at runtime.
+ # This is done by sifting through the emitter.map file from the linker for the base symbol names
+ # and mapping them to their found decorated name
+
+ $engine_symbols = @(
+ 'on_module_reload',
+ 'startup',
+ 'shutdown',
+ 'update_and_render',
+ 'update_audio'
+ )
+
+ $path_engine_map = Join-Path $path_build 'handmade_engine.map'
+
+ $engine_symbol_table = @()
+ $engine_symbol_list = @()
+ Get-Content -Path $path_engine_map | ForEach-Object {
+ # Split each line by whitespace
+ $tokens = $_ -split '\s+', 3
+
+ # Extract only the decorated name using regex for both MSVC and Clang conventions
+ $decoratedName = ($tokens[2] -match '(\?[\w@]+|_Z[\w@]+)' ) ? $matches[1] : $null
+
+ # Check each regular name against the current line
+ foreach ($name in $engine_symbols) {
+ if ($decoratedName -like "*$name*") {
+ $engine_symbol_table += $name + ', ' + $decoratedName
+ $engine_symbol_list += $decoratedName
+ $engine_symbols = $engine_symbols -ne $name # Remove the found symbol from the array
+ }
+ }
+ }
+
+ write-host "Engine Symbol Table:" -ForegroundColor Green
+ $engine_symbol_table | ForEach-Object {
+ write-host "`t$_" -ForegroundColor Green
+ }
+
+ # Write the symbol table to a file
+ $path_engine_symbols = Join-Path $path_data 'handmade_engine.symbols'
+ $engine_symbol_list | Out-File -Path $path_engine_symbols
+ }
+}
+
+if ( $platform )
+{
+ $platform_compiler_args = $compiler_args
+ $platform_compiler_args += ($flag_define + 'Build_DLL=0' )
+
+ $linker_args = @(
+ $lib_gdi32,
+ # $lib_xinput,
+ $lib_user32,
+ $lib_winmm,
+
+ $lib_jsl,
+
+ $flag_link_win_subsystem_windows
+ $flag_link_optimize_references
+ )
+
+ $unit = Join-Path $path_project 'handmade_win32.cpp'
+ $executable = Join-Path $path_build 'handmade_win32.exe'
+
+ build-simple $includes $platform_compiler_args $linker_args $unit $executable
+
+ if ( Test-Path $executable )
+ {
+ $data_path = Join-Path $path_data 'handmade_win32.exe'
+ move-item $executable $data_path -Force
+ }
+}
#endregion Handmade Runtime
Pop-Location
+#endregion Building
diff --git a/scripts/handmade.rdbg b/scripts/handmade.rdbg
index 64c7312..31f860a 100644
Binary files a/scripts/handmade.rdbg and b/scripts/handmade.rdbg differ
diff --git a/scripts/helpers/devshell.ps1 b/scripts/helpers/devshell.ps1
index dc68bd3..33ca0ce 100644
--- a/scripts/helpers/devshell.ps1
+++ b/scripts/helpers/devshell.ps1
@@ -23,5 +23,6 @@ if ( -not (Test-Path $vs_devshell) ) {
# Launch the Visual Studio Developer Shell
Push-Location
+write-host @args
& $vs_devshell @args
Pop-Location