2009-11-13 03:55:23 +00:00
|
|
|
/*
|
|
|
|
** i_system.cpp
|
|
|
|
** Timers, pre-console output, IWAD selection, and misc system routines.
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 1998-2009 Randy Heit
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <io.h>
|
|
|
|
#include <direct.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <process.h>
|
2009-03-27 04:49:17 +00:00
|
|
|
#include <time.h>
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/timeb.h>
|
|
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <windows.h>
|
|
|
|
#include <mmsystem.h>
|
2007-01-09 04:40:58 +00:00
|
|
|
#include <richedit.h>
|
2009-03-27 04:49:17 +00:00
|
|
|
#include <wincrypt.h>
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
#define USE_WINDOWS_DWORD
|
2006-02-24 04:48:15 +00:00
|
|
|
#include "hardware.h"
|
|
|
|
#include "doomerrors.h"
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include "doomtype.h"
|
|
|
|
#include "version.h"
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "m_argv.h"
|
|
|
|
#include "m_misc.h"
|
|
|
|
#include "i_video.h"
|
|
|
|
#include "i_sound.h"
|
|
|
|
#include "i_music.h"
|
|
|
|
#include "resource.h"
|
2008-08-10 03:25:08 +00:00
|
|
|
#include "x86.h"
|
2008-08-10 03:56:53 +00:00
|
|
|
#include "stats.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#include "d_main.h"
|
|
|
|
#include "d_net.h"
|
|
|
|
#include "g_game.h"
|
|
|
|
#include "i_input.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "templates.h"
|
2006-09-20 02:00:19 +00:00
|
|
|
#include "gameconfigfile.h"
|
2007-01-09 04:40:58 +00:00
|
|
|
#include "v_font.h"
|
2008-09-14 23:54:38 +00:00
|
|
|
#include "g_level.h"
|
2008-09-15 00:47:31 +00:00
|
|
|
#include "doomstat.h"
|
2009-03-18 05:02:32 +00:00
|
|
|
#include "v_palette.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
#include "stats.h"
|
2010-09-14 17:28:18 +00:00
|
|
|
#include "textures/bitmap.h"
|
2011-07-05 10:02:38 +00:00
|
|
|
#include "textures/textures.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
|
2010-09-14 17:28:18 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
// Turn off "conversion from 'LONG_PTR' to 'LONG', possible loss of data"
|
|
|
|
// generated by SetClassLongPtr().
|
|
|
|
#pragma warning(disable:4244)
|
|
|
|
#endif
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
- Ported vlinetallasm4 to AMD64 assembly. Even with the increased number of
registers AMD64 provides, this routine still needs to be written as self-
modifying code for maximum performance. The additional registers do allow
for further optimization over the x86 version by allowing all four pixels
to be in flight at the same time. The end result is that AMD64 ASM is about
2.18 times faster than AMD64 C and about 1.06 times faster than x86 ASM.
(For further comparison, AMD64 C and x86 C are practically the same for
this function.) Should I port any more assembly to AMD64, mvlineasm4 is the
most likely candidate, but it's not used enough at this point to bother.
Also, this may or may not work with Linux at the moment, since it doesn't
have the eh_handler metadata. Win64 is easier, since I just need to
structure the function prologue and epilogue properly and use some
assembler directives/macros to automatically generate the metadata. And
that brings up another point: You need YASM to assemble the AMD64 code,
because NASM doesn't support the Win64 metadata directives.
- Added an SSE version of DoBlending. This is strictly C intrinsics.
VC++ still throws around unneccessary register moves. GCC seems to be
pretty close to optimal, requiring only about 2 cycles/color. They're
both faster than my hand-written MMX routine, so I don't need to feel
bad about not hand-optimizing this for x64 builds.
- Removed an extra instruction from DoBlending_MMX, transposed two
instructions, and unrolled it once, shaving off about 80 cycles from the
time required to blend 256 palette entries. Why? Because I tried writing
a C version of the routine using compiler intrinsics and was appalled by
all the extra movq's VC++ added to the code. GCC was better, but still
generated extra instructions. I only wanted a C version because I can't
use inline assembly with VC++'s x64 compiler, and x64 assembly is a bit
of a pain. (It's a pain because Linux and Windows have different calling
conventions, and you need to maintain extra metadata for functions.) So,
the assembly version stays and the C version stays out.
- Removed all the pixel doubling r_detail modes, since the one platform they
were intended to assist (486) actually sees very little benefit from them.
- Rewrote CheckMMX in C and renamed it to CheckCPU.
- Fixed: CPUID function 0x80000005 is specified to return detailed L1 cache
only for AMD processors, so we must not use it on other architectures, or
we end up overwriting the L1 cache line size with 0 or some other number
we don't actually understand.
SVN r1134 (trunk)
2008-08-09 03:13:43 +00:00
|
|
|
extern void CheckCPUID(CPUInfo *cpu);
|
2009-11-13 03:55:23 +00:00
|
|
|
extern void LayoutMainWindow(HWND hWnd, HWND pane);
|
|
|
|
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
|
|
|
|
static void CalculateCPUSpeed();
|
|
|
|
static void I_SelectTimer();
|
|
|
|
|
|
|
|
static int I_GetTimePolled(bool saveMS);
|
|
|
|
static int I_WaitForTicPolled(int prevtic);
|
|
|
|
static void I_FreezeTimePolled(bool frozen);
|
|
|
|
static int I_GetTimeEventDriven(bool saveMS);
|
|
|
|
static int I_WaitForTicEvent(int prevtic);
|
|
|
|
static void I_FreezeTimeEventDriven(bool frozen);
|
|
|
|
static void CALLBACK TimerTicked(UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWORD_PTR dw2);
|
|
|
|
|
2010-09-14 17:28:18 +00:00
|
|
|
static HCURSOR CreateCompatibleCursor(FTexture *cursorpic);
|
|
|
|
static HCURSOR CreateAlphaCursor(FTexture *cursorpic);
|
|
|
|
static HCURSOR CreateBitmapCursor(int xhot, int yhot, HBITMAP and_mask, HBITMAP color_mask);
|
|
|
|
static void DestroyCustomCursor();
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
|
|
|
|
EXTERN_CVAR(String, language);
|
|
|
|
EXTERN_CVAR (Bool, queryiwad);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
extern HWND Window, ConWindow, GameTitleWindow;
|
2009-03-18 05:02:32 +00:00
|
|
|
extern HANDLE StdOut;
|
|
|
|
extern bool FancyStdOut;
|
2006-02-24 04:48:15 +00:00
|
|
|
extern HINSTANCE g_hInst;
|
2009-11-13 03:55:23 +00:00
|
|
|
extern FILE *Logfile;
|
2010-11-19 04:20:33 +00:00
|
|
|
extern bool NativeMouse;
|
2015-07-15 10:53:58 +00:00
|
|
|
extern bool ConWindowHidden;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
|
|
|
|
CVAR (String, queryiwad_key, "shift", CVAR_GLOBALCONFIG|CVAR_ARCHIVE);
|
2015-09-23 07:18:57 +00:00
|
|
|
CVAR (Bool, con_debugoutput, false, 0);
|
2008-08-10 03:56:53 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
double PerfToSec, PerfToMillisec;
|
2006-02-24 04:48:15 +00:00
|
|
|
UINT TimerPeriod;
|
|
|
|
UINT TimerEventID;
|
|
|
|
UINT MillisecondsPerTic;
|
|
|
|
HANDLE NewTicArrived;
|
2006-09-14 00:02:31 +00:00
|
|
|
uint32 LanguageIDs[4];
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
int (*I_GetTime) (bool saveMS);
|
|
|
|
int (*I_WaitForTic) (int);
|
2008-10-21 02:27:21 +00:00
|
|
|
void (*I_FreezeTime) (bool frozen);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
os_t OSPlatform;
|
2009-11-13 03:55:23 +00:00
|
|
|
bool gameisdead;
|
|
|
|
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
static ticcmd_t emptycmd;
|
|
|
|
static bool HasExited;
|
|
|
|
|
|
|
|
static DWORD basetime = 0;
|
|
|
|
// These are for the polled timer.
|
|
|
|
static DWORD TicStart;
|
|
|
|
static DWORD TicNext;
|
|
|
|
static int TicFrozen;
|
|
|
|
|
|
|
|
// These are for the event-driven timer.
|
|
|
|
static int tics;
|
|
|
|
static DWORD ted_start, ted_next;
|
|
|
|
|
|
|
|
static WadStuff *WadList;
|
|
|
|
static int NumWads;
|
|
|
|
static int DefaultWad;
|
|
|
|
|
2010-09-14 17:28:18 +00:00
|
|
|
static HCURSOR CustomCursor;
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_Tactile
|
|
|
|
//
|
|
|
|
// Doom calls it when you take damage, so presumably it could be converted
|
|
|
|
// to something compatible with force feedback.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void I_Tactile(int on, int off, int total)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
// UNUSED.
|
|
|
|
on = off = total = 0;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_BaseTiccmd
|
|
|
|
//
|
|
|
|
// Returns an empty ticcmd. I have no idea why this should be system-
|
|
|
|
// specific.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
ticcmd_t *I_BaseTiccmd()
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
return &emptycmd;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
// Stubs that select the timer to use and then call into it ----------------
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_GetTimeSelect
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
static int I_GetTimeSelect(bool saveMS)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
I_SelectTimer();
|
|
|
|
return I_GetTime(saveMS);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_WaitForTicSelect
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
static int I_WaitForTicSelect(int prevtic)
|
|
|
|
{
|
|
|
|
I_SelectTimer();
|
|
|
|
return I_WaitForTic(prevtic);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_SelectTimer
|
|
|
|
//
|
|
|
|
// Tries to create a timer event for efficent CPU use when the FPS is
|
|
|
|
// capped. Failing that, it sets things up for a polling timer instead.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void I_SelectTimer()
|
|
|
|
{
|
|
|
|
assert(basetime == 0);
|
|
|
|
|
|
|
|
// Use a timer event if possible.
|
|
|
|
NewTicArrived = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (NewTicArrived)
|
|
|
|
{
|
|
|
|
UINT delay;
|
2010-03-02 04:51:16 +00:00
|
|
|
const char *cmdDelay;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
cmdDelay = Args->CheckValue("-timerdelay");
|
|
|
|
delay = 0;
|
|
|
|
if (cmdDelay != 0)
|
|
|
|
{
|
|
|
|
delay = atoi(cmdDelay);
|
|
|
|
}
|
|
|
|
if (delay == 0)
|
|
|
|
{
|
|
|
|
delay = 1000/TICRATE;
|
|
|
|
}
|
|
|
|
MillisecondsPerTic = delay;
|
|
|
|
TimerEventID = timeSetEvent(delay, 0, TimerTicked, 0, TIME_PERIODIC);
|
|
|
|
}
|
|
|
|
// Get the current time as the basetime.
|
|
|
|
basetime = timeGetTime();
|
|
|
|
// Set timer functions.
|
|
|
|
if (TimerEventID != 0)
|
|
|
|
{
|
|
|
|
I_GetTime = I_GetTimeEventDriven;
|
|
|
|
I_WaitForTic = I_WaitForTicEvent;
|
|
|
|
I_FreezeTime = I_FreezeTimeEventDriven;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
I_GetTime = I_GetTimePolled;
|
|
|
|
I_WaitForTic = I_WaitForTicPolled;
|
|
|
|
I_FreezeTime = I_FreezeTimePolled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_MSTime
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
// Returns the current time in milliseconds, where 0 is the first call
|
|
|
|
// to I_GetTime or I_WaitForTic.
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
unsigned int I_MSTime()
|
|
|
|
{
|
|
|
|
assert(basetime != 0);
|
|
|
|
return timeGetTime() - basetime;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
2009-11-29 02:57:09 +00:00
|
|
|
//
|
|
|
|
// I_FPSTime
|
|
|
|
//
|
|
|
|
// Returns the current system time in milliseconds. This is used by the FPS
|
|
|
|
// meter of DFrameBuffer::DrawRateStuff(). Since the screen can display
|
|
|
|
// before the play simulation is ready to begin, this needs to be
|
|
|
|
// separate from I_MSTime().
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
unsigned int I_FPSTime()
|
|
|
|
{
|
|
|
|
return timeGetTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
2009-11-13 03:55:23 +00:00
|
|
|
//
|
|
|
|
// I_GetTimePolled
|
|
|
|
//
|
|
|
|
// Returns the current time in tics. If saveMS is true, then calls to
|
|
|
|
// I_GetTimeFrac() will use this tic as 0 and the next tic as 1.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static int I_GetTimePolled(bool saveMS)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
DWORD tm;
|
|
|
|
|
2008-10-21 02:27:21 +00:00
|
|
|
if (TicFrozen != 0)
|
|
|
|
{
|
|
|
|
return TicFrozen;
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
tm = timeGetTime();
|
2009-11-12 03:45:51 +00:00
|
|
|
if (basetime == 0)
|
2009-11-13 03:55:23 +00:00
|
|
|
{
|
2006-02-24 04:48:15 +00:00
|
|
|
basetime = tm;
|
2009-11-13 03:55:23 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
if (saveMS)
|
|
|
|
{
|
|
|
|
TicStart = tm;
|
|
|
|
TicNext = (tm * TICRATE / 1000 + 1) * 1000 / TICRATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ((tm-basetime)*TICRATE)/1000;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_WaitForTicPolled
|
|
|
|
//
|
|
|
|
// Busy waits until the current tic is greater than prevtic. Time must not
|
|
|
|
// be frozen.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static int I_WaitForTicPolled(int prevtic)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int time;
|
|
|
|
|
2008-10-21 02:27:21 +00:00
|
|
|
assert(TicFrozen == 0);
|
2006-02-24 04:48:15 +00:00
|
|
|
while ((time = I_GetTimePolled(false)) <= prevtic)
|
2009-11-13 03:55:23 +00:00
|
|
|
{ }
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_FreezeTimePolled
|
|
|
|
//
|
|
|
|
// Freeze/unfreeze the timer.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void I_FreezeTimePolled(bool frozen)
|
2008-10-21 02:27:21 +00:00
|
|
|
{
|
|
|
|
if (frozen)
|
|
|
|
{
|
|
|
|
assert(TicFrozen == 0);
|
|
|
|
TicFrozen = I_GetTimePolled(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(TicFrozen != 0);
|
|
|
|
int froze = TicFrozen;
|
|
|
|
TicFrozen = 0;
|
|
|
|
int now = I_GetTimePolled(false);
|
|
|
|
basetime += (now - froze) * 1000 / TICRATE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_GetTimeEventDriven
|
|
|
|
//
|
|
|
|
// Returns the current tick counter. This is incremented asynchronously as
|
|
|
|
// the timer event fires.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static int I_GetTimeEventDriven(bool saveMS)
|
|
|
|
{
|
|
|
|
if (saveMS)
|
|
|
|
{
|
|
|
|
TicStart = ted_start;
|
|
|
|
TicNext = ted_next;
|
|
|
|
}
|
|
|
|
return tics;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_WaitForTicEvent
|
|
|
|
//
|
|
|
|
// Waits on the timer event as long as the current tic is not later than
|
|
|
|
// prevtic.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
static int I_WaitForTicEvent(int prevtic)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-10-21 02:27:21 +00:00
|
|
|
assert(!TicFrozen);
|
2006-02-24 04:48:15 +00:00
|
|
|
while (prevtic >= tics)
|
|
|
|
{
|
2008-03-05 03:10:31 +00:00
|
|
|
WaitForSingleObject(NewTicArrived, 1000/TICRATE);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
return tics;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_FreezeTimeEventDriven
|
|
|
|
//
|
|
|
|
// Freeze/unfreeze the ticker.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void I_FreezeTimeEventDriven(bool frozen)
|
|
|
|
{
|
|
|
|
TicFrozen = frozen;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// TimerTicked
|
|
|
|
//
|
|
|
|
// Advance the tick count and signal the NewTicArrived event.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void CALLBACK TimerTicked(UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWORD_PTR dw2)
|
|
|
|
{
|
|
|
|
if (!TicFrozen)
|
|
|
|
{
|
|
|
|
tics++;
|
|
|
|
}
|
|
|
|
ted_start = timeGetTime ();
|
|
|
|
ted_next = ted_start + MillisecondsPerTic;
|
|
|
|
SetEvent(NewTicArrived);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_GetTimeFrac
|
|
|
|
//
|
|
|
|
// Returns the fractional amount of a tic passed since the most recently
|
|
|
|
// saved tic.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
fixed_t I_GetTimeFrac(uint32 *ms)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
DWORD now = timeGetTime();
|
2009-11-13 03:55:23 +00:00
|
|
|
if (ms != NULL)
|
|
|
|
{
|
|
|
|
*ms = TicNext;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
DWORD step = TicNext - TicStart;
|
|
|
|
if (step == 0)
|
|
|
|
{
|
|
|
|
return FRACUNIT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fixed_t frac = clamp<fixed_t> ((now - TicStart)*FRACUNIT/step, 0, FRACUNIT);
|
|
|
|
return frac;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_WaitVBL
|
|
|
|
//
|
|
|
|
// I_WaitVBL is never used to actually synchronize to the vertical blank.
|
|
|
|
// Instead, it's used for delay purposes. Doom used a 70 Hz display mode,
|
|
|
|
// so that's what we use to determine how long to wait for.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void I_WaitVBL(int count)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
Sleep(1000 * count / 70);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_DetectOS
|
|
|
|
//
|
|
|
|
// Determine which version of Windows the game is running on.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void I_DetectOS(void)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-24 04:29:36 +00:00
|
|
|
OSVERSIONINFOEX info;
|
2006-02-24 04:48:15 +00:00
|
|
|
const char *osname;
|
|
|
|
|
2009-11-24 04:29:36 +00:00
|
|
|
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
|
|
if (!GetVersionEx((OSVERSIONINFO *)&info))
|
|
|
|
{
|
|
|
|
// Retry with the older OSVERSIONINFO structure.
|
|
|
|
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
GetVersionEx((OSVERSIONINFO *)&info);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
switch (info.dwPlatformId)
|
|
|
|
{
|
|
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
|
|
OSPlatform = os_Win95;
|
|
|
|
if (info.dwMinorVersion < 10)
|
|
|
|
{
|
|
|
|
osname = "95";
|
|
|
|
}
|
|
|
|
else if (info.dwMinorVersion < 90)
|
|
|
|
{
|
|
|
|
osname = "98";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
osname = "Me";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VER_PLATFORM_WIN32_NT:
|
2006-10-19 20:20:56 +00:00
|
|
|
OSPlatform = info.dwMajorVersion < 5 ? os_WinNT4 : os_Win2k;
|
|
|
|
osname = "NT";
|
|
|
|
if (info.dwMajorVersion == 5)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-10-19 20:20:56 +00:00
|
|
|
if (info.dwMinorVersion == 0)
|
|
|
|
{
|
|
|
|
osname = "2000";
|
|
|
|
}
|
|
|
|
if (info.dwMinorVersion == 1)
|
|
|
|
{
|
|
|
|
osname = "XP";
|
|
|
|
}
|
|
|
|
else if (info.dwMinorVersion == 2)
|
|
|
|
{
|
|
|
|
osname = "Server 2003";
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2009-11-24 04:29:36 +00:00
|
|
|
else if (info.dwMajorVersion == 6)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-24 04:29:36 +00:00
|
|
|
if (info.dwMinorVersion == 0)
|
|
|
|
{
|
2015-03-27 05:25:53 +00:00
|
|
|
osname = (info.wProductType == VER_NT_WORKSTATION) ? "Vista" : "Server 2008";
|
2009-11-24 04:29:36 +00:00
|
|
|
}
|
|
|
|
else if (info.dwMinorVersion == 1)
|
|
|
|
{
|
2015-03-27 05:25:53 +00:00
|
|
|
osname = (info.wProductType == VER_NT_WORKSTATION) ? "7" : "Server 2008 R2";
|
2009-11-24 04:29:36 +00:00
|
|
|
}
|
2014-04-16 07:53:07 +00:00
|
|
|
else if (info.dwMinorVersion == 2)
|
|
|
|
{
|
2015-03-27 05:25:53 +00:00
|
|
|
// Starting with Windows 8.1, you need to specify in your manifest
|
|
|
|
// the highest version of Windows you support, which will also be the
|
|
|
|
// highest version of Windows this function returns.
|
|
|
|
osname = (info.wProductType == VER_NT_WORKSTATION) ? "8" : "Server 2012";
|
|
|
|
}
|
|
|
|
else if (info.dwMinorVersion == 3)
|
|
|
|
{
|
|
|
|
osname = (info.wProductType == VER_NT_WORKSTATION) ? "8.1" : "Server 2012 R2";
|
|
|
|
}
|
|
|
|
else if (info.dwMinorVersion == 4)
|
|
|
|
{
|
|
|
|
osname = (info.wProductType == VER_NT_WORKSTATION) ? "10 (or higher)" : "Server 10 (or higher)";
|
2014-04-16 07:53:07 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
OSPlatform = os_unknown;
|
|
|
|
osname = "Unknown OS";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
if (OSPlatform == os_Win95)
|
|
|
|
{
|
|
|
|
Printf ("OS: Windows %s %lu.%lu.%lu %s\n",
|
|
|
|
osname,
|
|
|
|
info.dwMajorVersion, info.dwMinorVersion,
|
|
|
|
info.dwBuildNumber & 0xffff, info.szCSDVersion);
|
|
|
|
}
|
|
|
|
else
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-24 04:29:36 +00:00
|
|
|
Printf ("OS: Windows %s (NT %lu.%lu) Build %lu\n %s\n",
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
osname,
|
|
|
|
info.dwMajorVersion, info.dwMinorVersion,
|
|
|
|
info.dwBuildNumber, info.szCSDVersion);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2006-10-19 20:20:56 +00:00
|
|
|
if (OSPlatform == os_unknown)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2012-08-02 04:00:40 +00:00
|
|
|
Printf ("(Assuming Windows 2000)\n");
|
|
|
|
OSPlatform = os_Win2k;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
// SubsetLanguageIDs
|
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
// Helper function for SetLanguageIDs.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void SubsetLanguageIDs(LCID id, LCTYPE type, int idx)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
char buf[8];
|
|
|
|
LCID langid;
|
|
|
|
char *idp;
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
if (!GetLocaleInfo(id, type, buf, 8))
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
2009-11-13 03:55:23 +00:00
|
|
|
langid = MAKELCID(strtoul(buf, NULL, 16), SORT_DEFAULT);
|
|
|
|
if (!GetLocaleInfo(langid, LOCALE_SABBREVLANGNAME, buf, 8))
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
idp = (char *)(&LanguageIDs[idx]);
|
|
|
|
memset (idp, 0, 4);
|
|
|
|
idp[0] = tolower(buf[0]);
|
|
|
|
idp[1] = tolower(buf[1]);
|
|
|
|
idp[2] = tolower(buf[2]);
|
|
|
|
idp[3] = 0;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
// SetLanguageIDs
|
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void SetLanguageIDs()
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
size_t langlen = strlen(language);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (langlen < 2 || langlen > 3)
|
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
memset(LanguageIDs, 0, sizeof(LanguageIDs));
|
|
|
|
SubsetLanguageIDs(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, 0);
|
|
|
|
SubsetLanguageIDs(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 1);
|
|
|
|
SubsetLanguageIDs(LOCALE_SYSTEM_DEFAULT, LOCALE_ILANGUAGE, 2);
|
|
|
|
SubsetLanguageIDs(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 3);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DWORD lang = 0;
|
|
|
|
|
|
|
|
((BYTE *)&lang)[0] = (language)[0];
|
|
|
|
((BYTE *)&lang)[1] = (language)[1];
|
|
|
|
((BYTE *)&lang)[2] = (language)[2];
|
|
|
|
LanguageIDs[0] = lang;
|
|
|
|
LanguageIDs[1] = lang;
|
|
|
|
LanguageIDs[2] = lang;
|
|
|
|
LanguageIDs[3] = lang;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// CalculateCPUSpeed
|
|
|
|
//
|
|
|
|
// Make a decent guess at how much time elapses between TSC steps. This can
|
|
|
|
// vary over runtime depending on power management settings, so should not
|
|
|
|
// be used anywhere that truely accurate timing actually matters.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2009-03-04 06:06:31 +00:00
|
|
|
void CalculateCPUSpeed()
|
|
|
|
{
|
|
|
|
LARGE_INTEGER freq;
|
|
|
|
|
|
|
|
QueryPerformanceFrequency (&freq);
|
|
|
|
|
|
|
|
if (freq.QuadPart != 0 && CPU.bRDTSC)
|
|
|
|
{
|
|
|
|
LARGE_INTEGER count1, count2;
|
|
|
|
cycle_t ClockCalibration;
|
|
|
|
DWORD min_diff;
|
|
|
|
|
|
|
|
ClockCalibration.Reset();
|
|
|
|
|
|
|
|
// Count cycles for at least 55 milliseconds.
|
|
|
|
// The performance counter may be very low resolution compared to CPU
|
|
|
|
// speeds today, so the longer we count, the more accurate our estimate.
|
|
|
|
// On the other hand, we don't want to count too long, because we don't
|
|
|
|
// want the user to notice us spend time here, since most users will
|
|
|
|
// probably never use the performance statistics.
|
|
|
|
min_diff = freq.LowPart * 11 / 200;
|
|
|
|
|
|
|
|
// Minimize the chance of task switching during the testing by going very
|
|
|
|
// high priority. This is another reason to avoid timing for too long.
|
|
|
|
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
|
|
|
|
|
|
// Make sure we start timing on a counter boundary.
|
|
|
|
QueryPerformanceCounter(&count1);
|
|
|
|
do { QueryPerformanceCounter(&count2); } while (count1.QuadPart == count2.QuadPart);
|
|
|
|
|
|
|
|
// Do the timing loop.
|
|
|
|
ClockCalibration.Clock();
|
|
|
|
do { QueryPerformanceCounter(&count1); } while ((count1.QuadPart - count2.QuadPart) < min_diff);
|
|
|
|
ClockCalibration.Unclock();
|
|
|
|
|
|
|
|
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
|
|
|
|
|
|
PerfToSec = double(count1.QuadPart - count2.QuadPart) / (double(ClockCalibration.GetRawCounter()) * freq.QuadPart);
|
|
|
|
PerfToMillisec = PerfToSec * 1000.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Printf ("CPU Speed: %.0f MHz\n", 0.001 / PerfToMillisec);
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
// I_Init
|
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
- Ported vlinetallasm4 to AMD64 assembly. Even with the increased number of
registers AMD64 provides, this routine still needs to be written as self-
modifying code for maximum performance. The additional registers do allow
for further optimization over the x86 version by allowing all four pixels
to be in flight at the same time. The end result is that AMD64 ASM is about
2.18 times faster than AMD64 C and about 1.06 times faster than x86 ASM.
(For further comparison, AMD64 C and x86 C are practically the same for
this function.) Should I port any more assembly to AMD64, mvlineasm4 is the
most likely candidate, but it's not used enough at this point to bother.
Also, this may or may not work with Linux at the moment, since it doesn't
have the eh_handler metadata. Win64 is easier, since I just need to
structure the function prologue and epilogue properly and use some
assembler directives/macros to automatically generate the metadata. And
that brings up another point: You need YASM to assemble the AMD64 code,
because NASM doesn't support the Win64 metadata directives.
- Added an SSE version of DoBlending. This is strictly C intrinsics.
VC++ still throws around unneccessary register moves. GCC seems to be
pretty close to optimal, requiring only about 2 cycles/color. They're
both faster than my hand-written MMX routine, so I don't need to feel
bad about not hand-optimizing this for x64 builds.
- Removed an extra instruction from DoBlending_MMX, transposed two
instructions, and unrolled it once, shaving off about 80 cycles from the
time required to blend 256 palette entries. Why? Because I tried writing
a C version of the routine using compiler intrinsics and was appalled by
all the extra movq's VC++ added to the code. GCC was better, but still
generated extra instructions. I only wanted a C version because I can't
use inline assembly with VC++'s x64 compiler, and x64 assembly is a bit
of a pain. (It's a pain because Linux and Windows have different calling
conventions, and you need to maintain extra metadata for functions.) So,
the assembly version stays and the C version stays out.
- Removed all the pixel doubling r_detail modes, since the one platform they
were intended to assist (486) actually sees very little benefit from them.
- Rewrote CheckMMX in C and renamed it to CheckCPU.
- Fixed: CPUID function 0x80000005 is specified to return detailed L1 cache
only for AMD processors, so we must not use it on other architectures, or
we end up overwriting the L1 cache line size with 0 or some other number
we don't actually understand.
SVN r1134 (trunk)
2008-08-09 03:13:43 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
void I_Init()
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-09 22:46:25 +00:00
|
|
|
CheckCPUID(&CPU);
|
2009-03-04 06:06:31 +00:00
|
|
|
CalculateCPUSpeed();
|
2008-08-10 03:25:08 +00:00
|
|
|
DumpCPUInfo(&CPU);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
I_GetTime = I_GetTimeSelect;
|
|
|
|
I_WaitForTic = I_WaitForTicSelect;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
atterm (I_ShutdownSound);
|
|
|
|
I_InitSound ();
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
// I_Quit
|
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
void I_Quit()
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
HasExited = true; /* Prevent infinitely recursive exits -- killough */
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
if (TimerEventID != 0)
|
|
|
|
{
|
|
|
|
timeKillEvent(TimerEventID);
|
|
|
|
}
|
|
|
|
if (NewTicArrived != NULL)
|
|
|
|
{
|
|
|
|
CloseHandle(NewTicArrived);
|
|
|
|
}
|
|
|
|
timeEndPeriod(TimerPeriod);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (demorecording)
|
2009-11-13 03:55:23 +00:00
|
|
|
{
|
2006-02-24 04:48:15 +00:00
|
|
|
G_CheckDemoStatus();
|
2009-11-13 03:55:23 +00:00
|
|
|
}
|
2014-07-16 22:59:49 +00:00
|
|
|
|
|
|
|
C_DeinitConsole();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
// I_FatalError
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
2009-11-13 03:55:23 +00:00
|
|
|
// Throw an error that will end the game.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
void STACK_ARGS I_FatalError(const char *error, ...)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
static BOOL alreadyThrown = false;
|
|
|
|
gameisdead = true;
|
|
|
|
|
|
|
|
if (!alreadyThrown) // ignore all but the first message -- killough
|
|
|
|
{
|
2006-12-29 20:28:23 +00:00
|
|
|
alreadyThrown = true;
|
2006-02-24 04:48:15 +00:00
|
|
|
char errortext[MAX_ERRORTEXT];
|
|
|
|
va_list argptr;
|
2009-11-13 03:55:23 +00:00
|
|
|
va_start(argptr, error);
|
|
|
|
myvsnprintf(errortext, MAX_ERRORTEXT, error, argptr);
|
|
|
|
va_end(argptr);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// Record error to log (if logging)
|
|
|
|
if (Logfile)
|
2009-02-04 01:23:47 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
fprintf(Logfile, "\n**** DIED WITH FATAL ERROR:\n%s\n", errortext);
|
|
|
|
fflush(Logfile);
|
2009-02-04 01:23:47 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
throw CFatalError(errortext);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
if (!HasExited) // If it hasn't exited yet, exit now -- killough
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
HasExited = 1; // Prevent infinitely recursive exits -- killough
|
2006-02-24 04:48:15 +00:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_Error
|
|
|
|
//
|
|
|
|
// Throw an error that will send us to the console if we are far enough
|
|
|
|
// along in the startup process.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void STACK_ARGS I_Error(const char *error, ...)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
va_list argptr;
|
|
|
|
char errortext[MAX_ERRORTEXT];
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
va_start(argptr, error);
|
|
|
|
myvsnprintf(errortext, MAX_ERRORTEXT, error, argptr);
|
|
|
|
va_end(argptr);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
throw CRecoverableError(errortext);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2010-03-04 04:45:48 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// ToEditControl
|
|
|
|
//
|
|
|
|
// Converts string to Unicode and inserts it into the control.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void ToEditControl(HWND edit, const char *buf, wchar_t *wbuf, int bpos)
|
|
|
|
{
|
|
|
|
// Let's just do this ourself. It's not hard, and we can compensate for
|
|
|
|
// special console characters at the same time.
|
|
|
|
#if 0
|
|
|
|
MultiByteToWideChar(1252 /* Western */, 0, buf, bpos, wbuf, countof(wbuf));
|
|
|
|
wbuf[bpos] = 0;
|
|
|
|
#else
|
|
|
|
static wchar_t notlatin1[32] = // code points 0x80-0x9F
|
|
|
|
{
|
|
|
|
0x20AC, // Euro sign
|
|
|
|
0x0081, // Undefined
|
|
|
|
0x201A, // Single low-9 quotation mark
|
|
|
|
0x0192, // Latin small letter f with hook
|
|
|
|
0x201E, // Double low-9 quotation mark
|
|
|
|
0x2026, // Horizontal ellipsis
|
|
|
|
0x2020, // Dagger
|
|
|
|
0x2021, // Double dagger
|
|
|
|
0x02C6, // Modifier letter circumflex accent
|
|
|
|
0x2030, // Per mille sign
|
|
|
|
0x0160, // Latin capital letter S with caron
|
|
|
|
0x2039, // Single left-pointing angle quotation mark
|
|
|
|
0x0152, // Latin capital ligature OE
|
|
|
|
0x008D, // Undefined
|
|
|
|
0x017D, // Latin capital letter Z with caron
|
|
|
|
0x008F, // Undefined
|
|
|
|
0x0090, // Undefined
|
|
|
|
0x2018, // Left single quotation mark
|
|
|
|
0x2019, // Right single quotation mark
|
|
|
|
0x201C, // Left double quotation mark
|
|
|
|
0x201D, // Right double quotation mark
|
|
|
|
0x2022, // Bullet
|
|
|
|
0x2013, // En dash
|
|
|
|
0x2014, // Em dash
|
|
|
|
0x02DC, // Small tilde
|
|
|
|
0x2122, // Trade mark sign
|
|
|
|
0x0161, // Latin small letter s with caron
|
|
|
|
0x203A, // Single right-pointing angle quotation mark
|
|
|
|
0x0153, // Latin small ligature oe
|
|
|
|
0x009D, // Undefined
|
|
|
|
0x017E, // Latin small letter z with caron
|
|
|
|
0x0178 // Latin capital letter Y with diaeresis
|
|
|
|
};
|
|
|
|
for (int i = 0; i <= bpos; ++i)
|
|
|
|
{
|
|
|
|
wchar_t code = (BYTE)buf[i];
|
|
|
|
if (code >= 0x1D && code <= 0x1F)
|
|
|
|
{ // The bar characters, most commonly used to indicate map changes
|
|
|
|
code = 0x2550; // Box Drawings Double Horizontal
|
|
|
|
}
|
|
|
|
else if (code >= 0x80 && code <= 0x9F)
|
|
|
|
{
|
|
|
|
code = notlatin1[code - 0x80];
|
|
|
|
}
|
|
|
|
wbuf[i] = code;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
SendMessageW(edit, EM_REPLACESEL, FALSE, (LPARAM)wbuf);
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_PrintStr
|
|
|
|
//
|
|
|
|
// Send output to the list box shown during startup (and hidden during
|
|
|
|
// gameplay).
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2015-07-15 10:53:58 +00:00
|
|
|
static void DoPrintStr(const char *cp, HWND edit, HANDLE StdOut)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2015-07-15 10:53:58 +00:00
|
|
|
if (edit == NULL && StdOut == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
char buf[256];
|
2010-03-04 04:45:48 +00:00
|
|
|
wchar_t wbuf[countof(buf)];
|
2006-02-24 04:48:15 +00:00
|
|
|
int bpos = 0;
|
2007-01-09 04:40:58 +00:00
|
|
|
CHARRANGE selection;
|
|
|
|
CHARRANGE endselection;
|
2009-03-27 04:49:17 +00:00
|
|
|
LONG lines_before = 0, lines_after;
|
2007-01-09 04:40:58 +00:00
|
|
|
CHARFORMAT format;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-03-18 05:02:32 +00:00
|
|
|
if (edit != NULL)
|
|
|
|
{
|
|
|
|
// Store the current selection and set it to the end so we can append text.
|
2009-11-13 03:55:23 +00:00
|
|
|
SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&selection);
|
|
|
|
endselection.cpMax = endselection.cpMin = GetWindowTextLength(edit);
|
|
|
|
SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&endselection);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-03-18 05:02:32 +00:00
|
|
|
// GetWindowTextLength and EM_EXSETSEL can disagree on where the end of
|
|
|
|
// the text is. Find out what EM_EXSETSEL thought it was and use that later.
|
2009-11-13 03:55:23 +00:00
|
|
|
SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&endselection);
|
2007-01-09 04:40:58 +00:00
|
|
|
|
2009-03-18 05:02:32 +00:00
|
|
|
// Remember how many lines there were before we added text.
|
2009-11-13 03:55:23 +00:00
|
|
|
lines_before = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0);
|
2009-03-18 05:02:32 +00:00
|
|
|
}
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
while (*cp != 0)
|
|
|
|
{
|
2007-01-09 04:40:58 +00:00
|
|
|
// 28 is the escape code for a color change.
|
|
|
|
if ((*cp == 28 && bpos != 0) || bpos == 255)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
buf[bpos] = 0;
|
2009-03-18 05:02:32 +00:00
|
|
|
if (edit != NULL)
|
|
|
|
{
|
2010-03-04 04:45:48 +00:00
|
|
|
ToEditControl(edit, buf, wbuf, bpos);
|
2009-03-18 05:02:32 +00:00
|
|
|
}
|
|
|
|
if (StdOut != NULL)
|
|
|
|
{
|
|
|
|
DWORD bytes_written;
|
|
|
|
WriteFile(StdOut, buf, bpos, &bytes_written, NULL);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
bpos = 0;
|
|
|
|
}
|
2007-01-09 04:40:58 +00:00
|
|
|
if (*cp != 28)
|
|
|
|
{
|
|
|
|
buf[bpos++] = *cp++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const BYTE *color_id = (const BYTE *)cp + 1;
|
2009-11-13 03:55:23 +00:00
|
|
|
EColorRange range = V_ParseFontColor(color_id, CR_UNTRANSLATED, CR_YELLOW);
|
2007-01-09 04:40:58 +00:00
|
|
|
cp = (const char *)color_id;
|
|
|
|
|
|
|
|
if (range != CR_UNDEFINED)
|
|
|
|
{
|
|
|
|
// Change the color of future text added to the control.
|
2009-11-13 03:55:23 +00:00
|
|
|
PalEntry color = V_LogColorFromColorRange(range);
|
2009-03-18 05:02:32 +00:00
|
|
|
if (StdOut != NULL && FancyStdOut)
|
|
|
|
{
|
|
|
|
// Unfortunately, we are pretty limited here: There are only
|
|
|
|
// eight basic colors, and each comes in a dark and a bright
|
|
|
|
// variety.
|
|
|
|
float h, s, v, r, g, b;
|
|
|
|
WORD attrib = 0;
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
RGBtoHSV(color.r / 255.f, color.g / 255.f, color.b / 255.f, &h, &s, &v);
|
2009-03-18 05:02:32 +00:00
|
|
|
if (s != 0)
|
|
|
|
{ // color
|
|
|
|
HSVtoRGB(&r, &g, &b, h, 1, 1);
|
|
|
|
if (r == 1) attrib = FOREGROUND_RED;
|
|
|
|
if (g == 1) attrib |= FOREGROUND_GREEN;
|
|
|
|
if (b == 1) attrib |= FOREGROUND_BLUE;
|
|
|
|
if (v > 0.6) attrib |= FOREGROUND_INTENSITY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // gray
|
|
|
|
if (v < 0.33) attrib = FOREGROUND_INTENSITY;
|
|
|
|
else if (v < 0.90) attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
|
|
else attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
|
|
|
}
|
|
|
|
SetConsoleTextAttribute(StdOut, attrib);
|
|
|
|
}
|
|
|
|
if (edit != NULL)
|
|
|
|
{
|
|
|
|
// GDI uses BGR colors, but color is RGB, so swap the R and the B.
|
2010-07-23 21:19:59 +00:00
|
|
|
swapvalues(color.r, color.b);
|
2009-03-18 05:02:32 +00:00
|
|
|
// Change the color.
|
|
|
|
format.cbSize = sizeof(format);
|
|
|
|
format.dwMask = CFM_COLOR;
|
|
|
|
format.dwEffects = 0;
|
|
|
|
format.crTextColor = color;
|
2009-11-13 03:55:23 +00:00
|
|
|
SendMessage(edit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
|
2009-03-18 05:02:32 +00:00
|
|
|
}
|
2007-01-09 04:40:58 +00:00
|
|
|
}
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
if (bpos != 0)
|
|
|
|
{
|
|
|
|
buf[bpos] = 0;
|
2009-03-18 05:02:32 +00:00
|
|
|
if (edit != NULL)
|
|
|
|
{
|
2010-03-04 04:45:48 +00:00
|
|
|
ToEditControl(edit, buf, wbuf, bpos);
|
2009-03-18 05:02:32 +00:00
|
|
|
}
|
|
|
|
if (StdOut != NULL)
|
|
|
|
{
|
|
|
|
DWORD bytes_written;
|
|
|
|
WriteFile(StdOut, buf, bpos, &bytes_written, NULL);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-01-09 04:40:58 +00:00
|
|
|
|
2009-03-18 05:02:32 +00:00
|
|
|
if (edit != NULL)
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
{
|
2009-03-18 05:02:32 +00:00
|
|
|
// If the old selection was at the end of the text, keep it at the end and
|
|
|
|
// scroll. Don't scroll if the selection is anywhere else.
|
|
|
|
if (selection.cpMin == endselection.cpMin && selection.cpMax == endselection.cpMax)
|
2007-01-09 04:40:58 +00:00
|
|
|
{
|
2009-03-18 05:02:32 +00:00
|
|
|
selection.cpMax = selection.cpMin = GetWindowTextLength (edit);
|
2009-11-13 03:55:23 +00:00
|
|
|
lines_after = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0);
|
2009-03-18 05:02:32 +00:00
|
|
|
if (lines_after > lines_before)
|
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
SendMessage(edit, EM_LINESCROLL, 0, lines_after - lines_before);
|
2009-03-18 05:02:32 +00:00
|
|
|
}
|
2007-01-09 04:40:58 +00:00
|
|
|
}
|
2009-03-18 05:02:32 +00:00
|
|
|
// Restore the previous selection.
|
2009-11-13 03:55:23 +00:00
|
|
|
SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&selection);
|
2009-03-18 05:02:32 +00:00
|
|
|
// Give the edit control a chance to redraw itself.
|
2009-11-13 03:55:23 +00:00
|
|
|
I_GetEvent();
|
2009-03-18 05:02:32 +00:00
|
|
|
}
|
|
|
|
if (StdOut != NULL && FancyStdOut)
|
|
|
|
{ // Set text back to gray, in case it was changed.
|
|
|
|
SetConsoleTextAttribute(StdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
Note: I have not tried compiling these recent changes under Linux. I wouldn't
be surprised if it doesn't work.
- Reorganized the network startup loops so now they are event driven. There is
a single function that gets called to drive it, and it uses callbacks to
perform the different stages of the synchronization. This lets me have a nice,
responsive abort button instead of the previous unannounced hit-escape-to-
abort behavior, and I think the rearranged code is slightly easier to
understand too.
- Increased the number of bytes for version info during D_ArbitrateNetStart(),
in preparation for the day when NETGAMEVERSION requires more than one byte.
- I noticed an issue with Vista RC1 and the new fatal error setup. Even after
releasing a DirectDraw or Direct3D interface, the DWM can still use the
last image drawn using them when it composites the window. It doesn't always
do it but it does often enough that it is a real problem. At this point, I
don't know if it's a problem with the release version of Vista or not.
After messing around, I discovered the problem was caused by ~Win32Video()
hiding the window and then having it immediately shown soon after. The DWM
kept an image of the window to do the transition effect with, and then when
it didn't get a chance to do the transition, it didn't properly forget about
its saved image and kept plastering it on top of everything else
underneath.
- Added a network synchronization panel to the window during netgame startup.
- Fixed: PClass::CreateDerivedClass() must initialize StateList to NULL.
Otherwise, classic DECORATE definitions generate a big, fat crash.
- Resurrected the R_Init progress bar, now as a standard Windows control.
- Removed the sound failure dialog. The FMOD setup already defaulted to no
sound if initialization failed, so this only applies when snd_output is set
to "alternate" which now also falls back to no sound. In addition, it wasn't
working right, and I didn't feel like fixing it for the probably 0% of users
it affected.
- Fixed: The edit control used for logging output added text in reverse order
on Win9x.
- Went back to the roots and made graphics initialization one of the last
things to happen during setup. Now the startup text is visible again. More
importantly, the main window is no longer created invisible, which seems
to cause trouble with it not always appearing in the taskbar. The fatal
error dialog is now also embedded in the main window instead of being a
separate modal dialog, so you can play with the log window to see any
problems that might be reported there.
Rather than completely restoring the original startup order, I tried to
keep things as close to the way they were with early graphics startup. In
particular, V_Init() now creates a dummy screen so that things that need
screen dimensions can get them. It gets replaced by the real screen later
in I_InitGraphics(). Will need to check this under Linux to make sure it
didn't cause any problems there.
- Removed the following stubs that just called functions in Video:
- I_StartModeIterator()
- I_NextMode()
- I_DisplayType()
I_FullscreenChanged() was also removed, and a new fullscreen parameter
was added to IVideo::StartModeIterator(), since that's all it controlled.
- Renamed I_InitHardware() back to I_InitGraphics(), since that's all it's
initialized post-1.22.
SVN r416 (trunk)
2006-12-19 04:09:10 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2015-07-15 10:53:58 +00:00
|
|
|
static TArray<FString> bufferedConsoleStuff;
|
|
|
|
|
|
|
|
void I_PrintStr(const char *cp)
|
|
|
|
{
|
2015-09-23 07:18:57 +00:00
|
|
|
if (con_debugoutput)
|
2015-09-22 12:19:44 +00:00
|
|
|
{
|
2015-09-23 07:18:57 +00:00
|
|
|
// Strip out any color escape sequences before writing to debug output
|
|
|
|
char * copy = new char[strlen(cp)+1];
|
|
|
|
const char * srcp = cp;
|
|
|
|
char * dstp = copy;
|
|
|
|
|
|
|
|
while (*srcp != 0)
|
2015-09-22 12:19:44 +00:00
|
|
|
{
|
2015-09-23 07:18:57 +00:00
|
|
|
if (*srcp!=0x1c && *srcp!=0x1d && *srcp!=0x1e && *srcp!=0x1f)
|
|
|
|
{
|
|
|
|
*dstp++=*srcp++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (srcp[1]!=0) srcp+=2;
|
|
|
|
else break;
|
|
|
|
}
|
2015-09-22 12:19:44 +00:00
|
|
|
}
|
2015-09-23 07:18:57 +00:00
|
|
|
*dstp=0;
|
2015-09-22 12:19:44 +00:00
|
|
|
|
2015-09-23 07:18:57 +00:00
|
|
|
OutputDebugStringA(copy);
|
|
|
|
delete [] copy;
|
|
|
|
}
|
2015-09-22 12:19:44 +00:00
|
|
|
|
2015-07-15 10:53:58 +00:00
|
|
|
if (ConWindowHidden)
|
|
|
|
{
|
|
|
|
bufferedConsoleStuff.Push(cp);
|
|
|
|
DoPrintStr(cp, NULL, StdOut);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DoPrintStr(cp, ConWindow, StdOut);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_FlushBufferedConsoleStuff()
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < bufferedConsoleStuff.Size(); i++)
|
|
|
|
{
|
|
|
|
DoPrintStr(bufferedConsoleStuff[i], ConWindow, NULL);
|
|
|
|
}
|
|
|
|
bufferedConsoleStuff.Clear();
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// SetQueryIWAD
|
|
|
|
//
|
|
|
|
// The user had the "Don't ask again" box checked when they closed the
|
|
|
|
// IWAD selection dialog.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
static void SetQueryIWad(HWND dialog)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
HWND checkbox = GetDlgItem(dialog, IDC_DONTASKIWAD);
|
|
|
|
int state = (int)SendMessage(checkbox, BM_GETCHECK, 0, 0);
|
2006-07-08 02:17:35 +00:00
|
|
|
bool query = (state != BST_CHECKED);
|
|
|
|
|
|
|
|
if (!query && queryiwad)
|
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
MessageBox(dialog,
|
2006-07-08 02:17:35 +00:00
|
|
|
"You have chosen not to show this dialog box in the future.\n"
|
|
|
|
"If you wish to see it again, hold down SHIFT while starting " GAMENAME ".",
|
|
|
|
"Don't ask me this again",
|
|
|
|
MB_OK | MB_ICONINFORMATION);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-07-08 02:17:35 +00:00
|
|
|
queryiwad = query;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// IWADBoxCallback
|
|
|
|
//
|
|
|
|
// Dialog proc for the IWAD selector.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-07-08 02:17:35 +00:00
|
|
|
HWND ctrl;
|
2006-02-24 04:48:15 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
switch (message)
|
|
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
2006-06-20 20:30:39 +00:00
|
|
|
// Add our program name to the window title
|
|
|
|
{
|
|
|
|
TCHAR label[256];
|
|
|
|
FString newlabel;
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
GetWindowText(hDlg, label, countof(label));
|
2013-06-23 02:49:51 +00:00
|
|
|
newlabel.Format(GAMESIG " %s: %s", GetVersionString(), label);
|
2009-11-13 03:55:23 +00:00
|
|
|
SetWindowText(hDlg, newlabel.GetChars());
|
2006-06-20 20:30:39 +00:00
|
|
|
}
|
|
|
|
// Populate the list with all the IWADs found
|
2009-11-13 03:55:23 +00:00
|
|
|
ctrl = GetDlgItem(hDlg, IDC_IWADLIST);
|
2006-02-24 04:48:15 +00:00
|
|
|
for (i = 0; i < NumWads; i++)
|
|
|
|
{
|
2006-06-20 20:30:39 +00:00
|
|
|
FString work;
|
2009-11-13 03:55:23 +00:00
|
|
|
const char *filepart = strrchr(WadList[i].Path, '/');
|
2006-02-24 04:48:15 +00:00
|
|
|
if (filepart == NULL)
|
|
|
|
filepart = WadList[i].Path;
|
|
|
|
else
|
|
|
|
filepart++;
|
2011-03-29 05:20:33 +00:00
|
|
|
work.Format("%s (%s)", WadList[i].Name.GetChars(), filepart);
|
2009-11-13 03:55:23 +00:00
|
|
|
SendMessage(ctrl, LB_ADDSTRING, 0, (LPARAM)work.GetChars());
|
|
|
|
SendMessage(ctrl, LB_SETITEMDATA, i, (LPARAM)i);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2009-11-13 03:55:23 +00:00
|
|
|
SendMessage(ctrl, LB_SETCURSEL, DefaultWad, 0);
|
|
|
|
SetFocus(ctrl);
|
2006-07-08 02:17:35 +00:00
|
|
|
// Set the state of the "Don't ask me again" checkbox
|
2009-11-13 03:55:23 +00:00
|
|
|
ctrl = GetDlgItem(hDlg, IDC_DONTASKIWAD);
|
|
|
|
SendMessage(ctrl, BM_SETCHECK, queryiwad ? BST_UNCHECKED : BST_CHECKED, 0);
|
2006-07-08 02:17:35 +00:00
|
|
|
// Make sure the dialog is in front. If SHIFT was pressed to force it visible,
|
|
|
|
// then the other window will normally be on top.
|
2009-11-13 03:55:23 +00:00
|
|
|
SetForegroundWindow(hDlg);
|
2006-02-24 04:48:15 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
if (LOWORD(wParam) == IDCANCEL)
|
|
|
|
{
|
|
|
|
EndDialog (hDlg, -1);
|
|
|
|
}
|
|
|
|
else if (LOWORD(wParam) == IDOK ||
|
|
|
|
(LOWORD(wParam) == IDC_IWADLIST && HIWORD(wParam) == LBN_DBLCLK))
|
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
SetQueryIWad(hDlg);
|
2006-07-08 02:17:35 +00:00
|
|
|
ctrl = GetDlgItem (hDlg, IDC_IWADLIST);
|
2009-11-13 03:55:23 +00:00
|
|
|
EndDialog(hDlg, SendMessage (ctrl, LB_GETCURSEL, 0, 0));
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_PickIWad
|
|
|
|
//
|
|
|
|
// Open a dialog to pick the IWAD, if there is more than one found.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-07-20 05:13:39 +00:00
|
|
|
int vkey;
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
if (stricmp(queryiwad_key, "shift") == 0)
|
2006-07-20 05:13:39 +00:00
|
|
|
{
|
|
|
|
vkey = VK_SHIFT;
|
|
|
|
}
|
2009-11-13 03:55:23 +00:00
|
|
|
else if (stricmp(queryiwad_key, "control") == 0 || stricmp (queryiwad_key, "ctrl") == 0)
|
2006-07-20 05:13:39 +00:00
|
|
|
{
|
|
|
|
vkey = VK_CONTROL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vkey = 0;
|
|
|
|
}
|
|
|
|
if (showwin || (vkey != 0 && GetAsyncKeyState(vkey)))
|
2006-07-08 02:17:35 +00:00
|
|
|
{
|
|
|
|
WadList = wads;
|
|
|
|
NumWads = numwads;
|
|
|
|
DefaultWad = defaultiwad;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
return (int)DialogBox(g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG),
|
2006-07-08 02:17:35 +00:00
|
|
|
(HWND)Window, (DLGPROC)IWADBoxCallback);
|
|
|
|
}
|
|
|
|
return defaultiwad;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2010-09-14 17:28:18 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_SetCursor
|
|
|
|
//
|
|
|
|
// Returns true if the cursor was successfully changed.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool I_SetCursor(FTexture *cursorpic)
|
|
|
|
{
|
|
|
|
HCURSOR cursor;
|
|
|
|
|
2012-07-05 21:46:03 +00:00
|
|
|
if (cursorpic != NULL && cursorpic->UseType != FTexture::TEX_Null &&
|
|
|
|
(screen == NULL || !screen->Is8BitMode()))
|
2010-09-14 17:28:18 +00:00
|
|
|
{
|
2010-09-26 07:46:34 +00:00
|
|
|
// Must be no larger than 32x32.
|
|
|
|
if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2010-09-14 17:28:18 +00:00
|
|
|
|
2010-09-26 07:46:34 +00:00
|
|
|
cursor = CreateAlphaCursor(cursorpic);
|
|
|
|
if (cursor == NULL)
|
|
|
|
{
|
|
|
|
cursor = CreateCompatibleCursor(cursorpic);
|
|
|
|
}
|
|
|
|
if (cursor == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Replace the existing cursor with the new one.
|
|
|
|
DestroyCustomCursor();
|
|
|
|
CustomCursor = cursor;
|
|
|
|
atterm(DestroyCustomCursor);
|
2010-09-14 17:28:18 +00:00
|
|
|
}
|
2010-09-26 07:46:34 +00:00
|
|
|
else
|
2010-09-14 17:28:18 +00:00
|
|
|
{
|
2010-09-26 07:46:34 +00:00
|
|
|
DestroyCustomCursor();
|
|
|
|
cursor = LoadCursor(NULL, IDC_ARROW);
|
2010-09-14 17:28:18 +00:00
|
|
|
}
|
|
|
|
SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)cursor);
|
2010-11-19 04:20:33 +00:00
|
|
|
if (NativeMouse)
|
|
|
|
{
|
|
|
|
POINT pt;
|
|
|
|
RECT client;
|
|
|
|
|
|
|
|
// If the mouse pointer is within the window's client rect, set it now.
|
|
|
|
if (GetCursorPos(&pt) && GetClientRect(Window, &client) &&
|
|
|
|
ClientToScreen(Window, (LPPOINT)&client.left) &&
|
|
|
|
ClientToScreen(Window, (LPPOINT)&client.right))
|
|
|
|
{
|
|
|
|
if (pt.x >= client.left && pt.x < client.right &&
|
|
|
|
pt.y >= client.top && pt.y < client.bottom)
|
|
|
|
{
|
|
|
|
SetCursor(cursor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-09-14 17:28:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// CreateCompatibleCursor
|
|
|
|
//
|
|
|
|
// Creates a cursor with a 1-bit alpha channel.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static HCURSOR CreateCompatibleCursor(FTexture *cursorpic)
|
|
|
|
{
|
|
|
|
int picwidth = cursorpic->GetWidth();
|
|
|
|
int picheight = cursorpic->GetHeight();
|
|
|
|
|
|
|
|
// Create bitmap masks for the cursor from the texture.
|
|
|
|
HDC dc = GetDC(NULL);
|
|
|
|
if (dc == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
HDC and_mask_dc = CreateCompatibleDC(dc);
|
|
|
|
HDC xor_mask_dc = CreateCompatibleDC(dc);
|
|
|
|
HBITMAP and_mask = CreateCompatibleBitmap(dc, 32, 32);
|
|
|
|
HBITMAP xor_mask = CreateCompatibleBitmap(dc, 32, 32);
|
|
|
|
ReleaseDC(NULL, dc);
|
|
|
|
|
|
|
|
SelectObject(and_mask_dc, and_mask);
|
|
|
|
SelectObject(xor_mask_dc, xor_mask);
|
|
|
|
|
|
|
|
// Initialize with an invisible cursor.
|
|
|
|
SelectObject(and_mask_dc, GetStockObject(WHITE_PEN));
|
|
|
|
SelectObject(and_mask_dc, GetStockObject(WHITE_BRUSH));
|
|
|
|
Rectangle(and_mask_dc, 0, 0, 32, 32);
|
|
|
|
SelectObject(xor_mask_dc, GetStockObject(BLACK_PEN));
|
|
|
|
SelectObject(xor_mask_dc, GetStockObject(BLACK_BRUSH));
|
|
|
|
Rectangle(xor_mask_dc, 0, 0, 32, 32);
|
|
|
|
|
|
|
|
FBitmap bmp;
|
|
|
|
const BYTE *pixels;
|
|
|
|
|
|
|
|
bmp.Create(picwidth, picheight);
|
|
|
|
cursorpic->CopyTrueColorPixels(&bmp, 0, 0);
|
|
|
|
pixels = bmp.GetPixels();
|
|
|
|
|
|
|
|
// Copy color data from the source texture to the cursor bitmaps.
|
|
|
|
for (int y = 0; y < picheight; ++y)
|
|
|
|
{
|
|
|
|
for (int x = 0; x < picwidth; ++x)
|
|
|
|
{
|
|
|
|
const BYTE *bgra = &pixels[x*4 + y*bmp.GetPitch()];
|
|
|
|
if (bgra[3] != 0)
|
|
|
|
{
|
|
|
|
SetPixelV(and_mask_dc, x, y, RGB(0,0,0));
|
|
|
|
SetPixelV(xor_mask_dc, x, y, RGB(bgra[2], bgra[1], bgra[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DeleteDC(and_mask_dc);
|
|
|
|
DeleteDC(xor_mask_dc);
|
|
|
|
|
|
|
|
// Create the cursor from the bitmaps.
|
|
|
|
return CreateBitmapCursor(cursorpic->LeftOffset, cursorpic->TopOffset, and_mask, xor_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// CreateAlphaCursor
|
|
|
|
//
|
|
|
|
// Creates a cursor with a full alpha channel.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static HCURSOR CreateAlphaCursor(FTexture *cursorpic)
|
|
|
|
{
|
|
|
|
HDC dc;
|
|
|
|
BITMAPV5HEADER bi;
|
|
|
|
HBITMAP color, mono;
|
|
|
|
void *bits;
|
|
|
|
|
|
|
|
memset(&bi, 0, sizeof(bi));
|
|
|
|
bi.bV5Size = sizeof(bi);
|
|
|
|
bi.bV5Width = 32;
|
|
|
|
bi.bV5Height = 32;
|
|
|
|
bi.bV5Planes = 1;
|
|
|
|
bi.bV5BitCount = 32;
|
|
|
|
bi.bV5Compression = BI_BITFIELDS;
|
|
|
|
bi.bV5RedMask = 0x00FF0000;
|
|
|
|
bi.bV5GreenMask = 0x0000FF00;
|
|
|
|
bi.bV5BlueMask = 0x000000FF;
|
|
|
|
bi.bV5AlphaMask = 0xFF000000;
|
|
|
|
|
|
|
|
dc = GetDC(NULL);
|
|
|
|
if (dc == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the DIB section with an alpha channel.
|
|
|
|
color = CreateDIBSection(dc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, &bits, NULL, 0);
|
|
|
|
ReleaseDC(NULL, dc);
|
|
|
|
|
|
|
|
if (color == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create an empty mask bitmap, since CreateIconIndirect requires this.
|
|
|
|
mono = CreateBitmap(32, 32, 1, 1, NULL);
|
|
|
|
if (mono == NULL)
|
|
|
|
{
|
|
|
|
DeleteObject(color);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy cursor to the color bitmap. Note that GDI bitmaps are upside down compared
|
|
|
|
// to normal conventions, so we create the FBitmap pointing at the last row and use
|
|
|
|
// a negative pitch so that CopyTrueColorPixels will use GDI's orientation.
|
|
|
|
FBitmap bmp((BYTE *)bits + 31*32*4, -32*4, 32, 32);
|
|
|
|
cursorpic->CopyTrueColorPixels(&bmp, 0, 0);
|
|
|
|
|
|
|
|
return CreateBitmapCursor(cursorpic->LeftOffset, cursorpic->TopOffset, mono, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// CreateBitmapCursor
|
|
|
|
//
|
|
|
|
// Create the cursor from the bitmaps. Deletes the bitmaps before returning.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static HCURSOR CreateBitmapCursor(int xhot, int yhot, HBITMAP and_mask, HBITMAP color_mask)
|
|
|
|
{
|
|
|
|
ICONINFO iconinfo =
|
|
|
|
{
|
|
|
|
FALSE, // fIcon
|
|
|
|
xhot, // xHotspot
|
|
|
|
yhot, // yHotspot
|
|
|
|
and_mask, // hbmMask
|
|
|
|
color_mask // hbmColor
|
|
|
|
};
|
|
|
|
HCURSOR cursor = CreateIconIndirect(&iconinfo);
|
|
|
|
|
|
|
|
// Delete the bitmaps.
|
|
|
|
DeleteObject(and_mask);
|
|
|
|
DeleteObject(color_mask);
|
|
|
|
|
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// DestroyCustomCursor
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void DestroyCustomCursor()
|
|
|
|
{
|
|
|
|
if (CustomCursor != NULL)
|
|
|
|
{
|
|
|
|
DestroyCursor(CustomCursor);
|
|
|
|
CustomCursor = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_WriteIniFailed
|
|
|
|
//
|
|
|
|
// Display a message when the config failed to save.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool I_WriteIniFailed()
|
2006-09-20 02:00:19 +00:00
|
|
|
{
|
|
|
|
char *lpMsgBuf;
|
|
|
|
FString errortext;
|
|
|
|
|
|
|
|
FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
NULL,
|
|
|
|
GetLastError(),
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
|
|
(LPSTR)&lpMsgBuf,
|
|
|
|
0,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
errortext.Format ("The config file %s could not be written:\n%s", GameConfig->GetPathName(), lpMsgBuf);
|
|
|
|
LocalFree (lpMsgBuf);
|
2009-11-13 03:55:23 +00:00
|
|
|
return MessageBox(Window, errortext.GetChars(), GAMENAME " configuration not saved", MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY;
|
2006-09-20 02:00:19 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_FindFirst
|
|
|
|
//
|
|
|
|
// Start a pattern matching sequence.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void *I_FindFirst(const char *filespec, findstate_t *fileinfo)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
return FindFirstFileA(filespec, (LPWIN32_FIND_DATAA)fileinfo);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2009-11-13 03:55:23 +00:00
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_FindNext
|
|
|
|
//
|
|
|
|
// Return the next file in a pattern matching sequence.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
int I_FindNext(void *handle, findstate_t *fileinfo)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
return !FindNextFileA((HANDLE)handle, (LPWIN32_FIND_DATAA)fileinfo);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_FindClose
|
|
|
|
//
|
|
|
|
// Finish a pattern matching sequence.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
int I_FindClose(void *handle)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-11-13 03:55:23 +00:00
|
|
|
return FindClose((HANDLE)handle);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-12-06 19:15:07 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// QueryPathKey
|
|
|
|
//
|
|
|
|
// Returns the value of a registry key into the output variable value.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2007-12-06 19:15:07 +00:00
|
|
|
static bool QueryPathKey(HKEY key, const char *keypath, const char *valname, FString &value)
|
|
|
|
{
|
2015-08-28 17:14:25 +00:00
|
|
|
HKEY pathkey;
|
2007-12-06 19:15:07 +00:00
|
|
|
DWORD pathtype;
|
|
|
|
DWORD pathlen;
|
|
|
|
LONG res;
|
|
|
|
|
2015-08-28 17:14:25 +00:00
|
|
|
if(ERROR_SUCCESS == RegOpenKeyEx(key, keypath, 0, KEY_QUERY_VALUE, &pathkey))
|
2007-12-06 19:15:07 +00:00
|
|
|
{
|
2015-08-28 17:14:25 +00:00
|
|
|
if (ERROR_SUCCESS == RegQueryValueEx(pathkey, valname, 0, &pathtype, NULL, &pathlen) &&
|
2007-12-06 19:15:07 +00:00
|
|
|
pathtype == REG_SZ && pathlen != 0)
|
|
|
|
{
|
|
|
|
// Don't include terminating null in count
|
|
|
|
char *chars = value.LockNewBuffer(pathlen - 1);
|
2015-08-28 17:14:25 +00:00
|
|
|
res = RegQueryValueEx(pathkey, valname, 0, NULL, (LPBYTE)chars, &pathlen);
|
2007-12-06 19:15:07 +00:00
|
|
|
value.UnlockBuffer();
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
value = "";
|
|
|
|
}
|
|
|
|
}
|
2015-08-28 17:14:25 +00:00
|
|
|
RegCloseKey(pathkey);
|
2007-12-06 19:15:07 +00:00
|
|
|
}
|
|
|
|
return value.IsNotEmpty();
|
|
|
|
}
|
|
|
|
|
2015-08-28 17:14:25 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_GetGogPaths
|
|
|
|
//
|
|
|
|
// Check the registry for GOG installation paths, so we can search for IWADs
|
|
|
|
// that were bought from GOG.com. This is a bit different from the Steam
|
|
|
|
// version because each game has its own independent installation path, no
|
|
|
|
// such thing as <steamdir>/SteamApps/common/<GameName>.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
TArray<FString> I_GetGogPaths()
|
|
|
|
{
|
|
|
|
TArray<FString> result;
|
|
|
|
FString path;
|
|
|
|
FString gamepath;
|
|
|
|
|
|
|
|
#ifdef _WIN64
|
|
|
|
FString gogregistrypath = "Software\\Wow6432Node\\GOG.com\\Games";
|
|
|
|
#else
|
|
|
|
// If a 32-bit ZDoom runs on a 64-bit Windows, this will be transparently and
|
|
|
|
// automatically redirected to the Wow6432Node address instead, so this address
|
|
|
|
// should be safe to use in all cases.
|
|
|
|
FString gogregistrypath = "Software\\GOG.com\\Games";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Look for Ultimate Doom
|
|
|
|
gamepath = gogregistrypath + "\\1435827232";
|
|
|
|
if (QueryPathKey(HKEY_LOCAL_MACHINE, gamepath.GetChars(), "Path", path))
|
|
|
|
{
|
|
|
|
result.Push(path); // directly in install folder
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for Doom II
|
|
|
|
gamepath = gogregistrypath + "\\1435848814";
|
|
|
|
if (QueryPathKey(HKEY_LOCAL_MACHINE, gamepath.GetChars(), "Path", path))
|
|
|
|
{
|
|
|
|
result.Push(path + "/doom2"); // in a subdirectory
|
|
|
|
// If direct support for the Master Levels is ever added, they are in path + /master/wads
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for Final Doom
|
|
|
|
gamepath = gogregistrypath + "\\1435848742";
|
|
|
|
if (QueryPathKey(HKEY_LOCAL_MACHINE, gamepath.GetChars(), "Path", path))
|
|
|
|
{
|
|
|
|
// in subdirectories
|
|
|
|
result.Push(path + "/TNT");
|
|
|
|
result.Push(path + "/Plutonia");
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_GetSteamPath
|
|
|
|
//
|
|
|
|
// Check the registry for the path to Steam, so that we can search for
|
|
|
|
// IWADs that were bought with Steam.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2014-12-12 21:19:37 +00:00
|
|
|
TArray<FString> I_GetSteamPath()
|
2007-12-06 19:15:07 +00:00
|
|
|
{
|
2014-12-12 21:19:37 +00:00
|
|
|
TArray<FString> result;
|
|
|
|
static const char *const steam_dirs[] =
|
|
|
|
{
|
|
|
|
"doom 2/base",
|
|
|
|
"final doom/base",
|
|
|
|
"heretic shadow of the serpent riders/base",
|
|
|
|
"hexen/base",
|
|
|
|
"hexen deathkings of the dark citadel/base",
|
|
|
|
"ultimate doom/base",
|
|
|
|
"DOOM 3 BFG Edition/base/wads",
|
|
|
|
"Strife"
|
|
|
|
};
|
|
|
|
|
2007-12-06 19:15:07 +00:00
|
|
|
FString path;
|
|
|
|
|
2014-12-12 21:19:37 +00:00
|
|
|
if (!QueryPathKey(HKEY_CURRENT_USER, "Software\\Valve\\Steam", "SteamPath", path))
|
2007-12-06 19:15:07 +00:00
|
|
|
{
|
2014-12-12 21:19:37 +00:00
|
|
|
if (!QueryPathKey(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", "InstallPath", path))
|
|
|
|
return result;
|
2007-12-06 19:15:07 +00:00
|
|
|
}
|
2014-12-12 21:19:37 +00:00
|
|
|
path += "/SteamApps/common/";
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i < countof(steam_dirs); ++i)
|
2007-12-06 19:15:07 +00:00
|
|
|
{
|
2014-12-12 21:19:37 +00:00
|
|
|
result.Push(path + steam_dirs[i]);
|
2007-12-06 19:15:07 +00:00
|
|
|
}
|
2014-12-12 21:19:37 +00:00
|
|
|
|
|
|
|
return result;
|
2007-12-06 19:15:07 +00:00
|
|
|
}
|
2008-08-10 03:56:53 +00:00
|
|
|
|
2009-11-13 03:55:23 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_MakeRNGSeed
|
|
|
|
//
|
|
|
|
// Returns a 32-bit random seed, preferably one with lots of entropy.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2009-03-27 04:49:17 +00:00
|
|
|
unsigned int I_MakeRNGSeed()
|
2008-08-10 03:56:53 +00:00
|
|
|
{
|
2009-03-27 04:49:17 +00:00
|
|
|
unsigned int seed;
|
2008-08-10 03:56:53 +00:00
|
|
|
|
2009-03-27 04:49:17 +00:00
|
|
|
// If RtlGenRandom is available, use that to avoid increasing the
|
|
|
|
// working set by pulling in all of the crytographic API.
|
|
|
|
HMODULE advapi = GetModuleHandle("advapi32.dll");
|
|
|
|
if (advapi != NULL)
|
|
|
|
{
|
|
|
|
BOOLEAN (APIENTRY *RtlGenRandom)(void *, ULONG) =
|
|
|
|
(BOOLEAN (APIENTRY *)(void *, ULONG))GetProcAddress(advapi, "SystemFunction036");
|
|
|
|
if (RtlGenRandom != NULL)
|
|
|
|
{
|
|
|
|
if (RtlGenRandom(&seed, sizeof(seed)))
|
|
|
|
{
|
|
|
|
return seed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use the full crytographic API to produce a seed. If that fails,
|
|
|
|
// time() is used as a fallback.
|
|
|
|
HCRYPTPROV prov;
|
|
|
|
|
|
|
|
if (!CryptAcquireContext(&prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
|
|
|
{
|
|
|
|
return (unsigned int)time(NULL);
|
|
|
|
}
|
|
|
|
if (!CryptGenRandom(prov, sizeof(seed), (BYTE *)&seed))
|
|
|
|
{
|
|
|
|
seed = (unsigned int)time(NULL);
|
|
|
|
}
|
|
|
|
CryptReleaseContext(prov, 0);
|
|
|
|
return seed;
|
2008-08-10 03:56:53 +00:00
|
|
|
}
|
2013-08-30 02:16:11 +00:00
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// I_GetLongPathName
|
|
|
|
//
|
|
|
|
// Returns the long version of the path, or the original if there isn't
|
|
|
|
// anything worth changing.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FString I_GetLongPathName(FString shortpath)
|
|
|
|
{
|
2015-04-25 02:26:57 +00:00
|
|
|
static TOptWin32Proc<DWORD (WINAPI*)(LPCTSTR, LPTSTR, DWORD)>
|
2015-04-27 01:53:16 +00:00
|
|
|
GetLongPathNameA("kernel32.dll", "GetLongPathNameA");
|
2015-04-25 02:26:57 +00:00
|
|
|
|
|
|
|
// Doesn't exist on NT4
|
|
|
|
if (GetLongPathName == NULL)
|
|
|
|
return shortpath;
|
|
|
|
|
2015-04-27 01:53:16 +00:00
|
|
|
DWORD buffsize = GetLongPathNameA.Call(shortpath.GetChars(), NULL, 0);
|
2013-08-30 02:16:11 +00:00
|
|
|
if (buffsize == 0)
|
|
|
|
{ // nothing to change (it doesn't exist, maybe?)
|
|
|
|
return shortpath;
|
|
|
|
}
|
|
|
|
TCHAR *buff = new TCHAR[buffsize];
|
2015-04-27 01:53:16 +00:00
|
|
|
DWORD buffsize2 = GetLongPathNameA.Call(shortpath.GetChars(), buff, buffsize);
|
2013-08-30 02:16:11 +00:00
|
|
|
if (buffsize2 >= buffsize)
|
|
|
|
{ // Failure! Just return the short path
|
|
|
|
delete[] buff;
|
|
|
|
return shortpath;
|
|
|
|
}
|
|
|
|
FString longpath(buff, buffsize2);
|
|
|
|
delete[] buff;
|
|
|
|
return longpath;
|
|
|
|
}
|
2015-12-07 09:49:40 +00:00
|
|
|
|
|
|
|
#if _MSC_VER == 1900 && defined(_USING_V110_SDK71_)
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// VS14Stat
|
|
|
|
//
|
|
|
|
// Work around an issue where stat doesn't work with v140_xp. This was
|
|
|
|
// supposedly fixed, but as of Update 1 continues to not function on XP.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
int VS14Stat(const char *path, struct _stat64i32 *buffer)
|
|
|
|
{
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
|
|
|
if(!GetFileAttributesEx(path, GetFileExInfoStandard, &data))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buffer->st_ino = 0;
|
|
|
|
buffer->st_mode = ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR : S_IFREG)|
|
|
|
|
((data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD|S_IWRITE);
|
|
|
|
buffer->st_dev = buffer->st_rdev = 0;
|
|
|
|
buffer->st_nlink = 1;
|
|
|
|
buffer->st_uid = 0;
|
|
|
|
buffer->st_gid = 0;
|
|
|
|
buffer->st_size = data.nFileSizeLow;
|
|
|
|
buffer->st_atime = (*(QWORD*)&data.ftLastAccessTime) / 10000000 - 11644473600LL;
|
|
|
|
buffer->st_mtime = (*(QWORD*)&data.ftLastWriteTime) / 10000000 - 11644473600LL;
|
|
|
|
buffer->st_ctime = (*(QWORD*)&data.ftCreationTime) / 10000000 - 11644473600LL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|