// -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
// ----------------------------------------------------------------------------
// :oCCCCOCoc.
// .cCO8OOOOOOOOO8Oo:
// .oOO8OOOOOOOOOOOOOOOCc
// cO8888: .:oOOOOC. TM
// :888888: :CCCc .oOOOOC. ### ### #########
// C888888: .ooo: .C######## ##### ##### ###### ###### ##########
// O888888: .oO### ### ##### ##### ######## ######## #### ###
// C888888: :8O. .C########## ### #### ### ## ## ## ## #### ###
// :8@@@@8: :888c o### ### #### ### ######## ######## ##########
// :8@@@@C C@@@@ oo######## ### ## ### ###### ###### #########
// cO@@@@@@@@@@@@@@@@@Oc0
// :oO8@@@@@@@@@@Oo.
// .oCOOOOOCc. http://remood.org/
// ----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2008-2013 GhostlyDeath <ghostlydeath@remood.org>
// <ghostlydeath@gmail.com>
// ----------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// ----------------------------------------------------------------------------
// DESCRIPTION: DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
// plus functions to determine game mode (shareware, registered),
// parse command line parameters, configure game parameters (turbo),
// and call the startup functions.
#include "doomtype.h"
#include "g_state.h"
#include "sn.h"
#include "d_player.h"
#include "console.h"
#include "md5.h"
#include "s_sound.h"
#include "w_wad.h"
#include "z_zone.h"
#include "dstrings.h"
#include "d_netcmd.h"
#include "d_prof.h"
#include "d_main.h"
#include "screen.h"
#include "v_video.h"
#include "r_main.h"
#include "f_wipe.h"
#include "r_draw.h"
#include "m_misc.h"
#include "p_local.h"
#include "vhw_wrap.h"
#include "m_argv.h"
#include "d_clisrv.h"
#include "d_rmod.h"
#include "g_game.h"
#include "i_system.h"
#include "i_video.h"
#include "m_menu.h"
#include "p_demcmp.h"
#include "p_info.h"
#include "p_spec.h"
#include "st_stuff.h"
#include "wi_stuff.h"
#include "cl.h"
#include "ui.h"
#include "t_ini.h"
#include "cx.h"
/* Define VideoFont_t */
#if !defined(__REMOOD_VIDEOFONTT_DEFINED)
typedef int VideoFont_t;
#define __REMOOD_VIDEOFONTT_DEFINED
#endif
#if defined(__REMOOD_DEDICATED)
bool_t g_DedicatedServer = true; // Dedicated Server
#else
bool_t g_DedicatedServer = false; // Dedicated Server
#endif
bool_t g_FramePipe = false;
bool_t l_UsingPWADs = false; // Was modifiedgame
//
// DEMO LOOP
//
int demosequence;
tic_t pagetic;
char* pagename = "TITLEPIC";
bool_t novideo = false;
// PROTOS
void D_AdvanceDemo(void);
#ifdef LINUX
void VID_PrepareModeList(void); // FIXME: very dirty; will use a proper include file
#endif
#define MAX_WADFILES 128 // Max passed via -file
char* startupwadfiles[MAX_WADFILES];
bool_t devparm; // started game with -devparm
bool_t nomonsters; // checkparm of -nomonsters
bool_t singletics = false; // timedemo
bool_t nomusic;
bool_t nosound;
bool_t digmusic; // OGG/MP3 Music SSNTails 12-13-2002
bool_t advancedemo;
char wadfile[1024]; // primary wad file
char mapdir[1024]; // directory of development maps
//
// EVENT HANDLING
//
// Events are asynchronous inputs generally generated by the game user.
// Events can be discarded if no responder claims them
// referenced from i_system.c for I_GetKey()
bool_t dedicated;
/* FPS */
static int l_FPSRealTics = 0;
static int l_FPSGameTicRatio = 0;
static int l_FPSTrueFPS = 0;
static int l_FPSRanFPS = 0;
//
// D_ProcessEvents
// Send all the events of the given timestamp down the responder chain
//
void D_ProcessEvents(void)
{
}
//
// D_Display
// draw current display, possibly wiping it from the previous
//
/*#ifdef _WIN32
void I_DoStartupMouse(void); //win_sys.c
#endif*/
// wipegamestate can be set to -1 to force a wipe on the next draw
// added comment : there is a wipe eatch change of the gamestate
G_State_t wipegamestate = GS_DEMOSCREEN;
// g_CVPVVidScreenLink -- Screen Link
const CONL_VarPossibleValue_t c_CVPVVidScreenLink[] =
{
{0, "None"},
{wipe_ColorXForm + 1, "Color"},
{wipe_Melt + 1, "Melt"},
{wipe_Blinds + 1, "Blinds"},
// End
{0, "MINVAL"},
{3, "MAXVAL"},
{0, NULL},
};
// vid_screenlink -- Screen linking effect
CONL_StaticVar_t l_VIDScreenLink =
{
CLVT_INTEGER, c_CVPVVidScreenLink, CLVF_SAVE,
"vid_screenlink", DSTR_CVHINT_VIDSCREENLINK, CLVVT_STRING, "Melt",
NULL
};
// vid_drawfps -- Draw Frames Per Second
CONL_StaticVar_t l_VIDDrawFPS =
{
CLVT_INTEGER, c_CVPVBoolean, CLVF_SAVE,
"vid_drawfps", DSTR_CVHINT_VIDDRAWFPS, CLVVT_STRING, "No",
NULL
};
extern int32_t g_IgnoreWipeTics; // Demo playback, ignore this many wipe tics
/* D_Display() -- Draws the game */
void D_Display(void)
{
#define BUFSIZE 96
char Buf[BUFSIZE];
static bool_t menuactivestate = false;
static G_State_t oldgamestate = -1;
static int borderdrawcount;
tic_t nowtime;
tic_t tics;
tic_t wipestart;
int i;
int y;
int a, b;
int oldviewwidth;
bool_t done;
bool_t wipe;
bool_t redrawsbar, CoolDemo;
bool_t viewactivestate = false;
V_Image_t* PausePic;
int32_t Junk;
#if !defined(__REMOOD_DEDICATED)
UI_BufferSpec_t Spec;
#endif
if (dedicated)
return;
redrawsbar = false;
//added:21-01-98: check for change of screen size (video mode)
if (setmodeneeded)
SCR_SetMode(); // change video mode
if (vid.recalc)
//added:26-01-98: NOTE! setsizeneeded is set by SCR_Recalc()
SCR_Recalc();
// change the view size if needed
if (setsizeneeded)
{
R_ExecuteSetViewSize();
oldgamestate = -1; // force background redraw
borderdrawcount = 3;
redrawsbar = true;
}
// GhostlyDeath <June 16, 2010> -- Only wipe if we set screen link (otherwise cleanup is never done)
// save the current screen if about to wipe
if (!singletics && l_VIDScreenLink.Value->Int && gamestate != wipegamestate)
{
wipe = true;
wipe_StartScreen(0, 0, vid.width, vid.height);
}
else
wipe = false;
/* Retrograde Stub to new UI Code */
#if !defined(__REMOOD_DEDICATED)
// Screen is locked by soft buffer, if needed
// Note that, the drawing code explicitely virtual always calls this
// function which results in multiple relocks.
Spec.Data = I_VideoSoftBuffer(&Spec.w, &Spec.h, &Spec.d, &Spec.p);
Spec.pd = Spec.p * Spec.d;
// Draw into spec
UI_DrawBGLayer(&Spec);
// Unlock buffer
I_GetVideoBuffer(IVS_DONEWITHBUFFER, NULL);
#endif
// draw buffered stuff to screen
// BP: Used only by linux GGI version
I_UpdateNoBlit();
// do buffered drawing
switch (gamestate)
{
case GS_LEVEL:
if (!gametic)
break;
if (wipe || menuactivestate || vid.recalc)
redrawsbar = true;
break;
case GS_INTERMISSION:
WI_Drawer();
break;
case GS_FINALE:
//F_Drawer();
break;
// Lobby
case GS_WAITINGPLAYERS:
SN_DrawLobby();
break;
case GS_NULL:
break;
}
// clean up border stuff
// see if the border needs to be initially drawn
if (gamestate == GS_LEVEL)
{
// GhostlyDeath <October 5, 2012> -- Draw Anti-HOM
//V_DrawColorBoxEx(0, 0, 0, 0, 320, 200);
if (oldgamestate != GS_LEVEL)
{
viewactivestate = false; // view was not active
R_FillBackScreen(); // draw the pattern into the back screen
}
// see if the border needs to be updated to the screen
if (/*(!automapactive || automapoverlay) &&*/ (scaledviewwidth != vid.width))
{
// the menu may draw over parts out of the view window,
// which are refreshed only when needed
if (menuactivestate || !viewactivestate)
borderdrawcount = 3;
if (borderdrawcount)
{
R_DrawViewBorder(); // erase old menu stuff
borderdrawcount--;
}
}
// draw the view directly
CoolDemo = (demoplayback && g_TitleScreenDemo);
// Better render loop
for (i = 0; i < g_SplitScreen + 1; i++)
if (ST_CheckDrawGameView(i))
{
// Calc screen size
ST_CalcScreen(i, &viewwindowx, &viewwindowy, &Junk, &Junk);
// Use certain y lookup
if (g_SplitScreen >= 2)
activeylookup = ylookup4[i];
else if (g_SplitScreen == 1)
{
if (i == 1)
activeylookup = ylookup2;
else
activeylookup = ylookup1;
}
else
activeylookup = ylookup;
// Draw game view
R_RenderPlayerView(P_SpecGetPOV(i), i);
}
// GhostlyDeath <April 25, 2012> -- Extended Status Bar
ST_DrawPlayerBarsEx();
}
// change gamma if needed
if (gamestate != oldgamestate && gamestate != GS_LEVEL)
V_SetPalette(0);
menuactivestate = M_SMFreezeGame();
oldgamestate = wipegamestate = gamestate;
// draw pause pic
if (paused && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
{
PausePic = V_ImageFindA("M_PAUSE", VCP_NONE);
if (PausePic)
V_ImageDraw(0, PausePic, (320 >> 1) - (PausePic->Width >> 1), 10, NULL);
}
//added:24-01-98:vid size change is now finished if it was on...
vid.recalc = 0;
//CON_Drawer();
// GhostlyDeath <July 8, 2009> -- Add FPS Counter
if (l_VIDDrawFPS.Value->Int)
{
//#if 0
i = VFO_TRANS(VEX_TRANS30);
// GhostlyDeath <july 8, 2009> -- Draw FPS
V_DrawCharacterA(VFONT_LARGE, i, '0' + ((l_FPSTrueFPS / 1000) % 10), 320 - 70, 0);
V_DrawCharacterA(VFONT_LARGE, i, '0' + ((l_FPSTrueFPS / 100) % 10), 320 - 60, 0);
V_DrawCharacterA(VFONT_LARGE, i, '0' + ((l_FPSTrueFPS / 10) % 10), 320 - 50, 0);
V_DrawCharacterA(VFONT_LARGE, i, '0' + (l_FPSTrueFPS % 10), 320 - 40, 0);
V_DrawCharacterA(VFONT_LARGE, i, 'F', 320 - 30, 0);
V_DrawCharacterA(VFONT_LARGE, i, 'P', 320 - 20, 0);
V_DrawCharacterA(VFONT_LARGE, i, 'S', 320 - 10, 0);
V_DrawCharacterA(VFONT_OEM, i, '0' + ((l_FPSRealTics / 10) % 10), 320 - 48, 15);
V_DrawCharacterA(VFONT_OEM, i, '0' + (l_FPSRealTics % 10), 320 - 40, 15);
V_DrawCharacterA(VFONT_OEM, i, 'T', 320 - 32, 15);
V_DrawCharacterA(VFONT_OEM, i, 'I', 320 - 24, 15);
V_DrawCharacterA(VFONT_OEM, i, 'C', 320 - 16, 15);
V_DrawCharacterA(VFONT_OEM, i, 'S', 320 - 8, 15);
V_DrawCharacterA(VFONT_OEM, i, '0' + ((l_FPSGameTicRatio / 1000) % 10), 320 - 72, 24);
V_DrawCharacterA(VFONT_OEM, i, '.', 320 - 64, 24);
V_DrawCharacterA(VFONT_OEM, i, '0' + ((l_FPSGameTicRatio / 100) % 10), 320 - 56, 24);
V_DrawCharacterA(VFONT_OEM, i, '0' + ((l_FPSGameTicRatio / 10) % 10), 320 - 48, 24);
V_DrawCharacterA(VFONT_OEM, i, '0' + (l_FPSGameTicRatio % 10), 320 - 40, 24);
V_DrawCharacterA(VFONT_OEM, i, 'G', 320 - 32, 24);
V_DrawCharacterA(VFONT_OEM, i, 'S', 320 - 24, 24);
V_DrawCharacterA(VFONT_OEM, i, '/', 320 - 16, 24);
V_DrawCharacterA(VFONT_OEM, i, 'S', 320 - 8, 24);
//#endif
}
// Simple Networking Draws below everything
SN_Drawer();
// Client Related Stuff
CL_SockDrawer();
// GhostlyDeath <March 22, 2013> -- Draw player text under menus
CONL_DrawConsole(false);
// GhostlyDeath <May 12, 2012> -- Extended UI Draw
M_SMDrawer();
// GhostlyDeath <March 22, 2013> -- Draw big dropped down console over menus
CONL_DrawConsole(true);
// GhostlyDeath <May 5, 2012> -- Update Music
I_UpdateMusic();
/* Retrograde Stub to new UI Code */
#if !defined(__REMOOD_DEDICATED)
// Screen is locked by soft buffer, if needed
// Note that, the drawing code explicitely virtual always calls this
// function which results in multiple relocks.
Spec.Data = I_VideoSoftBuffer(&Spec.w, &Spec.h, &Spec.d, &Spec.p);
Spec.pd = Spec.p * Spec.d;
// Draw into spec
UI_DrawFGLayer(&Spec);
// Unlock buffer
I_GetVideoBuffer(IVS_DONEWITHBUFFER, NULL);
#endif
//I_BeginProfile();
if (!noblit)
I_FinishUpdate(); // page flip or blit buffer
// GhostlyDeath <October 6, 2012> -- Video pipe
if (g_FramePipe)
M_ScreenShotEx(MSSF_FASTPPM, NULL, stdout);
//CONL_PrintF ("last frame update took %d\n", I_EndProfile());
if (!wipe)
{
g_IgnoreWipeTics = 0;
return;
}
//
// wipe update
//
if (!l_VIDScreenLink.Value->Int)
{
wipe = false;
return;
}
// Clear ignoring tic indicator
g_IgnoreWipeTics = 0;
wipe_EndScreen(0, 0, vid.width, vid.height);
// GhostlyDeath <March 10, 2013> -- Run a tic on wipe (for networking)
TryRunTics(0, NULL);
g_IgnoreWipeTics = 1; // And start ignoring wipe tics
wipestart = I_GetTime() - 1;
y = wipestart + 2 * TICRATE; // init a timeout
do
{
do
{
nowtime = I_GetTime();
tics = nowtime - wipestart;
}
while (!tics);
wipestart = nowtime;
done = wipe_ScreenWipe(l_VIDScreenLink.Value->Int - 1, 0, 0, vid.width, vid.height, tics);
// GhostlyDeath <May 5, 2012> -- Update Music
I_UpdateMusic();
// Do other stuff
I_OsPolling();
I_UpdateNoBlit();
M_SMDrawer();
if (!noblit)
I_FinishUpdate(); // page flip or blit buffer
// Appeal to the local timing code
// So that the game does not catch up during wipes!
g_IgnoreWipeTics++;
}
while (!done && nowtime < (unsigned)y);
// GhostlyDeath <June 4, 2010> -- If a wipe never finished 100% we must end if
if (!done)
{
//if (devparm)
// CONL_PrintFUL(SRCSTR__D_MAIN_C__WIPENEVERDONE, L"");
// Force an end
wipe_ScreenWipe(l_VIDScreenLink.Value->Int - 1, 0, 0, vid.width, vid.height, -tics);
}
ST_Invalidate();
#undef BUFSIZE
}
// =========================================================================
// D_DoomLoop
// =========================================================================
tic_t rendergametic, oldentertics;
bool_t supdate;
//#define SAVECPU_EXPERIMENTAL
static bool_t l_FPSPanic = false;
/* D_SetFPSPanicMode() -- Try to get more FPS */
void D_SetFPSPanicMode(const bool_t a_Set)
{
l_FPSPanic = a_Set;
}
void D_DoomLoop(void)
{
tic_t oldentertics, entertic, realtics, rendertimeout = -1;
uint32_t FPSNowTime, FPSLastTime, FPSLastTic = 0;
int32_t MissedRenders = 0;
// GhostlyDeath <January 29, 2013> -- No timing logic
uint32_t EnterTime, LogicTime, LeaveTime;
uint32_t FrameStart, DiffTime, LogicBase, RenderTime;
tic_t TicRunCount, LastRenderTic;
bool_t DoRender, Once, FrameSkip, DidRender;
uint32_t FPSLogics, FPSRenders, FPSVTicLogic;
/* Initialize Time Codes */
TicRunCount = 0;
FrameStart = LogicBase = RenderTime = 0;
FPSRenders = FPSLogics = FPSVTicLogic = 0;
Once = true;
FrameSkip = false;
DidRender = false;
// user settings
//COM_BufAddText("exec autoexec.cfg\n");
// end of loading screen: CONL_PrintF() will no more call FinishUpdate()
con_startup = false;
/*#ifdef _WIN32
CONL_PrintF("I_StartupMouse...\n");
I_DoStartupMouse();
#endif */
oldentertics = I_GetTime();
FPSLastTime = I_GetTimeMS();
// Playing any demos?
if (singledemo)
{
gameaction = ga_nothing;
gamestate = wipegamestate = GS_NULL;
G_PlayNextQ();
}
// Auto start?
else if (NG_IsAutoStart() || SN_IsConnected() || SN_HasSocket())
{
// Do nothing?
}
// Otherwise start the title sequence
else
D_StartTitle();
for (;;)
{
// get real tics
entertic = I_GetTime();
realtics = entertic - oldentertics;
oldentertics = entertic;
#ifdef SAVECPU_EXPERIMENTAL
if (realtics == 0)
{
usleep(10000);
continue;
}
#endif
// GhostlyDeath <August 30, 2011> -- Mouse grabbing
I_DoMouseGrabbing();
// frame syncronous IO operations
// UNUSED for the moment (18/12/98)
I_StartFrame();
// Time before logic
EnterTime = I_GetTimeMS();
// Recount FPS?
if (EnterTime - FrameStart >= UINT32_C(1000))
{
// Set FPS Counter locals
l_FPSRealTics = FPSLogics;
l_FPSTrueFPS = FPSRenders;
// Cap?
if (l_FPSTrueFPS > 999)
l_FPSTrueFPS = 999;
// Reset
FrameStart = EnterTime;
FPSLogics = 0;
FPSRenders = 0;
LastRenderTic = 0; // Force 1FPS always
Once = true;
}
// process tics (but maybe not if realtic==0)
TryRunTics(realtics, &TicRunCount);
FPSLogics += TicRunCount;
FPSVTicLogic += TicRunCount;
// Time when logic completed
LogicTime = I_GetTimeMS();
// Calculate logical seconds being passed
while (FPSVTicLogic >= TICRATE)
{
// Ratio of game seconds to wall seconds
l_FPSGameTicRatio = (LogicTime - LogicBase);
// Processed this amount of time
FPSVTicLogic -= TICRATE;
LogicBase = LogicTime;
}
// Calculate difference
DiffTime = LogicTime - EnterTime;
// Determine if game should be rendererd
DoRender = false;
// Single Tics? Draw every frame
if (singletics)
DoRender = true;
else
{
// Force 1FPS
if (Once)
{
DoRender = true;
Once = false;
}
// Frame Skipping?
else if (FrameSkip)
FrameSkip = false;
// Not frameskipping
else
{
// Rendered enough already?
if (RenderTime - LogicTime < TICSPERMS)
{
// Did a render?
if (DidRender)
DidRender = false;
}
// Enough time to render?
else if (DiffTime < TICSPERMS)
DoRender = true;
}
}
// Draw the screen
if (DoRender)
{
// Render Game
FPSRenders++;
D_Display();
// Set as rendered
DidRender = true;
}
// GhostlyDeath <June 22, 2012> -- Update sounds always, as long as the FPS
// is not in a panic mode. This is here because when the game is paused no
// sounds play because they are never updated!
if (!l_FPSPanic)
{
S_RepositionSounds();
S_UpdateSounds(false); // move positional sounds
}
// Update music
I_UpdateMusic();
// Sound mixing for the buffer is snychronous.
I_UpdateSound();
// Update sound output.
I_SubmitSound();
// Time when all done
LeaveTime = I_GetTimeMS();
// Remember time since last render
if (DoRender)
{
RenderTime = LeaveTime;
// Took too long to render?
if (LeaveTime - LogicTime >= TICSPERMS)
FrameSkip = true;
}
// Sleep
if (!singletics)
{
// Calculate Difference for entire frame
DiffTime = LeaveTime - EnterTime;
// Enough time to sleep?
if (DiffTime < TICSPERMS)
{
I_WaitVBL(TICSPERMS - DiffTime);
// Update network state when leaving loop
//D_XNetUpdate();
}
}
}
}
bool_t g_TitleScreenDemo = false; // Titlescreen demo
// =========================================================================
// D_DoomMain
// =========================================================================
//
// D_StartTitle
//
void D_StartTitle(void)
{
int i;
gameaction = ga_nothing;
for (i = 0; i < MAXSPLITS; i++)
g_Splits[i].Display = g_Splits[i].Console = 0;
demosequence = -1;
paused = false;
gamestate = GS_DEMOSCREEN;
//D_UITitleBump();
}
//
// D_AddFile
//
void D_AddFile(char* file)
{
int numwadfiles;
char* newfile;
for (numwadfiles = 0; startupwadfiles[numwadfiles]; numwadfiles++)
;
newfile = malloc(strlen(file) + 1);
strcpy(newfile, file);
startupwadfiles[numwadfiles] = newfile;
}
/***********************************************
*** NEW IWAD IDENTIFICATION AND LOADING CODE ***
***********************************************/
/*** STRUCTURES ***/
/*** CONSTANTS ***/
const D_IWADInfoEx_t c_IWADInfos[] =
{
/* In order of most wanted to least wanted */
// Doom II: Hell on Earth
{
"Doom II: Hell on Earth",
"doom2",
"doom2\0doomii\0doomtwo\0commercial\0hellonearth\0\0",
"doom2.wad\0\0",
"6ff4def4bd24c6943540c790fbfe2642",
"25e1459ca71d321525f84628f45ca8cd",
"7ec7652fcfce8ddc6e801839291f0e28ef1d5ae7",
"!FREEDOOM\0MAP01\0GRASS1\0MAP16\0MAP31\0MAP32\0!E2M1\0!E2M2\0!E2M3\0!E2M4\0!E2M5\0!E2M6\0!E2M7\0!E2M8\0!E2M9\0!E3M1\0!E3M3\0!E3M3\0!E3M4\0!E3M5\0!E3M6\0!E3M7\0!E3M8\0!E3M9\0!E4M1\0!E4M2\0!E4M3\0!E4M4\0!E4M5\0!E4M6\0!E4M7\0!E4M8\0!E4M9\0\0",
"!FREEDOOM\0\0\0",
14604584,
2919,
CG_DOOM,
false,
"MI_DOOM2",
"RMD_TTD2",
CIF_CANFILE | CIF_REGISTERED | CIF_COMMERCIAL,
"map##",
doom2,
commercial
},
// TNT Evilution
{
"TNT: Evilution",
"tnt",
"tnt\0evilution\0\0",
"tnt.wad\0\0",
"109bf7725eeb8b11cc30cd42c81d9ae4",
"4e158d9953c79ccf97bd0663244cc6b6",
"9fbc66aedef7fe3bae0986cdb9323d2b8db4c9d3",
"!FREEDOOM\0MAP01\0GRASS1\0MAP16\0MAP31\0MAP32\0!E2M1\0!E2M2\0!E2M3\0!E2M4\0!E2M5\0!E2M6\0!E2M7\0!E2M8\0!E2M9\0!E3M1\0!E3M3\0!E3M3\0!E3M4\0!E3M5\0!E3M6\0!E3M7\0!E3M8\0!E3M9\0!E4M1\0!E4M2\0!E4M3\0!E4M4\0!E4M5\0!E4M6\0!E4M7\0!E4M8\0!E4M9\0\0",
"!FREEDOOM\0\0\0",
14604584,
3101,
CG_DOOM,
false,
"MI_TNT",
"RMD_TTD2",
CIF_CANFILE | CIF_REGISTERED | CIF_COMMERCIAL,
"map##",
doom2,
commercial
},
// The Plutonia Experiment
{
"The Plutonia Experiment",
"plutonia",
"plutonia\0theplutoniaexperiment\0plutoniaexperiment\0\0",
"plutonia.wad\0\0",
"7ee851eb6711fa859dd3c649402382d5",
"75c8cf89566741fa9d22447604053bd7",
"90361e2a538d2388506657252ae41aceeb1ba360",
"!FREEDOOM\0MAP01\0GRASS1\0MAP16\0MAP31\0MAP32\0!E2M1\0!E2M2\0!E2M3\0!E2M4\0!E2M5\0!E2M6\0!E2M7\0!E2M8\0!E2M9\0!E3M1\0!E3M3\0!E3M3\0!E3M4\0!E3M5\0!E3M6\0!E3M7\0!E3M8\0!E3M9\0!E4M1\0!E4M2\0!E4M3\0!E4M4\0!E4M5\0!E4M6\0!E4M7\0!E4M8\0!E4M9\0\0",
"!FREEDOOM\0\0\0",
17373080,
2984,
CG_DOOM,
false,
"MI_PLUT",
"RMD_TTD2",
CIF_CANFILE | CIF_REGISTERED | CIF_COMMERCIAL,
"map##",
doom2,
commercial
},
// The Ultimate Doom
{
"The Ultimate Doom",
"ultimatedoom",
"ultimatedoom\0udoom\0doomu\0retail\0thyfleshconsumed\0tfc\0\0",
"doom.wad\0doomu.wad\0ultfdoom.wad\0\0",
"befb2905b2b5df3e43a36e84e920f71f",
"c4fe9fd920207691a9f493668e0a2083",
"9b07b02ab3c275a6a7570c3f73cc20d63a0e3833",
"!FREEDOOM\0E2M1\0E2M2\0E2M3\0E2M4\0E2M5\0E2M6\0E2M7\0E2M8\0E2M9\0E3M1\0E3M3\0E3M3\0E3M4\0E3M5\0E3M6\0E3M7\0E3M8\0E3M9\0DPHOOF\0BFGGA0\0HEADA1\0CYBRA1\0SPIDA1D1\0E4M1\0E4M2\0E4M3\0E4M4\0E4M5\0E4M6\0E4M7\0E4M8\0E4M9\0\0",
"!FREEDOOM\0\0\0",
12408292,
2306,
CG_DOOM,
false,
"MI_DOOM1",
"RMD_TTUD",
CIF_CANFILE | CIF_REGISTERED | CIF_EXTENDED | CIF_DOUBLEWARP,
"e$m#",
doom,
retail
},
// Doom Registered
{
"Doom Registered",
"doom",
"registereddoom\0doomregistered\0doomr\0rdoom\0registered\0\0",
"doom.wad\0doomr.wad\0\0",
"69abda21496c137592f70edb9e3f08fe",
"1cd63c5ddff1bf8ce844237f580e9cf3",
"7742089b4468a736cadb659a7deca3320fe6dcbd",
"!FREEDOOM\0E2M1\0E2M2\0E2M3\0E2M4\0E2M5\0E2M6\0E2M7\0E2M8\0E2M9\0E3M1\0E3M3\0E3M3\0E3M4\0E3M5\0E3M6\0E3M7\0E3M8\0E3M9\0DPHOOF\0BFGGA0\0HEADA1\0CYBRA1\0SPIDA1D1\0!E4M1\0!E4M2\0!E4M3\0!E4M4\0!E4M5\0!E4M6\0!E4M7\0!E4M8\0!E4M9\0\0",
"!FREEDOOM\0\0\0",
11124736,
2194,
CG_DOOM,
false,
"MI_DOOM1",
"RMD_TTD1",
CIF_CANFILE | CIF_REGISTERED | CIF_DOUBLEWARP,
"e$m#",
doom,
retail
},
// Doom Shareware
{
"Doom Shareware",
"sharewaredoom",
"sharewaredoom\0doomshareware\0shareware\0doom1\0kneedeepinthedead\0kditd\0\0",
"doom1.wad\0\0",
"b9e51b0a0174fb0f52f0f641a06164d7",
"f0cefca49926d00903cf57551d901abe",
"5b2e249b9c5133ec987b3ea77596381dc0d6bc1d",
"!FREEDOOM\0!E2M1\0!E2M2\0!E2M3\0!E2M4\0!E2M5\0!E2M6\0!E2M7\0!E2M8\0!E2M9\0!E3M1\0!E3M3\0!E3M3\0!E3M4\0!E3M5\0!E3M6\0!E3M7\0!E3M8\0!E3M9\0!DPHOOF\0!BFGGA0\0!HEADA1\0!CYBRA1\0!SPIDA1D1\0!E4M1\0!E4M2\0!E4M3\0!E4M4\0!E4M5\0!E4M6\0!E4M7\0!E4M8\0!E4M9\0\0",
"!FREEDOOM\0\0\0",
4196020,
1264,
CG_DOOM,
true,
"MI_DOOM1",
"RMD_TTDS",
CIF_SHAREWARE | CIF_DOWNLOADABLE | CIF_DOUBLEWARP,
"e$m#",
doom,
shareware
},
// FreeDoom
{
"FreeDoom",
"freedoom",
"freedoom2\0freedoomii\0freedoomtwo\0freecommercial\0freehellonearth\0\0",
"freedoom.wad\0freedm.wad\0\0",
NULL,
NULL,
NULL,
"FREEDOOM\0MAP01\0GRASS1\0MAP16\0MAP31\0MAP32\0!E2M1\0!E2M2\0!E2M3\0!E2M4\0!E2M5\0!E2M6\0!E2M7\0!E2M8\0!E2M9\0!E3M1\0!E3M3\0!E3M3\0!E3M4\0!E3M5\0!E3M6\0!E3M7\0!E3M8\0!E3M9\0!E4M1\0!E4M2\0!E4M3\0!E4M4\0!E4M5\0!E4M6\0!E4M7\0!E4M8\0!E4M9\0\0",
"FREEDOOM\0\0",
0,
0,
CG_DOOM,
false,
"MI_FDOM2",
"RMD_TTD2",
CIF_CANFILE | CIF_REGISTERED | CIF_COMMERCIAL | CIF_FREEDOOM,
"map##",
doom2,
commercial
},
// Heretic Extended
{
"Heretic: Shadow of the Serpent Riders",
"hereticext",
"hereticssr\0hereticsotsr\0hereticsosr\0\0",
"heretic.wad\0blasphem.wad\0\0",
"29ec38a4b4a0892a70dee3b8c81d7dee:3117e399cdb4298eaa3941625f4b2923",
"66d686b1ed6d35ff103f15dbd30e0341",
"f489d479371df32f6d280a0cb23b59a35ba2b833",
"ADVISOR\0M_HTIC\0BARBACK\0E2M1\0E2M2\0E2M3\0E2M4\0E2M5\0E2M6\0E2M7\0E2M8\0E2M9\0E3M1\0E3M3\0E3M3\0E3M4\0E3M5\0E3M6\0E3M7\0E3M8\0E3M9\0E4M1\0E4M2\0E4M3\0E4M4\0E4M5\0E4M6\0E4M7\0E4M8\0E4M9\0\0E5M1\0E5M2\0E5M3\0E5M4\0E5M5\0E5M6\0E5M7\0E5M8\0E5M9\0\0",
NULL,
0,//12408292,
2633,//2306,
CG_HERETIC,
false,
"MI_HERTC",
"RMD_TTHE",
CIF_CANFILE | CIF_REGISTERED | CIF_EXTENDED | CIF_DOUBLEWARP,
"e$m#",
heretic,
heretic
},
// Heretic Registered
// Heretic Shareware
// Hexen Registered
// Hexen Shareware
/* Last */
{NULL, "indeterminate", NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, CG_DOOM, false, NULL, NULL, 0, "", 0}
};
// l_BlockSums -- Checksums to disallow downloading from
const char* const l_BlockSums[] =
{
/* DOOM IWADS */
//"", // DOOM2 1.666g SS
"d9153ced9fd5b898b36cc5844e35b520", // DOOM2 1.666g MD5
//"", // DOOM2 1.666g SHA1
"242f10836fceb0e422624d6de13b139f", // DOOM2 1.666 SS
"30e3c2d0350b67bfbf47271970b74b2f", // DOOM2 1.666 MD5
"6d559b7ceece4f5ad457415049711992370d520a", // DOOM2 1.666 SHA1
"7784d4247a35bc87991c84f98d93c65e", // DOOM2 1.7 SS
"ea74a47a791fdef2e9f2ea8b8a9da13b", // DOOM2 1.7 MD5
"78009057420b792eacff482021db6fe13b370dcc", // DOOM2 1.7 SHA1
"7789d42b7a35bc80991984f38d97c65f", // DOOM2 1.7a SS
"d7a07e5d3f4625074312bc299d7ed33f", // DOOM2 1.7a MD5
"70192b8d5aba65c7e633a7c7bcfe7e3e90640c97", // DOOM2 1.7a SHA1
"7861decfa520bf778cfe84cc7a7e1d81", // DOOM2 1.8 SS
"c236745bb01d89bbb866c8fed81b6f8c", // DOOM2 1.8 MD5
"79c283b18e61b9a989cfd3e0f19a42ea98fda551", // DOOM2 1.8 SHA1
"6ff4def4bd24c6943540c790fbfe2642", // DOOM2 1.9 SS
"25e1459ca71d321525f84628f45ca8cd", // DOOM2 1.9 MD5
"7ec7652fcfce8ddc6e801839291f0e28ef1d5ae7", // DOOM2 1.9 SHA1
"ef78414fa1e6719f2ccec95174da8a23", // DOOM 1.1 SS
"981b03e6d1dc033301aa3095acc437ce", // DOOM 1.1 MD5
"df0040ccb29cc1622e74ceb3b7793a2304cca2c8", // DOOM 1.1 SHA1
"089b90826946715a24e39c5ed50871f5", // DOOM 1.2 SS
"792fd1fea023d61210857089a7c1e351", // DOOM 1.2 MD5
"b5f86a559642a2b3bdfb8a75e91c8da97f057fe6", // DOOM 1.2 SHA1
//"", // DOOM 1.6b SS
"464e3723a7e7f97039ac9fd057096adb", // DOOM 1.6b MD5
"0a2205d1b97bcc6f3221d3291c83b0e7", // DOOM 1.666 SS
"54978d12de87f162b9bcc011676cb3c0", // DOOM 1.666 MD5
"2e89b86859acd9fc1e552f587b710751efcffa8e", // DOOM 1.666 SHA1
"68aada20496c137493f60edb9f3808ff", // DOOM 1.8 SS
"11e1cd216801ea2657723abc86ecb01f", // DOOM 1.8 MD5
"2c8212631b37f21ad06d18b5638c733a75e179ff", // DOOM 1.8 SHA1
"69abda21496c137592f70edb9e3f08fe", // DOOM 1.9 SS
"1cd63c5ddff1bf8ce844237f580e9cf3", // DOOM 1.9 MD5
"7742089b4468a736cadb659a7deca3320fe6dcbd", // DOOM 1.9 SHA1
"befb2905b2b5df3e43a36e84e920f71f", // ULTIMATE SS
"c4fe9fd920207691a9f493668e0a2083", // ULTIMATE MD5
"9b07b02ab3c275a6a7570c3f73cc20d63a0e3833", // ULTIMATE SHA1
"7ee851eb6711fa859dd3c649402382d5", // PLUTONIA SS
"75c8cf89566741fa9d22447604053bd7", // PLUTONIA MD5
"90361e2a538d2388506657252ae41aceeb1ba360", // PLUTONIA SHA1
"109bf7725eeb8b11cc30cd42c81d9ae4", // TNT SS
"4e158d9953c79ccf97bd0663244cc6b6", // TNT MD5
"9fbc66aedef7fe3bae0986cdb9323d2b8db4c9d3", // TNT SHA1
/* HERETIC IWADS */
"84ca588353e6880718c823198ee81b60", // HERETIC 1.0 SS
"3117e399cdb4298eaa3941625f4b2923", // HERETIC 1.0 MD5
"b5a6cc79cde48d97905b44282e82c4c966a23a87", // HERETIC 1.0 SHA1
"f955093027c473826fb87d5881a80b23", // HERETIC 1.2 SS
"1e4cb4ef075ad344dd63971637307e04", // HERETIC 1.2 MD5
"a54c5d30629976a649119c5ce8babae2ddfb1a60", // HERETIC 1.2 SHA1
"29ec38a4b4a0892a70dee3b8c81d7dee", // HERETIC 1.3 SS
"66d686b1ed6d35ff103f15dbd30e0341", // HERETIC 1.3 MD5
"f489d479371df32f6d280a0cb23b59a35ba2b833", // HERETIC 1.3 SHA1
/* HEXEN IWADS */
"fe2102421d9c1cc6d8d59a2ec870824a", // HEXEN 1.0 SS
"b2543a03521365261d0a0f74d5dd90f0", // HEXEN 1.0 MD5
"ac129c4331bf26f0f080c4a56aaa40d64969c98a", // HEXEN 1.0 SHA1
"423998e1207c104f4ba55f41e4fc5cce", // HEXEN 1.1 SS
"abb033caf81e26f12a2103e1fa25453f", // HEXEN 1.1 MD5
"4b53832f0733c1e29e5f1de2428e5475e891af29", // HEXEN 1.1 SHA1
"b18fca9d5d185ed3a7012fe5914d8960", // HEXEN DK 1.0 SS
"1077432e2690d390c256ac908b5f4efa", // HEXEN DK 1.0 MD5
"c3065527d62b05a930fe75fe8181a64fb1982976", // HEXEN DK 1.0 SHA1
"518e096d7a95e2d1ad946cc4576961c0", // HEXEN DK 1.1 SS
"78d5898e99e220e4de64edaa0e479593", // HEXEN DK 1.1 MD5
"081f6a2024643b54ef4a436a85508539b6d20a1e", // HEXEN DK 1.1 SHA1
/* STRIFE IWADS */
"f62842c1d50d990588599037a1de39a2", // STRIFE 1.0 SS
"8f2d3a6a289f5d2f2f9c1eec02b47299", // STRIFE 1.0 MD5
"eb0f3e157b35c34d5a598701f775e789ec85b4ae", // STRIFE 1.0 SHA1
"d25f1923a6801a23646ea0923ced6bc6", // STRIFE 1.2 SS
"2fed2031a5b03892106e0f117f17901f", // STRIFE 1.2 MD5
"64c13b951a845ca7f8081f68138a6181557458d1", // STRIFE 1.2 SHA1
NULL
};
/*** GLOBALS ***/
CoreGame_t g_CoreGame = CG_UNKNOWN; // Core game mode
const void* g_ReMooDPtr = NULL; // Pointer to remood.wad
const char* g_IWADMapInfoName = NULL; // Name of IWAD MAPINFO
uint32_t g_IWADFlags = 0; // IWAD Flags
uint8_t* g_RandomData = NULL; // Random Data
uint32_t g_RandomDataSize = 0; // Size of random data
/*** LOCALS ***/
static const D_IWADInfoEx_t* l_IWADCur; // Current IWAD
static char l_IWADSum[33]; // IWAD Sum
/*** FUNCTIONS ***/
/* D_GetThisIWAD() -- Get current IWAD */
D_IWADInfoEx_t* D_GetThisIWAD(void)
{
return l_IWADCur;
}
/* D_GetIWADInfoByNum() -- Get IWAD Info by Number */
D_IWADInfoEx_t* D_GetIWADInfoByNum(const uint32_t a_Num)
{
uint32_t i;
/* Look through list */
for (i = 0; c_IWADInfos[i].NiceTitle; i++)
if (i == a_Num)
return &c_IWADInfos[i];
/* Not found */
return NULL;
}
/* D_FieldNumber() -- Get field number from string */
const char* D_FieldNumber(const char* const a_Str, const size_t a_Num)
{
const char* f;
size_t n;
/* Check */
if (!a_Str)
return NULL;
/* Seek around */
f = a_Str;
n = 0;
while (*f)
{
// Match?
if (n == a_Num)
return f;
// Move up
n++;
f += strlen(f) + 1;
}
/* Not found */
return NULL;
}
/* DS_DetectReMooDWAD() -- Detects for ReMooD.WAD */
static bool_t DS_DetectReMooDWAD(const bool_t a_Pushed, const struct WL_WADFile_s* const a_WAD)
{
const WL_WADFile_t* Rover;
const WL_WADEntry_t* Entry;
/* Clear always */
g_ReMooDPtr = NULL;
/* Detect */
Rover = NULL;
while ((Rover = WL_IterateVWAD(Rover, true)))
{
/* Check if it contains VERSION and REMOOD */
if (WL_FindEntry(Rover, 0, "REMOOD") && WL_FindEntry(Rover, 0, "VERSION"))
{
// Set to this WAD and return
g_ReMooDPtr = Rover;
break;
}
}
/* Failed to find remood.wad? */
if (!g_ReMooDPtr)
I_Error("Failed to find ReMooD.WAD");
/* Load Data */
Entry = WL_FindEntry(Rover, 0, "____DATA");
// Not found?
if (!Entry)
{
g_ReMooDPtr = NULL;
return false;
}
// Free
if (g_RandomData)
Z_Free(g_RandomData);
// Load
g_RandomDataSize = Entry->Size;
g_RandomData = Z_Malloc(g_RandomDataSize, PU_STATIC, NULL);
WL_ReadData(Entry, 0, g_RandomData, g_RandomDataSize);
/* Success */
return true;
}
/* DS_DetectGameMode() -- Detects game mode based on pushed WADs */
static bool_t DS_DetectGameMode(const bool_t a_Pushed, const struct WL_WADFile_s* const a_WAD)
{
int32_t* Confidence;
int32_t TotalScore;
size_t NumConf, i, j, Best;
const WL_WADFile_t* BaseWAD;
const char* Field;
const char* ForceName;
bool_t Match;
/* Get the first WAD */
BaseWAD = WL_IterateVWAD(NULL, true);
// No WAD? -- Must have all been popped off then
if (!BaseWAD)
{
if (devparm)
CONL_PrintF("DS_DetectGameMode: Stack empty, there is no game.\n");
g_CoreGame = CG_UNKNOWN;
return true;
}
// Determine if the IWAD changed
for (i = 0; i <= 32; i++)
if (l_IWADSum[i] != BaseWAD->SimpleSumChars[i])
{
if (devparm)
CONL_PrintF("DS_DetectGameMode: IWAD Signature Changed.\n");
g_CoreGame = CG_UNKNOWN;
break;
}
// Is the stack already placed?
if (g_CoreGame != CG_UNKNOWN)
{
if (devparm)
CONL_PrintF("DS_DetectGameMode: Already detected, no need to detect.\n");
return true;
}
// Copy IWAD sum to sum
for (i = 0; i <= 32; i++)
l_IWADSum[i] = BaseWAD->SimpleSumChars[i];
/* Debug */
if (devparm)
CONL_PrintF("DS_DetectGameMode: Detecting game type...\n");
/* Allocate Confidence */
NumConf = (sizeof(c_IWADInfos) / sizeof(D_IWADInfoEx_t)) - 1;
Confidence = Z_Malloc(sizeof(*Confidence) * NumConf, PU_STATIC, NULL);
/* Get forced name */
if (M_CheckParm("-game"))
ForceName = M_GetNextParm();
else
ForceName = NULL;
/* Determine confidence levels */
for (TotalScore = 0, i = 0; i < NumConf; i++)
{
// Conf = 500 :: Forced name
if (ForceName)
for (j = 0;; j++)
{
// Get field
Field = D_FieldNumber(c_IWADInfos[i].ForceNames, j);
// No more?
if (!Field)
break;
// Check based on field
else
if (strcasecmp(Field, ForceName) == 0)
{
Confidence[i] += 500;
TotalScore += 500;
}
}
// Conf = 5 :: Basename vs DOSName
for (j = 0;; j++)
{
// Get field
Field = D_FieldNumber(c_IWADInfos[i].BaseName, j);
// No more?
if (!Field)
break;
// Check based on field
else
if (strcasecmp(Field, BaseWAD->__Private.__DOSName) == 0)
{
Confidence[i] += 5;
TotalScore += 5;
}
}
// Conf = 40 :: Simple sum matches
if (c_IWADInfos[i].SimpleSum)
if (strcasecmp(c_IWADInfos[i].SimpleSum, BaseWAD->SimpleSumChars) == 0)
{
Confidence[i] += 40;
TotalScore += 40;
}
// Conf = 100 :: MD5 sum matches
if (c_IWADInfos[i].MD5Sum)
if (strcasecmp(c_IWADInfos[i].MD5Sum, BaseWAD->CheckSumChars) == 0)
{
Confidence[i] += 40;
TotalScore += 40;
}
// Conf = 15 :: Size
if (c_IWADInfos[i].Size)
if (BaseWAD->__Private.__Size == c_IWADInfos[i].Size)
{
Confidence[i] += 15;
TotalScore += 15;
}
// Conf = 15 :: NumLumps
if (c_IWADInfos[i].NumLumps)
if (BaseWAD->NumEntries == c_IWADInfos[i].NumLumps)
{
Confidence[i] += 15;
TotalScore += 15;
}
// Conf = 1 :: Lumps in WAD
for (j = 0;; j++)
{
// Get field
Field = D_FieldNumber(c_IWADInfos[i].Lumps, j);
// No more?
if (!Field)
break;
// Check based on field
else
{
// If Field starts with !, it is NOT in the WAD
Match = true;
if (Field[0] == '!')
{
Match = false;
Field++; // Remove !
}
// Find in WAD
if ((WL_FindEntry(BaseWAD, 0, Field) != NULL) == Match)
{
Confidence[i] += 1;
TotalScore += 1;
}
}
}
// Conf = 35 :: Unique lumps in WAD
for (j = 0;; j++)
{
// Get field
Field = D_FieldNumber(c_IWADInfos[i].BonusLumps, j);
// No more?
if (!Field)
break;
// Check based on field
else
{
// If Field starts with !, it is NOT in the WAD
Match = true;
if (Field[0] == '!')
{
Match = false;
Field++; // Remove !
}
// Find in WAD
if ((WL_FindEntry(BaseWAD, 0, Field) != NULL) == Match)
{
Confidence[i] += 35;
TotalScore += 35;
}
// Not found
else
{
Confidence[i] -= 35;
TotalScore -= 35;
}
}
}
}
/* Find the best match */
// Look for the best
for (i = 0, Best = 0; i < NumConf; i++)
{
// A nice message
if (devparm)
CONL_PrintF("DS_DetectGameMode: %3i/%-3i: %s\n", Confidence[i], TotalScore, c_IWADInfos[i].NiceTitle);
// Is this the best?
if (Confidence[i] > Confidence[Best])
Best = i;
}
// Set the best
if (devparm)
CONL_PrintF("DS_DetectGameMode: Selecting %s.\n", c_IWADInfos[Best].NiceTitle);
l_IWADCur = &c_IWADInfos[Best];
g_CoreGame = c_IWADInfos[Best].CoreGame;
gamemode = c_IWADInfos[Best].mode;
gamemission = c_IWADInfos[Best].mission;
g_IWADMapInfoName = c_IWADInfos[Best].MapInfoLump;
g_IWADFlags = c_IWADInfos[Best].Flags;
/* Do not convey FreeDoom */
g_IWADFlags &= ~CIF_FREEDOOM;
/* Based on core game, modify generic sounds */
switch (g_CoreGame)
{
// Doom
case CG_DOOM:
S_sfx[sfx_generic_switchon].link = &S_sfx[sfx_swtchn];
S_sfx[sfx_generic_switchoff].link = &S_sfx[sfx_swtchx];
S_sfx[sfx_generic_menupress].link = &S_sfx[sfx_pistol];
S_sfx[sfx_generic_menumove].link = &S_sfx[sfx_pstop];
S_sfx[sfx_generic_menuslide].link = &S_sfx[sfx_stnmov];
S_sfx[sfx_generic_menufail].link = &S_sfx[sfx_oof];
if (g_IWADFlags & CIF_COMMERCIAL)
S_sfx[sfx_generic_chat].link = &S_sfx[sfx_radio];
else
S_sfx[sfx_generic_chat].link = &S_sfx[sfx_tink];
break;
// Heretic
case CG_HERETIC:
S_sfx[sfx_generic_switchon].link = &S_sfx[sfx_dorcls];
S_sfx[sfx_generic_switchoff].link = &S_sfx[sfx_keyup];
S_sfx[sfx_generic_menupress].link = &S_sfx[sfx_dorcls];
S_sfx[sfx_generic_menumove].link = &S_sfx[sfx_switch];
S_sfx[sfx_generic_menuslide].link = &S_sfx[sfx_keyup];
S_sfx[sfx_generic_menufail].link = &S_sfx[sfx_oof];
S_sfx[sfx_generic_chat].link = &S_sfx[sfx_hchat];
break;
default:
break;
}
/* Cleanup */
Z_Free(Confidence);
return true;
}
/* D_BuildMapName() -- Builds map name */
void D_BuildMapName(char* const a_Dest, const size_t a_Len, const int32_t a_Epi, const int32_t a_Map)
{
const char* s;
char* d;
size_t i;
/* Check */
if (!a_Dest || !a_Len || !l_IWADCur)
return;
/* Setup */
s = l_IWADCur->MapNameFormat;
d = a_Dest;
/* Copy slowly */
for (i = 0; *s && i < a_Len;)
{
// Episode
if (*s == '$')
{
*(d++) = (a_Epi % 10) + '0';
s++; // Skip dollar
i++;
}
// Map
else if (*s == '#')
{
// Double map?
if (*(s + 1) == '#')
{
*(d++) = ((a_Map / 10) % 10) + '0';
s++;
i++;
}
// Lowest digit is the same otherwise
*(d++) = (a_Map % 10) + '0';
s++;
i++;
}
// Normal?
else
{
*(d++) = *(s++);
i++;
}
}
}
/* D_CheckWADBlacklist() -- Checks the blacklist */
bool_t D_CheckWADBlacklist(const char* const a_Sum)
{
size_t i;
/* Check */
if (!a_Sum)
return false;
/* Go through list */
for (i = 0; l_BlockSums[i]; i++)
if (!strcasecmp(a_Sum, l_BlockSums[i]))
return true;
/* Not found */
return false;
}
/* D_LoadGameFiles() -- Finds the game data */
void D_LoadGameFilesEx(void)
{
char DiscoveredPath[PATH_MAX];
const char* CheckWAD;
uint8_t OK;
size_t i, j;
const char* Field;
/* Register game identifier, based on pushes */
if (devparm)
CONL_PrintF("D_LoadGameFilesEx: Registering mode detector.\n");
if (!WL_RegisterOCCB(DS_DetectGameMode, WLDCO_IWADDETECT))
I_Error("D_LoadGameFilesEx: Failed to register IWAD OCCB!");
if (!WL_RegisterOCCB(DS_DetectReMooDWAD, WLDCO_RWADDETECT))
I_Error("D_LoadGameFilesEx: Failed to register ReMooD OCCB!");
/* Clear */
OK = 0;
memset(DiscoveredPath, 0, sizeof(DiscoveredPath));
/* Discover an IWAD */
// Via -iwad
if (M_CheckParm("-iwad"))
{
// Get the WAD
CheckWAD = M_GetNextParm();
// OK?
if (CheckWAD)
{
if (WL_LocateWAD(CheckWAD, NULL, DiscoveredPath, PATH_MAX))
OK |= 1;
else
{
// Try the base name of the IWAD
CheckWAD = WL_BaseNameEx(CheckWAD);
if (WL_LocateWAD(CheckWAD, NULL, DiscoveredPath, PATH_MAX))
OK |= 1;
}
}
// No argument passed!?
else
{
if (devparm)
CONL_PrintF("D_LoadGameFilesEx: Pass via -iwad not found\n");
OK &= ~1; // Not OK, missing argument
}
}
// Not found, do standard rotary search
if (!OK)
// For every WAD in the chain
for (i = 0; c_IWADInfos[i].BaseName; i++)
{
for (j = 0;; j++)
{
// Get field
Field = D_FieldNumber(c_IWADInfos[i].BaseName, j);
// No more fields
if (!Field)
break;
// Field was found
else
{
// Devparm here
if (devparm)
CONL_PrintF("D_LoadGameFilesEx: Discovering \"%s\"...\n", Field);
// Do the actual check
if (WL_LocateWAD(Field, NULL, DiscoveredPath, PATH_MAX))
{
OK |= 1;
break;
}
}
}
// Found something
if (OK)
break;
}
// Still not found?
if (!OK)
{
I_Error("D_LoadGameFilesEx: Could not find an IWAD. Please use -iwad to directly locate it, or pass -waddir a path to its location.");
return;
}
/* Prepare IWAD for loading */
// Debug
if (devparm)
CONL_PrintF("D_LoadGameFilesEx: Found IWAD \"%s\".\n", DiscoveredPath);
// Add it to the files to load
D_AddFile(DiscoveredPath);
/* Discover ReMooD.wad */
// Clear OK
OK = 0;
// Via -remoodwad
if (M_CheckParm("-remoodwad"))
{
// Get the WAD
CheckWAD = M_GetNextParm();
// OK?
if (CheckWAD)
if (WL_LocateWAD(CheckWAD, NULL, DiscoveredPath, PATH_MAX))
OK |= 1;
else
{
// Try the base name of the IWAD
CheckWAD = WL_BaseNameEx(CheckWAD);
if (WL_LocateWAD(CheckWAD, NULL, DiscoveredPath, PATH_MAX))
OK |= 1;
}
// Debug
if (devparm)
if (OK)
CONL_PrintF("D_LoadGameFilesEx: Pass via -remoodwad not found\n");
}
// Not found, do standard search
if (!OK)
if (WL_LocateWAD("remood.wad", NULL, DiscoveredPath, PATH_MAX))
OK |= 1;
// Still not found?
if (!OK)
{
I_Error("D_LoadGameFilesEx: Could not find a ReMooD.wad. Please use -remoodwad to directly locate it, or pass -waddir a path to its location.");
return;
}
/* Prepare ReMooD.wad for loading */
// Debug
if (devparm)
CONL_PrintF("D_LoadGameFilesEx: Found ReMooD.wad \"%s\".\n", DiscoveredPath);
// Add it to the files to load
D_AddFile(DiscoveredPath);
}
/***********************************************
************************************************
***********************************************/
#ifdef _WIN32
#define PATHDELIM '\\'
#else
#define PATHDELIM '/'
#endif
/* D_AddPWADs() -- Add PWADs from -file */
// GhostlyDeath <October 24, 2010> -- Greatly improved
void D_AddPWADs(void)
{
char* PWADArg = NULL;
char WADPath[256];
/* Load every -file */
if (M_CheckParm("-file"))
while (M_IsNextParm())
{
// Get it
PWADArg = M_GetNextParm();
// Find it
if (PWADArg)
if (WL_LocateWAD(PWADArg, NULL, WADPath, 256))
{
// Add it
D_AddFile(WADPath);
// Modify Game
l_UsingPWADs = true;
}
}
}
//added:11-01-98:
//
// Center the title string, then add the date and time of compilation.
//
void D_MakeTitleString(char* s)
{
char temp[82];
char* t;
char* u;
int i;
for (i = 0, t = temp; i < 82; i++)
*t++ = ' ';
for (t = temp + (80 - strlen(s)) / 2, u = s; *u != '\0';)
*t++ = *u++;
u = __DATE__;
for (t = temp + 1, i = 11; i--;)
*t++ = *u++;
u = __TIME__;
for (t = temp + 71, i = 8; i--;)
*t++ = *u++;
temp[80] = '\0';
strcpy(s, temp);
}
#define MAXPORTJOYS MAXJOYSTICKS // Max joys supported here (auto)
static int16_t l_JoyLastAxis[MAXPORTJOYS][3];
static uint8_t l_JoyTime[MAXPORTJOYS];
static I_EventEx_t l_JoyKeepEvent[MAXSPLITS];
/* D_JoyPortsEmpty() -- Returns true if all ports are empty */
bool_t D_JoyPortsEmpty(void)
{
size_t i;
/* Run through */
for (i = 0; i < MAXSPLITS; i++)
if (g_Splits[i].JoyBound)
return false; // Bound
/* Ports all empty */
return true;
}
/* D_PortToJoy() -- Converts port to joystick */
uint32_t D_PortToJoy(const uint8_t a_PortID)
{
/* Check */
if (a_PortID < 0 || a_PortID >= MAXSPLITS)
return 0;
/* Only if bound */
if (g_Splits[a_PortID].JoyBound)
return g_Splits[a_PortID].JoyBound;
/* Not Bound */
return 0;
}
/* D_JoyToPort() -- Converts joystick to port */
uint8_t D_JoyToPort(const uint32_t a_PortID)
{
size_t i;
/* Check */
if (a_PortID == 0)
return 0;
/* Look in list */
for (i = 0; i < MAXSPLITS; i++)
if (g_Splits[i].JoyBound)
if (g_Splits[i].JoyID == a_PortID)
return i + 1;
/* Not found */
return 0;
}
/* D_JoySpecialTicker() -- Ticker for joystick specials */
void D_JoySpecialTicker(void)
{
bool_t LastOK;
int i, dx, dy, PWanted;
static tic_t MultiEventTic[MAXSPLITS][2];
uint32_t NumJoys;
uint8_t WantForJoy[MAXSPLITS];
bool_t Wanted;
/* Obtain joy count */
NumJoys = I_NumJoysticks();
// Cap
if (NumJoys >= MAXPORTJOYS)
NumJoys = MAXPORTJOYS;
#if 0
/* Profile */
l_JoyMagicAt = MAXSPLITS;
for (i = 0; i < MAXSPLITS; i++)
// Choose location
if (!D_ScrSplitHasPlayer(i))
if (l_JoyMagicAt == MAXSPLITS)
l_JoyMagicAt = i;
#endif
/* No joysticks? Don't bother */
if (!NumJoys)
return;
/* Send OSK Events multiple times */
// If there are any events and only about every 1/4th of a second
LastOK = D_JoyPortsEmpty();
for (i = 0; i < MAXSPLITS; i++)
if (l_JoyKeepEvent[i].Data.SynthOSK.Down ||
l_JoyKeepEvent[i].Data.SynthOSK.Right ||
l_JoyKeepEvent[i].Data.SynthOSK.Press ||
l_JoyKeepEvent[i].Data.SynthOSK.Cancel ||
l_JoyKeepEvent[i].Data.SynthOSK.Shift)
{
// No joystick bound? (allow player 1 event to transmit)
if ((i > 0 && LastOK) || (!LastOK && !g_Splits[i].JoyBound))
{
// Trash events if no joy is bound
if (!g_Splits[i].JoyBound)
memset(&l_JoyKeepEvent[i], 0, sizeof(l_JoyKeepEvent[i]));
continue;
}
// Not Active
if (!((i == 0 && CONL_IsActive()) ||
CONL_OSKIsActive(i) || M_SMGenSynth(i)))
{
// Trash events to prevent retriggers
memset(&l_JoyKeepEvent[i], 0, sizeof(l_JoyKeepEvent[i]));
continue;
}
// Send
if (MultiEventTic[i][0] == 0)
{
// Push
I_EventExPush(&l_JoyKeepEvent[i]);
// Repeat Delay
MultiEventTic[i][0] = g_ProgramTic + (g_ProgramTic > MultiEventTic[i][1] ? 4 : 8);
// Faster Repeat after hold
if (!MultiEventTic[i][1])
MultiEventTic[i][1] = g_ProgramTic + 40;
}
// Timeout expired
else if (g_ProgramTic >= MultiEventTic[i][0])
MultiEventTic[i][0] = 0;
}
else
MultiEventTic[i][1] = MultiEventTic[i][0] = 0;
/* Alternative Joystick grabbing */
Wanted = false;
for (i = 0; i < NumJoys; i++)
{
// Ignore grabbed joys
if (D_JoyToPort(i + 1))
continue;
// Clear direction
dx = dy = 0;
// X direction is set when a certain range is statified
if (l_JoyLastAxis[i][0] >= 16384)
dx = 1;
else if (l_JoyLastAxis[i][0] <= -16384)
dx = -1;
// Same goes for Y
if (l_JoyLastAxis[i][1] >= 16384)
dy = 1;
else if (l_JoyLastAxis[i][1] <= -16384)
dy = -1;
// Joystick near center or near corner
if ((!dx && !dy) || (dx && dy))
{
// Ran out of time
if (l_JoyTime[i] > 0)
l_JoyTime[i]--;
}
// Joystick in cardinal direction
else
{
// Reached 3 seconds?
if (l_JoyTime[i] >= TICRATE * 3)
{
// Determine player wanted
if (dx == 0 && dy < 0)
PWanted = 0;
else if (dx > 0 && dy == 0)
PWanted = 1;
else if (dx == 0 && dy > 0)
PWanted = 2;
else
PWanted = 3;
// Only bind if not bound by anyone
if (!g_Splits[PWanted].JoyBound)
// And if not already unwanted
if (!WantForJoy[PWanted])
{
// Previously not wanted?
if (!Wanted)
{
// Clear
memset(WantForJoy, 0, sizeof(WantForJoy));
// Set as wanted
Wanted = true;
}
WantForJoy[PWanted] = i + 1;
}
}
// Otherwise, count up
else
l_JoyTime[i]++;
}
}
// If a player is wanted, process in player order so that players
// get the screen they want. Otherwise lower number joys would get lower
// player numbers. However for this to work, they'll need to do it all at
// the same time for this to ever happen. So if they are a tic off, then
// they are out of order.
if (Wanted)
for (i = 0; i < MAXSPLITS; i++)
if (WantForJoy[i])
{
// Add local player (super handled)
//D_NCLocalPlayerAdd(NULL, false, WantForJoy[i], i, true);
// Clear time
l_JoyTime[WantForJoy[i] - 1] = 0;
}
}
/* D_JoySpecialDrawer() -- Draws joy specials */
void D_JoySpecialDrawer(void)
{
#define BUFSIZE 32
int i;
bool_t LastOK;
int32_t tX, tY;
char* TextString;
char Buf[BUFSIZE];
uint32_t NumJoys;
uint8_t r, g, b;
int32_t x, y, fb, nb;
/* Obtain joy count */
NumJoys = I_NumJoysticks();
// Cap
if (NumJoys >= MAXPORTJOYS)
NumJoys = MAXPORTJOYS;
/* No joysticks? Don't bother */
if (!NumJoys)
return;
/* Do not draw if no menu of sort is active */
// Also don't draw if we -playdemo
LastOK = CONL_IsActive() || M_SMMenuVisible();
if (!LastOK && (G_GetDemoExplicit() || (!demoplayback && gamestate == GS_LEVEL)))
return;
#define BOXSHIFT 11
#define BOXSIZE (32768 >> (BOXSHIFT - 1))
#define BOXXPOS (80 - (BOXSIZE >> 1))
#define BOXYPOS (200 - BOXSIZE - 8)
#define BOXCENTERX (BOXXPOS + (BOXSIZE >> 1))
#define BOXCENTERY (BOXYPOS + (BOXSIZE >> 1))
/* Alternative Chooser Thing */
// GhostlyDeath <November 26, 2012> -- Much better than my previous
// JOY MOVE + 1 + 2. You just move the stick in said direction twords which
// game you want to control and there you have it.
// Draw for each joystick
r = 255;
g = b = 0;
LastOK = false;
fb = BOXXPOS + BOXSIZE + 4;
for (i = 0; i < NumJoys; i++)
{
// Not bound?
// If bound:
// Just don't draw it, but shift colors to avoid confusion
// "Why did my yellow dot turn red?"
if (!(D_JoyToPort(i + 1)))
{
// Was never drawn
if (!LastOK)
{
// Draw box underneath (where all the pretty stuff goes)
VHW_HUDDrawBox(0, 255, 255, 255,
BOXXPOS - 2, BOXYPOS - 2,
BOXXPOS + BOXSIZE + 2, BOXYPOS + BOXSIZE + 2
);
// Draw directions
V_DrawStringA(VFONT_OEM, 0, DS_GetString(DSTR_DMAINC_JOYINSTRUCT), BOXXPOS + BOXSIZE + 24, BOXYPOS);
// Draw P1, P2, P3, P4...
// P1
if (!g_Splits[0].JoyBound)
V_DrawStringA(VFONT_OEM, 0, DS_GetString(DSTR_DMAINC_PLAYER1), BOXCENTERX - 4, BOXCENTERY - (BOXSIZE >> 1) - 10);
// P2
if (!g_Splits[1].JoyBound)
V_DrawStringA(VFONT_OEM, 0, DS_GetString(DSTR_DMAINC_PLAYER2), BOXCENTERX + (BOXSIZE >> 1) + 2, BOXCENTERY - 4);
// P3
if (!g_Splits[2].JoyBound)
V_DrawStringA(VFONT_OEM, 0, DS_GetString(DSTR_DMAINC_PLAYER3), BOXCENTERX - 4, BOXCENTERY + (BOXSIZE >> 1) + 2);
// P4
if (!g_Splits[3].JoyBound)
V_DrawStringA(VFONT_OEM, 0, DS_GetString(DSTR_DMAINC_PLAYER4), BOXCENTERX - (BOXSIZE >> 1) - 18, BOXCENTERY - 4);
// Set as drawn
LastOK = true;
}
// Location to draw box
x = BOXCENTERX + (l_JoyLastAxis[i][0] >> BOXSHIFT);
y = BOXCENTERY + (l_JoyLastAxis[i][1] >> BOXSHIFT);
// Draw position box thing
VHW_HUDDrawBox(0, r, g, b, x - 2, y - 2, x + 2, y + 2);
// Draw Fill bar
if (l_JoyTime[i] > 0)
{
// Destination
nb = fb + (l_JoyTime[i] / 5);
// Draw box
VHW_HUDDrawBox(0, r, g, b,
fb, (BOXYPOS + BOXSIZE) - 8, nb, (BOXYPOS + BOXSIZE)
);
// Increase bar
fb = nb;;
}
}
// New Color
if (r && !g && !b)
g = 255;
else if (r && g && !b)
r = 0;
else if (!r && g && !b)
b = 255;
else if (!r && g && b)
g = 0;
else if (!r && !g && b)
r = 255;
else if (r && !g && b)
b = 0;
}
#undef BOXSHIFT
#undef BOXSIZE
#undef BOXXPOS
#undef BOXYPOS
#undef BOXCENTERX
#undef BOXCENTERY
#undef BUFSIZE
}
/* D_JoySpecialEvent() -- Specially Handles Joystick Events */
bool_t D_JoySpecialEvent(const I_EventEx_t* const a_Event)
{
uint8_t ForPlayer, RealPlayer, JoyID;
int8_t TrueVal;
bool_t Changed;
uint32_t NumJoys;
int32_t i;
/* Not a joystick event? */
if (a_Event->Type != IET_JOYSTICK)
return false;
/* Obtain joy count */
NumJoys = I_NumJoysticks();
// Cap
if (NumJoys >= MAXPORTJOYS)
NumJoys = MAXPORTJOYS;
/* Get ID */
JoyID = a_Event->Data.Joystick.JoyID;
// Out of bounds?
if (JoyID >= NumJoys)
return false;
/* Determine Player to Handle */
if (D_JoyPortsEmpty())
ForPlayer = MAXSPLITS + 1;
else
{
// See if the joystick is bound to a port first
ForPlayer = D_JoyToPort(JoyID + 1);
// If it isn't bound to any port, steal the last port
if (!ForPlayer)
if (g_SplitScreen < 3)
ForPlayer = g_SplitScreen + 1;
else
return false; // Does not belong to anyone, so ignore
else
ForPlayer--;
}
// Convert to real player
if (ForPlayer == (MAXSPLITS + 1))
RealPlayer = 0;
else
RealPlayer = ForPlayer;
/* Alternative Chooser Thing */
// Go through all joys
for (i = 0; i < NumJoys; i++)
{
// Ignore Bound Sticks
if (D_JoyToPort(i + 1))
continue;
// Place axis info into last info
if (a_Event->Data.Joystick.Axis > 0)
if (a_Event->Data.Joystick.Axis < 3)
l_JoyLastAxis[JoyID][a_Event->Data.Joystick.Axis - 1] = a_Event->Data.Joystick.Value;
// Place buttons also
if (a_Event->Data.Joystick.Button > 0)
if (a_Event->Data.Joystick.Button < 3)
if (!a_Event->Data.Joystick.Down)
l_JoyLastAxis[JoyID][2] = 0;
else
l_JoyLastAxis[JoyID][2] |= 1 << (a_Event->Data.Joystick.Button - 1);
}
/* Synthetic OSK Events */
if (ForPlayer == (MAXSPLITS + 1) || g_Splits[RealPlayer].JoyBound)
// Only if a menu is active, console, chat string, etc.
if (((RealPlayer == 0 && CONL_IsActive()) ||
CONL_OSKIsActive(RealPlayer) ||
M_SMGenSynth(RealPlayer) ||
(gamestate != GS_LEVEL && gamestate != GS_INTERMISSION)))
{
// Clear Event
l_JoyKeepEvent[RealPlayer].Type = IET_SYNTHOSK;
l_JoyKeepEvent[RealPlayer].Data.SynthOSK.SNum = RealPlayer;
Changed = false;
// Up/Down Movement?
if (a_Event->Data.Joystick.Axis == 1 || a_Event->Data.Joystick.Axis == 2)
{
// Get true value
if (a_Event->Data.Joystick.Value >= 16384)
TrueVal = 1;
else if (a_Event->Data.Joystick.Value <= -16384)
TrueVal = -1;
else
TrueVal = 0;
// Down?
if (a_Event->Data.Joystick.Axis == 2)
l_JoyKeepEvent[RealPlayer].Data.SynthOSK.Down = TrueVal;
else
l_JoyKeepEvent[RealPlayer].Data.SynthOSK.Right = TrueVal;
// Change
Changed = true;
}
// Buttons?
if (a_Event->Data.Joystick.Button)
switch (a_Event->Data.Joystick.Button)
{
// Trigger
case 1:
l_JoyKeepEvent[RealPlayer].Data.SynthOSK.Press = a_Event->Data.Joystick.Down;
Changed = true;
break;
// Cancel
case 2:
l_JoyKeepEvent[RealPlayer].Data.SynthOSK.Cancel = a_Event->Data.Joystick.Down;
Changed = true;
break;
// Shift
case 3:
l_JoyKeepEvent[RealPlayer].Data.SynthOSK.Shift = a_Event->Data.Joystick.Down;
Changed = true;
break;
// Unknown
default:
break;
}
// Was eaten, so steal it
//if (Changed)
return true;
//CONL_PrintF("Synth\n");
// Menu Button
//if (a_Event->Data.Joystick.Button)
//a_Event->Data.Joystick.Button
}
/* Not Handled */
return false;
}
extern wbstartstruct_t wminfo;
/* D_DoomMain() -- Main Doom Code */
void D_DoomMain(void)
{
int i;
int p;
char file[256];
char legacy[82]; //added:18-02-98: legacy title banner
char title[82]; //added:11-01-98:moved, doesn't need to be global
int startepisode;
int startmap;
bool_t autostart;
D_Prof_t* GuestProf;
char* PWADArg = NULL;
char WADPath[256];
char* v;
// GhostlyDeath <December 12, 2013> -- X Branch
if (M_CheckParm("-x"))
{
C_XMain();
return;
}
// GhostlyDeath <November 18, 2008> -- Move devparm up here
devparm = M_CheckParm("-devparm");
g_QuietConsole = M_CheckParm("-quiet");
// GhostlyDeath <January 15, 2012> -- Check for dedicated server
#if !defined(__REMOOD_DEDICATED)
g_DedicatedServer = M_CheckParm("-dedicated");
#else
g_DedicatedServer = true;
#endif
// GhostlyDeath <October 6, 2012> -- Frame Pipe
g_FramePipe = M_CheckParm("-videopipe");
noblit = M_CheckParm("-noblit");
// Replace old variable
dedicated = g_DedicatedServer;
// GhostlyDeath <July 6, 2008> -- initialize fields
memset(player_names, 0, sizeof(player_names));
memset(team_names, 0, sizeof(team_names));
memset(players, 0, sizeof(players));
memset(g_Splits, 0, sizeof(g_Splits));
memset(&wminfo, 0, sizeof(wminfo));
for (i = 0; i < MAXPLAYERS; i++)
{
sprintf(player_names[i], "Player %i", i + 1);
sprintf(team_names[i], "Team %i", i + 1);
}
if (M_CheckParm("-novideo"))
novideo = true;
//added:18-02-98:keep error messages until the final flush(stderr)
//if (setvbuf(stderr, NULL, _IOFBF, 1000))
// CONL_PrintF("setvbuf didnt work\n");
// get parameters from a response file (eg: doom3 @parms.txt)
M_FindResponseFile();
/*** New Initialization ***/
/* Core */
CONL_PrintF("Initializing the memory manager...\n");
Z_Init();
// Game Model to use
D_InitModelMode();
// Clear global profile list
for (i = 0; i < MAXPROFCONST; i++)
g_ProfList[i] = NULL;
// Profiles are considered somewhat core
GuestProf = D_CreateProfileEx("guest"); // Create guest account
GuestProf->Flags |= DPEXF_DONTSAVE; // Never save guest account
// Initialize Console
CONL_Init(4096, 1024);
// GhostlyDeath <December 14, 2011> -- Use extended identify version
D_LoadGameFilesEx();
/* Start Graphics REALLY early! */
VHW_Init(VHWMODE_IDXSOFT); // Just in case!
SCR_SetDefaultMode(); // Screen Size
CONL_PrintF("I_StartupGraphics...\n");
I_StartupGraphics();
// Make the console "started"
con_started = true;
// Network needs to be initialized very early, otherwise KABOOM!
CONL_PrintF("I_InitNetwork...\n");
I_InitNetwork();
// Initialize Buffers
SCR_Startup();
SCR_ReclassBuffers();
g_EarlyBootConsole = true;
/* Adapters */
#if !defined(__REMOOD_DEDICATED)
UI_Init(); // Initialize UI
#endif
CL_InitViews(); // Initialize views
V_ImageFindA(NULL, 0); // Bump image loaders
D_InitRMOD(); // Initialize RMOD
V_MapGraphicalCharacters(); // Unicode chars
P_PrepareLevelInfoEx(); // Level information
R_LoadTextures(); // Load texture info
P_ExtraSpecialStuff(); // Initialize extra special stuff
P_XGSRegisterStuff(); // Extended Game Settings stuff
//M_CheatInit(); // Initialize Cheats
ST_InitEx(); // Extended Status Bar
WL_Init(); // Initialize WL Code
M_SMInit(); // Simple Menus
G_PrepareDemoStuff(); // Demos
//B_InitBotCodes(); // Initialize bot coding
//D_CheckNetGame(); // initialize net game
BOT_Init(); // Initialize bots
/**************************/
nomonsters = M_CheckParm("-nomonsters");
//added:11-01-98:center the string, add compilation time and date.
sprintf(legacy, "ReMooD v%i.%i%c \"%s\"", REMOOD_MAJORVERSION, REMOOD_MINORVERSION, REMOOD_RELEASEVERSION, REMOOD_VERSIONCODESTRING);
D_MakeTitleString(legacy);
CONL_PrintF("%s\n", legacy);
if (devparm)
CONL_PrintF("Development mode activated!\n");
if (M_CheckParm("-file"))
// the parms after p are wadfile/lump names,
// until end of parms or another - preceded parm
D_AddPWADs();
// load dehacked file
p = M_CheckParm("-dehacked");
if (!p)
p = M_CheckParm("-deh"); //Fab:02-08-98:like Boom & DosDoom
if (p != 0)
while (M_IsNextParm())
D_AddFile(M_GetNextParm());
// GhostlyDeath <October 6, 2012> -- Force single tics
singletics = 0;
singletics = M_CheckParm("-singletics");
// GhostlyDeath <June 18, 2012> -- Demo Queues (woo!)
singletics |= M_CheckParm("-timedemo");
if (M_CheckParm("-playdemo") || M_CheckParm("-timedemo"))
while (M_IsNextParm())
{
// Get it
PWADArg = M_GetNextParm();
// See if we can find it on the disk
if (PWADArg)
{
// Get argument and strip @ (direct decoder choice)
memset(file, 0, sizeof(file));
strncpy(file, PWADArg, 255);
v = strchr(file, '@');
if (v)
*v = 0;
// It was found, add it
if (WL_LocateWAD(file, NULL, WADPath, 256))
{
// Add it
D_AddFile(WADPath);
// Modify Game
l_UsingPWADs = true;
}
// Must be an internal demo then
else
{
}
// Add to queue
G_DemoQueue(WL_BaseNameEx(PWADArg));
singledemo = true;
}
}
// load wad, including the main wad file
CONL_PrintF("Initializing the Lite-WAD Subsystem...\n");
// Start WADs
if (W_InitMultipleFiles(startupwadfiles) == 0)
I_Error("A WAD file was not found\n");
//---------------------------------------------------- READY SCREEN
//printf("\nI_StartupComm...");
CONL_PrintF("I_StartupTimer...\n");
I_StartupTimer();
I_StartupMouse ();
I_StartupKeyboard();
I_InitJoystick();
CONL_PrintF("I_StartupKeyboard...\n");
I_StartupKeyboard();
CL_InitSocks(); // Initialize sockets here, for joysticks
T_AddCommands();
R_RegisterEngineStuff();
S_RegisterSoundStuff();
CONL_VarRegister(&l_VIDScreenLink);
CONL_VarRegister(&l_VIDDrawFPS);
//Fab:29-04-98: do some dirty chatmacros strings initialisation
//--------------------------------------------------------- CONFIG.CFG
VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen
// set user default mode or mode set at cmdline
SCR_CheckDefaultMode();
wipegamestate = gamestate;
//------------------------------------------------ COMMAND LINE PARAMS
#if 0
if (M_CheckParm("-respawn"))
COM_BufAddText("respawnmonsters 1\n");
if (M_CheckParm("-teamplay"))
COM_BufAddText("teamplay 1\n");
if (M_CheckParm("-teamskin"))
COM_BufAddText("teamplay 2\n");
//if (M_CheckParm("-splitscreen"))
// CV_SetValue(&cv_splitscreen, 1);
if (M_CheckParm("-altdeath"))
COM_BufAddText("deathmatch 2\n");
else if (M_CheckParm("-deathmatch"))
COM_BufAddText("deathmatch 1\n");
if (M_CheckParm("-fast"))
COM_BufAddText("fastmonsters 1\n");
if (M_CheckParm("-predicting"))
COM_BufAddText("predictingmonsters 1\n"); //added by AC
if (M_CheckParm("-timer"))
{
char* s = M_GetNextParm();
COM_BufAddText(va("timelimit %s\n", s));
}
if (M_CheckParm("-avg"))
{
COM_BufAddText("timelimit 20\n");
}
// turbo option, is not meant to be saved in config, still
// supported at cmd-line for compatibility
if (M_CheckParm("-turbo") && M_IsNextParm())
COM_BufAddText(va("turbo %s\n", M_GetNextParm()));
#endif
CONL_PrintF("Initializing the renderer state...\n");
R_Init();
//
// setting up sound
//
CONL_PrintF("Initializing sound...\n");
nosound = M_CheckParm("-nosound");
nomusic = M_CheckParm("-nomusic"); // WARNING: DOS version initmusic in I_StartupSound
digmusic = M_CheckParm("-digmusic"); // SSNTails 12-13-2002
S_Init(-1, -1);
CONL_PrintF("Initializing the HUD...\n");
ST_Init();
////////////////////////////////
// SoM: Init FraggleScript
////////////////////////////////
T_Init();
/* Setup Screen */
// Moved to much later on
g_EarlyBootConsole = false;
SCR_SetMode(); // change video mode
SCR_Recalc();
/* Initial Game Setup */
// Hopefuly this is correct!
// Reset game variables
// All settings assume defaults, of course
NG_ResetVars();
// Load from command line
// Stuff like -warp, etc.
NG_FromCLine();
// Recording Demo?
if (M_CheckParm("-record"))
if (M_IsNextParm())
{
PWADArg = M_GetNextParm();
G_BeginRecording(PWADArg, (M_IsNextParm() ? M_GetNextParm() : "remood"));
NG_SetAutoStart(true);
}
// Initialize server
SN_ServerInit();
// Warp to map and reset new vars
if (NG_IsAutoStart())
NG_ResetVars();
// Process all + parms
// Commands like other things
M_PushSpecialParameters();
// ++ args are done at the first gametic
#if 0
// start the apropriate game based on parms
p = M_CheckParm("-record");
if (p && p < myargc - 1)
{
G_RecordDemo(myargv[p + 1]);
autostart = true;
}
// demo doesn't need anymore to be added with D_AddFile()
p = M_CheckParm("-playdemo");
if (!p)
p = M_CheckParm("-timedemo");
if (p && M_IsNextParm())
{
char tmp[MAX_WADPATH];
// add .lmp to identify the EXTERNAL demo file
strcpy(tmp, M_GetNextParm());
// get spaced filename or directory
while (M_IsNextParm())
{
strcat(tmp, " ");
strcat(tmp, M_GetNextParm());
}
#if 0
// GhostlyDeath <July 6, 2008> -- Enable playback of internal demos again
if (W_CheckNumForName(tmp) == INVALIDLUMP)
FIL_DefaultExtension(tmp, ".lmp");
#endif
CONL_PrintF("Playing demo %s.\n", tmp);
if ((p = M_CheckParm("-playdemo")))
{
singledemo = true; // quit after one demo
G_DeferedPlayDemo(tmp);
}
else
G_TimeDemo(tmp);
gamestate = wipegamestate = GS_NULL;
return;
}
p = M_CheckParm("-loadgame");
if (p && p < myargc - 1)
{
G_LoadGame(atoi(myargv[p + 1]));
}
else
{
if (autostart || netgame || M_CheckParm("+connect") || M_CheckParm("-connect"))
{
//added:27-02-98: reset the current version number
gameaction = ga_nothing;
//COM_BufAddText(va("map \"%s\"\n", G_BuildMapName(startepisode, startmap)));
}
else
D_StartTitle(); // start up intro loop
}
#endif
}
/****************
*** FUNCTIONS ***
****************/
// GhostlyDeath <December 1, 2012> -- Default System Model
#if !defined(__REMOOD_MODEL)
#define __REMOOD_MODEL_S "default"
#elif (__REMOOD_MODEL == 1)
#define __REMOOD_MODEL_S "gcw"
#elif (__REMOOD_MODEL == 2)
#define __REMOOD_MODEL_S "wii"
#endif
D_ModelMode_t g_ModelMode = 0; // Model to use
/* D_InitModelMode() -- Hardware specific features */
void D_InitModelMode(void)
{
const char* Input;
/* Determine input */
// Clear
Input = NULL;
// Command line override?
if (M_CheckParm("-model"))
if (M_IsNextParm())
Input = M_GetNextParm();
// Use specified default
Input = __REMOOD_MODEL_S;
/* Which Model is used? */
if (!strcasecmp(Input, "wii"))
g_ModelMode = DMM_WII;
else if (!strcasecmp(Input, "gcw"))
g_ModelMode = DMM_GCW;
else
g_ModelMode = DMM_DEFAULT;
}
/*********************************
*** CUSTOMIZED TITLE SEQUENCES ***
*********************************/
#if !defined(__REMOOD_DEDICATED)
typedef struct D_TitleSeq_s D_TitleSeq_t;
/* D_TitleSeq_t -- Title Sequence Info */
struct D_TitleSeq_s
{
char Pic[WLMAXENTRYNAME]; // Picture to show
char Music[WLMAXENTRYNAME]; // Music to play
char Demo[WLMAXENTRYNAME]; // Demo to play
uint32_t Tic; // Tics sequence should last
D_TitleSeq_t* Prev; // Prev Sequence
D_TitleSeq_t* Next; // Next sequence
};
static D_TitleSeq_t* l_TTSeqHead = NULL; // First sequence
static D_TitleSeq_t* l_TTSeqAt = NULL; // Current sequence at
static UI_Img_t* l_TTPic = NULL; // Title Pic
static int32_t l_TTTicker = 0; // Title Ticker
static bool_t l_TTBump = false; // Do title bump
/* D_UITitleDepthChange() -- Depth Change */
void D_UITitleDepthChange(void)
{
l_TTPic = NULL;
}
/* D_UITitleInitScreen() -- Initializes title screen */
bool_t D_UITitleInitScreen(void)
{
/* No current sequence? */
if (!l_TTSeqAt)
return false;
/* Attempt playing demo */
if (l_TTSeqAt->Demo[0])
{
// Demos last a long time
l_TTTicker = 9999999;
// Attempt play of demo
if (G_DoPlayDemo(l_TTSeqAt->Demo, true))
return false; // Demo did play
// Otherwise fail, set a short timer
l_TTTicker = 1;
return false;
}
/* Switch title page */
// Tic time
l_TTTicker = l_TTSeqAt->Tic;
// Prevent infinitely long pages
if (l_TTTicker <= 0)
l_TTTicker = 1;
// Change music
if (l_TTSeqAt->Music[0])
S_ChangeMusicName(l_TTSeqAt->Music, false);
// Delete old picture
if (l_TTPic)
{
UI_ImgDelete(l_TTPic);
l_TTPic = NULL;
}
// Change picture
//if (l_TTSeqAt->Pic[0])
// l_TTPic = UI_ImgLoadEntS(l_TTSeqAt->Pic);
// Reached end
return true;
}
/* D_UILoadTitles() -- Loads title screen data */
void D_UILoadTitles(void)
{
D_IWADInfoEx_t* Info;
D_TitleSeq_t* New, *Tail;
WL_WADEntry_t* Ent;
WL_ES_t* Stream;
TINI_Section_t* CurSect;
TINI_ConfigLine_t* ConfLine;
char* Opt, *Val;
/* Clear loaded image, would have been bleh */
// Would have been destroyed on new WAD load
l_TTPic = NULL;
/* Clear old sequences */
while (l_TTSeqHead)
{
New = l_TTSeqHead->Next;
Z_Free(l_TTSeqHead);
l_TTSeqHead = New;
}
// Current position
l_TTSeqAt = NULL;
/* Get current IWAD */
if (!(Info = D_GetThisIWAD()))
return;
/* If there is no lump specified, ignore */
if (!Info->TitleScreenLump)
return;
/* Attempt open of title sequences lump */
if (!(Ent = WL_FindEntry(NULL, 0, Info->TitleScreenLump)))
return;
/* Open stream, if possible */
if (!(Stream = WL_StreamOpen(Ent)))
return;
/* Begin */
// Unicode
WL_StreamCheckUnicode(Stream);
// Read each section
New = Tail = NULL;
for (CurSect = TINI_FindNextSection(NULL, Stream); CurSect; CurSect = CurSect->Next)
{
// Start reading section
ConfLine = TINI_BeginRead(CurSect);
// Create new link
New = Z_Malloc(sizeof(*New), PU_STATIC, NULL);
New->Prev = Tail;
if (!Tail)
l_TTSeqHead = New;
else
Tail->Next = New;
Tail = New;
// Read configuration lines
while (TINI_ReadLine(ConfLine, &Opt, &Val))
{
// Picture
if (!strcasecmp(Opt, "pic"))
strncpy(New->Pic, Val, WLMAXENTRYNAME);
// Music
else if (!strcasecmp(Opt, "music"))
strncpy(New->Music, Val, WLMAXENTRYNAME);
// Demo
else if (!strcasecmp(Opt, "demo"))
strncpy(New->Demo, Val, WLMAXENTRYNAME);
// Tic
else if (!strcasecmp(Opt, "tic"))
New->Tic = C_strtou32(Val, NULL, 10);
}
// End reading section
TINI_EndRead(ConfLine);
}
/* Close Stream */
WL_StreamClose(Stream);
/* Set first sequence */
l_TTSeqAt = l_TTSeqHead;
// Initialize
D_UITitleInitScreen();
}
/* D_UITitle() -- Title Screen Drawer */
void D_UITitle(UI_BufferSpec_t* const a_Spec)
{
/* If no image, try loading it */
if (!l_TTPic)
{
// There may be no sequence, or the bit depth changed on the title screen
if (l_TTSeqAt && l_TTSeqAt->Pic[0])
l_TTPic = UI_ImgLoadEntS(l_TTSeqAt->Pic);
// Still failed
if (!l_TTPic)
return;
}
/* Draw fullscreen */
UI_DrawImgFull(a_Spec, l_TTPic);
}
/* D_UITitleTick() -- Tics title screen */
void D_UITitleTick(void)
{
if (--l_TTTicker < 0)
D_UITitleBump();
}
/* D_UITitleBump() -- Bumps title screen up */
void D_UITitleBump(void)
{
l_TTBump = true;
}
/* D_UITitleNext() -- Possibly goes to next title screen page */
void D_UITitleNext(void)
{
/* Only if bumped */
if (!l_TTBump)
return;
// Do not bump again
l_TTBump = false;
/* Set initial action */
gameaction = ga_nothing;
/* Go to next sequence */
if (l_TTSeqAt)
l_TTSeqAt = l_TTSeqAt->Next;
// Start from beginning
if (!l_TTSeqAt)
l_TTSeqAt = l_TTSeqHead;
// Initialize
if (!D_UITitleInitScreen())
return;
// Set new state
gamestate = GS_DEMOSCREEN;
}
#endif