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 "view.h"
47 
48 int nBuild = 0;
49 
ReadGameOptionsLegacy(GAMEOPTIONS & gameOptions,GAMEOPTIONSLEGACY & gameOptionsLegacy)50 void ReadGameOptionsLegacy(GAMEOPTIONS &gameOptions, GAMEOPTIONSLEGACY &gameOptionsLegacy)
51 {
52     gameOptions.nGameType = gameOptionsLegacy.nGameType;
53     gameOptions.nDifficulty = gameOptionsLegacy.nDifficulty;
54     gameOptions.nEpisode = gameOptionsLegacy.nEpisode;
55     gameOptions.nLevel = gameOptionsLegacy.nLevel;
56     strcpy(gameOptions.zLevelName, gameOptionsLegacy.zLevelName);
57     strcpy(gameOptions.zLevelSong, gameOptionsLegacy.zLevelSong);
58     gameOptions.nTrackNumber = gameOptionsLegacy.nTrackNumber;
59     strcpy(gameOptions.szSaveGameName, gameOptionsLegacy.szSaveGameName);
60     strcpy(gameOptions.szUserGameName, gameOptionsLegacy.szUserGameName);
61     gameOptions.nSaveGameSlot = gameOptionsLegacy.nSaveGameSlot;
62     gameOptions.picEntry = gameOptionsLegacy.picEntry;
63     gameOptions.uMapCRC = gameOptionsLegacy.uMapCRC;
64     gameOptions.nMonsterSettings = gameOptionsLegacy.nMonsterSettings;
65     gameOptions.uGameFlags = gameOptionsLegacy.uGameFlags;
66     gameOptions.uNetGameFlags = gameOptionsLegacy.uNetGameFlags;
67     gameOptions.nWeaponSettings = gameOptionsLegacy.nWeaponSettings;
68     gameOptions.nItemSettings = gameOptionsLegacy.nItemSettings;
69     gameOptions.nRespawnSettings = gameOptionsLegacy.nRespawnSettings;
70     gameOptions.nTeamSettings = gameOptionsLegacy.nTeamSettings;
71     gameOptions.nMonsterRespawnTime = gameOptionsLegacy.nMonsterRespawnTime;
72     gameOptions.nWeaponRespawnTime = gameOptionsLegacy.nWeaponRespawnTime;
73     gameOptions.nItemRespawnTime = gameOptionsLegacy.nItemRespawnTime;
74     gameOptions.nSpecialRespawnTime = gameOptionsLegacy.nSpecialRespawnTime;
75 }
76 
77 CDemo gDemo;
78 
CDemo()79 CDemo::CDemo()
80 {
81     nBuild = 4;
82     at0 = 0;
83     at1 = 0;
84     at3 = 0;
85     hPFile = -1;
86     hRFile = NULL;
87     atb = 0;
88     pFirstDemo = NULL;
89     pCurrentDemo = NULL;
90     at59ef = 0;
91     at2 = 0;
92     memset(&atf, 0, sizeof(atf));
93     m_bLegacy = false;
94 }
95 
~CDemo()96 CDemo::~CDemo()
97 {
98     at0 = 0;
99     at1 = 0;
100     at3 = 0;
101     atb = 0;
102     memset(&atf, 0, sizeof(atf));
103     if (hPFile >= 0)
104     {
105         kclose(hPFile);
106         hPFile = -1;
107     }
108     if (hRFile != NULL)
109     {
110         fclose(hRFile);
111         hRFile = NULL;
112     }
113     auto pNextDemo = pFirstDemo;
114     for (auto pDemo = pFirstDemo; pDemo != NULL; pDemo = pNextDemo)
115     {
116         pNextDemo = pDemo->pNext;
117         delete pDemo;
118     }
119     pFirstDemo = NULL;
120     pCurrentDemo = NULL;
121     at59ef = 0;
122     m_bLegacy = false;
123 }
124 
Create(const char * pzFile)125 bool CDemo::Create(const char *pzFile)
126 {
127     char buffer[BMAX_PATH];
128     char vc = 0;
129     if (at0 || at1)
130         ThrowError("CDemo::Create called during demo record/playback process.");
131     if (!pzFile)
132     {
133         for (int i = 0; i < 8 && !vc; i++)
134         {
135             G_ModDirSnprintf(buffer, BMAX_PATH, "%s0%02d.dem", BloodIniPre, i);
136             if (access(buffer, F_OK) != -1)
137                 vc = 1;
138         }
139         if (vc == 1)
140         {
141             hRFile = fopen(buffer, "wb");
142             if (hRFile == NULL)
143                 return false;
144         }
145     }
146     else
147     {
148         G_ModDirSnprintfLite(buffer, BMAX_PATH, pzFile);
149         hRFile = fopen(buffer, "wb");
150         if (hRFile == NULL)
151             return false;
152     }
153     at0 = 1;
154     atb = 0;
155     return true;
156 }
157 
Write(GINPUT * pPlayerInputs)158 void CDemo::Write(GINPUT *pPlayerInputs)
159 {
160     dassert(pPlayerInputs != NULL);
161     if (!at0)
162         return;
163     if (atb == 0)
164     {
165         atf.signature = 0x1a4d4445; // '\x1aMDE';
166         atf.nVersion = BYTEVERSION;
167         atf.nBuild = nBuild;
168         atf.nInputCount = 0;
169         atf.nNetPlayers = gNetPlayers;
170         atf.nMyConnectIndex = myconnectindex;
171         atf.nConnectHead = connecthead;
172         memcpy(atf.connectPoints, connectpoint2, sizeof(atf.connectPoints));
173         memcpy(&m_gameOptions, &gGameOptions, sizeof(gGameOptions));
174         fwrite(&atf, sizeof(DEMOHEADER), 1, hRFile);
175         fwrite(&m_gameOptions, sizeof(GAMEOPTIONS), 1, hRFile);
176     }
177     for (int p = connecthead; p >= 0; p = connectpoint2[p])
178     {
179         memcpy(&at1aa[atb&1023], &pPlayerInputs[p], sizeof(GINPUT));
180         atb++;
181         if((atb&(kInputBufferSize-1))==0)
182             FlushInput(kInputBufferSize);
183     }
184 }
185 
Close(void)186 void CDemo::Close(void)
187 {
188     if (at0)
189     {
190         if (atb&(kInputBufferSize-1))
191             FlushInput(atb&(kInputBufferSize-1));
192         atf.nInputCount = atb;
193         fseek(hRFile, 0, SEEK_SET);
194         fwrite(&atf, sizeof(DEMOHEADER), 1, hRFile);
195         fwrite(&m_gameOptions, sizeof(GAMEOPTIONS), 1, hRFile);
196     }
197     if (hPFile >= 0)
198     {
199         kclose(hPFile);
200         hPFile = -1;
201     }
202     if (hRFile != NULL)
203     {
204         fclose(hRFile);
205         hRFile = NULL;
206     }
207     at0 = 0;
208     at1 = 0;
209 }
210 
SetupPlayback(const char * pzFile)211 bool CDemo::SetupPlayback(const char *pzFile)
212 {
213     at0 = 0;
214     at1 = 0;
215     if (pzFile)
216     {
217         hPFile = kopen4loadfrommod(pzFile, 0);
218         if (hPFile == -1)
219             return false;
220     }
221     else
222     {
223         if (!pCurrentDemo)
224             return false;
225         hPFile = kopen4loadfrommod(pCurrentDemo->zName, 0);
226         if (hPFile == -1)
227             return false;
228     }
229     kread(hPFile, &atf, sizeof(DEMOHEADER));
230 #if B_BIG_ENDIAN == 1
231     atf.signature = B_LITTLE32(atf.signature);
232     atf.nVersion = B_LITTLE16(atf.nVersion);
233     atf.nBuild = B_LITTLE32(atf.nBuild);
234     atf.nInputCount = B_LITTLE32(atf.nInputCount);
235     atf.nNetPlayers = B_LITTLE32(atf.nNetPlayers);
236     atf.nMyConnectIndex = B_LITTLE16(atf.nMyConnectIndex);
237     atf.nConnectHead = B_LITTLE16(atf.nConnectHead);
238     atf.nMyConnectIndex = B_LITTLE16(atf.nMyConnectIndex);
239     for (int i = 0; i < 8; i++)
240         atf.connectPoints[i] = B_LITTLE16(atf.connectPoints[i]);
241 #endif
242     // if (aimHeight.signature != '\x1aMED' && aimHeight.signature != '\x1aMDE')
243     if (atf.signature != 0x1a4d4544 && atf.signature != 0x1a4d4445)
244         return 0;
245     m_bLegacy = atf.signature == 0x1a4d4544;
246     if (m_bLegacy)
247     {
248         GAMEOPTIONSLEGACY gameOptions;
249         if (BloodVersion != atf.nVersion)
250             return 0;
251         kread(hPFile, &gameOptions, sizeof(GAMEOPTIONSLEGACY));
252         ReadGameOptionsLegacy(m_gameOptions, gameOptions);
253     }
254     else
255     {
256         if (BYTEVERSION != atf.nVersion)
257             return 0;
258         kread(hPFile, &m_gameOptions, sizeof(GAMEOPTIONS));
259     }
260 #if B_BIG_ENDIAN == 1
261     m_gameOptions.nEpisode = B_LITTLE32(m_gameOptions.nEpisode);
262     m_gameOptions.nLevel = B_LITTLE32(m_gameOptions.nLevel);
263     m_gameOptions.nTrackNumber = B_LITTLE32(m_gameOptions.nTrackNumber);
264     m_gameOptions.nSaveGameSlot = B_LITTLE16(m_gameOptions.nSaveGameSlot);
265     m_gameOptions.picEntry = B_LITTLE32(m_gameOptions.picEntry);
266     m_gameOptions.uMapCRC = B_LITTLE32(m_gameOptions.uMapCRC);
267     m_gameOptions.uGameFlags = B_LITTLE32(m_gameOptions.uGameFlags);
268     m_gameOptions.uNetGameFlags = B_LITTLE32(m_gameOptions.uNetGameFlags);
269     m_gameOptions.nMonsterRespawnTime = B_LITTLE32(m_gameOptions.nMonsterRespawnTime);
270     m_gameOptions.nWeaponRespawnTime = B_LITTLE32(m_gameOptions.nWeaponRespawnTime);
271     m_gameOptions.nItemRespawnTime = B_LITTLE32(m_gameOptions.nItemRespawnTime);
272     m_gameOptions.nSpecialRespawnTime = B_LITTLE32(m_gameOptions.nSpecialRespawnTime);
273 #endif
274     at0 = 0;
275     at1 = 1;
276     return 1;
277 }
278 
ProcessKeys(void)279 void CDemo::ProcessKeys(void)
280 {
281     switch (gInputMode)
282     {
283     case INPUT_MODE_1:
284         gGameMenuMgr.Process();
285         break;
286     case INPUT_MODE_2:
287         gPlayerMsg.ProcessKeys();
288         break;
289     case INPUT_MODE_0:
290     {
291         char nKey;
292         while ((nKey = keyGetScan()) != 0)
293         {
294 	        char UNUSED(alt) = keystatus[0x38] | keystatus[0xb8];
295 	        char UNUSED(ctrl) = keystatus[0x1d] | keystatus[0x9d];
296             switch (nKey)
297             {
298             case 1:
299                 if (!CGameMenuMgr::m_bActive)
300                 {
301                     gGameMenuMgr.Push(&menuMain, -1);
302                     at2 = 1;
303                 }
304                 break;
305             case 0x58:
306                 gViewIndex = connectpoint2[gViewIndex];
307                 if (gViewIndex == -1)
308                     gViewIndex = connecthead;
309                 gView = &gPlayer[gViewIndex];
310                 break;
311             }
312         }
313         break;
314     default:
315         gInputMode = INPUT_MODE_0;
316         break;
317     }
318     }
319 }
320 
Playback(void)321 void CDemo::Playback(void)
322 {
323     CONTROL_BindsEnabled = false;
324     ready2send = 0;
325     int v4 = 0;
326     if (!CGameMenuMgr::m_bActive)
327     {
328         gGameMenuMgr.Push(&menuMain, -1);
329         at2 = 1;
330     }
331     gNetFifoClock = totalclock;
332     gViewMode = 3;
333 _DEMOPLAYBACK:
334     while (at1 && !gQuitGame)
335     {
336         while (totalclock >= gNetFifoClock && !gQuitGame)
337         {
338             if (!v4)
339             {
340                 viewResizeView(gViewSize);
341                 viewSetMessage("");
342                 gNetPlayers = atf.nNetPlayers;
343                 atb = atf.nInputCount;
344                 myconnectindex = atf.nMyConnectIndex;
345                 connecthead = atf.nConnectHead;
346                 for (int i = 0; i < 8; i++)
347                     connectpoint2[i] = atf.connectPoints[i];
348                 memset(gNetFifoHead, 0, sizeof(gNetFifoHead));
349                 gNetFifoTail = 0;
350                 //memcpy(connectpoint2, aimHeight.connectPoints, sizeof(aimHeight.connectPoints));
351                 memcpy(&gGameOptions, &m_gameOptions, sizeof(GAMEOPTIONS));
352                 gSkill = gGameOptions.nDifficulty;
353                 for (int i = 0; i < 8; i++)
354                     playerInit(i, 0);
355                 StartLevel(&gGameOptions);
356                 for (int i = 0; i < 8; i++)
357                 {
358                     gProfile[i].nAutoAim = 1;
359                     gProfile[i].nWeaponSwitch = 1;
360                 }
361             }
362             ready2send = 0;
363             OSD_DispatchQueued();
364             if (!gDemo.at1)
365                 break;
366             ProcessKeys();
367             for (int p = connecthead; p >= 0; p = connectpoint2[p])
368             {
369                 if ((v4&1023) == 0)
370                 {
371                     unsigned int nSize = atb-v4;
372                     if (nSize > kInputBufferSize)
373                         nSize = kInputBufferSize;
374                     ReadInput(nSize);
375                 }
376                 memcpy(&gFifoInput[gNetFifoHead[p]&255], &at1aa[v4&1023], sizeof(GINPUT));
377                 gNetFifoHead[p]++;
378                 v4++;
379                 if (v4 >= atf.nInputCount)
380                 {
381                     ready2send = 0;
382                     if (at59ef != 1)
383                     {
384                         v4 = 0;
385                         Close();
386                         NextDemo();
387                         gNetFifoClock = totalclock;
388                         goto _DEMOPLAYBACK;
389                     }
390                     else
391                     {
392                         int const nOffset = sizeof(DEMOHEADER)+(m_bLegacy ? sizeof(GAMEOPTIONSLEGACY) : sizeof(GAMEOPTIONS));
393                         klseek(hPFile, nOffset, SEEK_SET);
394                         v4 = 0;
395                     }
396                 }
397             }
398             gNetFifoClock += 4;
399             if (!gQuitGame)
400                 ProcessFrame();
401             ready2send = 0;
402         }
403         if (engineFPSLimit())
404         {
405             if (handleevents() && quitevent)
406             {
407                 KB_KeyDown[sc_Escape] = 1;
408                 quitevent = 0;
409             }
410             MUSIC_Update();
411             viewDrawScreen();
412             if (gInputMode == INPUT_MODE_1 && CGameMenuMgr::m_bActive)
413                 gGameMenuMgr.Draw();
414             videoNextPage();
415         }
416         if (TestBitString(gotpic, 2342))
417         {
418             FireProcess();
419             ClearBitString(gotpic, 2342);
420         }
421     }
422     Close();
423 }
424 
StopPlayback(void)425 void CDemo::StopPlayback(void)
426 {
427     at1 = 0;
428 }
429 
LoadDemoInfo(void)430 void CDemo::LoadDemoInfo(void)
431 {
432     auto pDemo = &pFirstDemo;
433     const int opsm = pathsearchmode;
434     at59ef = 0;
435     pathsearchmode = 0;
436     char zFN[BMAX_PATH];
437     Bsnprintf(zFN, BMAX_PATH, "%s*.dem", BloodIniPre);
438     auto pList = klistpath("/", zFN, BUILDVFS_FIND_FILE);
439     auto pIterator = pList;
440     while (pIterator != NULL)
441     {
442         int hFile = kopen4loadfrommod(pIterator->name, 0);
443         if (hFile == -1)
444             ThrowError("Error loading demo file header.");
445         kread(hFile, &atf, sizeof(atf));
446         kclose(hFile);
447 #if B_BIG_ENDIAN == 1
448         atf.signature = B_LITTLE32(atf.signature);
449         atf.nVersion = B_LITTLE16(atf.nVersion);
450 #endif
451         if ((atf.signature == 0x1a4d4544 /* '\x1aMED' */&& atf.nVersion == BloodVersion)
452             || (atf.signature == 0x1a4d4445 /* '\x1aMDE' */ && atf.nVersion == BYTEVERSION))
453         {
454             *pDemo = new DEMOCHAIN;
455             (*pDemo)->pNext = NULL;
456             Bstrncpy((*pDemo)->zName, pIterator->name, BMAX_PATH);
457             at59ef++;
458             pDemo = &(*pDemo)->pNext;
459         }
460         pIterator = pIterator->next;
461     }
462     klistfree(pList);
463     pathsearchmode = opsm;
464     pCurrentDemo = pFirstDemo;
465 }
466 
NextDemo(void)467 void CDemo::NextDemo(void)
468 {
469     pCurrentDemo = pCurrentDemo->pNext ? pCurrentDemo->pNext : pFirstDemo;
470     SetupPlayback(NULL);
471 }
472 
473 const int nInputSize = 17;
474 const int nInputSizeLegacy = 22;
475 
FlushInput(int nCount)476 void CDemo::FlushInput(int nCount)
477 {
478     char pBuffer[nInputSize*kInputBufferSize];
479     BitWriter bitWriter(pBuffer, sizeof(pBuffer));
480     for (int i = 0; i < nCount; i++)
481     {
482         GINPUT *pInput = &at1aa[i];
483         bitWriter.writeBit(pInput->syncFlags.buttonChange);
484         bitWriter.writeBit(pInput->syncFlags.keyChange);
485         bitWriter.writeBit(pInput->syncFlags.useChange);
486         bitWriter.writeBit(pInput->syncFlags.weaponChange);
487         bitWriter.writeBit(pInput->syncFlags.mlookChange);
488         bitWriter.writeBit(pInput->syncFlags.run);
489         bitWriter.write(pInput->forward, 16);
490         bitWriter.write(pInput->q16turn, 32);
491         bitWriter.write(pInput->strafe, 16);
492         bitWriter.writeBit(pInput->buttonFlags.jump);
493         bitWriter.writeBit(pInput->buttonFlags.crouch);
494         bitWriter.writeBit(pInput->buttonFlags.shoot);
495         bitWriter.writeBit(pInput->buttonFlags.shoot2);
496         bitWriter.writeBit(pInput->buttonFlags.lookUp);
497         bitWriter.writeBit(pInput->buttonFlags.lookDown);
498         bitWriter.writeBit(pInput->keyFlags.action);
499         bitWriter.writeBit(pInput->keyFlags.jab);
500         bitWriter.writeBit(pInput->keyFlags.prevItem);
501         bitWriter.writeBit(pInput->keyFlags.nextItem);
502         bitWriter.writeBit(pInput->keyFlags.useItem);
503         bitWriter.writeBit(pInput->keyFlags.prevWeapon);
504         bitWriter.writeBit(pInput->keyFlags.nextWeapon);
505         bitWriter.writeBit(pInput->keyFlags.holsterWeapon);
506         bitWriter.writeBit(pInput->keyFlags.lookCenter);
507         bitWriter.writeBit(pInput->keyFlags.lookLeft);
508         bitWriter.writeBit(pInput->keyFlags.lookRight);
509         bitWriter.writeBit(pInput->keyFlags.spin180);
510         bitWriter.writeBit(pInput->keyFlags.pause);
511         bitWriter.writeBit(pInput->keyFlags.quit);
512         bitWriter.writeBit(pInput->keyFlags.restart);
513         bitWriter.writeBit(pInput->useFlags.useBeastVision);
514         bitWriter.writeBit(pInput->useFlags.useCrystalBall);
515         bitWriter.writeBit(pInput->useFlags.useJumpBoots);
516         bitWriter.writeBit(pInput->useFlags.useMedKit);
517         bitWriter.write(pInput->newWeapon, 8);
518         bitWriter.write(pInput->q16mlook, 32);
519         bitWriter.skipBits(1);
520     }
521     fwrite(pBuffer, 1, nInputSize*nCount, hRFile);
522 }
523 
ReadInput(int nCount)524 void CDemo::ReadInput(int nCount)
525 {
526     if (m_bLegacy)
527     {
528         char pBuffer[nInputSizeLegacy*kInputBufferSize];
529         kread(hPFile, pBuffer, nInputSizeLegacy*nCount);
530         BitReader bitReader(pBuffer, sizeof(pBuffer));
531         memset(at1aa, 0, nCount * sizeof(GINPUT));
532         for (int i = 0; i < nCount; i++)
533         {
534             GINPUT *pInput = &at1aa[i];
535             pInput->syncFlags.buttonChange = bitReader.readBit();
536             pInput->syncFlags.keyChange = bitReader.readBit();
537             pInput->syncFlags.useChange = bitReader.readBit();
538             pInput->syncFlags.weaponChange = bitReader.readBit();
539             pInput->syncFlags.mlookChange = bitReader.readBit();
540             pInput->syncFlags.run = bitReader.readBit();
541             bitReader.skipBits(26);
542             pInput->forward = bitReader.readSigned(8) << 8;
543             pInput->q16turn = fix16_from_int(bitReader.readSigned(16) >> 2);
544             pInput->strafe = bitReader.readSigned(8) << 8;
545             pInput->buttonFlags.jump = bitReader.readBit();
546             pInput->buttonFlags.crouch = bitReader.readBit();
547             pInput->buttonFlags.shoot = bitReader.readBit();
548             pInput->buttonFlags.shoot2 = bitReader.readBit();
549             pInput->buttonFlags.lookUp = bitReader.readBit();
550             pInput->buttonFlags.lookDown = bitReader.readBit();
551             bitReader.skipBits(26);
552             pInput->keyFlags.action = bitReader.readBit();
553             pInput->keyFlags.jab = bitReader.readBit();
554             pInput->keyFlags.prevItem = bitReader.readBit();
555             pInput->keyFlags.nextItem = bitReader.readBit();
556             pInput->keyFlags.useItem = bitReader.readBit();
557             pInput->keyFlags.prevWeapon = bitReader.readBit();
558             pInput->keyFlags.nextWeapon = bitReader.readBit();
559             pInput->keyFlags.holsterWeapon = bitReader.readBit();
560             pInput->keyFlags.lookCenter = bitReader.readBit();
561             pInput->keyFlags.lookLeft = bitReader.readBit();
562             pInput->keyFlags.lookRight = bitReader.readBit();
563             pInput->keyFlags.spin180 = bitReader.readBit();
564             pInput->keyFlags.pause = bitReader.readBit();
565             pInput->keyFlags.quit = bitReader.readBit();
566             pInput->keyFlags.restart = bitReader.readBit();
567             bitReader.skipBits(17);
568             pInput->useFlags.useBeastVision = bitReader.readBit();
569             pInput->useFlags.useCrystalBall = bitReader.readBit();
570             pInput->useFlags.useJumpBoots = bitReader.readBit();
571             pInput->useFlags.useMedKit = bitReader.readBit();
572             bitReader.skipBits(28);
573             pInput->newWeapon = bitReader.readUnsigned(8);
574             int mlook = bitReader.readSigned(8);
575             pInput->q16mlook = fix16_from_int(mlook / 4);
576         }
577     }
578     else
579     {
580         char pBuffer[nInputSize*kInputBufferSize];
581         kread(hPFile, pBuffer, nInputSize*nCount);
582         BitReader bitReader(pBuffer, sizeof(pBuffer));
583         memset(at1aa, 0, nCount * sizeof(GINPUT));
584         for (int i = 0; i < nCount; i++)
585         {
586             GINPUT *pInput = &at1aa[i];
587             pInput->syncFlags.buttonChange = bitReader.readBit();
588             pInput->syncFlags.keyChange = bitReader.readBit();
589             pInput->syncFlags.useChange = bitReader.readBit();
590             pInput->syncFlags.weaponChange = bitReader.readBit();
591             pInput->syncFlags.mlookChange = bitReader.readBit();
592             pInput->syncFlags.run = bitReader.readBit();
593             pInput->forward = bitReader.readSigned(16);
594             pInput->q16turn = bitReader.readSigned(32);
595             pInput->strafe = bitReader.readSigned(16);
596             pInput->buttonFlags.jump = bitReader.readBit();
597             pInput->buttonFlags.crouch = bitReader.readBit();
598             pInput->buttonFlags.shoot = bitReader.readBit();
599             pInput->buttonFlags.shoot2 = bitReader.readBit();
600             pInput->buttonFlags.lookUp = bitReader.readBit();
601             pInput->buttonFlags.lookDown = bitReader.readBit();
602             pInput->keyFlags.action = bitReader.readBit();
603             pInput->keyFlags.jab = bitReader.readBit();
604             pInput->keyFlags.prevItem = bitReader.readBit();
605             pInput->keyFlags.nextItem = bitReader.readBit();
606             pInput->keyFlags.useItem = bitReader.readBit();
607             pInput->keyFlags.prevWeapon = bitReader.readBit();
608             pInput->keyFlags.nextWeapon = bitReader.readBit();
609             pInput->keyFlags.holsterWeapon = bitReader.readBit();
610             pInput->keyFlags.lookCenter = bitReader.readBit();
611             pInput->keyFlags.lookLeft = bitReader.readBit();
612             pInput->keyFlags.lookRight = bitReader.readBit();
613             pInput->keyFlags.spin180 = bitReader.readBit();
614             pInput->keyFlags.pause = bitReader.readBit();
615             pInput->keyFlags.quit = bitReader.readBit();
616             pInput->keyFlags.restart = bitReader.readBit();
617             pInput->useFlags.useBeastVision = bitReader.readBit();
618             pInput->useFlags.useCrystalBall = bitReader.readBit();
619             pInput->useFlags.useJumpBoots = bitReader.readBit();
620             pInput->useFlags.useMedKit = bitReader.readBit();
621             pInput->newWeapon = bitReader.readUnsigned(8);
622             pInput->q16mlook = bitReader.readSigned(32);
623             bitReader.skipBits(1);
624         }
625     }
626 }
627