2017-05-30 03:38:38 +02:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
2017-10-29 04:20:20 -05:00
|
|
|
* Platform main support module for Windows.
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2018-07-19 04:39:27 +02:00
|
|
|
* Version: @(#)win.c 1.0.51 2018/07/19
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2017-05-29 22:21:55 -04:00
|
|
|
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
2017-05-30 03:38:38 +02:00
|
|
|
* Miran Grca, <mgrca8@gmail.com>
|
2017-06-05 01:20:51 -04:00
|
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
2017-10-07 04:34:04 -04:00
|
|
|
*
|
2018-01-19 15:39:13 +01:00
|
|
|
* Copyright 2008-2018 Sarah Walker.
|
|
|
|
|
* Copyright 2016-2018 Miran Grca.
|
|
|
|
|
* Copyright 2017,2018 Fred N. van Kempen.
|
2017-05-30 03:38:38 +02:00
|
|
|
*/
|
2017-09-25 04:31:20 -04:00
|
|
|
#define UNICODE
|
|
|
|
|
#include <windows.h>
|
2018-05-21 19:04:05 +02:00
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <stdarg.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <stdint.h>
|
2018-05-21 19:04:05 +02:00
|
|
|
#include <stdio.h>
|
2017-05-24 00:27:42 -04:00
|
|
|
#include <string.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <stdlib.h>
|
2017-10-11 05:40:44 -04:00
|
|
|
#include <time.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <wchar.h>
|
2018-05-21 19:04:05 +02:00
|
|
|
#define HAVE_STDARG_H
|
2017-05-18 14:03:43 -04:00
|
|
|
#include "../86box.h"
|
2017-09-04 01:52:29 -04:00
|
|
|
#include "../config.h"
|
2017-12-04 11:59:26 -05:00
|
|
|
#include "../device.h"
|
|
|
|
|
#include "../mouse.h"
|
|
|
|
|
#include "../video/video.h"
|
2017-10-17 01:59:09 -04:00
|
|
|
#define GLOBAL
|
2017-10-10 03:07:29 -04:00
|
|
|
#include "../plat.h"
|
2017-10-11 05:40:44 -04:00
|
|
|
#include "../plat_midi.h"
|
2017-10-10 03:07:29 -04:00
|
|
|
#include "../ui.h"
|
2017-12-04 11:59:26 -05:00
|
|
|
#ifdef USE_VNC
|
|
|
|
|
# include "../vnc.h"
|
|
|
|
|
#endif
|
|
|
|
|
# include "win_ddraw.h"
|
2018-07-19 04:39:27 +02:00
|
|
|
# include "win_d2d.h"
|
2017-12-04 11:59:26 -05:00
|
|
|
# include "win_d3d.h"
|
2018-07-15 01:41:53 +02:00
|
|
|
# include "win_sdl.h"
|
2017-05-18 14:03:43 -04:00
|
|
|
#include "win.h"
|
2017-05-27 03:53:32 +02:00
|
|
|
|
2017-10-08 19:14:46 -04:00
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
WCHAR str[512];
|
|
|
|
|
} rc_str_t;
|
|
|
|
|
|
|
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
/* Platform Public data, specific. */
|
2017-10-19 04:27:04 -04:00
|
|
|
HINSTANCE hinstance; /* application instance */
|
2017-10-14 00:49:08 -04:00
|
|
|
HANDLE ghMutex;
|
2017-10-19 04:27:04 -04:00
|
|
|
LCID lang_id; /* current language ID used */
|
|
|
|
|
DWORD dwSubLangID;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
|
|
|
|
|
2017-10-14 00:49:08 -04:00
|
|
|
/* Local data. */
|
2017-10-07 00:46:54 -04:00
|
|
|
static HANDLE thMain;
|
2017-10-14 00:49:08 -04:00
|
|
|
static rc_str_t *lpRCstr2048,
|
|
|
|
|
*lpRCstr4096,
|
|
|
|
|
*lpRCstr4352,
|
|
|
|
|
*lpRCstr4608,
|
|
|
|
|
*lpRCstr5120,
|
|
|
|
|
*lpRCstr5376,
|
|
|
|
|
*lpRCstr5632,
|
2018-01-19 15:39:13 +01:00
|
|
|
*lpRCstr5888,
|
|
|
|
|
*lpRCstr6144,
|
|
|
|
|
*lpRCstr7168;
|
2018-04-25 23:51:13 +02:00
|
|
|
static int vid_api_inited = 0;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
|
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
static struct {
|
|
|
|
|
char *name;
|
|
|
|
|
int local;
|
|
|
|
|
int (*init)(void *);
|
|
|
|
|
void (*close)(void);
|
|
|
|
|
void (*resize)(int x, int y);
|
|
|
|
|
int (*pause)(void);
|
2018-07-15 01:41:53 +02:00
|
|
|
} vid_apis[2][RENDERERS_NUM] = {
|
2017-12-04 11:59:26 -05:00
|
|
|
{
|
|
|
|
|
{ "DDraw", 1, (int(*)(void*))ddraw_init, ddraw_close, NULL, ddraw_pause },
|
2018-07-19 04:39:27 +02:00
|
|
|
{ "D2D", 1, (int(*)(void*))d2d_init, d2d_close, NULL, d2d_pause },
|
2017-12-04 11:59:26 -05:00
|
|
|
{ "D3D", 1, (int(*)(void*))d3d_init, d3d_close, d3d_resize, d3d_pause },
|
2018-07-15 01:41:53 +02:00
|
|
|
{ "SDL", 1, (int(*)(void*))sdl_init, sdl_close, NULL, sdl_pause }
|
2018-07-16 20:48:37 +02:00
|
|
|
#ifdef USE_VNC
|
|
|
|
|
,{ "VNC", 0, vnc_init, vnc_close, vnc_resize, vnc_pause }
|
2017-12-04 11:59:26 -05:00
|
|
|
#endif
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{ "DDraw", 1, (int(*)(void*))ddraw_init_fs, ddraw_close, NULL, ddraw_pause },
|
2018-07-19 04:39:27 +02:00
|
|
|
{ "D2D", 1, (int(*)(void*))d2d_init_fs, d2d_close, NULL, d2d_pause },
|
2017-12-04 11:59:26 -05:00
|
|
|
{ "D3D", 1, (int(*)(void*))d3d_init_fs, d3d_close, NULL, d3d_pause },
|
2018-07-15 01:41:53 +02:00
|
|
|
{ "SDL", 1, (int(*)(void*))sdl_init_fs, sdl_close, sdl_resize, sdl_pause }
|
2018-07-16 20:48:37 +02:00
|
|
|
#ifdef USE_VNC
|
|
|
|
|
,{ "VNC", 0, vnc_init, vnc_close, vnc_resize, vnc_pause }
|
2017-12-04 11:59:26 -05:00
|
|
|
#endif
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2018-05-21 19:04:05 +02:00
|
|
|
#ifdef ENABLE_WIN_LOG
|
|
|
|
|
int win_do_log = ENABLE_WIN_LOG;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
win_log(const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
#ifdef ENABLE_WIN_LOG
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
if (win_do_log) {
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
pclog_ex(fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
static void
|
2017-11-19 03:15:29 -05:00
|
|
|
LoadCommonStrings(void)
|
2017-08-22 02:47:22 +02:00
|
|
|
{
|
2017-11-19 03:15:29 -05:00
|
|
|
int i;
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
lpRCstr2048 = (rc_str_t *)malloc(STR_NUM_2048*sizeof(rc_str_t));
|
|
|
|
|
lpRCstr4096 = (rc_str_t *)malloc(STR_NUM_4096*sizeof(rc_str_t));
|
|
|
|
|
lpRCstr4352 = (rc_str_t *)malloc(STR_NUM_4352*sizeof(rc_str_t));
|
|
|
|
|
lpRCstr4608 = (rc_str_t *)malloc(STR_NUM_4608*sizeof(rc_str_t));
|
|
|
|
|
lpRCstr5120 = (rc_str_t *)malloc(STR_NUM_5120*sizeof(rc_str_t));
|
|
|
|
|
lpRCstr5376 = (rc_str_t *)malloc(STR_NUM_5376*sizeof(rc_str_t));
|
|
|
|
|
lpRCstr5632 = (rc_str_t *)malloc(STR_NUM_5632*sizeof(rc_str_t));
|
2018-01-19 15:39:13 +01:00
|
|
|
lpRCstr5888 = (rc_str_t *)malloc(STR_NUM_5888*sizeof(rc_str_t));
|
2017-11-19 03:15:29 -05:00
|
|
|
lpRCstr6144 = (rc_str_t *)malloc(STR_NUM_6144*sizeof(rc_str_t));
|
2018-01-19 15:39:13 +01:00
|
|
|
lpRCstr7168 = (rc_str_t *)malloc(STR_NUM_7168*sizeof(rc_str_t));
|
2017-11-19 03:15:29 -05:00
|
|
|
|
|
|
|
|
for (i=0; i<STR_NUM_2048; i++)
|
|
|
|
|
LoadString(hinstance, 2048+i, lpRCstr2048[i].str, 512);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
for (i=0; i<STR_NUM_4096; i++)
|
|
|
|
|
LoadString(hinstance, 4096+i, lpRCstr4096[i].str, 512);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
for (i=0; i<STR_NUM_4352; i++)
|
|
|
|
|
LoadString(hinstance, 4352+i, lpRCstr4352[i].str, 512);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
for (i=0; i<STR_NUM_4608; i++)
|
|
|
|
|
LoadString(hinstance, 4608+i, lpRCstr4608[i].str, 512);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
for (i=0; i<STR_NUM_5120; i++)
|
|
|
|
|
LoadString(hinstance, 5120+i, lpRCstr5120[i].str, 512);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2018-07-15 01:41:53 +02:00
|
|
|
for (i=0; i<STR_NUM_5376; i++) {
|
|
|
|
|
if ((i == 0) || (i > 3))
|
|
|
|
|
LoadString(hinstance, 5376+i, lpRCstr5376[i].str, 512);
|
|
|
|
|
}
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2018-07-15 01:41:53 +02:00
|
|
|
for (i=0; i<STR_NUM_5632; i++) {
|
|
|
|
|
if ((i == 0) || (i > 3))
|
|
|
|
|
LoadString(hinstance, 5632+i, lpRCstr5632[i].str, 512);
|
|
|
|
|
}
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2018-01-19 15:39:13 +01:00
|
|
|
for (i=0; i<STR_NUM_5888; i++)
|
|
|
|
|
LoadString(hinstance, 5888+i, lpRCstr5888[i].str, 512);
|
|
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
for (i=0; i<STR_NUM_6144; i++)
|
|
|
|
|
LoadString(hinstance, 6144+i, lpRCstr6144[i].str, 512);
|
2018-01-19 15:39:13 +01:00
|
|
|
|
|
|
|
|
for (i=0; i<STR_NUM_7168; i++)
|
|
|
|
|
LoadString(hinstance, 7168+i, lpRCstr7168[i].str, 512);
|
2017-10-07 00:46:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
/* Set (or re-set) the language for the application. */
|
|
|
|
|
void
|
|
|
|
|
set_language(int id)
|
2017-10-07 00:46:54 -04:00
|
|
|
{
|
2017-11-19 03:15:29 -05:00
|
|
|
LCID lcidNew = MAKELCID(id, dwSubLangID);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
if (lang_id != lcidNew) {
|
|
|
|
|
/* Set our new language ID. */
|
|
|
|
|
lang_id = lcidNew;
|
2017-10-07 00:46:54 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
SetThreadLocale(lang_id);
|
2017-08-22 02:47:22 +02:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
/* Load the strings table for this ID. */
|
|
|
|
|
LoadCommonStrings();
|
2017-08-22 02:47:22 +02:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
#if 0
|
|
|
|
|
/* Update the menus for this ID. */
|
|
|
|
|
MenuUpdate();
|
2017-10-07 00:46:54 -04:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
|
|
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
wchar_t *
|
|
|
|
|
plat_get_string(int i)
|
2017-10-08 19:14:46 -04:00
|
|
|
{
|
2017-11-19 03:15:29 -05:00
|
|
|
LPTSTR str;
|
2017-10-08 19:14:46 -04:00
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
if ((i >= 2048) && (i <= 3071))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr2048[i-2048].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 4096) && (i <= 4351))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr4096[i-4096].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 4352) && (i <= 4607))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr4352[i-4352].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 4608) && (i <= 5119))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr4608[i-4608].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 5120) && (i <= 5375))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr5120[i-5120].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 5376) && (i <= 5631))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr5376[i-5376].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 5632) && (i <= 5887))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr5632[i-5632].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 5888) && (i <= 6143))
|
2018-01-19 15:39:13 +01:00
|
|
|
str = lpRCstr5888[i-5888].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else if ((i >= 6144) && (i <= 7167))
|
2017-11-19 03:15:29 -05:00
|
|
|
str = lpRCstr6144[i-6144].str;
|
2018-04-25 23:51:13 +02:00
|
|
|
else
|
2018-01-19 15:39:13 +01:00
|
|
|
str = lpRCstr7168[i-7168].str;
|
2017-10-08 19:14:46 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
return((wchar_t *)str);
|
2017-10-08 19:14:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
/* Create a console if we don't already have one. */
|
|
|
|
|
static void
|
|
|
|
|
CreateConsole(int init)
|
|
|
|
|
{
|
|
|
|
|
HANDLE h;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
fpos_t p;
|
|
|
|
|
int i;
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
if (! init) {
|
|
|
|
|
FreeConsole();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
/* Are we logging to a file? */
|
|
|
|
|
p = 0;
|
|
|
|
|
(void)fgetpos(stdout, &p);
|
|
|
|
|
if (p != -1) return;
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
/* Not logging to file, attach to console. */
|
|
|
|
|
if (! AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|
|
|
|
/* Parent has no console, create one. */
|
2018-04-25 23:51:13 +02:00
|
|
|
if (! AllocConsole()) {
|
|
|
|
|
/* Cannot create console, just give up. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fp = NULL;
|
|
|
|
|
if ((h = GetStdHandle(STD_OUTPUT_HANDLE)) != NULL) {
|
|
|
|
|
/* We got the handle, now open a file descriptor. */
|
|
|
|
|
if ((i = _open_osfhandle((intptr_t)h, _O_TEXT)) != -1) {
|
|
|
|
|
/* We got a file descriptor, now allocate a new stream. */
|
|
|
|
|
if ((fp = _fdopen(i, "w")) != NULL) {
|
|
|
|
|
/* Got the stream, re-initialize stdout without it. */
|
|
|
|
|
(void)freopen("CONOUT$", "w", stdout);
|
|
|
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-07 00:46:54 -04:00
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
/* Process the commandline, and create standard argc/argv array. */
|
2017-11-19 03:15:29 -05:00
|
|
|
static int
|
|
|
|
|
ProcessCommandLine(wchar_t ***argw)
|
2017-10-07 00:46:54 -04:00
|
|
|
{
|
|
|
|
|
WCHAR *cmdline;
|
2017-11-19 03:15:29 -05:00
|
|
|
wchar_t *argbuf;
|
|
|
|
|
wchar_t **args;
|
2017-10-07 00:46:54 -04:00
|
|
|
int argc_max;
|
2017-11-19 03:15:29 -05:00
|
|
|
int i, q, argc;
|
2017-10-07 00:46:54 -04:00
|
|
|
|
|
|
|
|
cmdline = GetCommandLine();
|
|
|
|
|
i = wcslen(cmdline) + 1;
|
2017-11-19 03:15:29 -05:00
|
|
|
argbuf = (wchar_t *)malloc(sizeof(wchar_t)*i);
|
|
|
|
|
wcscpy(argbuf, cmdline);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
|
|
|
|
argc = 0;
|
|
|
|
|
argc_max = 64;
|
2017-11-19 03:15:29 -05:00
|
|
|
args = (wchar_t **)malloc(sizeof(wchar_t *) * argc_max);
|
|
|
|
|
if (args == NULL) {
|
2017-10-07 00:46:54 -04:00
|
|
|
free(argbuf);
|
2017-11-19 03:15:29 -05:00
|
|
|
return(0);
|
2017-10-07 00:46:54 -04:00
|
|
|
}
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
/* parse commandline into argc/argv format */
|
|
|
|
|
i = 0;
|
|
|
|
|
while (argbuf[i]) {
|
|
|
|
|
while (argbuf[i] == L' ')
|
|
|
|
|
i++;
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
if (argbuf[i]) {
|
|
|
|
|
if ((argbuf[i] == L'\'') || (argbuf[i] == L'"')) {
|
|
|
|
|
q = argbuf[i++];
|
|
|
|
|
if (!argbuf[i])
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
q = 0;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
args[argc++] = &argbuf[i];
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
if (argc >= argc_max) {
|
|
|
|
|
argc_max += 64;
|
2017-11-19 03:15:29 -05:00
|
|
|
args = realloc(args, sizeof(wchar_t *)*argc_max);
|
|
|
|
|
if (args == NULL) {
|
2017-10-07 00:46:54 -04:00
|
|
|
free(argbuf);
|
2017-11-19 03:15:29 -05:00
|
|
|
return(0);
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
2017-10-07 00:46:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((argbuf[i]) && ((q)
|
|
|
|
|
? (argbuf[i]!=q) : (argbuf[i]!=L' '))) i++;
|
|
|
|
|
|
|
|
|
|
if (argbuf[i]) {
|
|
|
|
|
argbuf[i] = 0;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
2017-10-07 00:46:54 -04:00
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
args[argc] = NULL;
|
|
|
|
|
*argw = args;
|
|
|
|
|
|
|
|
|
|
return(argc);
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2017-05-27 03:53:32 +02:00
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
/* For the Windows platform, this is the start of the application. */
|
2017-10-07 00:46:54 -04:00
|
|
|
int WINAPI
|
2017-11-23 17:42:00 -05:00
|
|
|
WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszArg, int nCmdShow)
|
2017-10-07 00:46:54 -04:00
|
|
|
{
|
2017-11-19 03:15:29 -05:00
|
|
|
wchar_t **argw = NULL;
|
|
|
|
|
int argc, i;
|
2017-10-19 04:27:04 -04:00
|
|
|
|
2017-12-15 18:47:29 +01:00
|
|
|
/* Set this to the default value (windowed mode). */
|
|
|
|
|
video_fullscreen = 0;
|
|
|
|
|
|
2017-10-19 04:27:04 -04:00
|
|
|
/* We need this later. */
|
|
|
|
|
hinstance = hInst;
|
|
|
|
|
|
2017-11-13 02:06:00 -05:00
|
|
|
/* Set the application version ID string. */
|
|
|
|
|
sprintf(emu_version, "%s v%s", EMU_NAME, EMU_VERSION);
|
|
|
|
|
|
|
|
|
|
#ifdef USE_CRASHDUMP
|
|
|
|
|
/* Enable crash dump services. */
|
|
|
|
|
InitCrashDump();
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-10-19 04:27:04 -04:00
|
|
|
/* First, set our (default) language. */
|
|
|
|
|
set_language(0x0409);
|
|
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
/* Create console window. */
|
|
|
|
|
CreateConsole(1);
|
|
|
|
|
|
|
|
|
|
/* Process the command line for options. */
|
2017-11-19 03:15:29 -05:00
|
|
|
argc = ProcessCommandLine(&argw);
|
2017-10-07 00:46:54 -04:00
|
|
|
|
|
|
|
|
/* Pre-initialize the system, this loads the config file. */
|
2017-11-19 03:15:29 -05:00
|
|
|
if (! pc_init(argc, argw)) {
|
2017-10-07 00:46:54 -04:00
|
|
|
/* Detach from console. */
|
|
|
|
|
CreateConsole(0);
|
|
|
|
|
return(1);
|
|
|
|
|
}
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
/* Cleanup: we may no longer need the console. */
|
2017-11-23 17:42:00 -05:00
|
|
|
if (! force_debug)
|
|
|
|
|
CreateConsole(0);
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
/* Handle our GUI. */
|
2017-11-23 17:42:00 -05:00
|
|
|
i = ui_init(nCmdShow);
|
2017-10-02 02:15:35 -04:00
|
|
|
|
2017-11-19 03:15:29 -05:00
|
|
|
return(i);
|
2017-10-19 04:27:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We do this here since there is platform-specific stuff
|
|
|
|
|
* going on here, and we do it in a function separate from
|
|
|
|
|
* main() so we can call it from the UI module as well.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
do_start(void)
|
|
|
|
|
{
|
|
|
|
|
LARGE_INTEGER qpc;
|
|
|
|
|
|
|
|
|
|
/* We have not stopped yet. */
|
|
|
|
|
quited = 0;
|
|
|
|
|
|
|
|
|
|
/* Initialize the high-precision timer. */
|
|
|
|
|
timeBeginPeriod(1);
|
|
|
|
|
QueryPerformanceFrequency(&qpc);
|
|
|
|
|
timer_freq = qpc.QuadPart;
|
2018-05-21 19:04:05 +02:00
|
|
|
win_log("Main timer precision: %llu\n", timer_freq);
|
2017-10-19 04:27:04 -04:00
|
|
|
|
|
|
|
|
/* Start the emulator, really. */
|
|
|
|
|
thMain = thread_create(pc_thread, &quited);
|
|
|
|
|
SetThreadPriority(thMain, THREAD_PRIORITY_HIGHEST);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Cleanly stop the emulator. */
|
|
|
|
|
void
|
|
|
|
|
do_stop(void)
|
|
|
|
|
{
|
|
|
|
|
quited = 1;
|
|
|
|
|
|
|
|
|
|
plat_delay_ms(100);
|
|
|
|
|
|
|
|
|
|
pc_close(thMain);
|
|
|
|
|
|
|
|
|
|
thMain = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
plat_get_exe_name(wchar_t *s, int size)
|
|
|
|
|
{
|
|
|
|
|
GetModuleFileName(hinstance, s, size);
|
2017-10-12 14:25:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
plat_getcwd(wchar_t *bufp, int max)
|
|
|
|
|
{
|
|
|
|
|
(void)_wgetcwd(bufp, max);
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
plat_chdir(wchar_t *path)
|
|
|
|
|
{
|
|
|
|
|
return(_wchdir(path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-19 04:27:04 -04:00
|
|
|
FILE *
|
|
|
|
|
plat_fopen(wchar_t *path, wchar_t *mode)
|
|
|
|
|
{
|
|
|
|
|
return(_wfopen(path, mode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
void
|
2017-10-19 04:27:04 -04:00
|
|
|
plat_remove(wchar_t *path)
|
2017-10-07 00:46:54 -04:00
|
|
|
{
|
2017-10-19 04:27:04 -04:00
|
|
|
_wremove(path);
|
2017-10-07 00:46:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
/* Make sure a path ends with a trailing (back)slash. */
|
|
|
|
|
void
|
|
|
|
|
plat_path_slash(wchar_t *path)
|
|
|
|
|
{
|
|
|
|
|
if ((path[wcslen(path)-1] != L'\\') &&
|
|
|
|
|
(path[wcslen(path)-1] != L'/')) {
|
|
|
|
|
wcscat(path, L"\\");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check if the given path is absolute or not. */
|
|
|
|
|
int
|
|
|
|
|
plat_path_abs(wchar_t *path)
|
|
|
|
|
{
|
2017-12-04 20:35:05 +01:00
|
|
|
if ((path[1] == L':') || (path[0] == L'\\') || (path[0] == L'/'))
|
2017-12-04 11:59:26 -05:00
|
|
|
return(1);
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-14 20:04:21 -04:00
|
|
|
wchar_t *
|
2017-10-17 01:59:09 -04:00
|
|
|
plat_get_filename(wchar_t *s)
|
|
|
|
|
{
|
|
|
|
|
int c = wcslen(s) - 1;
|
|
|
|
|
|
|
|
|
|
while (c > 0) {
|
|
|
|
|
if (s[c] == L'/' || s[c] == L'\\')
|
|
|
|
|
return(&s[c+1]);
|
|
|
|
|
c--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wchar_t *
|
|
|
|
|
plat_get_extension(wchar_t *s)
|
|
|
|
|
{
|
|
|
|
|
int c = wcslen(s) - 1;
|
|
|
|
|
|
|
|
|
|
if (c <= 0)
|
|
|
|
|
return(s);
|
|
|
|
|
|
|
|
|
|
while (c && s[c] != L'.')
|
|
|
|
|
c--;
|
|
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
|
return(&s[wcslen(s)]);
|
|
|
|
|
|
|
|
|
|
return(&s[c+1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2017-12-04 11:59:26 -05:00
|
|
|
plat_append_filename(wchar_t *dest, wchar_t *s1, wchar_t *s2)
|
2017-10-17 01:59:09 -04:00
|
|
|
{
|
|
|
|
|
wcscat(dest, s1);
|
|
|
|
|
wcscat(dest, s2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
plat_put_backslash(wchar_t *s)
|
|
|
|
|
{
|
|
|
|
|
int c = wcslen(s) - 1;
|
|
|
|
|
|
|
|
|
|
if (s[c] != L'/' && s[c] != L'\\')
|
|
|
|
|
s[c] = L'/';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-10 03:07:29 -04:00
|
|
|
int
|
2017-10-17 01:59:09 -04:00
|
|
|
plat_dir_check(wchar_t *path)
|
2017-10-10 03:07:29 -04:00
|
|
|
{
|
|
|
|
|
DWORD dwAttrib = GetFileAttributes(path);
|
|
|
|
|
|
|
|
|
|
return(((dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))) ? 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2017-10-17 01:59:09 -04:00
|
|
|
plat_dir_create(wchar_t *path)
|
2017-10-10 03:07:29 -04:00
|
|
|
{
|
|
|
|
|
return((int)CreateDirectory(path, NULL));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
uint64_t
|
2017-10-17 01:59:09 -04:00
|
|
|
plat_timer_read(void)
|
2017-10-07 00:46:54 -04:00
|
|
|
{
|
|
|
|
|
LARGE_INTEGER li;
|
2017-05-27 03:53:32 +02:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
QueryPerformanceCounter(&li);
|
2017-05-27 03:53:32 +02:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
return(li.QuadPart);
|
|
|
|
|
}
|
2017-05-27 03:53:32 +02:00
|
|
|
|
|
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
uint32_t
|
2017-10-17 01:59:09 -04:00
|
|
|
plat_get_ticks(void)
|
2017-10-07 00:46:54 -04:00
|
|
|
{
|
|
|
|
|
return(GetTickCount());
|
|
|
|
|
}
|
2017-05-27 03:53:32 +02:00
|
|
|
|
2017-06-01 17:52:39 +02:00
|
|
|
|
2017-10-07 00:46:54 -04:00
|
|
|
void
|
2017-10-17 01:59:09 -04:00
|
|
|
plat_delay_ms(uint32_t count)
|
2017-10-07 00:46:54 -04:00
|
|
|
{
|
|
|
|
|
Sleep(count);
|
|
|
|
|
}
|
2017-12-04 11:59:26 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the VIDAPI number for the given name. */
|
|
|
|
|
int
|
|
|
|
|
plat_vidapi(char *name)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
if (!strcasecmp(name, "default") || !strcasecmp(name, "system")) return(0);
|
2017-12-04 11:59:26 -05:00
|
|
|
|
2018-07-15 01:41:53 +02:00
|
|
|
for (i = 0; i < RENDERERS_NUM; i++) {
|
2017-12-04 11:59:26 -05:00
|
|
|
if (vid_apis[0][i].name &&
|
|
|
|
|
!strcasecmp(vid_apis[0][i].name, name)) return(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Default value. */
|
2018-04-25 23:51:13 +02:00
|
|
|
return(0);
|
2017-12-04 11:59:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the VIDAPI name for the given number. */
|
|
|
|
|
char *
|
|
|
|
|
plat_vidapi_name(int api)
|
|
|
|
|
{
|
|
|
|
|
char *name = "default";
|
|
|
|
|
|
|
|
|
|
switch(api) {
|
|
|
|
|
case 0:
|
2018-04-25 23:51:13 +02:00
|
|
|
#if 0
|
|
|
|
|
/* DirectDraw is default. */
|
2017-12-04 11:59:26 -05:00
|
|
|
name = "ddraw";
|
2018-04-25 23:51:13 +02:00
|
|
|
#endif
|
2017-12-04 11:59:26 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1:
|
2018-07-19 04:39:27 +02:00
|
|
|
name = "d2d";
|
2017-12-04 11:59:26 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2:
|
2018-07-19 04:39:27 +02:00
|
|
|
name = "d3d";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3:
|
2018-07-15 01:41:53 +02:00
|
|
|
name = "sdl";
|
2017-12-04 11:59:26 -05:00
|
|
|
break;
|
|
|
|
|
|
2018-07-15 01:41:53 +02:00
|
|
|
#ifdef USE_VNC
|
2018-07-19 04:39:27 +02:00
|
|
|
case 4:
|
2018-07-15 01:41:53 +02:00
|
|
|
name = "vnc";
|
2017-12-04 11:59:26 -05:00
|
|
|
break;
|
2018-07-15 01:41:53 +02:00
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
plat_setvid(int api)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2018-05-21 19:04:05 +02:00
|
|
|
win_log("Initializing VIDAPI: api=%d\n", api);
|
2017-12-04 11:59:26 -05:00
|
|
|
startblit();
|
|
|
|
|
video_wait_for_blit();
|
|
|
|
|
|
|
|
|
|
/* Close the (old) API. */
|
|
|
|
|
vid_apis[0][vid_api].close();
|
|
|
|
|
vid_api = api;
|
|
|
|
|
|
|
|
|
|
if (vid_apis[0][vid_api].local)
|
|
|
|
|
ShowWindow(hwndRender, SW_SHOW);
|
|
|
|
|
else
|
|
|
|
|
ShowWindow(hwndRender, SW_HIDE);
|
|
|
|
|
|
|
|
|
|
/* Initialize the (new) API. */
|
|
|
|
|
i = vid_apis[0][vid_api].init((void *)hwndRender);
|
|
|
|
|
endblit();
|
|
|
|
|
if (! i) return(0);
|
|
|
|
|
|
|
|
|
|
device_force_redraw();
|
|
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
vid_api_inited = 1;
|
|
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
return(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Tell the renderers about a new screen resolution. */
|
|
|
|
|
void
|
|
|
|
|
plat_vidsize(int x, int y)
|
|
|
|
|
{
|
2018-04-25 23:51:13 +02:00
|
|
|
if (!vid_api_inited || !vid_apis[video_fullscreen][vid_api].resize) return;
|
2017-12-04 11:59:26 -05:00
|
|
|
|
|
|
|
|
startblit();
|
|
|
|
|
video_wait_for_blit();
|
|
|
|
|
vid_apis[video_fullscreen][vid_api].resize(x, y);
|
|
|
|
|
endblit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
get_vidpause(void)
|
|
|
|
|
{
|
|
|
|
|
return(vid_apis[video_fullscreen][vid_api].pause());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
plat_setfullscreen(int on)
|
|
|
|
|
{
|
|
|
|
|
HWND *hw;
|
|
|
|
|
|
|
|
|
|
/* Want off and already off? */
|
|
|
|
|
if (!on && !video_fullscreen) return;
|
|
|
|
|
|
|
|
|
|
/* Want on and already on? */
|
|
|
|
|
if (on && video_fullscreen) return;
|
|
|
|
|
|
2017-12-15 18:47:29 +01:00
|
|
|
if (on && video_fullscreen_first) {
|
2017-12-04 11:59:26 -05:00
|
|
|
video_fullscreen_first = 0;
|
2018-07-15 01:41:53 +02:00
|
|
|
ui_msgbox(MBX_INFO, (wchar_t *)IDS_2052);
|
2017-12-04 11:59:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* OK, claim the video. */
|
|
|
|
|
startblit();
|
|
|
|
|
video_wait_for_blit();
|
|
|
|
|
|
|
|
|
|
win_mouse_close();
|
|
|
|
|
|
|
|
|
|
/* Close the current mode, and open the new one. */
|
|
|
|
|
vid_apis[video_fullscreen][vid_api].close();
|
|
|
|
|
video_fullscreen = on;
|
|
|
|
|
hw = (video_fullscreen) ? &hwndMain : &hwndRender;
|
|
|
|
|
vid_apis[video_fullscreen][vid_api].init((void *) *hw);
|
|
|
|
|
|
|
|
|
|
win_mouse_init();
|
|
|
|
|
|
|
|
|
|
/* Release video and make it redraw the screen. */
|
|
|
|
|
endblit();
|
|
|
|
|
device_force_redraw();
|
2017-12-15 00:42:10 -05:00
|
|
|
|
|
|
|
|
/* Finally, handle the host's mouse cursor. */
|
2018-05-21 19:04:05 +02:00
|
|
|
/* win_log("%s full screen, %s cursor\n", on ? "enter" : "leave", on ? "hide" : "show"); */
|
2017-12-15 00:42:10 -05:00
|
|
|
show_cursor(video_fullscreen ? 0 : -1);
|
2017-12-04 11:59:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
take_screenshot(void)
|
|
|
|
|
{
|
|
|
|
|
wchar_t path[1024], fn[128];
|
|
|
|
|
struct tm *info;
|
|
|
|
|
time_t now;
|
|
|
|
|
|
2018-05-21 19:04:05 +02:00
|
|
|
win_log("Screenshot: video API is: %i\n", vid_api);
|
2018-07-15 01:41:53 +02:00
|
|
|
if ((vid_api < 0) || (vid_api > 2)) return;
|
2017-12-04 11:59:26 -05:00
|
|
|
|
|
|
|
|
memset(fn, 0, sizeof(fn));
|
|
|
|
|
memset(path, 0, sizeof(path));
|
|
|
|
|
|
|
|
|
|
(void)time(&now);
|
|
|
|
|
info = localtime(&now);
|
|
|
|
|
|
|
|
|
|
plat_append_filename(path, usr_path, SCREENSHOT_PATH);
|
|
|
|
|
|
|
|
|
|
if (! plat_dir_check(path))
|
|
|
|
|
plat_dir_create(path);
|
|
|
|
|
|
|
|
|
|
wcscat(path, L"\\");
|
|
|
|
|
|
2018-03-16 15:46:41 +01:00
|
|
|
wcsftime(fn, 128, L"%Y%m%d_%H%M%S.png", info);
|
|
|
|
|
wcscat(path, fn);
|
|
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
switch(vid_api) {
|
|
|
|
|
case 0: /* ddraw */
|
|
|
|
|
ddraw_take_screenshot(path);
|
|
|
|
|
break;
|
|
|
|
|
|
2018-07-19 04:39:27 +02:00
|
|
|
case 1: /* d2d */
|
|
|
|
|
d2d_take_screenshot(path);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2: /* d3d9 */
|
2017-12-04 11:59:26 -05:00
|
|
|
d3d_take_screenshot(path);
|
|
|
|
|
break;
|
2018-07-15 01:41:53 +02:00
|
|
|
|
2018-07-19 04:39:27 +02:00
|
|
|
case 3: /* sdl */
|
2018-07-15 01:41:53 +02:00
|
|
|
sdl_take_screenshot(path);
|
|
|
|
|
break;
|
2017-12-04 11:59:26 -05:00
|
|
|
|
|
|
|
|
#ifdef USE_VNC
|
2018-07-19 04:39:27 +02:00
|
|
|
case 4: /* vnc */
|
2017-12-04 11:59:26 -05:00
|
|
|
vnc_take_screenshot(path);
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
/* LPARAM interface to plat_get_string(). */
|
|
|
|
|
LPARAM win_get_string(int id)
|
|
|
|
|
{
|
|
|
|
|
wchar_t *ret;
|
|
|
|
|
|
|
|
|
|
ret = plat_get_string(id);
|
|
|
|
|
return ((LPARAM) ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-12-04 11:59:26 -05:00
|
|
|
void /* plat_ */
|
|
|
|
|
startblit(void)
|
|
|
|
|
{
|
|
|
|
|
WaitForSingleObject(ghMutex, INFINITE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void /* plat_ */
|
|
|
|
|
endblit(void)
|
|
|
|
|
{
|
|
|
|
|
ReleaseMutex(ghMutex);
|
|
|
|
|
}
|