2019-09-19 22:42:45 +00:00
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
/*
|
|
|
|
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
|
|
|
Copyright (C) 2019 Nuke.YKT
|
|
|
|
|
|
|
|
This file is part of NBlood.
|
|
|
|
|
|
|
|
NBlood is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License version 2
|
|
|
|
as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
//-------------------------------------------------------------------------
|
2019-09-21 18:59:54 +00:00
|
|
|
|
|
|
|
#include "ns.h" // Must come before everything else!
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "common.h"
|
|
|
|
#include "common_game.h"
|
|
|
|
#include "keyboard.h"
|
|
|
|
#include "control.h"
|
|
|
|
#include "osd.h"
|
|
|
|
#include "mmulti.h"
|
|
|
|
|
|
|
|
#include "blood.h"
|
|
|
|
#include "controls.h"
|
|
|
|
#include "demo.h"
|
|
|
|
#include "fire.h"
|
|
|
|
#include "gamemenu.h"
|
|
|
|
#include "globals.h"
|
|
|
|
#include "levels.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "messages.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "music.h"
|
|
|
|
#include "network.h"
|
|
|
|
#include "player.h"
|
|
|
|
#include "screen.h"
|
2019-11-01 23:38:30 +00:00
|
|
|
#include "i_specialpaths.h"
|
2019-09-19 22:42:45 +00:00
|
|
|
#include "view.h"
|
2019-11-01 23:38:30 +00:00
|
|
|
#include "gamecontrol.h"
|
2019-09-19 22:42:45 +00:00
|
|
|
|
2019-09-22 06:39:22 +00:00
|
|
|
BEGIN_BLD_NS
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
int nBuild = 0;
|
|
|
|
|
|
|
|
void ReadGameOptionsLegacy(GAMEOPTIONS &gameOptions, GAMEOPTIONSLEGACY &gameOptionsLegacy)
|
|
|
|
{
|
|
|
|
gameOptions.nGameType = gameOptionsLegacy.nGameType;
|
|
|
|
gameOptions.nDifficulty = gameOptionsLegacy.nDifficulty;
|
|
|
|
gameOptions.nEpisode = gameOptionsLegacy.nEpisode;
|
|
|
|
gameOptions.nLevel = gameOptionsLegacy.nLevel;
|
|
|
|
strcpy(gameOptions.zLevelName, gameOptionsLegacy.zLevelName);
|
|
|
|
strcpy(gameOptions.zLevelSong, gameOptionsLegacy.zLevelSong);
|
|
|
|
gameOptions.nTrackNumber = gameOptionsLegacy.nTrackNumber;
|
|
|
|
strcpy(gameOptions.szSaveGameName, gameOptionsLegacy.szSaveGameName);
|
|
|
|
strcpy(gameOptions.szUserGameName, gameOptionsLegacy.szUserGameName);
|
|
|
|
gameOptions.nSaveGameSlot = gameOptionsLegacy.nSaveGameSlot;
|
|
|
|
gameOptions.picEntry = gameOptionsLegacy.picEntry;
|
|
|
|
gameOptions.uMapCRC = gameOptionsLegacy.uMapCRC;
|
|
|
|
gameOptions.nMonsterSettings = gameOptionsLegacy.nMonsterSettings;
|
|
|
|
gameOptions.uGameFlags = gameOptionsLegacy.uGameFlags;
|
|
|
|
gameOptions.uNetGameFlags = gameOptionsLegacy.uNetGameFlags;
|
|
|
|
gameOptions.nWeaponSettings = gameOptionsLegacy.nWeaponSettings;
|
|
|
|
gameOptions.nItemSettings = gameOptionsLegacy.nItemSettings;
|
|
|
|
gameOptions.nRespawnSettings = gameOptionsLegacy.nRespawnSettings;
|
|
|
|
gameOptions.nTeamSettings = gameOptionsLegacy.nTeamSettings;
|
|
|
|
gameOptions.nMonsterRespawnTime = gameOptionsLegacy.nMonsterRespawnTime;
|
|
|
|
gameOptions.nWeaponRespawnTime = gameOptionsLegacy.nWeaponRespawnTime;
|
|
|
|
gameOptions.nItemRespawnTime = gameOptionsLegacy.nItemRespawnTime;
|
|
|
|
gameOptions.nSpecialRespawnTime = gameOptionsLegacy.nSpecialRespawnTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
CDemo gDemo;
|
|
|
|
|
|
|
|
CDemo::CDemo()
|
|
|
|
{
|
|
|
|
nBuild = 4;
|
|
|
|
at0 = 0;
|
|
|
|
at1 = 0;
|
|
|
|
at3 = 0;
|
|
|
|
hRFile = NULL;
|
|
|
|
atb = 0;
|
|
|
|
pFirstDemo = NULL;
|
|
|
|
pCurrentDemo = NULL;
|
|
|
|
at59ef = 0;
|
|
|
|
at2 = 0;
|
|
|
|
memset(&atf, 0, sizeof(atf));
|
|
|
|
m_bLegacy = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CDemo::~CDemo()
|
|
|
|
{
|
|
|
|
at0 = 0;
|
|
|
|
at1 = 0;
|
|
|
|
at3 = 0;
|
|
|
|
atb = 0;
|
|
|
|
memset(&atf, 0, sizeof(atf));
|
|
|
|
if (hRFile != NULL)
|
|
|
|
{
|
|
|
|
fclose(hRFile);
|
|
|
|
hRFile = NULL;
|
|
|
|
}
|
|
|
|
auto pNextDemo = pFirstDemo;
|
|
|
|
for (auto pDemo = pFirstDemo; pDemo != NULL; pDemo = pNextDemo)
|
|
|
|
{
|
|
|
|
pNextDemo = pDemo->pNext;
|
|
|
|
delete pDemo;
|
|
|
|
}
|
|
|
|
pFirstDemo = NULL;
|
|
|
|
pCurrentDemo = NULL;
|
|
|
|
at59ef = 0;
|
|
|
|
m_bLegacy = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDemo::Create(const char *pzFile)
|
|
|
|
{
|
|
|
|
char buffer[BMAX_PATH];
|
|
|
|
char vc = 0;
|
|
|
|
if (at0 || at1)
|
|
|
|
ThrowError("CDemo::Create called during demo record/playback process.");
|
|
|
|
if (!pzFile)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 8 && !vc; i++)
|
|
|
|
{
|
2019-11-01 23:38:30 +00:00
|
|
|
snprintf(buffer, BMAX_PATH, "%s%s0%02d.dem", M_GetDemoPath().GetChars(), BloodIniPre, i);
|
2019-10-30 23:41:56 +00:00
|
|
|
if (access(buffer, 0) != -1)
|
2019-09-19 22:42:45 +00:00
|
|
|
vc = 1;
|
|
|
|
}
|
|
|
|
if (vc == 1)
|
|
|
|
{
|
|
|
|
hRFile = fopen(buffer, "wb");
|
|
|
|
if (hRFile == NULL)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-01 23:38:30 +00:00
|
|
|
snprintf(buffer, BMAX_PATH, "%s%s", M_GetDemoPath().GetChars(), pzFile);
|
2019-09-19 22:42:45 +00:00
|
|
|
hRFile = fopen(buffer, "wb");
|
|
|
|
if (hRFile == NULL)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
at0 = 1;
|
|
|
|
atb = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::Write(GINPUT *pPlayerInputs)
|
|
|
|
{
|
|
|
|
dassert(pPlayerInputs != NULL);
|
|
|
|
if (!at0)
|
|
|
|
return;
|
|
|
|
if (atb == 0)
|
|
|
|
{
|
|
|
|
atf.signature = 0x1a4d4445; // '\x1aMDE';
|
|
|
|
atf.nVersion = BYTEVERSION;
|
|
|
|
atf.nBuild = nBuild;
|
|
|
|
atf.nInputCount = 0;
|
|
|
|
atf.nNetPlayers = gNetPlayers;
|
|
|
|
atf.nMyConnectIndex = myconnectindex;
|
|
|
|
atf.nConnectHead = connecthead;
|
|
|
|
memcpy(atf.connectPoints, connectpoint2, sizeof(atf.connectPoints));
|
|
|
|
memcpy(&m_gameOptions, &gGameOptions, sizeof(gGameOptions));
|
|
|
|
fwrite(&atf, sizeof(DEMOHEADER), 1, hRFile);
|
|
|
|
fwrite(&m_gameOptions, sizeof(GAMEOPTIONS), 1, hRFile);
|
|
|
|
}
|
|
|
|
for (int p = connecthead; p >= 0; p = connectpoint2[p])
|
|
|
|
{
|
|
|
|
memcpy(&at1aa[atb&1023], &pPlayerInputs[p], sizeof(GINPUT));
|
|
|
|
atb++;
|
|
|
|
if((atb&(kInputBufferSize-1))==0)
|
|
|
|
FlushInput(kInputBufferSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::Close(void)
|
|
|
|
{
|
|
|
|
if (at0)
|
|
|
|
{
|
|
|
|
if (atb&(kInputBufferSize-1))
|
|
|
|
FlushInput(atb&(kInputBufferSize-1));
|
|
|
|
atf.nInputCount = atb;
|
|
|
|
fseek(hRFile, 0, SEEK_SET);
|
|
|
|
fwrite(&atf, sizeof(DEMOHEADER), 1, hRFile);
|
|
|
|
fwrite(&m_gameOptions, sizeof(GAMEOPTIONS), 1, hRFile);
|
|
|
|
}
|
2019-10-21 17:36:54 +00:00
|
|
|
if (hPFile.isOpen())
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
2019-10-21 17:36:54 +00:00
|
|
|
hPFile.Close();
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
if (hRFile != NULL)
|
|
|
|
{
|
|
|
|
fclose(hRFile);
|
|
|
|
hRFile = NULL;
|
|
|
|
}
|
|
|
|
at0 = 0;
|
|
|
|
at1 = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDemo::SetupPlayback(const char *pzFile)
|
|
|
|
{
|
|
|
|
at0 = 0;
|
|
|
|
at1 = 0;
|
|
|
|
if (pzFile)
|
|
|
|
{
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile = fopenFileReader(pzFile, 0);
|
|
|
|
if (!hPFile.isOpen())
|
2019-09-19 22:42:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!pCurrentDemo)
|
|
|
|
return false;
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile = fopenFileReader(pCurrentDemo->zName, 0);
|
|
|
|
if (hPFile.isOpen())
|
2019-09-19 22:42:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile.Read(&atf, sizeof(DEMOHEADER));
|
2019-09-19 22:42:45 +00:00
|
|
|
#if B_BIG_ENDIAN == 1
|
|
|
|
atf.signature = B_LITTLE32(atf.signature);
|
|
|
|
atf.nVersion = B_LITTLE16(atf.nVersion);
|
|
|
|
atf.nBuild = B_LITTLE32(atf.nBuild);
|
|
|
|
atf.nInputCount = B_LITTLE32(atf.nInputCount);
|
|
|
|
atf.nNetPlayers = B_LITTLE32(atf.nNetPlayers);
|
|
|
|
atf.nMyConnectIndex = B_LITTLE16(atf.nMyConnectIndex);
|
|
|
|
atf.nConnectHead = B_LITTLE16(atf.nConnectHead);
|
|
|
|
atf.nMyConnectIndex = B_LITTLE16(atf.nMyConnectIndex);
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
atf.connectPoints[i] = B_LITTLE16(atf.connectPoints[i]);
|
|
|
|
#endif
|
|
|
|
// if (aimHeight.signature != '\x1aMED' && aimHeight.signature != '\x1aMDE')
|
|
|
|
if (atf.signature != 0x1a4d4544 && atf.signature != 0x1a4d4445)
|
|
|
|
return 0;
|
|
|
|
m_bLegacy = atf.signature == 0x1a4d4544;
|
|
|
|
if (m_bLegacy)
|
|
|
|
{
|
|
|
|
GAMEOPTIONSLEGACY gameOptions;
|
|
|
|
if (BloodVersion != atf.nVersion)
|
|
|
|
return 0;
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile.Read(&gameOptions, sizeof(GAMEOPTIONSLEGACY));
|
2019-09-19 22:42:45 +00:00
|
|
|
ReadGameOptionsLegacy(m_gameOptions, gameOptions);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (BYTEVERSION != atf.nVersion)
|
|
|
|
return 0;
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile.Read(&m_gameOptions, sizeof(GAMEOPTIONS));
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
#if B_BIG_ENDIAN == 1
|
|
|
|
m_gameOptions.nEpisode = B_LITTLE32(m_gameOptions.nEpisode);
|
|
|
|
m_gameOptions.nLevel = B_LITTLE32(m_gameOptions.nLevel);
|
|
|
|
m_gameOptions.nTrackNumber = B_LITTLE32(m_gameOptions.nTrackNumber);
|
|
|
|
m_gameOptions.nSaveGameSlot = B_LITTLE16(m_gameOptions.nSaveGameSlot);
|
|
|
|
m_gameOptions.picEntry = B_LITTLE32(m_gameOptions.picEntry);
|
|
|
|
m_gameOptions.uMapCRC = B_LITTLE32(m_gameOptions.uMapCRC);
|
|
|
|
m_gameOptions.uGameFlags = B_LITTLE32(m_gameOptions.uGameFlags);
|
|
|
|
m_gameOptions.uNetGameFlags = B_LITTLE32(m_gameOptions.uNetGameFlags);
|
|
|
|
m_gameOptions.nMonsterRespawnTime = B_LITTLE32(m_gameOptions.nMonsterRespawnTime);
|
|
|
|
m_gameOptions.nWeaponRespawnTime = B_LITTLE32(m_gameOptions.nWeaponRespawnTime);
|
|
|
|
m_gameOptions.nItemRespawnTime = B_LITTLE32(m_gameOptions.nItemRespawnTime);
|
|
|
|
m_gameOptions.nSpecialRespawnTime = B_LITTLE32(m_gameOptions.nSpecialRespawnTime);
|
|
|
|
#endif
|
|
|
|
at0 = 0;
|
|
|
|
at1 = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::ProcessKeys(void)
|
|
|
|
{
|
|
|
|
switch (gInputMode)
|
|
|
|
{
|
2019-10-19 15:20:06 +00:00
|
|
|
case kInputMenu:
|
2019-09-19 22:42:45 +00:00
|
|
|
gGameMenuMgr.Process();
|
|
|
|
break;
|
2019-10-19 15:20:06 +00:00
|
|
|
case kInputMessage:
|
2019-09-19 22:42:45 +00:00
|
|
|
gPlayerMsg.ProcessKeys();
|
|
|
|
break;
|
2019-10-19 15:20:06 +00:00
|
|
|
case kInputGame:
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
char nKey;
|
2019-11-03 23:53:55 +00:00
|
|
|
while ((nKey = inputState.keyGetScan()) != 0)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
switch (nKey)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
if (!CGameMenuMgr::m_bActive)
|
|
|
|
{
|
|
|
|
gGameMenuMgr.Push(&menuMain, -1);
|
|
|
|
at2 = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x58:
|
|
|
|
gViewIndex = connectpoint2[gViewIndex];
|
|
|
|
if (gViewIndex == -1)
|
|
|
|
gViewIndex = connecthead;
|
|
|
|
gView = &gPlayer[gViewIndex];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2019-10-19 15:20:06 +00:00
|
|
|
gInputMode = kInputGame;
|
2019-09-19 22:42:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::Playback(void)
|
|
|
|
{
|
2019-10-28 06:10:56 +00:00
|
|
|
inputState.SetBindsEnabled(false);
|
2019-09-19 22:42:45 +00:00
|
|
|
ready2send = 0;
|
|
|
|
int v4 = 0;
|
|
|
|
if (!CGameMenuMgr::m_bActive)
|
|
|
|
{
|
|
|
|
gGameMenuMgr.Push(&menuMain, -1);
|
|
|
|
at2 = 1;
|
|
|
|
}
|
2019-09-07 13:15:39 +00:00
|
|
|
gNetFifoClock = totalclock;
|
2019-09-19 22:42:45 +00:00
|
|
|
gViewMode = 3;
|
|
|
|
_DEMOPLAYBACK:
|
|
|
|
while (at1 && !gQuitGame)
|
|
|
|
{
|
|
|
|
if (handleevents() && quitevent)
|
|
|
|
{
|
2019-10-28 00:12:31 +00:00
|
|
|
inputState.SetKeyStatus(sc_Escape, 1);
|
|
|
|
quitevent = 0;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
MUSIC_Update();
|
2019-09-07 13:15:39 +00:00
|
|
|
while (totalclock >= gNetFifoClock && !gQuitGame)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
if (!v4)
|
|
|
|
{
|
|
|
|
viewResizeView(gViewSize);
|
|
|
|
viewSetMessage("");
|
|
|
|
gNetPlayers = atf.nNetPlayers;
|
|
|
|
atb = atf.nInputCount;
|
|
|
|
myconnectindex = atf.nMyConnectIndex;
|
|
|
|
connecthead = atf.nConnectHead;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
connectpoint2[i] = atf.connectPoints[i];
|
|
|
|
memset(gNetFifoHead, 0, sizeof(gNetFifoHead));
|
|
|
|
gNetFifoTail = 0;
|
|
|
|
//memcpy(connectpoint2, aimHeight.connectPoints, sizeof(aimHeight.connectPoints));
|
|
|
|
memcpy(&gGameOptions, &m_gameOptions, sizeof(GAMEOPTIONS));
|
|
|
|
gSkill = gGameOptions.nDifficulty;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
playerInit(i, 0);
|
|
|
|
StartLevel(&gGameOptions);
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
gProfile[i].nAutoAim = 1;
|
|
|
|
gProfile[i].nWeaponSwitch = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ready2send = 0;
|
|
|
|
OSD_DispatchQueued();
|
|
|
|
if (!gDemo.at1)
|
|
|
|
break;
|
|
|
|
ProcessKeys();
|
|
|
|
for (int p = connecthead; p >= 0; p = connectpoint2[p])
|
|
|
|
{
|
|
|
|
if ((v4&1023) == 0)
|
|
|
|
{
|
|
|
|
unsigned int nSize = atb-v4;
|
|
|
|
if (nSize > kInputBufferSize)
|
|
|
|
nSize = kInputBufferSize;
|
|
|
|
ReadInput(nSize);
|
|
|
|
}
|
|
|
|
memcpy(&gFifoInput[gNetFifoHead[p]&255], &at1aa[v4&1023], sizeof(GINPUT));
|
|
|
|
gNetFifoHead[p]++;
|
|
|
|
v4++;
|
|
|
|
if (v4 >= atf.nInputCount)
|
|
|
|
{
|
|
|
|
ready2send = 0;
|
|
|
|
if (at59ef != 1)
|
|
|
|
{
|
|
|
|
v4 = 0;
|
|
|
|
Close();
|
|
|
|
NextDemo();
|
2019-09-07 13:15:39 +00:00
|
|
|
gNetFifoClock = totalclock;
|
2019-09-19 22:42:45 +00:00
|
|
|
goto _DEMOPLAYBACK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int const nOffset = sizeof(DEMOHEADER)+(m_bLegacy ? sizeof(GAMEOPTIONSLEGACY) : sizeof(GAMEOPTIONS));
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile.Seek(nOffset, FileReader::SeekSet);
|
2019-09-19 22:42:45 +00:00
|
|
|
v4 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gNetFifoClock += 4;
|
|
|
|
if (!gQuitGame)
|
|
|
|
ProcessFrame();
|
|
|
|
ready2send = 0;
|
|
|
|
}
|
2019-10-23 15:07:29 +00:00
|
|
|
if (G_FPSLimit())
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
viewDrawScreen();
|
2019-10-19 15:20:06 +00:00
|
|
|
if (gInputMode == kInputMenu && CGameMenuMgr::m_bActive)
|
2019-09-19 22:42:45 +00:00
|
|
|
gGameMenuMgr.Draw();
|
2019-07-28 10:15:35 +00:00
|
|
|
videoNextPage();
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
if (TestBitString(gotpic, 2342))
|
|
|
|
{
|
|
|
|
FireProcess();
|
|
|
|
ClearBitString(gotpic, 2342);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::StopPlayback(void)
|
|
|
|
{
|
|
|
|
at1 = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::LoadDemoInfo(void)
|
|
|
|
{
|
|
|
|
auto pDemo = &pFirstDemo;
|
|
|
|
at59ef = 0;
|
|
|
|
char zFN[BMAX_PATH];
|
2019-11-01 23:38:30 +00:00
|
|
|
Bsnprintf(zFN, BMAX_PATH, "%s%s*.dem", M_GetDemoPath().GetChars(), BloodIniPre);
|
|
|
|
TArray<FString> demos;
|
|
|
|
D_AddWildFile(demos, zFN);
|
|
|
|
for (auto &filename : demos)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
2019-11-01 23:38:30 +00:00
|
|
|
auto hFile = fopenFileReader(filename, 0);
|
2019-10-21 15:16:08 +00:00
|
|
|
if (!hFile.isOpen())
|
2019-09-19 22:42:45 +00:00
|
|
|
ThrowError("Error loading demo file header.");
|
2019-10-21 15:16:08 +00:00
|
|
|
hFile.Read(&atf, sizeof(atf));
|
2019-09-19 22:42:45 +00:00
|
|
|
#if B_BIG_ENDIAN == 1
|
|
|
|
atf.signature = B_LITTLE32(atf.signature);
|
|
|
|
atf.nVersion = B_LITTLE16(atf.nVersion);
|
|
|
|
#endif
|
|
|
|
if ((atf.signature == 0x1a4d4544 /* '\x1aMED' */&& atf.nVersion == BloodVersion)
|
|
|
|
|| (atf.signature == 0x1a4d4445 /* '\x1aMDE' */ && atf.nVersion == BYTEVERSION))
|
|
|
|
{
|
|
|
|
*pDemo = new DEMOCHAIN;
|
|
|
|
(*pDemo)->pNext = NULL;
|
2019-11-01 23:38:30 +00:00
|
|
|
Bstrncpy((*pDemo)->zName, filename, BMAX_PATH);
|
2019-09-19 22:42:45 +00:00
|
|
|
at59ef++;
|
|
|
|
pDemo = &(*pDemo)->pNext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pCurrentDemo = pFirstDemo;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::NextDemo(void)
|
|
|
|
{
|
|
|
|
pCurrentDemo = pCurrentDemo->pNext ? pCurrentDemo->pNext : pFirstDemo;
|
|
|
|
SetupPlayback(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int nInputSize = 17;
|
|
|
|
const int nInputSizeLegacy = 22;
|
|
|
|
|
|
|
|
void CDemo::FlushInput(int nCount)
|
|
|
|
{
|
|
|
|
char pBuffer[nInputSize*kInputBufferSize];
|
|
|
|
BitWriter bitWriter(pBuffer, sizeof(pBuffer));
|
|
|
|
for (int i = 0; i < nCount; i++)
|
|
|
|
{
|
|
|
|
GINPUT *pInput = &at1aa[i];
|
|
|
|
bitWriter.writeBit(pInput->syncFlags.buttonChange);
|
|
|
|
bitWriter.writeBit(pInput->syncFlags.keyChange);
|
|
|
|
bitWriter.writeBit(pInput->syncFlags.useChange);
|
|
|
|
bitWriter.writeBit(pInput->syncFlags.weaponChange);
|
|
|
|
bitWriter.writeBit(pInput->syncFlags.mlookChange);
|
|
|
|
bitWriter.writeBit(pInput->syncFlags.run);
|
|
|
|
bitWriter.write(pInput->forward, 16);
|
|
|
|
bitWriter.write(pInput->q16turn, 32);
|
|
|
|
bitWriter.write(pInput->strafe, 16);
|
|
|
|
bitWriter.writeBit(pInput->buttonFlags.jump);
|
|
|
|
bitWriter.writeBit(pInput->buttonFlags.crouch);
|
|
|
|
bitWriter.writeBit(pInput->buttonFlags.shoot);
|
|
|
|
bitWriter.writeBit(pInput->buttonFlags.shoot2);
|
|
|
|
bitWriter.writeBit(pInput->buttonFlags.lookUp);
|
|
|
|
bitWriter.writeBit(pInput->buttonFlags.lookDown);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.action);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.jab);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.prevItem);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.nextItem);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.useItem);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.prevWeapon);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.nextWeapon);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.holsterWeapon);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.lookCenter);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.lookLeft);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.lookRight);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.spin180);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.pause);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.quit);
|
|
|
|
bitWriter.writeBit(pInput->keyFlags.restart);
|
|
|
|
bitWriter.writeBit(pInput->useFlags.useBeastVision);
|
|
|
|
bitWriter.writeBit(pInput->useFlags.useCrystalBall);
|
|
|
|
bitWriter.writeBit(pInput->useFlags.useJumpBoots);
|
|
|
|
bitWriter.writeBit(pInput->useFlags.useMedKit);
|
|
|
|
bitWriter.write(pInput->newWeapon, 8);
|
|
|
|
bitWriter.write(pInput->q16mlook, 32);
|
|
|
|
bitWriter.skipBits(1);
|
|
|
|
}
|
|
|
|
fwrite(pBuffer, 1, nInputSize*nCount, hRFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDemo::ReadInput(int nCount)
|
|
|
|
{
|
|
|
|
if (m_bLegacy)
|
|
|
|
{
|
|
|
|
char pBuffer[nInputSizeLegacy*kInputBufferSize];
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile.Read(pBuffer, nInputSizeLegacy*nCount);
|
2019-09-19 22:42:45 +00:00
|
|
|
BitReader bitReader(pBuffer, sizeof(pBuffer));
|
|
|
|
memset(at1aa, 0, nCount * sizeof(GINPUT));
|
|
|
|
for (int i = 0; i < nCount; i++)
|
|
|
|
{
|
|
|
|
GINPUT *pInput = &at1aa[i];
|
|
|
|
pInput->syncFlags.buttonChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.keyChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.useChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.weaponChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.mlookChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.run = bitReader.readBit();
|
|
|
|
bitReader.skipBits(26);
|
|
|
|
pInput->forward = bitReader.readSigned(8) << 8;
|
|
|
|
pInput->q16turn = fix16_from_int(bitReader.readSigned(16) >> 2);
|
|
|
|
pInput->strafe = bitReader.readSigned(8) << 8;
|
|
|
|
pInput->buttonFlags.jump = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.crouch = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.shoot = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.shoot2 = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.lookUp = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.lookDown = bitReader.readBit();
|
|
|
|
bitReader.skipBits(26);
|
|
|
|
pInput->keyFlags.action = bitReader.readBit();
|
|
|
|
pInput->keyFlags.jab = bitReader.readBit();
|
|
|
|
pInput->keyFlags.prevItem = bitReader.readBit();
|
|
|
|
pInput->keyFlags.nextItem = bitReader.readBit();
|
|
|
|
pInput->keyFlags.useItem = bitReader.readBit();
|
|
|
|
pInput->keyFlags.prevWeapon = bitReader.readBit();
|
|
|
|
pInput->keyFlags.nextWeapon = bitReader.readBit();
|
|
|
|
pInput->keyFlags.holsterWeapon = bitReader.readBit();
|
|
|
|
pInput->keyFlags.lookCenter = bitReader.readBit();
|
|
|
|
pInput->keyFlags.lookLeft = bitReader.readBit();
|
|
|
|
pInput->keyFlags.lookRight = bitReader.readBit();
|
|
|
|
pInput->keyFlags.spin180 = bitReader.readBit();
|
|
|
|
pInput->keyFlags.pause = bitReader.readBit();
|
|
|
|
pInput->keyFlags.quit = bitReader.readBit();
|
|
|
|
pInput->keyFlags.restart = bitReader.readBit();
|
|
|
|
bitReader.skipBits(17);
|
|
|
|
pInput->useFlags.useBeastVision = bitReader.readBit();
|
|
|
|
pInput->useFlags.useCrystalBall = bitReader.readBit();
|
|
|
|
pInput->useFlags.useJumpBoots = bitReader.readBit();
|
|
|
|
pInput->useFlags.useMedKit = bitReader.readBit();
|
|
|
|
bitReader.skipBits(28);
|
|
|
|
pInput->newWeapon = bitReader.readUnsigned(8);
|
|
|
|
int mlook = bitReader.readSigned(8);
|
|
|
|
pInput->q16mlook = fix16_from_int(mlook / 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char pBuffer[nInputSize*kInputBufferSize];
|
2019-10-21 15:16:08 +00:00
|
|
|
hPFile.Read(pBuffer, nInputSize*nCount);
|
2019-09-19 22:42:45 +00:00
|
|
|
BitReader bitReader(pBuffer, sizeof(pBuffer));
|
|
|
|
memset(at1aa, 0, nCount * sizeof(GINPUT));
|
|
|
|
for (int i = 0; i < nCount; i++)
|
|
|
|
{
|
|
|
|
GINPUT *pInput = &at1aa[i];
|
|
|
|
pInput->syncFlags.buttonChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.keyChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.useChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.weaponChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.mlookChange = bitReader.readBit();
|
|
|
|
pInput->syncFlags.run = bitReader.readBit();
|
|
|
|
pInput->forward = bitReader.readSigned(16);
|
|
|
|
pInput->q16turn = bitReader.readSigned(32);
|
|
|
|
pInput->strafe = bitReader.readSigned(16);
|
|
|
|
pInput->buttonFlags.jump = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.crouch = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.shoot = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.shoot2 = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.lookUp = bitReader.readBit();
|
|
|
|
pInput->buttonFlags.lookDown = bitReader.readBit();
|
|
|
|
pInput->keyFlags.action = bitReader.readBit();
|
|
|
|
pInput->keyFlags.jab = bitReader.readBit();
|
|
|
|
pInput->keyFlags.prevItem = bitReader.readBit();
|
|
|
|
pInput->keyFlags.nextItem = bitReader.readBit();
|
|
|
|
pInput->keyFlags.useItem = bitReader.readBit();
|
|
|
|
pInput->keyFlags.prevWeapon = bitReader.readBit();
|
|
|
|
pInput->keyFlags.nextWeapon = bitReader.readBit();
|
|
|
|
pInput->keyFlags.holsterWeapon = bitReader.readBit();
|
|
|
|
pInput->keyFlags.lookCenter = bitReader.readBit();
|
|
|
|
pInput->keyFlags.lookLeft = bitReader.readBit();
|
|
|
|
pInput->keyFlags.lookRight = bitReader.readBit();
|
|
|
|
pInput->keyFlags.spin180 = bitReader.readBit();
|
|
|
|
pInput->keyFlags.pause = bitReader.readBit();
|
|
|
|
pInput->keyFlags.quit = bitReader.readBit();
|
|
|
|
pInput->keyFlags.restart = bitReader.readBit();
|
|
|
|
pInput->useFlags.useBeastVision = bitReader.readBit();
|
|
|
|
pInput->useFlags.useCrystalBall = bitReader.readBit();
|
|
|
|
pInput->useFlags.useJumpBoots = bitReader.readBit();
|
|
|
|
pInput->useFlags.useMedKit = bitReader.readBit();
|
|
|
|
pInput->newWeapon = bitReader.readUnsigned(8);
|
|
|
|
pInput->q16mlook = bitReader.readSigned(32);
|
|
|
|
bitReader.skipBits(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-22 06:39:22 +00:00
|
|
|
|
|
|
|
END_BLD_NS
|