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