4coder-non-source/test_data/lots_of_files/win32_cd.cpp

983 lines
28 KiB
C++
Raw Normal View History

2023-09-30 01:17:40 +00:00
/*
* 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