/ source / blood / src / demo.cpp
demo.cpp
  1  //-------------------------------------------------------------------------
  2  /*
  3  Copyright (C) 2010-2019 EDuke32 developers and contributors
  4  Copyright (C) 2019 Nuke.YKT
  5  
  6  This file is part of NBlood.
  7  
  8  NBlood is free software; you can redistribute it and/or
  9  modify it under the terms of the GNU General Public License version 2
 10  as published by the Free Software Foundation.
 11  
 12  This program is distributed in the hope that it will be useful,
 13  but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15  
 16  See the GNU General Public License for more details.
 17  
 18  You should have received a copy of the GNU General Public License
 19  along with this program; if not, write to the Free Software
 20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 21  */
 22  //-------------------------------------------------------------------------
 23  #include <stdio.h>
 24  #include <string.h>
 25  #include "common.h"
 26  #include "common_game.h"
 27  #include "keyboard.h"
 28  #include "control.h"
 29  #include "osd.h"
 30  #include "mmulti.h"
 31  
 32  #include "blood.h"
 33  #include "controls.h"
 34  #include "demo.h"
 35  #include "fire.h"
 36  #include "gamemenu.h"
 37  #include "globals.h"
 38  #include "levels.h"
 39  #include "menu.h"
 40  #include "messages.h"
 41  #include "misc.h"
 42  #include "music.h"
 43  #include "network.h"
 44  #include "player.h"
 45  #include "screen.h"
 46  #include "sound.h"
 47  #include "view.h"
 48  
 49  bool gDemoRunValidation = false;
 50  
 51  struct DEMOVALIDATE {
 52      const char *zName;
 53      int32_t nInputTicks;
 54      uint32_t wrandomseed;
 55      uint32_t health;
 56      vec3_t xyz;
 57      int nAutoAim;
 58  };
 59  
 60  const char *gDemoInvalid[] = { // compiler hall of shame
 61  #if defined(__GNUC__) && __GNUC__ >= 15
 62      "TEST085.DEM",
 63      "TEST096.DEM",
 64      "TEST098.DEM",
 65  #else
 66      ""
 67  #endif
 68  };
 69  
 70  const DEMOVALIDATE gDemoValidate[] = {
 71      {"/validatedemos/TEST000.DEM", (int32_t)0x00000DB1, 0x68E811AD, 0x00000000, {(int32_t)0x00002239, (int32_t)0x00006A75, (int32_t)0x000029A4}, 1},
 72      {"/validatedemos/TEST001.DEM", (int32_t)0x000008E3, 0x898BFCFE, 0x00000000, {(int32_t)0x00001AC0, (int32_t)0x00003F49, (int32_t)0x000025A4}, 1},
 73      {"/validatedemos/TEST002.DEM", (int32_t)0x00000CE0, 0xCAF89634, 0x00000000, {(int32_t)0x0000C8ED, (int32_t)0x00009AF9, (int32_t)0x000159A4}, 1},
 74      {"/validatedemos/TEST003.DEM", (int32_t)0x00000B87, 0x4488E1A6, 0x00000000, {(int32_t)0xFFFFD54C, (int32_t)0xFFFFD41F, (int32_t)0x000019A4}, 1},
 75      // custom validation demos below
 76      {"/validatedemos/TEST004.DEM", (int32_t)0x00000C14, 0x6E8F8936, 0x00000000, {(int32_t)0x00003D02, (int32_t)0x00009F6E, (int32_t)0x000069A4}, 1},
 77      {"/validatedemos/TEST005.DEM", (int32_t)0x00001BF9, 0xE8B80FD0, 0x00000000, {(int32_t)0x00000162, (int32_t)0x00007AE0, (int32_t)0xFFFEFDA4}, 1},
 78      {"/validatedemos/TEST006.DEM", (int32_t)0x000036C8, 0xABF22136, 0x00000000, {(int32_t)0xFFFF2181, (int32_t)0x000091B1, (int32_t)0x000083CE}, 1},
 79      {"/validatedemos/TEST007.DEM", (int32_t)0x00004DA6, 0xB7FB11EB, 0x00000000, {(int32_t)0x0000F4DC, (int32_t)0x000053C9, (int32_t)0x000125A4}, 1},
 80      {"/validatedemos/TEST008.DEM", (int32_t)0x0000347B, 0x02CB1319, 0x00000BED, {(int32_t)0xFFFF8138, (int32_t)0xFFFED30A, (int32_t)0xFFFF7D50}, 1},
 81      {"/validatedemos/TEST009.DEM", (int32_t)0x000026AF, 0x4F91391D, 0x00000000, {(int32_t)0xFFFF3824, (int32_t)0xFFFF91BF, (int32_t)0x00007BA4}, 1},
 82      {"/validatedemos/TEST010.DEM", (int32_t)0x00000E96, 0xFF23FAE8, 0x00000000, {(int32_t)0xFFFF23DD, (int32_t)0xFFFF9EE0, (int32_t)0x00006DA4}, 1},
 83      {"/validatedemos/TEST011.DEM", (int32_t)0x00001706, 0x3A7DD0B0, 0x00000000, {(int32_t)0x0000CD98, (int32_t)0x00004C0A, (int32_t)0x00011DF5}, 1},
 84      {"/validatedemos/TEST012.DEM", (int32_t)0x00003D72, 0xE4A69539, 0x00000000, {(int32_t)0xFFFF8745, (int32_t)0xFFFED4C6, (int32_t)0xFFFF99A4}, 1},
 85      {"/validatedemos/TEST013.DEM", (int32_t)0x000028CE, 0x93096ADB, 0x0000046A, {(int32_t)0xFFFF826E, (int32_t)0xFFFED2C1, (int32_t)0xFFFF7D50}, 1},
 86      {"/validatedemos/TEST014.DEM", (int32_t)0x00000F77, 0xA8160C3C, 0x00000000, {(int32_t)0xFFFFC8D7, (int32_t)0xFFFF930F, (int32_t)0xFFFFA5A4}, 1},
 87      {"/validatedemos/TEST015.DEM", (int32_t)0x000016CB, 0xF6F9315E, 0x00000000, {(int32_t)0xFFFFF7A1, (int32_t)0x00007494, (int32_t)0xFFFE6651}, 1},
 88      {"/validatedemos/TEST016.DEM", (int32_t)0x0000165C, 0x45F41D2E, 0x00000000, {(int32_t)0x00001A1C, (int32_t)0x00008D3F, (int32_t)0xFFFF39A4}, 1},
 89      {"/validatedemos/TEST017.DEM", (int32_t)0x000033F7, 0x52634976, 0x00000640, {(int32_t)0x0000AC77, (int32_t)0x000012AB, (int32_t)0xFFFFFD50}, 1},
 90      {"/validatedemos/TEST018.DEM", (int32_t)0x00002D0C, 0x60DCE40A, 0x00000000, {(int32_t)0x0000645F, (int32_t)0x0000CB1B, (int32_t)0xFFFFC9A4}, 1},
 91      {"/validatedemos/TEST019.DEM", (int32_t)0x00000337, 0x4A0013E6, 0x00000640, {(int32_t)0xFFFF9FCC, (int32_t)0x00003E52, (int32_t)0xFFFF63D4}, 1},
 92      {"/validatedemos/TEST020.DEM", (int32_t)0x000009DE, 0xC0725AF1, 0x00000000, {(int32_t)0xFFFFC1CF, (int32_t)0x000064AD, (int32_t)0x000061A4}, 1},
 93      {"/validatedemos/TEST021.DEM", (int32_t)0x000019A4, 0x8BE81A09, 0x00000000, {(int32_t)0xFFFF161C, (int32_t)0x00004DB3, (int32_t)0xFFFFEEFE}, 1},
 94      {"/validatedemos/TEST022.DEM", (int32_t)0x00000EB3, 0x37E4D6D6, 0x00000000, {(int32_t)0x00004869, (int32_t)0x0000170D, (int32_t)0x000129A4}, 1},
 95      {"/validatedemos/TEST023.DEM", (int32_t)0x00001F68, 0x19B46535, 0x00000000, {(int32_t)0xFFFFDE10, (int32_t)0xFFFFD970, (int32_t)0xFFFFE9A4}, 1},
 96      {"/validatedemos/TEST024.DEM", (int32_t)0x00003C97, 0x7F3AE994, 0x00000640, {(int32_t)0xFFFFD2F2, (int32_t)0xFFFF8DD4, (int32_t)0xFFFF0950}, 1},
 97      {"/validatedemos/TEST025.DEM", (int32_t)0x0000182B, 0x330C60F2, 0x00000000, {(int32_t)0xFFFF94C1, (int32_t)0xFFFFAA81, (int32_t)0x000019A4}, 1},
 98      {"/validatedemos/TEST026.DEM", (int32_t)0x00002364, 0x37059BD3, 0x00000000, {(int32_t)0x0000456F, (int32_t)0xFFFFF8B8, (int32_t)0x000099A4}, 1},
 99      {"/validatedemos/TEST027.DEM", (int32_t)0x00001731, 0x250E075B, 0x00000000, {(int32_t)0x0000043F, (int32_t)0xFFFFD0C1, (int32_t)0x000119A4}, 1},
100      {"/validatedemos/TEST028.DEM", (int32_t)0x00002FB4, 0xDF294819, 0x00000842, {(int32_t)0x000056A8, (int32_t)0xFFFFD628, (int32_t)0xFFFFFD9F}, 1},
101      {"/validatedemos/TEST029.DEM", (int32_t)0x000017BB, 0xD184192B, 0x00000A1A, {(int32_t)0xFFFFAD5B, (int32_t)0xFFFF4573, (int32_t)0xFFFFE550}, 1},
102      {"/validatedemos/TEST030.DEM", (int32_t)0x0000396B, 0x5031BC75, 0x00000000, {(int32_t)0x0000B326, (int32_t)0x000074C4, (int32_t)0x0000D9A4}, 1},
103      {"/validatedemos/TEST031.DEM", (int32_t)0x000023D1, 0xC5DCAC81, 0x00000640, {(int32_t)0x00009BEE, (int32_t)0x00006CC8, (int32_t)0x00003550}, 1},
104      {"/validatedemos/TEST032.DEM", (int32_t)0x000013BC, 0xCA389D8F, 0x00000000, {(int32_t)0x00009B71, (int32_t)0x000092A4, (int32_t)0x000159A4}, 1},
105      {"/validatedemos/TEST033.DEM", (int32_t)0x00000BA4, 0xB06865D4, 0x00000000, {(int32_t)0x00009415, (int32_t)0x00007E5D, (int32_t)0x000159A4}, 1},
106      {"/validatedemos/TEST034.DEM", (int32_t)0x000019C7, 0x7F7D0178, 0x00000640, {(int32_t)0x00009B90, (int32_t)0x00006CF1, (int32_t)0x000035E4}, 1},
107      {"/validatedemos/TEST035.DEM", (int32_t)0x0000066A, 0xD7607579, 0x00000000, {(int32_t)0x0000B31C, (int32_t)0x0000B03F, (int32_t)0x000159A4}, 1},
108      {"/validatedemos/TEST036.DEM", (int32_t)0x000016DC, 0x79095E8C, 0x00000000, {(int32_t)0x0000C2AB, (int32_t)0x000094C1, (int32_t)0x000059A4}, 1},
109      {"/validatedemos/TEST037.DEM", (int32_t)0x00000B4A, 0xF9F76876, 0x00000000, {(int32_t)0x0000D00B, (int32_t)0x0000ACC8, (int32_t)0x000159A4}, 1},
110      {"/validatedemos/TEST038.DEM", (int32_t)0x0000384B, 0x46086DB2, 0x0000032F, {(int32_t)0x000103E8, (int32_t)0xFFFFC601, (int32_t)0x000005E4}, 1}, // at this point msvc created binaries will desync for some weird reason
111      {"/validatedemos/TEST039.DEM", (int32_t)0x000034D7, 0xC6E9D184, 0x00000000, {(int32_t)0x0000FA87, (int32_t)0x00006C32, (int32_t)0x000025A4}, 1},
112      {"/validatedemos/TEST040.DEM", (int32_t)0x0000385B, 0x246696C4, 0x00000000, {(int32_t)0x00010BC8, (int32_t)0x00005F46, (int32_t)0xFFFFC1A4}, 1},
113      {"/validatedemos/TEST041.DEM", (int32_t)0x00001BA1, 0x8377D75C, 0x00000000, {(int32_t)0x00006EBF, (int32_t)0x0000463F, (int32_t)0x00079DA4}, 1},
114      {"/validatedemos/TEST042.DEM", (int32_t)0x00000A75, 0x61288D5E, 0x00000000, {(int32_t)0x0000A14F, (int32_t)0x00005D22, (int32_t)0x000069A4}, 1},
115      {"/validatedemos/TEST043.DEM", (int32_t)0x000036FF, 0xD89DFDDD, 0x00000000, {(int32_t)0x00008B93, (int32_t)0x0000873F, (int32_t)0x000099A4}, 1},
116      {"/validatedemos/TEST044.DEM", (int32_t)0x000012BD, 0xD53A7E17, 0x00000000, {(int32_t)0x0000BF3F, (int32_t)0x000079F1, (int32_t)0xFFFFC9A4}, 1},
117      {"/validatedemos/TEST045.DEM", (int32_t)0x00000579, 0xDCE04A38, 0x00000000, {(int32_t)0x0000CA14, (int32_t)0x000081E9, (int32_t)0x000159A4}, 1},
118      {"/validatedemos/TEST046.DEM", (int32_t)0x00000106, 0x363D780D, 0x00000000, {(int32_t)0xFFFFFF64, (int32_t)0x00009418, (int32_t)0xFFFFE1A4}, 1},
119      {"/validatedemos/TEST047.DEM", (int32_t)0x00000B5F, 0x11F8A1D5, 0x000003BD, {(int32_t)0x00009A45, (int32_t)0x0000847F, (int32_t)0x0000BD50}, 1},
120      {"/validatedemos/TEST048.DEM", (int32_t)0x00000AF3, 0x6E949D93, 0x00000000, {(int32_t)0xFFFF3468, (int32_t)0xFFFFD441, (int32_t)0x000061A4}, 1},
121      {"/validatedemos/TEST049.DEM", (int32_t)0x00000C8F, 0x4E28530B, 0x00000000, {(int32_t)0xFFFFED7C, (int32_t)0x0000C8C0, (int32_t)0x000009A4}, 1},
122      {"/validatedemos/TEST050.DEM", (int32_t)0x0000230B, 0xEA61B563, 0x00000000, {(int32_t)0x000029C1, (int32_t)0x00002C3F, (int32_t)0x000071A4}, 1},
123      {"/validatedemos/TEST051.DEM", (int32_t)0x00002EA8, 0x19510828, 0x00000000, {(int32_t)0x00001FE4, (int32_t)0x000031D9, (int32_t)0x00000DA4}, 1},
124      {"/validatedemos/TEST052.DEM", (int32_t)0x00005088, 0x04A4F873, 0x00000C80, {(int32_t)0x0000C0F4, (int32_t)0x0000B5ED, (int32_t)0xFFFF9F3F}, 1},
125      {"/validatedemos/TEST053.DEM", (int32_t)0x000022BA, 0x93F42352, 0x00000000, {(int32_t)0x00001B69, (int32_t)0x000045D8, (int32_t)0x000011A4}, 1},
126      {"/validatedemos/TEST054.DEM", (int32_t)0x00002C67, 0xE914FDC5, 0x00000000, {(int32_t)0xFFFF66E4, (int32_t)0x000049FF, (int32_t)0x000012E4}, 1},
127      {"/validatedemos/TEST055.DEM", (int32_t)0x00001F8B, 0x1B5F1C0F, 0x00000000, {(int32_t)0xFFFFDD1F, (int32_t)0x00003A35, (int32_t)0x000059A4}, 1},
128      {"/validatedemos/TEST056.DEM", (int32_t)0x00003EE5, 0xD02F860D, 0x00000000, {(int32_t)0xFFFF8824, (int32_t)0x0000543E, (int32_t)0x0000C1A4}, 1},
129      {"/validatedemos/TEST057.DEM", (int32_t)0x00006979, 0xD9EC81A9, 0x00000000, {(int32_t)0xFFFF9197, (int32_t)0x000063D2, (int32_t)0x000061A4}, 1},
130      {"/validatedemos/TEST058.DEM", (int32_t)0x00001C16, 0xD85F3129, 0x0000082F, {(int32_t)0xFFFFADAA, (int32_t)0xFFFF67D4, (int32_t)0xFFFFDD50}, 1},
131      {"/validatedemos/TEST059.DEM", (int32_t)0x0000244B, 0x1E82B956, 0x00000000, {(int32_t)0x000000A4, (int32_t)0x00001F09, (int32_t)0x000219A4}, 1},
132      {"/validatedemos/TEST060.DEM", (int32_t)0x000067CF, 0x086B69C7, 0x00000315, {(int32_t)0xFFFFA704, (int32_t)0x0000D1A8, (int32_t)0xFFFFFD50}, 1},
133      {"/validatedemos/TEST061.DEM", (int32_t)0x000014BB, 0x89393200, 0x00000000, {(int32_t)0xFFFFC242, (int32_t)0xFFFF7FAD, (int32_t)0xFFFFCDA4}, 1},
134      {"/validatedemos/TEST062.DEM", (int32_t)0x00002180, 0xB2F4CC77, 0x00000000, {(int32_t)0xFFFFB4AD, (int32_t)0x0000137E, (int32_t)0x000061A4}, 1},
135      {"/validatedemos/TEST063.DEM", (int32_t)0x00000736, 0xB077ACC3, 0x00000000, {(int32_t)0xFFFF61FF, (int32_t)0xFFFFE7BA, (int32_t)0x000119A4}, 1},
136      {"/validatedemos/TEST064.DEM", (int32_t)0x0000443E, 0x5FE50A44, 0x00000000, {(int32_t)0xFFFFF382, (int32_t)0x000086A6, (int32_t)0x000235A4}, 1},
137      {"/validatedemos/TEST065.DEM", (int32_t)0x00003EBD, 0xA7A0D9C5, 0x00000247, {(int32_t)0xFFFF6B20, (int32_t)0xFFFFC641, (int32_t)0xFFFE1950}, 1},
138      {"/validatedemos/TEST066.DEM", (int32_t)0x00003229, 0xAD1A9240, 0x00000000, {(int32_t)0xFFFF2809, (int32_t)0x0000A21E, (int32_t)0xFFFF59A4}, 1},
139      {"/validatedemos/TEST067.DEM", (int32_t)0x0000374C, 0x19C88BDF, 0x00000615, {(int32_t)0xFFFFBC69, (int32_t)0xFFFF71B6, (int32_t)0xFFF9B97D}, 1},
140      {"/validatedemos/TEST068.DEM", (int32_t)0x000045FF, 0x06B9C43C, 0x000004B9, {(int32_t)0xFFFEFF2E, (int32_t)0xFFFEE747, (int32_t)0xFFFE7950}, 1},
141      {"/validatedemos/TEST069.DEM", (int32_t)0x00004B51, 0x1806DD38, 0x00000000, {(int32_t)0xFFFF430F, (int32_t)0x0000CF3F, (int32_t)0x000211A4}, 1},
142      {"/validatedemos/TEST070.DEM", (int32_t)0x00001070, 0xA2B9B378, 0x00000000, {(int32_t)0x0000D06B, (int32_t)0x0001176F, (int32_t)0x000189A4}, 1},
143      {"/validatedemos/TEST071.DEM", (int32_t)0x000082E6, 0xC74AB5A2, 0x0000016C, {(int32_t)0xFFFF80A0, (int32_t)0x00005BA3, (int32_t)0x00002950}, 1},
144      {"/validatedemos/TEST072.DEM", (int32_t)0x000047C6, 0xA4405651, 0x00000000, {(int32_t)0x000099B6, (int32_t)0x000064BE, (int32_t)0x00006DA4}, 1},
145      {"/validatedemos/TEST073.DEM", (int32_t)0x00002FD7, 0x11377903, 0x00000000, {(int32_t)0xFFFF7EC1, (int32_t)0xFFFEEF5F, (int32_t)0xFFF701A4}, 1},
146      {"/validatedemos/TEST074.DEM", (int32_t)0x0000287A, 0xACAC602D, 0x00000000, {(int32_t)0x0000D43F, (int32_t)0x000092C1, (int32_t)0x00002844}, 1},
147      {"/validatedemos/TEST075.DEM", (int32_t)0x000013DD, 0x78D2DFA4, 0x00000000, {(int32_t)0x00002362, (int32_t)0x00013B0D, (int32_t)0x000069A4}, 1},
148      {"/validatedemos/TEST076.DEM", (int32_t)0x0000368A, 0x99FF70C3, 0x00000094, {(int32_t)0x00005624, (int32_t)0xFFFFD547, (int32_t)0x000000CE}, 1},
149      {"/validatedemos/TEST077.DEM", (int32_t)0x00000837, 0x731125E0, 0x00000536, {(int32_t)0xFFFF8914, (int32_t)0xFFFF7F20, (int32_t)0x00002AEA}, 1},
150      {"/validatedemos/TEST078.DEM", (int32_t)0x000004BB, 0x40CD5E4D, 0x00000640, {(int32_t)0x0001FCBB, (int32_t)0x0000412E, (int32_t)0x0004BAB4}, 1},
151      {"/validatedemos/TEST079.DEM", (int32_t)0x000019E3, 0x4B34312A, 0x00000000, {(int32_t)0x000144F5, (int32_t)0x000143BE, (int32_t)0x00006DA4}, 1},
152      {"/validatedemos/TEST080.DEM", (int32_t)0x00001169, 0xF488E48C, 0x00000000, {(int32_t)0x00008898, (int32_t)0xFFFF5BCD, (int32_t)0x00005DA4}, 1},
153      {"/validatedemos/TEST081.DEM", (int32_t)0x00000864, 0xC181DEFE, 0x00000442, {(int32_t)0x00007927, (int32_t)0x00009CD7, (int32_t)0x00013D50}, 1},
154      {"/validatedemos/TEST082.DEM", (int32_t)0x0000147B, 0x52769222, 0x000005EC, {(int32_t)0x00009BC9, (int32_t)0x00006EF9, (int32_t)0x00003550}, 1},
155      {"/validatedemos/TEST083.DEM", (int32_t)0x00005D8D, 0xB9847039, 0x00000000, {(int32_t)0xFFFFC05A, (int32_t)0x0000AADA, (int32_t)0x000035A4}, 1},
156      {"/validatedemos/TEST084.DEM", (int32_t)0x00004328, 0x74A65BA3, 0x0000009E, {(int32_t)0x000089F0, (int32_t)0xFFFF861C, (int32_t)0xFFFFF950}, 1},
157      {"/validatedemos/TEST085.DEM", (int32_t)0x0000561D, 0x759EAFB7, 0x00000A6E, {(int32_t)0xFFFFF591, (int32_t)0x000059D9, (int32_t)0x00001DE4}, 1},
158      {"/validatedemos/TEST086.DEM", (int32_t)0x00002820, 0x3701B79E, 0x00000000, {(int32_t)0x000018C5, (int32_t)0xFFFFC8C1, (int32_t)0x000019A4}, 0},
159      {"/validatedemos/TEST087.DEM", (int32_t)0x000068CF, 0x0112953A, 0x00000640, {(int32_t)0x00000913, (int32_t)0x00005B0A, (int32_t)0x0001C150}, 0},
160      {"/validatedemos/TEST088.DEM", (int32_t)0x000023DF, 0x4F0E5AB3, 0x00000000, {(int32_t)0xFFFF99A5, (int32_t)0xFFFFE2E1, (int32_t)0x000021A4}, 0},
161      {"/validatedemos/TEST089.DEM", (int32_t)0x00005437, 0x5DEE1B13, 0x00000405, {(int32_t)0xFFFF65C1, (int32_t)0xFFFFA4A3, (int32_t)0xFFFEF550}, 1},
162      {"/validatedemos/TEST090.DEM", (int32_t)0x0000258B, 0xFD21283F, 0x00000AF9, {(int32_t)0x0000A0C1, (int32_t)0x0000A10A, (int32_t)0x00020DE4}, 1},
163      {"/validatedemos/TEST091.DEM", (int32_t)0x000034C5, 0x8893658D, 0x00000958, {(int32_t)0x0000AB42, (int32_t)0x00001360, (int32_t)0xFFFFFD50}, 1},
164      {"/validatedemos/TEST092.DEM", (int32_t)0x00000D99, 0xF75E793F, 0x00000000, {(int32_t)0x00003C9D, (int32_t)0x0000D7BB, (int32_t)0xFFFF99A4}, 1},
165      {"/validatedemos/TEST093.DEM", (int32_t)0x000075C7, 0xB1752688, 0x00000640, {(int32_t)0x00006693, (int32_t)0xFFFF226B, (int32_t)0xFFFD79E4}, 1},
166      {"/validatedemos/TEST094.DEM", (int32_t)0x00005264, 0x119CF6D1, 0x00000000, {(int32_t)0x0000AE5B, (int32_t)0x00005036, (int32_t)0x000025A4}, 1},
167      {"/validatedemos/TEST095.DEM", (int32_t)0x00002C7D, 0x6E7B8D97, 0x00000000, {(int32_t)0xFFFFEF48, (int32_t)0xFFFF9643, (int32_t)0x000099A4}, 1},
168      {"/validatedemos/TEST096.DEM", (int32_t)0x00003647, 0xC2DE5F9B, 0x00000000, {(int32_t)0xFFFFF402, (int32_t)0x00000096, (int32_t)0x000389A4}, 1},
169      {"/validatedemos/TEST097.DEM", (int32_t)0x00003902, 0x04ADDBE5, 0x00000000, {(int32_t)0x00014C09, (int32_t)0x00011CC1, (int32_t)0x000005A4}, 1}, // this demo crashes DOS v1.21 (ERROR (1344) src\ai.cpp)
170      {"/validatedemos/TEST098.DEM", (int32_t)0x00003061, 0xB813201F, 0x0000043C, {(int32_t)0x0000957B, (int32_t)0x0001362C, (int32_t)0xFFFFE9E4}, 1},
171      {"/validatedemos/TEST099.DEM", (int32_t)0x00000D8E, 0xCC24C186, 0x00000000, {(int32_t)0x0000143E, (int32_t)0x0000F753, (int32_t)0x0001F5A4}, 1},
172      {"/validatedemos/TEST100.DEM", (int32_t)0x000050EE, 0x4A970C28, 0x00000128, {(int32_t)0x00001872, (int32_t)0x0000B208, (int32_t)0xFFFF0D50}, 1},
173      {"/validatedemos/TEST101.DEM", (int32_t)0x00000DF2, 0x6059C7F7, 0x000004A4, {(int32_t)0x000017EB, (int32_t)0x000035CA, (int32_t)0xFFFFF1E4}, 1},
174      {"/validatedemos/TEST102.DEM", (int32_t)0x000018C9, 0x972B0857, 0x00000053, {(int32_t)0x000016CF, (int32_t)0x00003540, (int32_t)0xFFFFF150}, 1},
175      {"/validatedemos/TEST103.DEM", (int32_t)0x000005D9, 0x79F3760B, 0x00000000, {(int32_t)0x00001B3F, (int32_t)0xFFFFAC41, (int32_t)0x000041A4}, 0},
176      {"/validatedemos/TEST104.DEM", (int32_t)0x00000ED3, 0xC1E67725, 0x00000000, {(int32_t)0x000029C1, (int32_t)0x000054BD, (int32_t)0x000069A4}, 0},
177      {"/validatedemos/TEST105.DEM", (int32_t)0x000007A2, 0x48007932, 0x00000000, {(int32_t)0x0000267F, (int32_t)0xFFFFFC17, (int32_t)0x000041A4}, 0},
178      {"/validatedemos/TEST106.DEM", (int32_t)0x000008E5, 0x195F6658, 0x00000000, {(int32_t)0x0000113F, (int32_t)0x000034C1, (int32_t)0x000019A4}, 0},
179      {"/validatedemos/TEST107.DEM", (int32_t)0x000017D4, 0x5C2E2774, 0x00000000, {(int32_t)0x0000180D, (int32_t)0x000021C1, (int32_t)0xFFFFD9A4}, 0},
180      {"/validatedemos/TEST108.DEM", (int32_t)0x000045C3, 0x79AE9FDD, 0x0000029D, {(int32_t)0x000011AD, (int32_t)0xFFFFB247, (int32_t)0x00002550}, 0},
181      {"/validatedemos/TEST109.DEM", (int32_t)0x000038B4, 0x2197456B, 0x00000000, {(int32_t)0x0000196A, (int32_t)0xFFFF5C13, (int32_t)0xFFFFF9A4}, 1},
182      {"/validatedemos/TEST110.DEM", (int32_t)0x00004ACC, 0xB2E036DC, 0x00000000, {(int32_t)0x000102C5, (int32_t)0x00004CC3, (int32_t)0x000031A4}, 1},
183      {"/validatedemos/TEST111.DEM", (int32_t)0x000018A1, 0x7E4E907C, 0x00000000, {(int32_t)0x0000BDC1, (int32_t)0x0000583F, (int32_t)0x000019A4}, 1},
184      {"/validatedemos/TEST112.DEM", (int32_t)0x00003F4C, 0x50D29210, 0x00000000, {(int32_t)0x0000F6A1, (int32_t)0xFFFED26B, (int32_t)0x00024DA4}, 1},
185      {"/validatedemos/TEST113.DEM", (int32_t)0x000025DC, 0xCD2519A0, 0x0000001F, {(int32_t)0xFFFFFA41, (int32_t)0xFFFFD353, (int32_t)0x0000FD50}, 0},
186      {"/validatedemos/TEST114.DEM", (int32_t)0x00001664, 0x62DBFA86, 0x00000000, {(int32_t)0x0000787B, (int32_t)0xFFFFDB53, (int32_t)0x000019A4}, 1},
187      {"/validatedemos/TEST115.DEM", (int32_t)0x00001B5E, 0xB8CFF3D2, 0x00000000, {(int32_t)0xFFFF37FF, (int32_t)0xFFFFDB3F, (int32_t)0x0000E9A4}, 1},
188      {"/validatedemos/TEST116.DEM", (int32_t)0x0000638C, 0xBB29CB40, 0x00000405, {(int32_t)0xFFFF7632, (int32_t)0x000053F7, (int32_t)0xFFFF8D50}, 1},
189      {"/validatedemos/TEST117.DEM", (int32_t)0x00001549, 0xABD36DE5, 0x00000640, {(int32_t)0x00009ACB, (int32_t)0x00006CC1, (int32_t)0x00003550}, 1}, // this demo crashes DOS v1.21 (ERROR (3339) src\actor.cpp Bad Dude Failed: initial=0 type=0 NORMAL)
190      {"/validatedemos/TEST118.DEM", (int32_t)0x00001B56, 0xF88C22B7, 0x00000640, {(int32_t)0x00009C57, (int32_t)0x00006F1B, (int32_t)0x00003550}, 1},
191      {"/validatedemos/TEST119.DEM", (int32_t)0x00000E93, 0x499EF35B, 0x00000000, {(int32_t)0x00009AD5, (int32_t)0x000096BC, (int32_t)0x000159A4}, 1},
192      {"/validatedemos/TEST120.DEM", (int32_t)0x000022A5, 0xC323F11D, 0x00000000, {(int32_t)0xFFFFAF9D, (int32_t)0xFFFF93D4, (int32_t)0x000009A4}, 1},
193      {"/validatedemos/TEST121.DEM", (int32_t)0x0000349D, 0x2A8C0171, 0x00000000, {(int32_t)0xFFFFC62C, (int32_t)0xFFFFA38D, (int32_t)0x000009A4}, 1},
194      {"/validatedemos/TEST122.DEM", (int32_t)0x00001205, 0xFBAFA0C9, 0x00000000, {(int32_t)0x0000399A, (int32_t)0x0000033F, (int32_t)0x0000B9A4}, 1},
195      {"/validatedemos/TEST123.DEM", (int32_t)0x00002672, 0x5660A5AA, 0x00000AF6, {(int32_t)0xFFFFFAD0, (int32_t)0xFFFFD48D, (int32_t)0x0000FDE4}, 1},
196      {"/validatedemos/TEST124.DEM", (int32_t)0x000015A5, 0xFC3CC30B, 0x00000000, {(int32_t)0xFFFF9DE6, (int32_t)0xFFFF907D, (int32_t)0x000009A4}, 1},
197      {"/validatedemos/TEST125.DEM", (int32_t)0x000009AB, 0x68DB200E, 0x00000000, {(int32_t)0xFFFFF93C, (int32_t)0x00006C0C, (int32_t)0xFFFEFDA4}, 1},
198      {"/validatedemos/TEST126.DEM", (int32_t)0x000008F7, 0xC60781A6, 0x00000000, {(int32_t)0x000026A0, (int32_t)0x00004087, (int32_t)0xFFFFC9A4}, 0},
199      {"/validatedemos/TEST127.DEM", (int32_t)0x00001BD3, 0xA53BC5B3, 0x00000C21, {(int32_t)0x000017E7, (int32_t)0x000036A7, (int32_t)0xFFFFF1E4}, 0},
200      {"/validatedemos/TEST128.DEM", (int32_t)0x000022CE, 0x1F4DC465, 0x00000640, {(int32_t)0x000016D6, (int32_t)0x00003526, (int32_t)0xFFFFF1E4}, 0},
201      {"/validatedemos/TEST129.DEM", (int32_t)0x00000B4C, 0x4A2A6308, 0x00000000, {(int32_t)0x0000393F, (int32_t)0x00005E42, (int32_t)0x000051A4}, 0},
202      {"/validatedemos/TEST130.DEM", (int32_t)0x00002B93, 0xF4B28CC5, 0x00000000, {(int32_t)0x000093EF, (int32_t)0x00001244, (int32_t)0x000119A4}, 1},
203      {"/validatedemos/TEST131.DEM", (int32_t)0x0000633B, 0x169354F2, 0x0000048F, {(int32_t)0x0000BDC6, (int32_t)0x00005363, (int32_t)0x000009E4}, 1},
204      {"/validatedemos/TEST132.DEM", (int32_t)0x000044A2, 0x9AB1E4C7, 0x00000640, {(int32_t)0xFFFFB6EA, (int32_t)0xFFFF87BA, (int32_t)0x00005D50}, 1},
205      {"/validatedemos/TEST133.DEM", (int32_t)0x00001075, 0x090AEA76, 0x00000000, {(int32_t)0xFFFF327B, (int32_t)0x00009CEB, (int32_t)0x00003EE4}, 1},
206      {"/validatedemos/TEST134.DEM", (int32_t)0x000008E1, 0xB172DEB5, 0x00000000, {(int32_t)0x00000482, (int32_t)0x000078C1, (int32_t)0xFFFF39A4}, 1},
207      {"/validatedemos/TEST135.DEM", (int32_t)0x00002B13, 0xED1F723F, 0x000002FF, {(int32_t)0x0000A241, (int32_t)0x0000A067, (int32_t)0x00020D50}, 1},
208      {"/validatedemos/TEST136.DEM", (int32_t)0x00003419, 0x5BD6E490, 0x00000B1B, {(int32_t)0x0000A245, (int32_t)0x00009F55, (int32_t)0x00020DE4}, 1},
209      {"/validatedemos/TEST137.DEM", (int32_t)0x00004077, 0x9C07094D, 0x00000330, {(int32_t)0x00005CCD, (int32_t)0x00009154, (int32_t)0x00003D50}, 1},
210      {"/validatedemos/TEST138.DEM", (int32_t)0x00002C1A, 0x0E08B278, 0x000006AC, {(int32_t)0x00005DBF, (int32_t)0x000092B4, (int32_t)0x00003D50}, 0},
211      {"/validatedemos/TEST139.DEM", (int32_t)0x000005EE, 0x1E12503F, 0x00000000, {(int32_t)0xFFFFFE24, (int32_t)0xFFFFDB4E, (int32_t)0xFFFFF9A4}, 1},
212      {"/validatedemos/TEST140.DEM", (int32_t)0x0000AFAA, 0x49A8C956, 0x00000479, {(int32_t)0xFFFFB6DD, (int32_t)0xFFFF8798, (int32_t)0x00005D50}, 1},
213      {"/validatedemos/TEST141.DEM", (int32_t)0x00003B15, 0xBBFDA9CB, 0x00000B15, {(int32_t)0x00001A89, (int32_t)0x00001EE2, (int32_t)0x00002550}, 1},
214      {"/validatedemos/TEST142.DEM", (int32_t)0x0000452E, 0x0980D36B, 0x000008DB, {(int32_t)0x0000273F, (int32_t)0x00004E87, (int32_t)0xFFFFFD50}, 1},
215      {"/validatedemos/TEST143.DEM", (int32_t)0x000020B1, 0x23CF6A95, 0x00000000, {(int32_t)0x00001C53, (int32_t)0x00004932, (int32_t)0x000019A4}, 1},
216  };
217  
218  int nBuild = 0;
219  
220  void ReadGameOptionsLegacy(GAMEOPTIONS &gameOptions, GAMEOPTIONSLEGACY &gameOptionsLegacy)
221  {
222      gameOptions.nGameType = gameOptionsLegacy.nGameType;
223      gameOptions.nDifficulty = gameOptionsLegacy.nDifficulty;
224      gameOptions.nEpisode = gameOptionsLegacy.nEpisode;
225      gameOptions.nLevel = gameOptionsLegacy.nLevel;
226      strcpy(gameOptions.zLevelName, gameOptionsLegacy.zLevelName);
227      strcpy(gameOptions.zLevelSong, gameOptionsLegacy.zLevelSong);
228      gameOptions.nTrackNumber = gameOptionsLegacy.nTrackNumber;
229      //strcpy(gameOptions.szSaveGameName, gameOptionsLegacy.szSaveGameName);
230      //strcpy(gameOptions.szUserGameName, gameOptionsLegacy.szUserGameName);
231      gameOptions.nSaveGameSlot = gameOptionsLegacy.nSaveGameSlot;
232      gameOptions.picEntry = gameOptionsLegacy.picEntry;
233      gameOptions.uMapCRC = gameOptionsLegacy.uMapCRC;
234      gameOptions.nMonsterSettings = gameOptionsLegacy.nMonsterSettings;
235      gameOptions.uGameFlags = gameOptionsLegacy.uGameFlags;
236      gameOptions.uNetGameFlags = gameOptionsLegacy.uNetGameFlags;
237      gameOptions.nWeaponSettings = gameOptionsLegacy.nWeaponSettings;
238      gameOptions.nItemSettings = gameOptionsLegacy.nItemSettings;
239      gameOptions.nRespawnSettings = gameOptionsLegacy.nRespawnSettings;
240      gameOptions.nTeamSettings = gameOptionsLegacy.nTeamSettings;
241      gameOptions.nMonsterRespawnTime = gameOptionsLegacy.nMonsterRespawnTime;
242      gameOptions.nWeaponRespawnTime = gameOptionsLegacy.nWeaponRespawnTime;
243      gameOptions.nItemRespawnTime = gameOptionsLegacy.nItemRespawnTime;
244      gameOptions.nSpecialRespawnTime = gameOptionsLegacy.nSpecialRespawnTime;
245      gameOptions.nEnemyQuantity = gameOptions.nDifficulty;
246      gameOptions.nEnemyHealth = gameOptions.nDifficulty;
247      gameOptions.nEnemySpeed = 0;
248      gameOptions.nPlayerSpeed = 0;
249      gameOptions.bEnemyShuffle = false;
250      gameOptions.bPitchforkOnly = false;
251      gameOptions.bPermaDeath = false;
252      gameOptions.bFriendlyFire = true;
253      gameOptions.nKeySettings = 0;
254      gameOptions.bItemWeaponSettings = 0;
255      gameOptions.bAutoTeams = 1;
256      gameOptions.nSpawnProtection = 0;
257      gameOptions.nSpawnWeapon = 0;
258      gameOptions.uSpriteBannedFlags = BANNED_NONE;
259  }
260  
261  void WriteGameOptionsLegacy(GAMEOPTIONSLEGACY &gameOptionsLegacy, GAMEOPTIONS &gameOptions)
262  {
263      gameOptionsLegacy.nGameType = gameOptions.nGameType;
264      gameOptionsLegacy.nDifficulty = gameOptions.nDifficulty;
265      gameOptionsLegacy.nEpisode = gameOptions.nEpisode;
266      gameOptionsLegacy.nLevel = gameOptions.nLevel;
267      memset(gameOptionsLegacy.zLevelName, '\0', sizeof(gameOptionsLegacy.zLevelName));
268      memset(gameOptionsLegacy.zLevelSong, '\0', sizeof(gameOptionsLegacy.zLevelSong));
269      strncpy(gameOptionsLegacy.zLevelName, gameOptions.zLevelName, sizeof(gameOptionsLegacy.zLevelName));
270      strncpy(gameOptionsLegacy.zLevelSong, gameOptions.zLevelSong, sizeof(gameOptionsLegacy.zLevelSong));
271      gameOptionsLegacy.nTrackNumber = gameOptions.nTrackNumber;
272      memset(gameOptionsLegacy.szSaveGameName, '\0', sizeof(gameOptionsLegacy.szSaveGameName));
273      memset(gameOptionsLegacy.szUserGameName, '\0', sizeof(gameOptionsLegacy.szUserGameName));
274      gameOptionsLegacy.nSaveGameSlot = gameOptionsLegacy.nSaveGameSlot;
275      gameOptionsLegacy.picEntry = gameOptionsLegacy.picEntry;
276      gameOptionsLegacy.uMapCRC = gameOptionsLegacy.uMapCRC;
277      gameOptionsLegacy.nMonsterSettings = gameOptions.nMonsterSettings;
278      gameOptionsLegacy.uGameFlags = gameOptions.uGameFlags;
279      gameOptionsLegacy.uNetGameFlags = gameOptions.uNetGameFlags;
280      gameOptionsLegacy.nWeaponSettings = gameOptions.nWeaponSettings;
281      gameOptionsLegacy.nItemSettings = gameOptions.nItemSettings;
282      gameOptionsLegacy.nRespawnSettings = gameOptions.nRespawnSettings;
283      gameOptionsLegacy.nTeamSettings = gameOptions.nTeamSettings;
284      gameOptionsLegacy.nMonsterRespawnTime = gameOptions.nMonsterRespawnTime;
285      gameOptionsLegacy.nWeaponRespawnTime = gameOptions.nWeaponRespawnTime;
286      gameOptionsLegacy.nItemRespawnTime = gameOptions.nItemRespawnTime;
287      gameOptionsLegacy.nSpecialRespawnTime = gameOptions.nSpecialRespawnTime;
288  }
289  
290  CDemo gDemo;
291  
292  CDemo::CDemo()
293  {
294      nBuild = 4;
295      bRecording = 0;
296      bPlaying = 0;
297      at3 = 0;
298      hPFile = -1;
299      hRFile = NULL;
300      nInputTicks = 0;
301      pFirstDemo = NULL;
302      pCurrentDemo = NULL;
303      nDemosFound = 0;
304      at2 = 0;
305      memset(&atf, 0, sizeof(atf));
306  }
307  
308  CDemo::~CDemo()
309  {
310      bRecording = 0;
311      bPlaying = 0;
312      at3 = 0;
313      nInputTicks = 0;
314      memset(&atf, 0, sizeof(atf));
315      if (hPFile >= 0)
316      {
317          kclose(hPFile);
318          hPFile = -1;
319      }
320      if (hRFile != NULL)
321      {
322          fclose(hRFile);
323          hRFile = NULL;
324      }
325      auto pNextDemo = pFirstDemo;
326      for (auto pDemo = pFirstDemo; pDemo != NULL; pDemo = pNextDemo)
327      {
328          pNextDemo = pDemo->pNext;
329          delete pDemo;
330      }
331      pFirstDemo = NULL;
332      pCurrentDemo = NULL;
333      nDemosFound = 0;
334  }
335  
336  bool CDemo::Create(const char *pzFile)
337  {
338      char buffer[BMAX_PATH];
339      char vc = 0;
340      if (bRecording || bPlaying)
341          ThrowError("CDemo::Create called during demo record/playback process.");
342      if (!pzFile)
343      {
344          for (int i = 0; i < 8 && !vc; i++)
345          {
346              if (G_ModDirSnprintf(buffer, BMAX_PATH, "%s0%02d.dem", BloodIniPre, i))
347                  return false;
348              if (access(buffer, F_OK) != -1)
349                  vc = 1;
350          }
351          if (vc == 1)
352          {
353              hRFile = fopen(buffer, "wb");
354              if (hRFile == NULL)
355                  return false;
356          }
357      }
358      else
359      {
360          G_ModDirSnprintfLite(buffer, BMAX_PATH, pzFile);
361          hRFile = fopen(buffer, "wb");
362          if (hRFile == NULL)
363              return false;
364      }
365      bRecording = 1;
366      nInputTicks = 0;
367      return true;
368  }
369  
370  void CDemo::Write(GINPUT *pPlayerInputs)
371  {
372      dassert(pPlayerInputs != NULL);
373      if (!bRecording)
374          return;
375      if (nInputTicks == 0)
376      {
377          atf.signature = 0x1a4d4544; // '\x1aMED';
378          atf.nVersion = BloodVersion;
379          atf.nBuild = nBuild;
380          atf.nInputCount = 0;
381          atf.nNetPlayers = gNetPlayers;
382          atf.nMyConnectIndex = myconnectindex;
383          atf.nConnectHead = connecthead;
384          memcpy(atf.connectPoints, connectpoint2, sizeof(atf.connectPoints));
385          GAMEOPTIONSLEGACY gameOptions;
386          memset(&gameOptions, 0, sizeof(gameOptions));
387          WriteGameOptionsLegacy(gameOptions, gGameOptions);
388  #if B_BIG_ENDIAN == 1
389          atf.signature = B_LITTLE32(atf.signature);
390          atf.nVersion = B_LITTLE16(atf.nVersion);
391          atf.nBuild = B_LITTLE32(atf.nBuild);
392          atf.nInputCount = B_LITTLE32(atf.nInputCount);
393          atf.nNetPlayers = B_LITTLE32(atf.nNetPlayers);
394          atf.nMyConnectIndex = B_LITTLE16(atf.nMyConnectIndex);
395          atf.nConnectHead = B_LITTLE16(atf.nConnectHead);
396          for (int i = 0; i < 8; i++)
397              atf.connectPoints[i] = B_LITTLE16(atf.connectPoints[i]);
398  #endif
399          fwrite(&atf, sizeof(DEMOHEADER), 1, hRFile);
400  #if B_BIG_ENDIAN == 1
401          gameOptions.nEpisode = B_LITTLE32(gameOptions.nEpisode);
402          gameOptions.nLevel = B_LITTLE32(gameOptions.nLevel);
403          gameOptions.nTrackNumber = B_LITTLE32(gameOptions.nTrackNumber);
404          gameOptions.nSaveGameSlot = B_LITTLE16(gameOptions.nSaveGameSlot);
405          gameOptions.picEntry = B_LITTLE32(gameOptions.picEntry);
406          gameOptions.uMapCRC = B_LITTLE32(gameOptions.uMapCRC);
407          gameOptions.uGameFlags = B_LITTLE32(gameOptions.uGameFlags);
408          gameOptions.uNetGameFlags = B_LITTLE32(gameOptions.uNetGameFlags);
409          gameOptions.nMonsterRespawnTime = B_LITTLE32(gameOptions.nMonsterRespawnTime);
410          gameOptions.nWeaponRespawnTime = B_LITTLE32(gameOptions.nWeaponRespawnTime);
411          gameOptions.nItemRespawnTime = B_LITTLE32(gameOptions.nItemRespawnTime);
412          gameOptions.nSpecialRespawnTime = B_LITTLE32(gameOptions.nSpecialRespawnTime);
413  #endif
414          fwrite(&gameOptions, sizeof(GAMEOPTIONSLEGACY), 1, hRFile);
415      }
416      for (int p = connecthead; p >= 0; p = connectpoint2[p])
417      {
418          memcpy(&at1aa[nInputTicks&1023], &pPlayerInputs[p], sizeof(GINPUT));
419          nInputTicks++;
420          if ((nInputTicks&(kInputBufferSize-1))==0)
421              FlushInput(kInputBufferSize);
422      }
423  }
424  
425  void CDemo::Close(void)
426  {
427      if (bRecording)
428      {
429          if (nInputTicks&(kInputBufferSize-1))
430              FlushInput(nInputTicks&(kInputBufferSize-1));
431          atf.nInputCount = nInputTicks;
432  #if B_BIG_ENDIAN == 1
433          atf.nInputCount = B_LITTLE32(atf.nInputCount);
434  #endif
435          fseek(hRFile, 0, SEEK_SET);
436          fwrite(&atf, sizeof(DEMOHEADER), 1, hRFile);
437      }
438      if (hPFile >= 0)
439      {
440          kclose(hPFile);
441          hPFile = -1;
442      }
443      if (hRFile != NULL)
444      {
445          fclose(hRFile);
446          hRFile = NULL;
447      }
448      bRecording = 0;
449      bPlaying = 0;
450  }
451  
452  bool CDemo::SetupPlayback(const char *pzFile)
453  {
454      bRecording = 0;
455      bPlaying = 0;
456      if (pzFile)
457      {
458          hPFile = kopen4loadfrommod(pzFile, 0);
459          if (hPFile == -1)
460              return false;
461      }
462      else
463      {
464          if (!pCurrentDemo)
465              return false;
466          hPFile = kopen4loadfrommod(pCurrentDemo->zName, 0);
467          if (hPFile == -1)
468              return false;
469      }
470      kread(hPFile, &atf, sizeof(DEMOHEADER));
471  #if B_BIG_ENDIAN == 1
472      atf.signature = B_LITTLE32(atf.signature);
473      atf.nVersion = B_LITTLE16(atf.nVersion);
474      atf.nBuild = B_LITTLE32(atf.nBuild);
475      atf.nInputCount = B_LITTLE32(atf.nInputCount);
476      atf.nNetPlayers = B_LITTLE32(atf.nNetPlayers);
477      atf.nMyConnectIndex = B_LITTLE16(atf.nMyConnectIndex);
478      atf.nConnectHead = B_LITTLE16(atf.nConnectHead);
479      for (int i = 0; i < 8; i++)
480          atf.connectPoints[i] = B_LITTLE16(atf.connectPoints[i]);
481  #endif
482      if (atf.signature != 0x1a4d4544)
483          return 0;
484      if (BloodVersion != atf.nVersion)
485          return 0;
486      m_gameOptions = gGameOptions;
487      GAMEOPTIONSLEGACY gameOptions;
488      kread(hPFile, &gameOptions, sizeof(GAMEOPTIONSLEGACY));
489      ReadGameOptionsLegacy(m_gameOptions, gameOptions);
490  #if B_BIG_ENDIAN == 1
491      m_gameOptions.nEpisode = B_LITTLE32(m_gameOptions.nEpisode);
492      m_gameOptions.nLevel = B_LITTLE32(m_gameOptions.nLevel);
493      m_gameOptions.nTrackNumber = B_LITTLE32(m_gameOptions.nTrackNumber);
494      m_gameOptions.nSaveGameSlot = B_LITTLE16(m_gameOptions.nSaveGameSlot);
495      m_gameOptions.picEntry = B_LITTLE32(m_gameOptions.picEntry);
496      m_gameOptions.uMapCRC = B_LITTLE32(m_gameOptions.uMapCRC);
497      m_gameOptions.uGameFlags = B_LITTLE32(m_gameOptions.uGameFlags);
498      m_gameOptions.uNetGameFlags = B_LITTLE32(m_gameOptions.uNetGameFlags);
499      m_gameOptions.nMonsterRespawnTime = B_LITTLE32(m_gameOptions.nMonsterRespawnTime);
500      m_gameOptions.nWeaponRespawnTime = B_LITTLE32(m_gameOptions.nWeaponRespawnTime);
501      m_gameOptions.nItemRespawnTime = B_LITTLE32(m_gameOptions.nItemRespawnTime);
502      m_gameOptions.nSpecialRespawnTime = B_LITTLE32(m_gameOptions.nSpecialRespawnTime);
503  #endif
504      bRecording = 0;
505      bPlaying = 1;
506      if (gDemoRunValidation)
507      {
508          timerInit(CLOCKTICKSPERSECOND*500);
509          SoundToggle = MusicToggle = 0; // mute audio while we speedrun demos
510      }
511      return 1;
512  }
513  
514  void CDemo::ProcessKeys(void)
515  {
516      switch (gInputMode)
517      {
518      case INPUT_MODE_1:
519          gGameMenuMgr.Process();
520          break;
521      case INPUT_MODE_2:
522          gPlayerMsg.ProcessKeys();
523          break;
524      case INPUT_MODE_0:
525      {
526          char nKey;
527          while ((nKey = keyGetScan()) != 0)
528          {
529  	        char UNUSED(alt) = keystatus[sc_LeftAlt] | keystatus[sc_RightAlt];
530  	        char UNUSED(ctrl) = keystatus[sc_LeftControl] | keystatus[sc_RightControl];
531              switch (nKey)
532              {
533              case sc_Escape:
534                  if (!CGameMenuMgr::m_bActive)
535                  {
536                      gGameMenuMgr.Push(&menuMain, -1);
537                      at2 = 1;
538                  }
539                  break;
540              case sc_F12:
541                  gViewIndex = connectpoint2[gViewIndex];
542                  if (gViewIndex == -1)
543                      gViewIndex = connecthead;
544                  gView = &gPlayer[gViewIndex];
545                  break;
546              }
547          }
548          if (!nKey && CONTROL_JoystickEnabled)
549          {
550              static int32_t joyold = 0;
551              int32_t joy = JOYSTICK_GetControllerButtons() == (1 << CONTROLLER_BUTTON_START);
552              if (joy && !joyold)
553              {
554                  JOYSTICK_ClearAllButtons();
555                  if (!CGameMenuMgr::m_bActive)
556                  {
557                      gGameMenuMgr.Push(&menuMain, -1);
558                      at2 = 1;
559                  }
560              }
561              joyold = joy;
562          }
563          break;
564      default:
565          gInputMode = INPUT_MODE_0;
566          break;
567      }
568      }
569  }
570  
571  void CDemo::Playback(void)
572  {
573      const DEMOVALIDATE *pValidateInfo;
574      CONTROL_BindsEnabled = false;
575      ready2send = 0;
576      int v4 = 0;
577      if (!CGameMenuMgr::m_bActive)
578      {
579          gGameMenuMgr.Push(&menuMain, -1);
580          if (gSetup.firstlaunch)
581              gGameMenuMgr.Push(&menuFirstLaunch, -1);
582          at2 = 1;
583      }
584      gNetFifoClock = totalclock;
585      gViewMode = 3;
586  _DEMOPLAYBACK:
587      pValidateInfo = NULL;
588      while (bPlaying && !gQuitGame)
589      {
590          while (totalclock >= gNetFifoClock && !gQuitGame)
591          {
592              if (!v4)
593              {
594                  viewResizeView(gViewSize);
595                  viewSetMessage("");
596                  gNetPlayers = atf.nNetPlayers;
597                  nInputTicks = atf.nInputCount;
598                  myconnectindex = atf.nMyConnectIndex;
599                  connecthead = atf.nConnectHead;
600                  for (int i = 0; i < 8; i++)
601                      connectpoint2[i] = atf.connectPoints[i];
602                  memset(gNetFifoHead, 0, sizeof(gNetFifoHead));
603                  gNetFifoTail = 0;
604                  //memcpy(connectpoint2, aimHeight.connectPoints, sizeof(aimHeight.connectPoints));
605                  memcpy(&gGameOptions, &m_gameOptions, sizeof(GAMEOPTIONS));
606                  gGameOptions.uGameFlags &= ~kGameFlagContinuing; // don't let demo attempt to load player health from gHealthTemp
607                  playerSetSkill(gGameOptions.nDifficulty); // set skill to same value as current difficulty
608                  for (int i = 0; i < kMaxPlayers; i++)
609                      playerInit(i, 0);
610                  StartLevel(&gGameOptions);
611                  for (int i = 0; i < kMaxPlayers; i++) // force player settings for demos
612                  {
613                      gProfile[i].nAutoAim = 1;
614                      gProfile[i].nWeaponSwitch = 1;
615                      gProfile[i].bWeaponFastSwitch = 0;
616                      gProfile[i].nWeaponHBobbing = 1;
617                      gProfileNet[i] = gProfile[i];
618                  }
619                  if (gDemoRunValidation) // if we're executing validation test
620                  {
621                      for (int index = 0; index < ARRAY_SSIZE(gDemoValidate); index++) // search for current demo in list of known valid results
622                      {
623                          if (nInputTicks != gDemoValidate[index].nInputTicks) // demo ticks not matching/demo name does not exist, skip
624                              continue;
625                          if (!pCurrentDemo || Bstrcasecmp(pCurrentDemo->zName, gDemoValidate[index].zName)) // demo name does not match, skip
626                              continue;
627                          pValidateInfo = &gDemoValidate[index]; // found demo's verified results, set as validate info
628                          gProfile[myconnectindex].nAutoAim = pValidateInfo->nAutoAim; // assign auto aim setting from validate info
629                          break;
630                      }
631                      if (!pValidateInfo) // run newly added verify demos at a slower speed for visual verification
632                          timerInit(CLOCKTICKSPERSECOND*5);
633                  }
634              }
635              ready2send = 0;
636              OSD_DispatchQueued();
637              if (!gDemo.bPlaying)
638                  break;
639              ProcessKeys();
640              for (int p = connecthead; p >= 0; p = connectpoint2[p])
641              {
642                  if ((v4&1023) == 0)
643                  {
644                      unsigned int nSize = nInputTicks-v4;
645                      if (nSize > kInputBufferSize)
646                          nSize = kInputBufferSize;
647                      ReadInput(nSize);
648                  }
649                  memcpy(&gFifoInput[gNetFifoHead[p]&255], &at1aa[v4&1023], sizeof(GINPUT));
650                  gNetFifoHead[p]++;
651                  v4++;
652                  if (v4 >= atf.nInputCount)
653                  {
654                      ready2send = 0;
655                      if (pValidateInfo) // if validate demo info are known, run checks
656                      {
657                          char bInvalid = 0;
658                          if (wrandomseed != pValidateInfo->wrandomseed)
659                          {
660                              bInvalid = 1;
661                              OSD_Printf("Error: Random seed desync\n");
662                          }
663                          if (gPlayer[myconnectindex].pSprite->xyz != pValidateInfo->xyz)
664                          {
665                              bInvalid = 1;
666                              OSD_Printf("Error: Player position desync\n");
667                          }
668                          if (xsprite[gPlayer[myconnectindex].pSprite->extra].health != pValidateInfo->health)
669                          {
670                              bInvalid = 1;
671                              OSD_Printf("Error: Player health desync\n");
672                          }
673                          if (bInvalid)
674                          {
675                              OSD_Printf("Error: %s desync (see log for detail)\n", pCurrentDemo->zName);
676                              ThrowError("Error: %s desync (see log for detail)", pCurrentDemo->zName);
677                              gQuitGame = true;
678                          }
679                          else
680                          {
681                              OSD_Printf("Demo Synced\n");
682                          }
683                      }
684                      else if (gDemoRunValidation && pCurrentDemo) // print validation result for new demo
685                      {
686                          OSD_Printf("{\"%s\", (int32_t)0x%08X, 0x%08X, 0x%08X, {(int32_t)0x%08X, (int32_t)0x%08X, (int32_t)0x%08X}, %d},", pCurrentDemo->zName, nInputTicks, wrandomseed, (unsigned int)xsprite[gPlayer[myconnectindex].pSprite->extra].health, (unsigned int)gPlayer[myconnectindex].pSprite->x, (unsigned int)gPlayer[myconnectindex].pSprite->y, (unsigned int)gPlayer[myconnectindex].pSprite->z, gProfile[myconnectindex].nAutoAim);
687                      }
688                      if (nDemosFound > 1)
689                      {
690                          v4 = 0;
691                          Close();
692                          if (gDemoRunValidation && pCurrentDemo && !pCurrentDemo->pNext) // finished validation, abort
693                          {
694                              uint32_t nTotalTicks = 0;
695                              for (int index = 0; index < ARRAY_SSIZE(gDemoValidate); index++)
696                                  nTotalTicks += (uint32_t)gDemoValidate[index].nInputTicks;
697                              OSD_Printf("1.21 Validation Successful!\nTotal Demo Hours: %02d:%02d", nTotalTicks/(kTicsPerSec*60)/60, nTotalTicks/(kTicsPerSec*60)%60);
698                              gQuitGame = true;
699                              break;
700                          }
701                          NextDemo();
702                          gNetFifoClock = totalclock;
703                          goto _DEMOPLAYBACK;
704                      }
705                      else
706                      {
707                          int const nOffset = sizeof(DEMOHEADER)+sizeof(GAMEOPTIONSLEGACY);
708                          klseek(hPFile, nOffset, SEEK_SET);
709                          v4 = 0;
710                      }
711                  }
712              }
713              gNetFifoClock += kTicsPerFrame;
714              if (!gQuitGame)
715                  ProcessFrame();
716              ready2send = 0;
717          }
718          if (engineFPSLimit())
719          {
720              if (handleevents() && quitevent)
721              {
722                  KB_KeyDown[sc_Escape] = 1;
723                  quitevent = 0;
724              }
725              if (!gDemoRunValidation)
726                  MUSIC_Update();
727              viewDrawScreen();
728              if ((gInputMode == INPUT_MODE_1) && CGameMenuMgr::m_bActive && !gDemoRunValidation)
729              {
730                  if (gGameStarted && gViewDim) // dim background
731                      viewDimScreen();
732                  gGameMenuMgr.Draw();
733              }
734              else if (gDemoRunValidation) // keep game locked
735                  gInputMode = INPUT_MODE_1;
736              videoNextPage();
737          }
738          if (TestBitString(gotpic, 2342))
739          {
740              FireProcess();
741              ClearBitString(gotpic, 2342);
742          }
743      }
744      Close();
745  }
746  
747  void CDemo::StopPlayback(void)
748  {
749      bPlaying = 0;
750  }
751  
752  void CDemo::LoadDemoInfo(void)
753  {
754      auto pDemo = &pFirstDemo;
755      const int opsm = pathsearchmode;
756      nDemosFound = 0;
757      pathsearchmode = 0;
758      char zFN[BMAX_PATH];
759      Bsnprintf(zFN, BMAX_PATH, "%s*.dem", BloodIniPre);
760      auto pList = klistpath(!gDemoRunValidation ? "/" : "/validatedemos/", zFN, BUILDVFS_FIND_FILE);
761      auto pIterator = pList;
762      while (pIterator != NULL)
763      {
764          int hFile;
765          if (gDemoRunValidation)
766          {
767              char bSkipBadDemo = 0;
768              for (size_t i = 0; i < ARRAY_SIZE(gDemoInvalid); i++)
769              {
770                  if (!strcmp(pIterator->name, gDemoInvalid[i]))
771                  {
772                      bSkipBadDemo = 1;
773                      break;
774                  }
775              }
776              if (bSkipBadDemo)
777              {
778                  pIterator = pIterator->next;
779                  continue;
780              }
781              Bsnprintf(zFN, BMAX_PATH, "/validatedemos/%s", pIterator->name);
782              hFile = kopen4loadfrommod(zFN, 0);
783          }
784          else
785          {
786              hFile = kopen4loadfrommod(pIterator->name, 0);
787          }
788          if (hFile == -1)
789              ThrowError("Error loading demo file header.");
790          kread(hFile, &atf, sizeof(atf));
791          kclose(hFile);
792  #if B_BIG_ENDIAN == 1
793          atf.signature = B_LITTLE32(atf.signature);
794          atf.nVersion = B_LITTLE16(atf.nVersion);
795  #endif
796          if ((atf.signature == 0x1a4d4544 /* '\x1aMED' */&& atf.nVersion == BloodVersion))
797          {
798              *pDemo = new DEMOCHAIN;
799              (*pDemo)->pNext = NULL;
800              Bstrncpy((*pDemo)->zName, !gDemoRunValidation ? pIterator->name : zFN, BMAX_PATH);
801              nDemosFound++;
802              pDemo = &(*pDemo)->pNext;
803          }
804          pIterator = pIterator->next;
805      }
806      klistfree(pList);
807      pathsearchmode = opsm;
808      pCurrentDemo = pFirstDemo;
809  }
810  
811  void CDemo::NextDemo(void)
812  {
813      pCurrentDemo = pCurrentDemo->pNext ? pCurrentDemo->pNext : pFirstDemo;
814      SetupPlayback(NULL);
815  }
816  
817  #define kInputSize 22
818  static char pBuffer[kInputSize*kInputBufferSize];
819  
820  void CDemo::FlushInput(int nCount)
821  {
822      BitWriter bitWriter(pBuffer, sizeof(pBuffer));
823      for (int i = 0; i < nCount; i++)
824      {
825          GINPUT *pInput = &at1aa[i];
826          bitWriter.writeBit(pInput->syncFlags.buttonChange);
827          bitWriter.writeBit(pInput->syncFlags.keyChange);
828          bitWriter.writeBit(pInput->syncFlags.useChange);
829          bitWriter.writeBit(pInput->syncFlags.weaponChange);
830          bitWriter.writeBit(pInput->syncFlags.mlookChange);
831          bitWriter.writeBit(pInput->syncFlags.run);
832          bitWriter.skipBits(26);
833          bitWriter.write(pInput->forward>>8, 8);
834          bitWriter.write(fix16_to_int(pInput->q16turn<<2), 16);
835          bitWriter.write(pInput->strafe>>8, 8);
836          bitWriter.writeBit(pInput->buttonFlags.jump);
837          bitWriter.writeBit(pInput->buttonFlags.crouch);
838          bitWriter.writeBit(pInput->buttonFlags.shoot);
839          bitWriter.writeBit(pInput->buttonFlags.shoot2);
840          bitWriter.writeBit(pInput->buttonFlags.lookUp);
841          bitWriter.writeBit(pInput->buttonFlags.lookDown);
842          bitWriter.skipBits(26);
843          bitWriter.writeBit(pInput->keyFlags.action);
844          //bitWriter.writeBit(pInput->keyFlags.jab); // unused
845          bitWriter.skipBits(1);
846          bitWriter.writeBit(pInput->keyFlags.prevItem);
847          bitWriter.writeBit(pInput->keyFlags.nextItem);
848          bitWriter.writeBit(pInput->keyFlags.useItem);
849          bitWriter.writeBit(pInput->keyFlags.prevWeapon);
850          bitWriter.writeBit(pInput->keyFlags.nextWeapon);
851          bitWriter.writeBit(pInput->keyFlags.holsterWeapon);
852          bitWriter.writeBit(pInput->keyFlags.lookCenter);
853          bitWriter.writeBit(pInput->keyFlags.lookLeft);
854          bitWriter.writeBit(pInput->keyFlags.lookRight);
855          bitWriter.writeBit(pInput->keyFlags.spin180);
856          bitWriter.writeBit(pInput->keyFlags.pause);
857          bitWriter.writeBit(pInput->keyFlags.quit);
858          bitWriter.writeBit(pInput->keyFlags.restart);
859          bitWriter.skipBits(17);
860          bitWriter.writeBit(pInput->useFlags.useBeastVision);
861          bitWriter.writeBit(pInput->useFlags.useCrystalBall);
862          bitWriter.writeBit(pInput->useFlags.useJumpBoots);
863          bitWriter.writeBit(pInput->useFlags.useMedKit);
864          bitWriter.skipBits(28);
865          bitWriter.write(pInput->newWeapon, 8);
866          bitWriter.write(fix16_to_int(pInput->q16mlook*4), 8);
867      }
868      fwrite(pBuffer, 1, kInputSize*nCount, hRFile);
869  }
870  
871  void CDemo::ReadInput(int nCount)
872  {
873      kread(hPFile, pBuffer, kInputSize*nCount);
874      BitReader bitReader(pBuffer, sizeof(pBuffer));
875      memset(at1aa, 0, nCount * sizeof(GINPUT));
876      for (int i = 0; i < nCount; i++)
877      {
878          GINPUT *pInput = &at1aa[i];
879          pInput->syncFlags.buttonChange = bitReader.readBit();
880          pInput->syncFlags.keyChange = bitReader.readBit();
881          pInput->syncFlags.useChange = bitReader.readBit();
882          pInput->syncFlags.weaponChange = bitReader.readBit();
883          pInput->syncFlags.mlookChange = bitReader.readBit();
884          pInput->syncFlags.run = bitReader.readBit();
885          bitReader.skipBits(26);
886          pInput->forward = bitReader.readSigned(8) << 8;
887          pInput->q16turn = fix16_from_int(bitReader.readSigned(16)) >> 2;
888          pInput->strafe = bitReader.readSigned(8) << 8;
889          pInput->buttonFlags.jump = bitReader.readBit();
890          pInput->buttonFlags.crouch = bitReader.readBit();
891          pInput->buttonFlags.shoot = bitReader.readBit();
892          pInput->buttonFlags.shoot2 = bitReader.readBit();
893          pInput->buttonFlags.lookUp = bitReader.readBit();
894          pInput->buttonFlags.lookDown = bitReader.readBit();
895          bitReader.skipBits(26);
896          pInput->keyFlags.action = bitReader.readBit();
897          //pInput->keyFlags.jab = bitReader.readBit(); // unused
898          pInput->keyFlags.isTyping = 0;
899          bitReader.skipBits(1);
900          pInput->keyFlags.prevItem = bitReader.readBit();
901          pInput->keyFlags.nextItem = bitReader.readBit();
902          pInput->keyFlags.useItem = bitReader.readBit();
903          pInput->keyFlags.prevWeapon = bitReader.readBit();
904          pInput->keyFlags.nextWeapon = bitReader.readBit();
905          pInput->keyFlags.holsterWeapon = bitReader.readBit();
906          pInput->keyFlags.lookCenter = bitReader.readBit();
907          pInput->keyFlags.lookLeft = bitReader.readBit();
908          pInput->keyFlags.lookRight = bitReader.readBit();
909          pInput->keyFlags.spin180 = bitReader.readBit();
910          pInput->keyFlags.pause = bitReader.readBit();
911          pInput->keyFlags.quit = bitReader.readBit();
912          pInput->keyFlags.restart = bitReader.readBit();
913          pInput->keyFlags.lastWeapon = 0;
914          bitReader.skipBits(17);
915          pInput->useFlags.useBeastVision = bitReader.readBit();
916          pInput->useFlags.useCrystalBall = bitReader.readBit();
917          pInput->useFlags.useJumpBoots = bitReader.readBit();
918          pInput->useFlags.useMedKit = bitReader.readBit();
919          bitReader.skipBits(28);
920          pInput->newWeapon = bitReader.readUnsigned(8);
921          int mlook = bitReader.readSigned(8);
922          pInput->q16mlook = fix16_from_int(mlook / 4);
923      }
924  }