983 lines
28 KiB
C++
983 lines
28 KiB
C++
|
/*
|
||
|
* Win32 Layer for CipherDrive
|
||
|
*/
|
||
|
|
||
|
// TOP
|
||
|
|
||
|
#define LOGICAL_SIZE Mbytes(8)
|
||
|
#define TRANSIENT_SIZE Gbytes(1)
|
||
|
|
||
|
extern "C"{
|
||
|
#include "DragAndDrop.h"
|
||
|
}
|
||
|
|
||
|
#define FTECH_STRING_IMPLEMENTATION
|
||
|
#include "4tech_string.h"
|
||
|
|
||
|
#include "cd_windows_render_vars.h"
|
||
|
|
||
|
struct DLL_Reload{
|
||
|
HMODULE handle;
|
||
|
FILETIME time;
|
||
|
i32 counter;
|
||
|
};
|
||
|
|
||
|
struct Win32{
|
||
|
Win32_Render_Vars render_vars;
|
||
|
|
||
|
b32 keep_playing;
|
||
|
|
||
|
u64 perf_frequency;
|
||
|
u64 perf_start;
|
||
|
|
||
|
Memory memory;
|
||
|
Render_Target target;
|
||
|
Render_Target dbg_target;
|
||
|
Asset_Bank bank;
|
||
|
Input_State input;
|
||
|
Dev_Input_State dev_input;
|
||
|
Key_Events keys;
|
||
|
|
||
|
System_API system;
|
||
|
|
||
|
|
||
|
App_Functions app;
|
||
|
|
||
|
i32 w, h, out_w, out_h;
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
void *logical_backup;
|
||
|
void *logical_backup_stopped;
|
||
|
Input_State *past_inputs;
|
||
|
i32 max_inputs;
|
||
|
i32 loop_i, loop_end;
|
||
|
i32 loop_mode;
|
||
|
|
||
|
Input_State input_stopped;
|
||
|
i32 loop_stopped;
|
||
|
b32 file_drop_lock;
|
||
|
|
||
|
DLL_Reload renderer_reload;
|
||
|
DLL_Reload game_reload;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
Win32 win32;
|
||
|
|
||
|
static HANDLE
|
||
|
convert_handle(Platform_Handle handle){
|
||
|
HANDLE result;
|
||
|
Assert(sizeof(HANDLE) <= sizeof(Platform_Handle));
|
||
|
result = *(HANDLE*)(&handle);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
static Platform_Handle
|
||
|
convert_handle(HANDLE handle){
|
||
|
Platform_Handle result = {0};
|
||
|
Assert(sizeof(HANDLE) <= sizeof(Platform_Handle));
|
||
|
*(HANDLE*)(&result) = handle;
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
static u64
|
||
|
win32_get_time(){
|
||
|
u64 result = 0;
|
||
|
LARGE_INTEGER time;
|
||
|
if (QueryPerformanceCounter(&time)){
|
||
|
result = (time.QuadPart - win32.perf_start) * 1000000 / win32.perf_frequency;
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
win32_init_gl(){
|
||
|
Assert(win32.target.init != 0);
|
||
|
win32.target.init(&win32.render_vars);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
win32_set_size(i32 w, i32 h, i32 out_w, i32 out_h){
|
||
|
Assert(win32.target.set_screen != 0);
|
||
|
win32.target.set_screen(w, h, out_w, out_h);
|
||
|
}
|
||
|
|
||
|
static u8 keycode_lookup_table[255];
|
||
|
|
||
|
static void
|
||
|
win32_keycode_init(){
|
||
|
cd_memset(keycode_lookup_table, 0, sizeof(keycode_lookup_table));
|
||
|
|
||
|
keycode_lookup_table[VK_BACK] = key_back;
|
||
|
keycode_lookup_table[VK_DELETE] = key_del;
|
||
|
keycode_lookup_table[VK_UP] = key_up;
|
||
|
keycode_lookup_table[VK_DOWN] = key_down;
|
||
|
keycode_lookup_table[VK_LEFT] = key_left;
|
||
|
keycode_lookup_table[VK_RIGHT] = key_right;
|
||
|
keycode_lookup_table[VK_INSERT] = key_insert;
|
||
|
keycode_lookup_table[VK_HOME] = key_home;
|
||
|
keycode_lookup_table[VK_END] = key_end;
|
||
|
keycode_lookup_table[VK_PRIOR] = key_page_up;
|
||
|
keycode_lookup_table[VK_NEXT] = key_page_down;
|
||
|
keycode_lookup_table[VK_ESCAPE] = key_esc;
|
||
|
|
||
|
keycode_lookup_table[VK_F1] = key_f1;
|
||
|
keycode_lookup_table[VK_F2] = key_f2;
|
||
|
keycode_lookup_table[VK_F3] = key_f3;
|
||
|
keycode_lookup_table[VK_F4] = key_f4;
|
||
|
keycode_lookup_table[VK_F5] = key_f5;
|
||
|
keycode_lookup_table[VK_F6] = key_f6;
|
||
|
keycode_lookup_table[VK_F7] = key_f7;
|
||
|
keycode_lookup_table[VK_F8] = key_f8;
|
||
|
keycode_lookup_table[VK_F9] = key_f9;
|
||
|
|
||
|
keycode_lookup_table[VK_F10] = key_f10;
|
||
|
keycode_lookup_table[VK_F11] = key_f11;
|
||
|
keycode_lookup_table[VK_F12] = key_f12;
|
||
|
keycode_lookup_table[VK_F13] = key_f13;
|
||
|
keycode_lookup_table[VK_F14] = key_f14;
|
||
|
keycode_lookup_table[VK_F15] = key_f15;
|
||
|
keycode_lookup_table[VK_F16] = key_f16;
|
||
|
}
|
||
|
|
||
|
static LRESULT
|
||
|
win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
|
||
|
LRESULT result = 0;
|
||
|
|
||
|
switch (uMsg){
|
||
|
case WM_CLOSE:
|
||
|
case WM_DESTROY:
|
||
|
{
|
||
|
win32.keep_playing = false;
|
||
|
}break;
|
||
|
|
||
|
case WM_SIZE:
|
||
|
{
|
||
|
win32.w = LOWORD(lParam);
|
||
|
win32.h = HIWORD(lParam);
|
||
|
win32_set_size(win32.w, win32.h, win32.out_w, win32.out_h);
|
||
|
}break;
|
||
|
|
||
|
case WM_MENUCHAR:
|
||
|
case WM_SYSCHAR:break;
|
||
|
|
||
|
case WM_SYSKEYDOWN:
|
||
|
case WM_SYSKEYUP:
|
||
|
case WM_KEYDOWN:
|
||
|
case WM_KEYUP:
|
||
|
{
|
||
|
switch (wParam){
|
||
|
case VK_CONTROL:case VK_LCONTROL:case VK_RCONTROL:
|
||
|
case VK_MENU:case VK_LMENU:case VK_RMENU:
|
||
|
case VK_SHIFT:case VK_LSHIFT:case VK_RSHIFT:break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
b8 current_state = ((lParam & Bit_31)?(0):(1));
|
||
|
if (current_state){
|
||
|
char key = keycode_lookup_table[(u8)wParam];
|
||
|
|
||
|
if (!key){
|
||
|
BYTE state[256];
|
||
|
GetKeyboardState(state);
|
||
|
state[VK_CONTROL] = 0;
|
||
|
|
||
|
UINT vk = (UINT)wParam;
|
||
|
UINT scan = (UINT)((lParam >> 16) & 0x7F);
|
||
|
WORD c = 0;
|
||
|
i32 result = ToAscii(vk, scan, state, &c, 0);
|
||
|
|
||
|
if (result < 0){
|
||
|
ToAscii(vk, scan, state, &c, 0);
|
||
|
}
|
||
|
else if (result == 1){
|
||
|
key = (char)c;
|
||
|
if (key == '\r'){
|
||
|
key = '\n';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (key){
|
||
|
i32 max = ArrayCount(win32.keys.events);
|
||
|
if (win32.keys.count < max){
|
||
|
win32.keys.events[win32.keys.count++] = key;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}break;
|
||
|
}
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||
|
}break;
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
static char*
|
||
|
win32_cf_type(CLIPFORMAT cf){
|
||
|
char *which = "";
|
||
|
switch (cf){
|
||
|
case CF_TEXT: which = "CF_TEXT"; break;
|
||
|
case CF_BITMAP: which = "CF_BITMAP"; break;
|
||
|
case CF_METAFILEPICT: which = "CF_METAFILEPICT"; break;
|
||
|
case CF_SYLK: which = "CF_SYLK"; break;
|
||
|
case CF_DIF: which = "CF_DIF"; break;
|
||
|
case CF_TIFF: which = "CF_TIFF"; break;
|
||
|
case CF_OEMTEXT: which = "CF_OEMTEXT"; break;
|
||
|
case CF_DIB: which = "CF_DIB"; break;
|
||
|
case CF_PALETTE: which = "CF_PALETTE"; break;
|
||
|
case CF_PENDATA: which = "CF_PENDATA"; break;
|
||
|
case CF_RIFF: which = "CF_RIFF"; break;
|
||
|
case CF_WAVE: which = "CF_WAVE"; break;
|
||
|
case CF_UNICODETEXT: which = "CF_UNICODETEXT"; break;
|
||
|
case CF_ENHMETAFILE: which = "CF_ENHMETAFILE"; break;
|
||
|
case CF_HDROP: which = "CF_HDROP"; break;
|
||
|
case CF_LOCALE: which = "CF_LOCALE"; break;
|
||
|
case CF_MAX: which = "CF_MAX"; break;
|
||
|
case CF_OWNERDISPLAY: which = "CF_OWNERDISPLAY"; break;
|
||
|
case CF_DSPTEXT: which = "CF_DSPTEXT"; break;
|
||
|
case CF_DSPBITMAP: which = "CF_DSPBITMAP"; break;
|
||
|
case CF_DSPMETAFILEPICT: which = "CF_DSPMETAFILEPICT"; break;
|
||
|
case CF_DSPENHMETAFILE: which = "CF_DSPENHMETAFILE"; break;
|
||
|
case CF_PRIVATEFIRST: which = "CF_PRIVATEFIRST"; break;
|
||
|
case CF_PRIVATELAST: which = "CF_PRIVATELAST"; break;
|
||
|
case CF_GDIOBJFIRST: which = "CF_GDIOBJFIRST"; break;
|
||
|
case CF_GDIOBJLAST: which = "CF_GDIOBJLAST"; break;
|
||
|
}
|
||
|
return(which);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
DWORD
|
||
|
win32_drop_callback(CLIPFORMAT cf, HGLOBAL hData, HWND hWnd,
|
||
|
DWORD dwKeyState, POINTL pt,
|
||
|
void *pUserData){
|
||
|
DWORD effect = DROPEFFECT_NONE;
|
||
|
|
||
|
Assert(win32.file_drop_lock);
|
||
|
|
||
|
u32 count = DragQueryFile((HDROP)hData, 0xFFFFFFFF, 0, 0);
|
||
|
u32 max = ArrayCount(win32.dev_input.drops);
|
||
|
max -= win32.dev_input.drop_count;
|
||
|
|
||
|
if (count > max){
|
||
|
count = max;
|
||
|
}
|
||
|
|
||
|
for (u32 i = 0; i < count; ++i){
|
||
|
TCHAR file_path[1024];
|
||
|
|
||
|
DWORD len = DragQueryFile((HDROP)hData, i,
|
||
|
file_path, ArrayCount(file_path));
|
||
|
|
||
|
if (len < 1024){
|
||
|
Dev_File_Drop *drop = &win32.dev_input.drops[win32.dev_input.drop_count++];
|
||
|
cd_memcpy(drop->name, file_path, len);
|
||
|
drop->name[len] = 0;
|
||
|
}
|
||
|
else{
|
||
|
// TODO(allen): Issue warning to developer person who has long file name.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
win32.dev_input.drop_x = (f32)(pt.x);
|
||
|
win32.dev_input.drop_y = (f32)(pt.y);
|
||
|
|
||
|
return(effect);
|
||
|
}
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
|
||
|
File_Dump
|
||
|
DBG_dump_begin(char *filename){
|
||
|
File_Dump dump = {0};
|
||
|
HANDLE file = 0;
|
||
|
LARGE_INTEGER size = {0};
|
||
|
|
||
|
file = CreateFile(filename, GENERIC_READ, 0, 0,
|
||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||
|
|
||
|
if (file != INVALID_HANDLE_VALUE){
|
||
|
if (GetFileSizeEx(file, &size)){
|
||
|
if (size.HighPart == 0){
|
||
|
dump.handle = convert_handle(file);
|
||
|
dump.size = size.LowPart;
|
||
|
}
|
||
|
else{
|
||
|
CloseHandle(file);
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
CloseHandle(file);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(dump);
|
||
|
}
|
||
|
|
||
|
b32
|
||
|
DBG_dump_end(File_Dump dump, void *buffer){
|
||
|
b32 result = false;
|
||
|
HANDLE file = convert_handle(dump.handle);
|
||
|
DWORD total_unread = dump.size;
|
||
|
DWORD read_amount = 0;
|
||
|
|
||
|
if (file != 0){
|
||
|
if (buffer){
|
||
|
while (total_unread > 0){
|
||
|
if (ReadFile(file, buffer, total_unread, &read_amount, 0)){
|
||
|
buffer = (char*)buffer + read_amount;
|
||
|
total_unread -= read_amount;
|
||
|
}
|
||
|
else{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (total_unread == 0){
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
CloseHandle(file);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
b32
|
||
|
DBG_dump_out(char *file_name, void *buffer, i32 size){
|
||
|
b32 result = false;
|
||
|
|
||
|
if (buffer){
|
||
|
HANDLE file =
|
||
|
CreateFile(file_name,
|
||
|
GENERIC_WRITE,
|
||
|
0,
|
||
|
0,
|
||
|
CREATE_ALWAYS,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
0);
|
||
|
DWORD size_written = 0;
|
||
|
|
||
|
if (file != INVALID_HANDLE_VALUE){
|
||
|
WriteFile(file, buffer, size, &size_written, 0);
|
||
|
result = true;
|
||
|
CloseHandle(file);
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
DeleteFile(file_name);
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
b32
|
||
|
DBG_copy(char *source, char *name){
|
||
|
b32 result = CopyFile(source, name, false);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
i32
|
||
|
DBG_module_path(char *out, i32 capacity){
|
||
|
i32 result = 0;
|
||
|
i32 len = GetModuleFileName(0, out, capacity);
|
||
|
|
||
|
if (len < capacity-1){
|
||
|
String str = make_string(out, len, len);
|
||
|
remove_last_folder(&str);
|
||
|
if (str.str[str.size-1] == '\\'){
|
||
|
str.size-=1;
|
||
|
}
|
||
|
terminate_with_null(&str);
|
||
|
result = str.size;
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
i32
|
||
|
DBG_working_path(char *out, i32 capacity){
|
||
|
i32 result = 0;
|
||
|
i32 len = GetCurrentDirectory(capacity, out);
|
||
|
|
||
|
if (len < capacity-1){
|
||
|
result = len;
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
b32
|
||
|
DBG_call_script(char *script){
|
||
|
char cmd[] = "c:\\windows\\system32\\cmd.exe";
|
||
|
char *env_variables = 0;
|
||
|
char command_line[2048];
|
||
|
|
||
|
String s = make_fixed_width_string(command_line);
|
||
|
|
||
|
copy(&s, make_lit_string("/C "));
|
||
|
append_partial(&s, script);
|
||
|
b32 success = terminate_with_null(&s);
|
||
|
|
||
|
if (success){
|
||
|
success = false;
|
||
|
|
||
|
char path[2048];
|
||
|
i32 path_len = DBG_module_path(path, sizeof(path));
|
||
|
|
||
|
if (path_len > 0){
|
||
|
STARTUPINFO startup = {};
|
||
|
startup.cb = sizeof(STARTUPINFO);
|
||
|
|
||
|
PROCESS_INFORMATION info = {};
|
||
|
|
||
|
if (CreateProcess(cmd, command_line,
|
||
|
0, 0, FALSE, CREATE_NO_WINDOW,
|
||
|
env_variables, path,
|
||
|
&startup, &info)){
|
||
|
|
||
|
CloseHandle(info.hThread);
|
||
|
CloseHandle(info.hProcess);
|
||
|
|
||
|
success = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(success);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
static void*
|
||
|
win32_alloc(i32 size){
|
||
|
void *result = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
win32_free(void *ptr){
|
||
|
VirtualFree(ptr, 0, MEM_RELEASE);
|
||
|
}
|
||
|
|
||
|
static char*
|
||
|
game_temp_name(i32 counter){
|
||
|
char *temp = "game_temp0.dll";
|
||
|
switch (counter % 4){
|
||
|
case 1: temp = "game_temp1.dll"; break;
|
||
|
case 2: temp = "game_temp2.dll"; break;
|
||
|
case 3: temp = "game_temp3.dll"; break;
|
||
|
}
|
||
|
return(temp);
|
||
|
}
|
||
|
|
||
|
static char*
|
||
|
renderer_temp_name(i32 counter){
|
||
|
char *temp = "renderer_temp0.dll";
|
||
|
switch (counter % 4){
|
||
|
case 1: temp = "renderer_temp1.dll"; break;
|
||
|
case 2: temp = "renderer_temp2.dll"; break;
|
||
|
case 3: temp = "renderer_temp3.dll"; break;
|
||
|
}
|
||
|
return(temp);
|
||
|
}
|
||
|
|
||
|
// TODO(allen): Rewrite using CopyFile dumbass.
|
||
|
static HMODULE
|
||
|
win32_copy_load(String path, char *file_name, char *temp_name){
|
||
|
HMODULE module = 0;
|
||
|
|
||
|
append(&path, file_name);
|
||
|
terminate_with_null(&path);
|
||
|
|
||
|
File_Dump dump = DBG_dump_begin(path.str);
|
||
|
if (dump.size > 0){
|
||
|
void *buffer = win32_alloc(dump.size);
|
||
|
if (buffer){
|
||
|
if (DBG_dump_end(dump, buffer)){
|
||
|
remove_last_folder(&path);
|
||
|
append(&path, temp_name);
|
||
|
terminate_with_null(&path);
|
||
|
DBG_dump_out(path.str, buffer, dump.size);
|
||
|
module = LoadLibraryA(path.str);
|
||
|
}
|
||
|
win32_free(buffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
remove_last_folder(&path);
|
||
|
terminate_with_null(&path);
|
||
|
|
||
|
return(module);
|
||
|
}
|
||
|
|
||
|
static HMODULE
|
||
|
win32_try_reload(DLL_Reload *reload, String path, char *file_name, char *temp_name){
|
||
|
HMODULE module = 0;
|
||
|
FILETIME file_time_now;
|
||
|
LONG updated = 0;
|
||
|
HANDLE file = 0;
|
||
|
|
||
|
append(&path, file_name);
|
||
|
terminate_with_null(&path);
|
||
|
|
||
|
file = CreateFile(path.str,
|
||
|
GENERIC_READ,
|
||
|
0,
|
||
|
0,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
0);
|
||
|
|
||
|
remove_last_folder(&path);
|
||
|
|
||
|
if (file != INVALID_HANDLE_VALUE){
|
||
|
GetFileTime(file, 0, 0, &file_time_now);
|
||
|
CloseHandle(file);
|
||
|
|
||
|
updated = (CompareFileTime(&reload->time, &file_time_now) < 0);
|
||
|
|
||
|
if (reload->handle == 0 || updated){
|
||
|
if (reload->handle != 0){
|
||
|
FreeLibrary(reload->handle);
|
||
|
}
|
||
|
|
||
|
reload->time = file_time_now;
|
||
|
reload->handle = win32_copy_load(path, file_name, temp_name);
|
||
|
|
||
|
module = reload->handle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(module);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
win32_reload_renderer(String path_string){
|
||
|
HMODULE game_render_module = 0;
|
||
|
Render_Get_Functions *target_get_functions = 0;
|
||
|
Bank_Get_Functions *bank_get_functions = 0;
|
||
|
|
||
|
game_render_module =
|
||
|
win32_try_reload(&win32.renderer_reload,
|
||
|
path_string,
|
||
|
"CDRenderer.dll",
|
||
|
renderer_temp_name(win32.renderer_reload.counter++));
|
||
|
|
||
|
if (game_render_module != 0){
|
||
|
target_get_functions = (Render_Get_Functions*)
|
||
|
GetProcAddress(game_render_module, "target_get_functions");
|
||
|
Assert(target_get_functions != 0);
|
||
|
|
||
|
bank_get_functions = (Bank_Get_Functions*)
|
||
|
GetProcAddress(game_render_module, "bank_get_functions");
|
||
|
Assert(target_get_functions != 0);
|
||
|
|
||
|
target_get_functions(&win32.target);
|
||
|
bank_get_functions(&win32.bank);
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
target_get_functions(&win32.dbg_target);
|
||
|
win32.dbg_target.execute = dbg_render_execute;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
win32_reload_game(String path_string){
|
||
|
HMODULE application_module = 0;
|
||
|
App_Step_Function *app_step = 0;
|
||
|
|
||
|
application_module =
|
||
|
win32_try_reload(&win32.game_reload,
|
||
|
path_string,
|
||
|
"CDGame.dll",
|
||
|
game_temp_name(win32.game_reload.counter++));
|
||
|
|
||
|
if (application_module != 0){
|
||
|
app_step = (App_Step_Function*)
|
||
|
GetProcAddress(application_module, "app_step");
|
||
|
Assert(app_step != 0);
|
||
|
|
||
|
win32.app.step = app_step;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
WinMain(HINSTANCE hInstance,
|
||
|
HINSTANCE hPrevInstance,
|
||
|
LPSTR lpCmdLine,
|
||
|
int nCmdShow){
|
||
|
|
||
|
{
|
||
|
char dir_space[1024];
|
||
|
int len = GetCurrentDirectory(sizeof(dir_space), dir_space);
|
||
|
String dir = make_string(dir_space, len, sizeof(dir_space));
|
||
|
append(&dir, "\\data");
|
||
|
terminate_with_null(&dir);
|
||
|
|
||
|
if (SetCurrentDirectory(dir.str) == FALSE){
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Window initialization
|
||
|
win32 = {0};
|
||
|
win32_keycode_init();
|
||
|
|
||
|
{
|
||
|
LARGE_INTEGER lpf;
|
||
|
QueryPerformanceFrequency(&lpf);
|
||
|
win32.perf_frequency = lpf.QuadPart;
|
||
|
QueryPerformanceCounter(&lpf);
|
||
|
win32.perf_start = lpf.QuadPart;
|
||
|
}
|
||
|
|
||
|
// Memory initialization
|
||
|
{
|
||
|
u64 offset = 0;
|
||
|
LPVOID ptr;
|
||
|
|
||
|
ptr = (LPVOID)Gbytes(1);
|
||
|
win32.memory.logical_size = LOGICAL_SIZE;
|
||
|
win32.memory.logical = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
offset += LOGICAL_SIZE;
|
||
|
ptr = (LPVOID)(Gbytes(1) + offset);
|
||
|
win32.logical_backup = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||
|
|
||
|
offset += LOGICAL_SIZE;
|
||
|
ptr = (LPVOID)(Gbytes(1) + offset);
|
||
|
win32.logical_backup_stopped = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||
|
|
||
|
offset += LOGICAL_SIZE;
|
||
|
ptr = (LPVOID)(Gbytes(1) + offset);
|
||
|
win32.past_inputs = (Input_State*)
|
||
|
VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||
|
win32.max_inputs = LOGICAL_SIZE/sizeof(Input_State);
|
||
|
|
||
|
offset += LOGICAL_SIZE;
|
||
|
ptr = (LPVOID)(Gbytes(1) + offset);
|
||
|
win32.memory.developer = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||
|
win32.memory.developer_size = LOGICAL_SIZE;
|
||
|
#endif
|
||
|
|
||
|
ptr = (LPVOID)Gbytes(2);
|
||
|
win32.memory.transient_size = TRANSIENT_SIZE;
|
||
|
win32.memory.transient = VirtualAlloc(ptr, TRANSIENT_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||
|
}
|
||
|
|
||
|
// Linkage initialization
|
||
|
char file_name[1024];
|
||
|
DWORD len =
|
||
|
GetModuleFileName(0,
|
||
|
file_name,
|
||
|
sizeof(file_name));
|
||
|
|
||
|
String path_string = make_string(file_name, len, sizeof(file_name));
|
||
|
remove_last_folder(&path_string);
|
||
|
|
||
|
{
|
||
|
win32.system.DBG_dump_begin = DBG_dump_begin;
|
||
|
win32.system.DBG_dump_end = DBG_dump_end;
|
||
|
win32.system.DBG_dump_out = DBG_dump_out;
|
||
|
win32.system.DBG_copy = DBG_copy;
|
||
|
win32.system.DBG_call_script = DBG_call_script;
|
||
|
win32.system.DBG_module_path = DBG_module_path;
|
||
|
win32.system.DBG_working_path = DBG_working_path;
|
||
|
win32.system.DBG_memory_allocate = win32_alloc;
|
||
|
win32.system.DBG_memory_free = win32_free;
|
||
|
|
||
|
win32_reload_renderer(path_string);
|
||
|
win32_reload_game(path_string);
|
||
|
}
|
||
|
|
||
|
WNDCLASSEX winclass;
|
||
|
winclass.cbSize = sizeof(WNDCLASSEX);
|
||
|
winclass.style = CS_HREDRAW | CS_VREDRAW;
|
||
|
winclass.lpfnWndProc = win32_proc;
|
||
|
winclass.cbClsExtra = 0;
|
||
|
winclass.cbWndExtra = 0;
|
||
|
winclass.hInstance = hInstance;
|
||
|
winclass.hIcon = 0;
|
||
|
winclass.hCursor = 0;
|
||
|
winclass.hbrBackground = 0;
|
||
|
winclass.lpszMenuName = 0;
|
||
|
winclass.lpszClassName = "cipher-drive-class";
|
||
|
winclass.hIconSm = 0;
|
||
|
|
||
|
ATOM register_result = RegisterClassEx(&winclass);
|
||
|
Assert(register_result);
|
||
|
|
||
|
RECT window_rect;
|
||
|
window_rect.left = 0;
|
||
|
window_rect.top = 0;
|
||
|
window_rect.right = DEFAULT_WIDTH;
|
||
|
window_rect.bottom = DEFAULT_HEIGHT;
|
||
|
|
||
|
AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, 0);
|
||
|
|
||
|
HWND hwnd =
|
||
|
CreateWindowEx(0,
|
||
|
"cipher-drive-class",
|
||
|
"Cipher Drive - Dev",
|
||
|
WS_OVERLAPPEDWINDOW,
|
||
|
CW_USEDEFAULT,
|
||
|
CW_USEDEFAULT,
|
||
|
window_rect.right - window_rect.left,
|
||
|
window_rect.bottom - window_rect.top,
|
||
|
0,
|
||
|
0,
|
||
|
hInstance,
|
||
|
0);
|
||
|
|
||
|
Assert(hwnd);
|
||
|
|
||
|
win32.render_vars.hwnd = hwnd;
|
||
|
|
||
|
GetClientRect(win32.render_vars.hwnd, &window_rect);
|
||
|
|
||
|
win32.w = window_rect.right - window_rect.left;
|
||
|
win32.h = window_rect.bottom - window_rect.top;
|
||
|
win32.out_w = DEFAULT_WIDTH;
|
||
|
win32.out_h = DEFAULT_HEIGHT;
|
||
|
|
||
|
win32.target.dim = v2((f32)win32.out_w, (f32)win32.out_h);
|
||
|
win32.dbg_target.dim = v2((f32)win32.out_w, (f32)win32.out_h);
|
||
|
|
||
|
|
||
|
// GL initialization
|
||
|
{
|
||
|
win32_init_gl();
|
||
|
win32_set_size(win32.w, win32.h, win32.out_w, win32.out_h);
|
||
|
}
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
{
|
||
|
// Begin drag&drop
|
||
|
MyDragDropInit(0);
|
||
|
CLIPFORMAT formats[] = {
|
||
|
CF_HDROP
|
||
|
};
|
||
|
MyRegisterDragDrop(win32.render_vars.hwnd,
|
||
|
formats, ArrayCount(formats),
|
||
|
WM_NULL, win32_drop_callback,
|
||
|
0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Main loop
|
||
|
u64 FPS = 24;
|
||
|
u64 frame_target = 1000000 / FPS;
|
||
|
u64 frame_start = 0, frame_end = 0, frame_used = 0;
|
||
|
|
||
|
timeBeginPeriod(1);
|
||
|
win32.keep_playing = true;
|
||
|
|
||
|
ShowCursor(FALSE);
|
||
|
ShowWindow(win32.render_vars.hwnd, SW_SHOW);
|
||
|
|
||
|
{
|
||
|
glFlush();
|
||
|
HDC dc = GetDC(win32.render_vars.hwnd);
|
||
|
SwapBuffers(dc);
|
||
|
ReleaseDC(win32.render_vars.hwnd, dc);
|
||
|
}
|
||
|
|
||
|
frame_start = win32_get_time();
|
||
|
while (win32.keep_playing){
|
||
|
MSG msg;
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
win32.dev_input.drop_count = 0;
|
||
|
win32.file_drop_lock = 1;
|
||
|
cd_memset(&win32.keys, 0, sizeof(win32.keys));
|
||
|
#endif
|
||
|
|
||
|
while (PeekMessage(&msg,
|
||
|
0,0,0,
|
||
|
PM_REMOVE)){
|
||
|
TranslateMessage(&msg);
|
||
|
DispatchMessage(&msg);
|
||
|
}
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
win32.file_drop_lock = 0;
|
||
|
|
||
|
win32_reload_renderer(path_string);
|
||
|
win32_reload_game(path_string);
|
||
|
#endif
|
||
|
|
||
|
Render_Target *dbg_target = 0;
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
dbg_target = &win32.dbg_target;
|
||
|
#endif
|
||
|
|
||
|
BYTE keys[256];
|
||
|
GetKeyboardState(keys);
|
||
|
|
||
|
// NOTE(allen): MSDN says that the high order bit
|
||
|
// indicates whether the key is down, other bits can
|
||
|
// be set for apparently no reason just to screw you.
|
||
|
#define win32_down(b) (0x80 & b)
|
||
|
|
||
|
win32.input.left_button.prev_down = win32.input.left_button.down;
|
||
|
win32.input.right_button.prev_down = win32.input.right_button.down;
|
||
|
|
||
|
win32.input.left_button.down = win32_down(keys[VK_LBUTTON]);
|
||
|
win32.input.right_button.down = win32_down(keys[VK_RBUTTON]);
|
||
|
|
||
|
for (i32 i = 0; i < 26; ++i){
|
||
|
win32.input.letter_[i].prev_down = win32.input.letter_[i].down;
|
||
|
win32.input.letter_[i].down = win32_down(keys['A' + i]);
|
||
|
}
|
||
|
win32.input.letter = win32.input.letter_ - 'A';
|
||
|
|
||
|
for (i32 i = 0; i < 10; ++i){
|
||
|
win32.input.number[i].prev_down = win32.input.number[i].down;
|
||
|
win32.input.number[i].down = win32_down(keys['0' + i]);
|
||
|
}
|
||
|
|
||
|
win32.input.up.prev_down = win32.input.up.down;
|
||
|
win32.input.down.prev_down = win32.input.down.down;
|
||
|
win32.input.left.prev_down = win32.input.left.down;
|
||
|
win32.input.right.prev_down = win32.input.right.down;
|
||
|
|
||
|
win32.input.up.down = win32_down(keys[VK_UP]);
|
||
|
win32.input.down.down = win32_down(keys[VK_DOWN]);
|
||
|
win32.input.left.down = win32_down(keys[VK_LEFT]);
|
||
|
win32.input.right.down = win32_down(keys[VK_RIGHT]);
|
||
|
|
||
|
win32.input.esc.prev_down = win32.input.esc.down;
|
||
|
win32.input.esc.down = win32_down(keys[VK_ESCAPE]);
|
||
|
|
||
|
POINT cursor_point;
|
||
|
GetCursorPos(&cursor_point);
|
||
|
ScreenToClient(win32.render_vars.hwnd, &cursor_point);
|
||
|
|
||
|
win32.input.mx = cursor_point.x;
|
||
|
win32.input.my = win32.out_h - cursor_point.y;
|
||
|
|
||
|
win32.input.dt = 1.f / FPS;
|
||
|
|
||
|
Input_State input = win32.input;
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
win32.dev_input.input = input;
|
||
|
|
||
|
for (i32 i = 0; i < 12; ++i){
|
||
|
win32.dev_input.fkeys_[i].prev_down = win32.dev_input.fkeys_[i].down;
|
||
|
win32.dev_input.fkeys_[i].down = win32_down(keys[VK_F1 + i]);
|
||
|
}
|
||
|
win32.dev_input.fkeys = win32.dev_input.fkeys_ - 1;
|
||
|
|
||
|
if (win32.dev_input.fkeys[8].down && !win32.dev_input.fkeys[8].prev_down){
|
||
|
if (win32.loop_stopped){
|
||
|
win32.loop_stopped = 0;
|
||
|
}
|
||
|
else{
|
||
|
cd_memcpy(win32.logical_backup_stopped, win32.memory.logical, LOGICAL_SIZE);
|
||
|
win32.input_stopped = input;
|
||
|
win32.loop_stopped = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TODO(allen): replace memcpy with our own swankier version.
|
||
|
if (win32.dev_input.fkeys[7].down && !win32.dev_input.fkeys[7].prev_down){
|
||
|
if (win32.loop_stopped){
|
||
|
win32.loop_stopped = 0;
|
||
|
}
|
||
|
else{
|
||
|
switch (win32.loop_mode){
|
||
|
case 0:
|
||
|
cd_memcpy(win32.logical_backup, win32.memory.logical, LOGICAL_SIZE);
|
||
|
win32.loop_mode = 1;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
cd_memcpy(win32.memory.logical, win32.logical_backup, LOGICAL_SIZE);
|
||
|
win32.loop_mode = 2;
|
||
|
win32.loop_end = win32.loop_i;
|
||
|
win32.loop_i = 0;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
win32.loop_mode = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
win32.dev_input.keys = win32.keys;
|
||
|
|
||
|
if (win32.loop_stopped){
|
||
|
cd_memcpy(win32.memory.logical, win32.logical_backup_stopped, LOGICAL_SIZE);
|
||
|
input = win32.input_stopped;
|
||
|
}
|
||
|
else{
|
||
|
if (win32.loop_mode == 1 && win32.loop_i == win32.max_inputs){
|
||
|
cd_memcpy(win32.memory.logical, win32.logical_backup, LOGICAL_SIZE);
|
||
|
win32.loop_mode = 2;
|
||
|
win32.loop_end = win32.loop_i;
|
||
|
win32.loop_i = 0;
|
||
|
}
|
||
|
|
||
|
switch (win32.loop_mode){
|
||
|
case 1:
|
||
|
win32.past_inputs[win32.loop_i] = input;
|
||
|
++win32.loop_i;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
if (win32.loop_i == win32.loop_end){
|
||
|
cd_memcpy(win32.memory.logical, win32.logical_backup, LOGICAL_SIZE);
|
||
|
win32.loop_i = 0;
|
||
|
}
|
||
|
input = win32.past_inputs[win32.loop_i];
|
||
|
++win32.loop_i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (win32.app.step && win32.target.execute){
|
||
|
win32.app.step(&win32.system,
|
||
|
win32.memory,
|
||
|
&win32.target,
|
||
|
dbg_target,
|
||
|
&win32.bank,
|
||
|
&input,
|
||
|
&win32.dev_input,
|
||
|
win32.loop_mode + win32.loop_stopped * 3);
|
||
|
}
|
||
|
|
||
|
#ifdef DEVELOPER
|
||
|
{
|
||
|
GLenum error = glGetError();
|
||
|
GLenum copy = error;
|
||
|
AllowLocal(copy);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
win32.target.display(&win32.render_vars);
|
||
|
|
||
|
frame_end = win32_get_time();
|
||
|
frame_used = frame_end - frame_start;
|
||
|
if (frame_used < frame_target){
|
||
|
Sleep((DWORD)(frame_target - frame_used) / 1000);
|
||
|
}
|
||
|
frame_start = win32_get_time();
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// BOTTOM
|