1 /*
2 Relay -- a tool to record and play Quake2 demos
3 Copyright (C) 2000 Conor Davis
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 Conor Davis
20 cedavis@planetquake.com
21 */
22
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "block.h"
28 #include "cmd.h"
29 #include "dm2.h"
30 #include "endian.h"
31 #include "pak.h"
32 #include "q2defines.h"
33 #include "shared.h"
34 #include "utils.h"
35
36 //
37 // DM2_Init
38 // Initializes a dm2_t struct so that it can
39 // be properly read or written to.
40 //
41
DM2_Init(dm2_t * dm2)42 void DM2_Init(dm2_t *dm2)
43 {
44 int i;
45
46 memset(dm2, 0, sizeof(dm2_t));
47 for (i = 1; i < MAX_EDICTS; i++)
48 dm2->baselines.entities[i].number = i;
49 for (i = 0; i < UPDATE_BACKUP; i++)
50 dm2->states[i].frame = BASELINES_FRAME;
51 dm2->baselines.frame = BASELINES_FRAME;
52 }
53
54 // pre-0.4 relay demos could leave gaps in the model table (bad);
55 // this attempts to fix it
DM2_FillConfigstrings(char (* configstrings)[64])56 void DM2_FillConfigstrings(char (*configstrings)[64])
57 {
58 int i;
59
60 for (i = 2; i < MAX_MODELS; i++)
61 {
62 if (configstrings[CS_MODELS+i][0] && configstrings[CS_MODELS+i][0] != '*')
63 break;
64
65 if (!configstrings[CS_MODELS+i][0])
66 sprintf(configstrings[CS_MODELS+i], "*%d", i - 1);
67 }
68 }
69
70 //
71 // DM2_ReadBlock
72 // Reads a dm2 block from a file.
73 //
74
DM2_ReadBlock(block_t * block,PFILE * fd)75 int DM2_ReadBlock(block_t *block, PFILE *fd)
76 {
77 if (!pfread(&block->writeoffset, 4, 1, fd))
78 block->writeoffset = 0xffffffff;
79 else
80 block->writeoffset = LittleLong(block->writeoffset);
81
82 if (block->writeoffset == 0xffffffff)
83 return 0;
84
85 if (WriteOverflow(block))
86 return -1;
87
88 if (!pfread(block->buffer, block->writeoffset, 1, fd))
89 {
90 block->writeoffset = 0;
91 return -1;
92 }
93 block->readoffset = 0;
94
95 return 0;
96 }
97
98 //
99 // DM2_WriteBlock
100 // Writes a demo block to a file.
101 //
102
DM2_WriteBlock(block_t * block,PFILE * fd)103 int DM2_WriteBlock(block_t *block, PFILE *fd)
104 {
105 int len;
106
107 len = LittleLong(block->writeoffset);
108 if (!pfwrite(&len, 4, 1, fd))
109 return -1;
110
111 if (!pfwrite(block->buffer, block->writeoffset, 1, fd))
112 return -1;
113
114 return 0;
115 }
116
117 //
118 // functions to read/write dm2 messages
119 // they should return the number of bytes read, or -1
120 // on error
121 //
122
123 #define SET(a,b) ((a) ? *(a) = (b) : (b))
124 #define SETSTR(a,b) ((a) ? strcpy((a), (b)) : (b))
125 #define SETSTRN(a,b,c) ((a) ? ( (a)[(c)-1] = 0, strncpy((a), (b), (c)) ) : (b))
126
127 // for SVC_LAYOUT, SVC_CENTERPRINT, and SVC_STUFFTEXT
DM2_ReadGenericString(block_t * block,char * p,size_t len)128 int DM2_ReadGenericString(block_t *block, char *p, size_t len)
129 {
130 const char *s;
131 size_t start;
132
133 start = block->readoffset;
134 s = ReadString(block);
135
136 if (ReadOverflow(block))
137 return -1;
138
139 if (p)
140 {
141 strncpy(p, s, len-1);
142 p[len-1] = 0;
143 }
144
145 return block->readoffset - start;
146 }
147
DM2_WriteGenericString(block_t * block,const char * p)148 int DM2_WriteGenericString(block_t *block, const char *p)
149 {
150 size_t start;
151
152 start = block->writeoffset;
153 WriteString(block, p);
154
155 if (WriteOverflow(block))
156 return -1;
157
158 return block->writeoffset - start;
159 }
160
DM2_ReadMuzzleflash(block_t * block,int * entity,int * value)161 int DM2_ReadMuzzleflash(block_t *block, int *entity, int *value)
162 {
163 SET(entity, (unsigned short)ReadShort(block));
164 SET(value, ReadByte(block));
165
166 if (ReadOverflow(block))
167 return -1;
168
169 // 3 bytes were read
170 return 3;
171 }
172
DM2_WriteMuzzleflash(block_t * block,int entity,int value)173 int DM2_WriteMuzzleflash(block_t *block, int entity, int value)
174 {
175 WriteShort(block, (unsigned short)entity);
176 WriteByte(block, (byte)value);
177
178 if (WriteOverflow(block))
179 return -1;
180
181 // wrote 3 bytes
182 return 3;
183 }
184
DM2_ReadTempEntity(block_t * block,const dm2_t * dm2,temp_entity_t * p)185 int DM2_ReadTempEntity(block_t *block, const dm2_t *dm2, temp_entity_t *p)
186 {
187 size_t start;
188 temp_entity_t m;
189
190 start = block->readoffset;
191 m.entity = 0;
192 m.dest_entity = 0;
193
194 m.type = ReadByte(block);
195 switch(m.type)
196 {
197 // case TE_PLASMATRAIL: // old meaning
198 case TE_GREENBLOOD: // new meaning (3.15+)
199 if (dm2->svd.version >= 32)
200 goto impact_entity;
201 else
202 goto line_entity;
203 // case TE_GREENBLOOD_old: // old meaning
204 case TE_BLUEHYPERBLASTER: // new meaning (3.15+)
205 if (dm2->svd.version >= 32)
206 goto line_entity;
207 else
208 goto impact_entity;
209 // point entity
210 case TE_EXPLOSION1:
211 case TE_EXPLOSION2:
212 case TE_ROCKET_EXPLOSION:
213 case TE_GRENADE_EXPLOSION:
214 case TE_ROCKET_EXPLOSION_WATER:
215 case TE_GRENADE_EXPLOSION_WATER:
216 case TE_BFG_EXPLOSION:
217 case TE_BFG_BIGEXPLOSION:
218 case TE_BOSSTPORT:
219 case TE_PLASMA_EXPLOSION:
220 case TE_PLAIN_EXPLOSION:
221 case TE_CHAINFIST_SMOKE:
222 case TE_TRACKER_EXPLOSION:
223 case TE_TELEPORT_EFFECT:
224 case TE_DBALL_GOAL:
225 case TE_NUKEBLAST:
226 case TE_WIDOWSPLASH:
227 case TE_EXPLOSION1_BIG:
228 case TE_EXPLOSION1_NP:
229 ReadPosition(block, m.origin);
230 break;
231 // impact entity
232 case TE_GUNSHOT:
233 case TE_BLOOD:
234 case TE_BLASTER:
235 case TE_SHOTGUN:
236 case TE_SPARKS:
237 case TE_SCREEN_SPARKS:
238 case TE_SHIELD_SPARKS:
239 case TE_BULLET_SPARKS:
240 // case TE_GREENBLOOD_new:
241 // case TE_GREENBLOOD_old:
242 case TE_BLASTER2:
243 case TE_MOREBLOOD:
244 case TE_HEATBEAM_SPARKS:
245 case TE_HEATBEAM_STEAM:
246 case TE_ELECTRIC_SPARKS:
247 case TE_FLECHETTE:
248 impact_entity:
249 ReadPosition(block, m.origin);
250 ReadDir(block, m.movedir);
251 break;
252 // line entity
253 case TE_RAILTRAIL:
254 case TE_BUBBLETRAIL:
255 case TE_BFG_LASER:
256 // case TE_PLASMATRAIL:
257 // case TE_BLUEHYPERBLASTER:
258 case TE_DEBUGTRAIL:
259 case TE_BUBBLETRAIL2:
260 line_entity:
261 ReadPosition(block, m.origin);
262 ReadPosition(block, m.endpos);
263 break;
264 // special entity
265 case TE_SPLASH:
266 case TE_LASER_SPARKS:
267 case TE_WELDING_SPARKS:
268 case TE_TUNNEL_SPARKS:
269 m.count = ReadByte(block);
270 ReadPosition(block, m.origin);
271 ReadDir(block, m.movedir);
272 m.style = ReadByte(block);
273 break;
274 case TE_PARASITE_ATTACK:
275 case TE_MEDIC_CABLE_ATTACK:
276 case TE_HEATBEAM:
277 case TE_MONSTER_HEATBEAM:
278 m.entity = ReadShort(block);
279 ReadPosition(block, m.origin);
280 ReadPosition(block, m.endpos);
281 break;
282 case TE_GRAPPLE_CABLE:
283 m.entity = ReadShort(block);
284 ReadPosition(block, m.origin);
285 ReadPosition(block, m.endpos);
286 ReadPosition(block, m.pos1);
287 break;
288 case TE_FLAME:
289 m.entity = ReadShort(block);
290 m.count = ReadShort(block);
291 ReadPosition(block, m.endpos);
292 ReadPosition(block, m.origin);
293 ReadPosition(block, m.pos1);
294 ReadPosition(block, m.pos2);
295 ReadPosition(block, m.pos3);
296 ReadPosition(block, m.pos4);
297 break;
298 case TE_LIGHTNING:
299 m.dest_entity = ReadShort(block);
300 m.entity = ReadShort(block);
301 ReadPosition(block, m.endpos);
302 ReadPosition(block, m.origin);
303 break;
304 case TE_FLASHLIGHT:
305 ReadPosition(block, m.origin);
306 m.entity = ReadShort(block);
307 break;
308 case TE_FORCEWALL:
309 ReadPosition(block, m.origin);
310 ReadPosition(block, m.endpos);
311 m.style = ReadShort(block);
312 break;
313 case TE_STEAM:
314 m.nextid = ReadShort(block);
315 m.count = ReadByte(block);
316 ReadPosition(block, m.origin);
317 ReadDir(block, m.movedir);
318 m.style = ReadByte(block);
319 m.plat2flags = ReadShort(block);
320 if (m.nextid != -1)
321 m.wait = ReadLong(block);
322 break;
323 default:
324 return -1;
325 break;
326 }
327
328 if (ReadOverflow(block))
329 return -1;
330
331 if (p)
332 *p = m;
333
334 return block->readoffset - start;
335 }
336
DM2_WriteTempEntity(block_t * block,const dm2_t * dm2,const temp_entity_t * p)337 int DM2_WriteTempEntity(block_t *block, const dm2_t *dm2, const temp_entity_t *p)
338 {
339 size_t start;
340
341 start = block->writeoffset;
342
343 WriteByte(block, p->type);
344 switch(p->type)
345 {
346 // case TE_PLASMATRAIL: // old meaning
347 case TE_GREENBLOOD: // new meaning (3.15+)
348 if (dm2->svd.version >= 32)
349 goto impact_entity;
350 else
351 goto line_entity;
352 // case TE_GREENBLOOD_old: // old meaning
353 case TE_BLUEHYPERBLASTER: // new meaning (3.15+)
354 if (dm2->svd.version >= 32)
355 goto line_entity;
356 else
357 goto impact_entity;
358 // point entity
359 case TE_EXPLOSION1:
360 case TE_EXPLOSION2:
361 case TE_ROCKET_EXPLOSION:
362 case TE_GRENADE_EXPLOSION:
363 case TE_ROCKET_EXPLOSION_WATER:
364 case TE_GRENADE_EXPLOSION_WATER:
365 case TE_BFG_EXPLOSION:
366 case TE_BFG_BIGEXPLOSION:
367 case TE_BOSSTPORT:
368 case TE_PLASMA_EXPLOSION:
369 case TE_PLAIN_EXPLOSION:
370 case TE_CHAINFIST_SMOKE:
371 case TE_TRACKER_EXPLOSION:
372 case TE_TELEPORT_EFFECT:
373 case TE_DBALL_GOAL:
374 case TE_NUKEBLAST:
375 case TE_WIDOWSPLASH:
376 case TE_EXPLOSION1_BIG:
377 case TE_EXPLOSION1_NP:
378 WritePosition(block, p->origin);
379 break;
380 // impact entity
381 case TE_GUNSHOT:
382 case TE_BLOOD:
383 case TE_BLASTER:
384 case TE_SHOTGUN:
385 case TE_SPARKS:
386 case TE_SCREEN_SPARKS:
387 case TE_SHIELD_SPARKS:
388 case TE_BULLET_SPARKS:
389 // case TE_GREENBLOOD_new:
390 // case TE_GREENBLOOD_old:
391 case TE_BLASTER2:
392 case TE_MOREBLOOD:
393 case TE_HEATBEAM_SPARKS:
394 case TE_HEATBEAM_STEAM:
395 case TE_ELECTRIC_SPARKS:
396 case TE_FLECHETTE:
397 impact_entity:
398 WritePosition(block, p->origin);
399 WriteDir(block, p->movedir);
400 break;
401 // line entity
402 case TE_RAILTRAIL:
403 case TE_BUBBLETRAIL:
404 case TE_BFG_LASER:
405 // case TE_PLASMATRAIL:
406 // case TE_BLUEHYPERBLASTER:
407 case TE_DEBUGTRAIL:
408 case TE_BUBBLETRAIL2:
409 line_entity:
410 WritePosition(block, p->origin);
411 WritePosition(block, p->endpos);
412 break;
413 // special entity
414 case TE_SPLASH:
415 case TE_LASER_SPARKS:
416 case TE_WELDING_SPARKS:
417 case TE_TUNNEL_SPARKS:
418 WriteByte(block, (byte)p->count);
419 WritePosition(block, p->origin);
420 WriteDir(block, p->movedir);
421 WriteByte(block, (byte)p->style);
422 break;
423 case TE_PARASITE_ATTACK:
424 case TE_MEDIC_CABLE_ATTACK:
425 case TE_HEATBEAM:
426 case TE_MONSTER_HEATBEAM:
427 WriteShort(block, p->entity);
428 WritePosition(block, p->origin);
429 WritePosition(block, p->endpos);
430 break;
431 case TE_GRAPPLE_CABLE:
432 WriteShort(block, p->entity);
433 WritePosition(block, p->origin);
434 WritePosition(block, p->endpos);
435 WritePosition(block, p->pos1);
436 break;
437 case TE_FLAME:
438 WriteShort(block, p->entity);
439 WriteShort(block, p->count);
440 WritePosition(block, p->endpos);
441 WritePosition(block, p->origin);
442 WritePosition(block, p->pos1);
443 WritePosition(block, p->pos2);
444 WritePosition(block, p->pos3);
445 WritePosition(block, p->pos4);
446 break;
447 case TE_LIGHTNING:
448 WriteShort(block, p->dest_entity);
449 WriteShort(block, p->entity);
450 WritePosition(block, p->endpos);
451 WritePosition(block, p->origin);
452 break;
453 case TE_FLASHLIGHT:
454 WritePosition(block, p->origin);
455 WriteShort(block, p->entity);
456 break;
457 case TE_FORCEWALL:
458 WritePosition(block, p->origin);
459 WritePosition(block, p->endpos);
460 WriteShort(block, p->style);
461 break;
462 case TE_STEAM:
463 WriteShort(block, p->nextid);
464 WriteByte(block, (byte)p->count);
465 WritePosition(block, p->origin);
466 WriteDir(block, p->movedir);
467 WriteByte(block, (byte)p->style);
468 WriteShort(block, p->plat2flags);
469 if (p->nextid != -1)
470 WriteLong(block, p->wait);
471 break;
472 default:
473 return -1;
474 break;
475 }
476
477 if (WriteOverflow(block))
478 return -1;
479
480 return block->writeoffset - start;
481 }
482
DM2_ReadInventory(block_t * block,short p[MAX_ITEMS])483 int DM2_ReadInventory(block_t *block, short p[MAX_ITEMS])
484 {
485 int i;
486
487 if (p)
488 {
489 for (i = 0; i < MAX_ITEMS; i++)
490 p[i] = ReadShort(block);
491 }
492 else
493 BlockRead(block, NULL, MAX_ITEMS*2);
494
495 if (ReadOverflow(block))
496 return -1;
497
498 return MAX_ITEMS*2;
499 }
500
DM2_WriteInventory(block_t * block,const short p[MAX_ITEMS])501 int DM2_WriteInventory(block_t *block, const short p[MAX_ITEMS])
502 {
503 int i;
504
505 for (i = 0; i < MAX_ITEMS; i++)
506 WriteShort(block, p[i]);
507
508 if (WriteOverflow(block))
509 return -1;
510
511 return MAX_ITEMS*2;
512 }
513
DM2_ReadSound(block_t * block,int * soundindex,float * volume,float * attenuation,float * timeofs,int * entity,int * channel,vec3_t origin,qboolean * positioned)514 int DM2_ReadSound(block_t *block, int *soundindex, float *volume, float *attenuation, float *timeofs, int *entity, int *channel, vec3_t origin, qboolean *positioned)
515 {
516 size_t start;
517 int mask, mix;
518
519 start = block->readoffset;
520
521 mask = ReadByte(block);
522 SET(soundindex, ReadByte(block));
523
524 if (mask & SND_VOLUME)
525 SET(volume, (float)ReadByte(block) / 255);
526 else
527 SET(volume, 1.0F);
528
529 if (mask & SND_ATTENUATION)
530 SET(attenuation, (float)ReadByte(block) / 64);
531 else
532 SET(attenuation, 1.0F);
533
534 if (mask & SND_OFFSET)
535 SET(timeofs, (float)ReadByte(block) * 0.001);
536 else
537 SET(timeofs, 0.0F);
538
539 if (mask & SND_ENT)
540 {
541 mix = ReadShort(block);
542 SET(entity, mix >> 3);
543 SET(channel, mix & 0x07);
544 }
545 else
546 {
547 SET(entity, 0);
548 SET(channel, 0);
549 }
550
551 if (mask & SND_POS)
552 {
553 if (origin)
554 ReadPosition(block, origin);
555 else
556 BlockRead(block, NULL, 6);
557
558 SET(positioned, true);
559 }
560 else
561 SET(positioned, false);
562
563 if (ReadOverflow(block))
564 return -1;
565
566 return block->readoffset - start;
567 }
568
DM2_WriteSound(block_t * block,int soundindex,float volume,float attenuation,float timeofs,int entity,int channel,vec3_t origin,qboolean positioned)569 int DM2_WriteSound(block_t *block, int soundindex, float volume, float attenuation, float timeofs, int entity, int channel, vec3_t origin, qboolean positioned)
570 {
571 int mask;
572 size_t start;
573
574 start = block->writeoffset;
575
576 mask = 0;
577 if (volume != 1.0F)
578 mask |= SND_VOLUME;
579 if (attenuation != 1.0F)
580 mask |= SND_ATTENUATION;
581 if (timeofs != 0.0F)
582 mask |= SND_OFFSET;
583 if (entity)
584 mask |= SND_ENT;
585 if (positioned)
586 mask |= SND_POS;
587
588 WriteByte(block, (byte)mask);
589 WriteByte(block, (byte)soundindex);
590 if (mask & SND_VOLUME)
591 WriteByte(block, (byte)(volume * 255));
592 if (mask & SND_ATTENUATION)
593 WriteByte(block, (byte)(attenuation * 64));
594 if (mask & SND_OFFSET)
595 WriteByte(block, (byte)(timeofs * 1000));
596 if (mask & SND_ENT)
597 WriteShort(block, (unsigned short)((entity << 3) | (channel & 0x07)));
598 if (mask & SND_POS)
599 WritePosition(block, origin);
600
601 if (WriteOverflow(block))
602 return -1;
603
604 return block->writeoffset - start;
605 }
606
DM2_ReadPrint(block_t * block,int * level,char * string,size_t len)607 int DM2_ReadPrint(block_t *block, int *level, char *string, size_t len)
608 {
609 size_t start;
610
611 start = block->readoffset;
612
613 SET(level, ReadByte(block));
614 SETSTRN(string, ReadString(block), len);
615
616 if (ReadOverflow(block))
617 return -1;
618
619 return block->readoffset - start;
620 }
621
DM2_WritePrint(block_t * block,int level,const char * string)622 int DM2_WritePrint(block_t *block, int level, const char *string)
623 {
624 size_t start;
625
626 start = block->writeoffset;
627
628 WriteByte(block, (byte)level);
629 WriteString(block, string);
630
631 if (WriteOverflow(block))
632 return -1;
633
634 return block->writeoffset - start;
635 }
636
DM2_ReadServerdata(block_t * block,serverdata_t * p)637 int DM2_ReadServerdata(block_t *block, serverdata_t *p)
638 {
639 serverdata_t m;
640 size_t start;
641
642 start = block->readoffset;
643
644 m.version = ReadLong(block);
645 m.key = ReadLong(block);
646 m.isdemo = ReadByte(block);
647 strncpy(m.game, ReadString(block), sizeof(m.game)-1);
648 m.game[sizeof(m.game)-1] = 0;
649 m.player = ReadShort(block);
650 strncpy(m.mapname, ReadString(block), sizeof(m.mapname)-1);
651 m.mapname[sizeof(m.mapname)-1] = 0;
652
653 if (m.isdemo == RECORD_RELAY)
654 {
655 m.relayversion = m.version >> 16;
656 m.version &= 0xffff;
657 }
658 else
659 m.relayversion = 0;
660
661 if (ReadOverflow(block))
662 return -1;
663
664 if (p)
665 *p = m;
666
667 return block->readoffset - start;
668 }
669
DM2_WriteServerdata(block_t * block,const serverdata_t * p)670 int DM2_WriteServerdata(block_t *block, const serverdata_t *p)
671 {
672 size_t start;
673
674 start = block->writeoffset;
675
676 if (p->isdemo == RECORD_RELAY)
677 WriteLong(block, (p->relayversion << 16) | p->version);
678 else
679 WriteLong(block, p->version);
680 WriteLong(block, p->key);
681 WriteByte(block, p->isdemo);
682 WriteString(block, p->game);
683 WriteShort(block, p->player);
684 WriteString(block, p->mapname);
685
686 if (WriteOverflow(block))
687 return -1;
688
689 return block->writeoffset - start;
690 }
691
DM2_ReadConfigstring(block_t * block,int * index,char * string)692 int DM2_ReadConfigstring(block_t *block, int *index, char *string)
693 {
694 size_t start;
695
696 start = block->readoffset;
697
698 SET(index, ReadShort(block));
699 SETSTR(string, ReadString(block));
700
701 if (ReadOverflow(block))
702 return -1;
703
704 return block->readoffset - start;
705 }
706
DM2_WriteConfigstring(block_t * block,int index,const char * string)707 int DM2_WriteConfigstring(block_t *block, int index, const char *string)
708 {
709 size_t start;
710
711 start = block->writeoffset;
712
713 WriteShort(block, (unsigned short)index);
714 WriteString(block, string);
715
716 if (WriteOverflow(block))
717 return -1;
718
719 return block->writeoffset - start;
720 }
721
DM2_ReadFrame(block_t * block,const serverdata_t * svd,int * seq1,int * seq2,int * area_count,byte * areas,int * connected_count,byte * connected)722 int DM2_ReadFrame(block_t *block, const serverdata_t *svd, int *seq1, int *seq2, int *area_count, byte *areas, int *connected_count, byte *connected)
723 {
724 int len;
725 size_t start;
726
727 start = block->readoffset;
728
729 if (svd->isdemo != RECORD_SERVER)
730 {
731 SET(seq1, ReadLong(block));
732 SET(seq2, ReadLong(block));
733 if (svd->version != 26)
734 ReadByte(block); // ???
735 len = ReadByte(block);
736 SET(area_count, len);
737 if (areas)
738 BlockRead(block, areas, len);
739 else
740 BlockRead(block, NULL, len);
741
742 if (svd->isdemo == RECORD_RELAY)
743 {
744 len = ReadByte(block);
745 SET(connected_count, len);
746 if (connected)
747 BlockRead(block, connected, len);
748 else
749 BlockRead(block, NULL, len);
750 }
751 }
752 else
753 {
754 SET(seq1, ReadLong(block));
755 }
756
757 if (ReadOverflow(block))
758 return -1;
759
760 return block->readoffset - start;
761 }
762
DM2_WriteFrame(block_t * block,const serverdata_t * svd,int seq1,int seq2,int area_count,const byte * areas,int connected_count,const byte * connected)763 int DM2_WriteFrame(block_t *block, const serverdata_t *svd, int seq1, int seq2, int area_count, const byte *areas, int connected_count, const byte *connected)
764 {
765 size_t start;
766
767 start = block->writeoffset;
768
769 if (svd->isdemo != RECORD_SERVER)
770 {
771 WriteLong(block, seq1);
772 WriteLong(block, seq2);
773 if (svd->version != 26)
774 WriteByte(block, 0); // ???
775
776 WriteByte(block, (byte)area_count);
777 BlockWrite(block, areas, area_count);
778
779 if (svd->isdemo == RECORD_RELAY)
780 {
781 WriteByte(block, (byte)connected_count);
782 BlockWrite(block, connected, connected_count);
783 }
784 }
785 else
786 {
787 WriteLong(block, seq1);
788 }
789
790 if (WriteOverflow(block))
791 return -1;
792
793 return block->writeoffset - start;
794 }
795
796 //
797 // ReadPS
798 // Reads a SVC_PLAYERINFO message from a block.
799 //
800
DM2_ReadPS(block_t * block,player_state_t * ps)801 int DM2_ReadPS(block_t *block, player_state_t *ps)
802 {
803 int mask, i;
804 size_t start;
805
806 start = block->readoffset;
807
808 mask = (unsigned short)ReadShort(block);
809
810 if (mask & PS_M_TYPE)
811 ps->pmove.pm_type = ReadByte(block);
812 if (mask & PS_M_ORIGIN)
813 ReadShortPosition(block, ps->pmove.origin);
814 if (mask & PS_M_VELOCITY)
815 ReadShortPosition(block, ps->pmove.velocity);
816 if (mask & PS_M_TIME)
817 ps->pmove.pm_type = ReadByte(block);
818 if (mask & PS_M_FLAGS)
819 ps->pmove.pm_flags = ReadByte(block);
820 if (mask & PS_M_GRAVITY)
821 ps->pmove.gravity = ReadShort(block);
822 if (mask & PS_M_DELTA_ANGLES)
823 ReadShortPosition(block, ps->pmove.delta_angles);
824
825 if (mask & PS_VIEWOFFSET)
826 ReadOffsetVec(block, ps->viewoffset);
827 if (mask & PS_VIEWANGLES)
828 {
829 ps->viewangles[0] = ReadAngle16(block);
830 ps->viewangles[1] = ReadAngle16(block);
831 ps->viewangles[2] = ReadAngle16(block);
832 }
833 if (mask & PS_KICKANGLES)
834 ReadOffsetVec(block, ps->kick_angles);
835 if (mask & PS_WEAPONINDEX)
836 ps->gunindex = ReadByte(block);
837 if (mask & PS_WEAPONFRAME)
838 {
839 ps->gunframe = ReadByte(block);
840 ReadOffsetVec(block, ps->gunoffset);
841 ReadOffsetVec(block, ps->gunangles);
842 }
843 if (mask & PS_BLEND)
844 ReadBlendVec(block, ps->blend);
845 if (mask & PS_FOV)
846 ps->fov = ReadByte(block);
847 if (mask & PS_RDFLAGS)
848 ps->rdflags = ReadByte(block);
849
850 mask = ReadLong(block);
851 for (i = 0; i < MAX_STATS; i++)
852 {
853 if (mask & (1 << i))
854 ps->stats[i] = ReadShort(block);
855 }
856
857 if (ReadOverflow(block))
858 return -1;
859
860 return block->readoffset - start;
861 }
862
863 //
864 // WritePS
865 // Generates a SVC_PLAYERINFO message from two player states and
866 // writes it to a block.
867 //
868
DM2_WritePS(block_t * block,const player_state_t * to,const player_state_t * from)869 int DM2_WritePS(block_t *block, const player_state_t *to, const player_state_t *from)
870 {
871 unsigned short mask;
872 unsigned long mask2;
873 int i;
874 size_t start;
875
876 start = block->writeoffset;
877
878 // generate masks to save bandwidth
879 mask = mask2 = 0;
880 if (to->pmove.pm_type != from->pmove.pm_type)
881 mask |= PS_M_TYPE;
882 if (!VectorCompare(to->pmove.origin, from->pmove.origin))
883 mask |= PS_M_ORIGIN;
884 if (!VectorCompare(to->pmove.velocity, from->pmove.velocity))
885 mask |= PS_M_VELOCITY;
886 if (to->pmove.pm_time != from->pmove.pm_time)
887 mask |= PS_M_TIME;
888 if (to->pmove.pm_flags != from->pmove.pm_flags)
889 mask |= PS_M_FLAGS;
890 if (to->pmove.gravity != from->pmove.gravity)
891 mask |= PS_M_GRAVITY;
892 if (!VectorCompare(to->pmove.delta_angles, from->pmove.delta_angles))
893 mask |= PS_M_DELTA_ANGLES;
894
895 if (!VectorCompare(to->viewoffset, from->viewoffset))
896 mask |= PS_VIEWOFFSET;
897 if (!VectorCompare(to->viewangles, from->viewangles))
898 mask |= PS_VIEWANGLES;
899 if (!VectorCompare(to->kick_angles, from->kick_angles))
900 mask |= PS_KICKANGLES;
901 if (to->gunindex != from->gunindex)
902 mask |= PS_WEAPONINDEX;
903 if (to->gunframe != from->gunframe ||
904 !VectorCompare(to->gunoffset, from->gunoffset) ||
905 !VectorCompare(to->gunangles, from->gunangles))
906 mask |= PS_WEAPONFRAME;
907 if (to->blend[0] != from->blend[0] ||
908 to->blend[1] != from->blend[1] ||
909 to->blend[2] != from->blend[2] ||
910 to->blend[3] != from->blend[3])
911 mask |= PS_BLEND;
912 if (to->fov != from->fov)
913 mask |= PS_FOV;
914 if (to->rdflags != from->rdflags)
915 mask |= PS_RDFLAGS;
916
917 for (i = 0; i < MAX_STATS; i++)
918 {
919 if (i == STAT_FLASHES)
920 { // stats are cleared every frame, so delta
921 // from 0
922 if (to->stats[i])
923 mask2 |= (1 << i);
924 }
925 else
926 {
927 if (to->stats[i] != from->stats[i])
928 mask2 |= (1 << i);
929 }
930 }
931
932 WriteShort(block, mask);
933 if (mask & PS_M_TYPE)
934 WriteByte(block, (byte)to->pmove.pm_type);
935 if (mask & PS_M_ORIGIN)
936 WriteShortPosition(block, to->pmove.origin);
937 if (mask & PS_M_VELOCITY)
938 WriteShortPosition(block, to->pmove.velocity);
939 if (mask & PS_M_TIME)
940 WriteByte(block, to->pmove.pm_time);
941 if (mask & PS_M_FLAGS)
942 WriteByte(block, to->pmove.pm_flags);
943 if (mask & PS_M_GRAVITY)
944 WriteShort(block, to->pmove.gravity);
945 if (mask & PS_M_DELTA_ANGLES)
946 WriteShortPosition(block, to->pmove.delta_angles);
947
948 if (mask & PS_VIEWOFFSET)
949 WriteOffsetVec(block, to->viewoffset);
950 if (mask & PS_VIEWANGLES)
951 {
952 WriteAngle16(block, to->viewangles[0]);
953 WriteAngle16(block, to->viewangles[1]);
954 WriteAngle16(block, to->viewangles[2]);
955 }
956 if (mask & PS_KICKANGLES)
957 WriteOffsetVec(block, to->kick_angles);
958 if (mask & PS_WEAPONINDEX)
959 WriteByte(block, (byte)to->gunindex);
960 if (mask & PS_WEAPONFRAME)
961 {
962 WriteByte(block, (byte)to->gunframe);
963 WriteOffsetVec(block, to->gunoffset);
964 WriteOffsetVec(block, to->gunangles);
965 }
966 if (mask & PS_BLEND)
967 WriteBlendVec(block, to->blend);
968 if (mask & PS_FOV)
969 WriteByte(block, (byte)to->fov);
970 if (mask & PS_RDFLAGS)
971 WriteByte(block, (byte)to->rdflags);
972
973 WriteULong(block, mask2);
974 for (i = 0; i < MAX_STATS; i++)
975 {
976 if (mask2 & (1 << i))
977 WriteShort(block, to->stats[i]);
978 }
979
980 if (WriteOverflow(block))
981 return -1;
982
983 return block->writeoffset - start;
984 }
985
986 //
987 // ReadEntityMask
988 // Reads in the mask and entity number of a SVC_SPAWNBASELINE or
989 // SVC_PACKETENTITIES message. This is separate from ReadEntity so
990 // the caller can choose which entity state to read to.
991 //
992
DM2_ReadEntityMask(block_t * block,int * mask)993 int DM2_ReadEntityMask(block_t *block, int *mask)
994 {
995 int entity;
996
997 *mask = ReadByte(block);
998 if (*mask & U_MOREBITS1)
999 *mask |= ReadByte(block) << 8;
1000 if (*mask & U_MOREBITS2)
1001 *mask |= ReadByte(block) << 16;
1002 if (*mask & U_MOREBITS3)
1003 *mask |= ReadByte(block) << 24;
1004
1005 if (*mask & U_NUMBER16)
1006 entity = (unsigned short)ReadShort(block);
1007 else
1008 entity = ReadByte(block);
1009
1010 return entity;
1011 }
1012
1013 //
1014 // ReadEntity
1015 // Used by ReadPacketEntity or ReadBaselineEntity to read the changes to
1016 // one entity.
1017 //
1018
DM2_ReadEntity(block_t * block,entity_state_t * es,int mask)1019 qboolean DM2_ReadEntity(block_t *block, entity_state_t *es, int mask)
1020 {
1021 if (mask & U_MODEL)
1022 es->modelindex = ReadByte(block);
1023 if (mask & U_MODEL2)
1024 es->modelindex2 = ReadByte(block);
1025 if (mask & U_MODEL3)
1026 es->modelindex3 = ReadByte(block);
1027 if (mask & U_MODEL4)
1028 es->modelindex4 = ReadByte(block);
1029
1030 if (mask & U_FRAME8)
1031 es->frame = ReadByte(block);
1032 if (mask & U_FRAME16)
1033 es->frame = ReadShort(block);
1034
1035 if (mask & U_SKIN8)
1036 {
1037 if (mask & U_SKIN16)
1038 es->skinnum = ReadLong(block);
1039 else
1040 es->skinnum = ReadByte(block);
1041 }
1042 else if (mask & U_SKIN16)
1043 es->skinnum = (unsigned short)ReadShort(block);
1044
1045 if (mask & U_EFFECTS8)
1046 {
1047 if (mask & U_EFFECTS16)
1048 es->effects = ReadLong(block);
1049 else
1050 es->effects = ReadByte(block);
1051 }
1052 else if (mask & U_EFFECTS16)
1053 es->effects = (unsigned short)ReadShort(block);
1054
1055 if (mask & U_RENDERFX8)
1056 {
1057 if (mask & U_RENDERFX16)
1058 es->renderfx = ReadLong(block);
1059 else
1060 es->renderfx = ReadByte(block);
1061 }
1062 else if (mask & U_RENDERFX16)
1063 es->renderfx = (unsigned short)ReadShort(block);
1064
1065 if (mask & U_ORIGIN1)
1066 es->origin[0] = ReadCoord(block);
1067 if (mask & U_ORIGIN2)
1068 es->origin[1] = ReadCoord(block);
1069 if (mask & U_ORIGIN3)
1070 es->origin[2] = ReadCoord(block);
1071
1072 if (mask & U_ANGLE1)
1073 es->angles[0] = ReadAngle(block);
1074 if (mask & U_ANGLE2)
1075 es->angles[1] = ReadAngle(block);
1076 if (mask & U_ANGLE3)
1077 es->angles[2] = ReadAngle(block);
1078
1079 if (mask & U_OLDORIGIN)
1080 ReadPosition(block, es->old_origin);
1081 if (mask & U_SOUND)
1082 es->sound = ReadByte(block);
1083 if (mask & U_EVENT)
1084 es->event = ReadByte(block);
1085 if (mask & U_SOLID)
1086 es->solid = ReadShort(block);
1087
1088 if (mask & U_REMOVE)
1089 return false;
1090
1091 return true;
1092 }
1093
1094 //
1095 // ReadPacketEntity
1096 // Reads one entity out of a SVC_PACKETENTITES message.
1097 //
1098
DM2_ReadPacketEntity(block_t * block,state_t * state,const state_t * baselines)1099 int DM2_ReadPacketEntity(block_t *block, state_t *state, const state_t *baselines)
1100 {
1101 int entity, mask;
1102
1103 entity = DM2_ReadEntityMask(block, &mask);
1104 if (ReadOverflow(block))
1105 return -1;
1106
1107 if (!entity && !mask)
1108 return 0;
1109
1110 if (!ISBITSET(state->active, entity))
1111 state->entities[entity] = baselines->entities[entity];
1112
1113 if (DM2_ReadEntity(block, &state->entities[entity], mask))
1114 SETBIT(state->active, entity, true);
1115 else
1116 SETBIT(state->active, entity, false);
1117
1118 if (ReadOverflow(block))
1119 return -1;
1120
1121 return entity;
1122 }
1123
1124 //
1125 // ReadBaselineEntity
1126 // Reads a SVC_SPAWNBASELINE message.
1127 //
1128
DM2_ReadBaselineEntity(block_t * block,state_t * baselines)1129 int DM2_ReadBaselineEntity(block_t *block, state_t *baselines)
1130 {
1131 int entity, mask;
1132
1133 entity = DM2_ReadEntityMask(block, &mask);
1134 DM2_ReadEntity(block, &baselines->entities[entity], mask);
1135
1136 if (ReadOverflow(block))
1137 return -1;
1138
1139 return entity;
1140 }
1141
1142 //
1143 // WriteEntity
1144 // Given a current entity and a delta entity, generates a bit mask
1145 // of the changes and writes them to a block.
1146 // With SVC_SPAWNBASELINE, use a zero-filled delta entity
1147 // With SVC_PACKETENTITIES, determine which delta entity to use (previous
1148 // frame or baseline)
1149 //
1150
DM2_WriteEntity(block_t * block,const entity_state_t * to,const entity_state_t * from,qboolean is_active,qboolean was_active)1151 int DM2_WriteEntity(block_t *block, const entity_state_t *to, const entity_state_t *from, qboolean is_active, qboolean was_active)
1152 {
1153 int mask;
1154 size_t start;
1155
1156 start = block->writeoffset;
1157
1158 mask = 0;
1159 if (!is_active && was_active)
1160 { // entity is no longer active
1161 mask |= U_REMOVE;
1162 }
1163 else
1164 { // generate delta bit mask
1165 if (to->modelindex != from->modelindex)
1166 mask |= U_MODEL;
1167 if (to->modelindex2 != from->modelindex2)
1168 mask |= U_MODEL2;
1169 if (to->modelindex3 != from->modelindex3)
1170 mask |= U_MODEL3;
1171 if (to->modelindex4 != from->modelindex4)
1172 mask |= U_MODEL4;
1173 if (to->origin[0] != from->origin[0])
1174 mask |= U_ORIGIN1;
1175 if (to->origin[1] != from->origin[1])
1176 mask |= U_ORIGIN2;
1177 if (to->origin[2] != from->origin[2])
1178 mask |= U_ORIGIN3;
1179 if (to->angles[0] != from->angles[0])
1180 mask |= U_ANGLE1;
1181 if (to->angles[1] != from->angles[1])
1182 mask |= U_ANGLE2;
1183 if (to->angles[2] != from->angles[2])
1184 mask |= U_ANGLE3;
1185 if (to->frame != from->frame)
1186 {
1187 if ((unsigned)to->frame < 0x100)
1188 mask |= U_FRAME8;
1189 else
1190 mask |= U_FRAME16;
1191 }
1192
1193 // Use '< 0x8000' instead of '< 0x10000' because shorts are
1194 // read as signed shorts. If bit 0x8000 is set and read
1195 // as a short, it will be treated as negative when expanded
1196 // to an int, which messes things up. "Fixing" this
1197 // (writing it as a short and expecting the reader to typecast it
1198 // to unsigned short before expanding to int)
1199 // would break compatibility with quake2.exe
1200 if (to->skinnum != from->skinnum)
1201 {
1202 if ((unsigned)to->skinnum < 0x100)
1203 mask |= U_SKIN8;
1204 else if ((unsigned)to->skinnum < 0x8000)
1205 mask |= U_SKIN16;
1206 else
1207 mask |= U_SKIN16|U_SKIN8;
1208 }
1209 if (to->effects != from->effects)
1210 {
1211 if ((unsigned)to->effects < 0x100)
1212 mask |= U_EFFECTS8;
1213 else if ((unsigned)to->effects < 0x8000)
1214 mask |= U_EFFECTS16;
1215 else
1216 mask |= U_EFFECTS16|U_EFFECTS8;
1217 }
1218 if (to->renderfx != from->renderfx)
1219 {
1220 if ((unsigned)to->renderfx < 0x100)
1221 mask |= U_RENDERFX8;
1222 else if ((unsigned)to->renderfx < 0x8000)
1223 mask |= U_RENDERFX16;
1224 else
1225 mask |= U_RENDERFX16|U_RENDERFX8;
1226 }
1227 if (!VectorCompare(to->old_origin, from->origin))
1228 mask |= U_OLDORIGIN;
1229 if (to->sound != from->sound)
1230 mask |= U_SOUND;
1231 if (to->event)
1232 mask |= U_EVENT;
1233 if (to->solid != from->solid)
1234 mask |= U_SOLID;
1235 }
1236
1237 // check whether the entity needs to be sent
1238 if (!mask && is_active && was_active)
1239 return 0;
1240 if (!is_active && !was_active)
1241 return 0;
1242
1243 if (to->number > 0xFF)
1244 mask |= U_NUMBER16;
1245
1246 if (mask & 0xFF000000)
1247 mask |= U_MOREBITS1|U_MOREBITS2|U_MOREBITS3;
1248 else if (mask & 0x00FF0000)
1249 mask |= U_MOREBITS1|U_MOREBITS2;
1250 else if (mask & 0x0000FF00)
1251 mask |= U_MOREBITS1;
1252
1253 WriteByte(block, (byte)(mask & 0xFF));
1254 if (mask & U_MOREBITS1)
1255 WriteByte(block, (byte)((mask >> 8) & 0xFF));
1256 if (mask & U_MOREBITS2)
1257 WriteByte(block, (byte)((mask >> 16) & 0xFF));
1258 if (mask & U_MOREBITS3)
1259 WriteByte(block, (byte)((mask >> 24) & 0xFF));
1260
1261 if (mask & U_NUMBER16)
1262 WriteShort(block, (unsigned short)to->number);
1263 else
1264 WriteByte(block, (byte)to->number);
1265
1266 if (mask & U_MODEL)
1267 WriteByte(block, (byte)to->modelindex);
1268 if (mask & U_MODEL2)
1269 WriteByte(block, (byte)to->modelindex2);
1270 if (mask & U_MODEL3)
1271 WriteByte(block, (byte)to->modelindex3);
1272 if (mask & U_MODEL4)
1273 WriteByte(block, (byte)to->modelindex4);
1274
1275 if (mask & U_FRAME8)
1276 WriteByte(block, (byte)to->frame);
1277 if (mask & U_FRAME16)
1278 WriteShort(block, (unsigned short)to->frame);
1279
1280 if (mask & U_SKIN8)
1281 {
1282 if (mask & U_SKIN16)
1283 WriteLong(block, to->skinnum);
1284 else
1285 WriteByte(block, (byte)to->skinnum);
1286 }
1287 else if (mask & U_SKIN16)
1288 WriteShort(block, (unsigned short)to->skinnum);
1289
1290 if (mask & U_EFFECTS8)
1291 {
1292 if (mask & U_EFFECTS16)
1293 WriteLong(block, to->effects);
1294 else
1295 WriteByte(block, (byte)to->effects);
1296 }
1297 else if (mask & U_EFFECTS16)
1298 WriteShort(block, (unsigned short)to->effects);
1299
1300 if (mask & U_RENDERFX8)
1301 {
1302 if (mask & U_RENDERFX16)
1303 WriteLong(block, to->renderfx);
1304 else
1305 WriteByte(block, (byte)to->renderfx);
1306 }
1307 else if (mask & U_RENDERFX16)
1308 WriteShort(block, (unsigned short)to->renderfx);
1309
1310 if (mask & U_ORIGIN1)
1311 WriteCoord(block, to->origin[0]);
1312 if (mask & U_ORIGIN2)
1313 WriteCoord(block, to->origin[1]);
1314 if (mask & U_ORIGIN3)
1315 WriteCoord(block, to->origin[2]);
1316
1317 if (mask & U_ANGLE1)
1318 WriteAngle(block, to->angles[0]);
1319 if (mask & U_ANGLE2)
1320 WriteAngle(block, to->angles[1]);
1321 if (mask & U_ANGLE3)
1322 WriteAngle(block, to->angles[2]);
1323
1324 if (mask & U_OLDORIGIN)
1325 WritePosition(block, to->old_origin);
1326 if (mask & U_SOUND)
1327 WriteByte(block, (byte)to->sound);
1328 if (mask & U_EVENT)
1329 WriteByte(block, (byte)to->event);
1330 if (mask & U_SOLID)
1331 WriteShort(block, (unsigned short)to->solid);
1332
1333 if (WriteOverflow(block))
1334 return -1;
1335
1336 return block->writeoffset - start;
1337 }
1338
1339 //
1340 // WritePacketEntities
1341 // Generates a full SVC_PACKETENTITIES message via WriteEntity
1342 //
1343
DM2_WritePacketEntities(block_t * block,const state_t * current,const state_t * delta,const state_t * baselines)1344 int DM2_WritePacketEntities(block_t *block, const state_t *current, const state_t *delta, const state_t *baselines)
1345 {
1346 int i;
1347 size_t start;
1348 const entity_state_t *to, *from;
1349
1350 start = block->writeoffset;
1351
1352 for (i = 1; i < MAX_EDICTS; i++)
1353 {
1354 to = ¤t->entities[i];
1355 from = &delta->entities[i];
1356
1357 if (ISBITSET(delta->active, i))
1358 DM2_WriteEntity(block, to, from, ISBITSET(current->active, i), true);
1359 else
1360 DM2_WriteEntity(block, to, &baselines->entities[i], ISBITSET(current->active, i), false);
1361 }
1362
1363 WriteShort(block, 0); // terminating packetentities message (mask=0, entity=0)
1364
1365 if (WriteOverflow(block))
1366 return -1;
1367
1368 return block->writeoffset - start;
1369 }
1370
1371 //
1372 // PRE-FRAME FUNCTIONS
1373 //
1374
DM2_WriteConfigstrings(block_t * block,const char (* configstrings)[64],int start,size_t stopsize)1375 int DM2_WriteConfigstrings(block_t *block, const char (*configstrings)[64], int start, size_t stopsize)
1376 {
1377 int i;
1378
1379 for (i = start; i < MAX_CONFIGSTRINGS; i++)
1380 {
1381 if (!configstrings[i][0])
1382 continue;
1383
1384 if (i != 0 && strlen(configstrings[i-1]) >= 64)
1385 continue;
1386
1387 if (block->writelen > stopsize)
1388 break;
1389
1390 WriteByte(block, SVC_CONFIGSTRING);
1391 DM2_WriteConfigstring(block, i, configstrings[i]);
1392 }
1393
1394 return i;
1395 }
1396
DM2_WriteBaselines(block_t * block,const state_t * baselines,int start,size_t stopsize)1397 int DM2_WriteBaselines(block_t *block, const state_t *baselines, int start, size_t stopsize)
1398 {
1399 int i;
1400 const entity_state_t *es;
1401 static entity_state_t null_es = {0};
1402
1403 for (i = start, es = &baselines->entities[start]; i < MAX_EDICTS; i++, es++)
1404 {
1405 if (!es->origin[0] &&
1406 !es->origin[1] &&
1407 !es->origin[2] &&
1408 !es->angles[0] &&
1409 !es->angles[1] &&
1410 !es->angles[2] &&
1411 !es->old_origin[0] &&
1412 !es->old_origin[1] &&
1413 !es->old_origin[2] &&
1414 !es->modelindex &&
1415 !es->modelindex2 &&
1416 !es->modelindex3 &&
1417 !es->modelindex4 &&
1418 !es->frame &&
1419 !es->skinnum &&
1420 !es->effects &&
1421 !es->renderfx &&
1422 !es->solid &&
1423 !es->sound &&
1424 !es->event)
1425 {
1426 continue;
1427 }
1428
1429 if (block->writeoffset > stopsize)
1430 break;
1431
1432 WriteByte(block, SVC_SPAWNBASELINE);
1433 DM2_WriteEntity(block, es, &null_es, true, false);
1434 }
1435
1436 return i;
1437 }
1438
DM2_ReadPreFrame(serverdata_t * svd,relayinfo_t * relayinfo,char (* configstrings)[64],state_t * baselines,PFILE * fd)1439 int DM2_ReadPreFrame(serverdata_t *svd, relayinfo_t *relayinfo, char (*configstrings)[64], state_t *baselines, PFILE *fd)
1440 {
1441 block_t in;
1442 char in_buffer[MAX_SVSLEN];
1443 int numblocks, id;
1444 qboolean end;
1445
1446 BlockInit(&in, in_buffer, sizeof(in_buffer));
1447 end = false;
1448 numblocks = 0;
1449
1450 while (!end)
1451 {
1452 numblocks++;
1453 if (DM2_ReadBlock(&in, fd) < 0)
1454 return -1;
1455
1456 if (in.writeoffset == 0xffffffff)
1457 return -1;
1458
1459 while (in.readoffset < in.writeoffset)
1460 {
1461 id = ReadByte(&in);
1462
1463 if (ReadOverflow(&in))
1464 return -1;
1465
1466 switch(id)
1467 {
1468 case SVC_STUFFTEXT:
1469 {
1470 char string[MAX_MSGLEN];
1471 const char *cur;
1472
1473 if (DM2_ReadStufftext(&in, string, sizeof(string)) < 0)
1474 return -1;
1475
1476 cur = string;
1477 while (cur)
1478 {
1479 cur = Cmd_TokenizeString(cur);
1480 if (Cmd_Argc() == 0)
1481 continue;
1482
1483 if (!strcmp(Cmd_Argv(0), "precache"))
1484 end = true;
1485 }
1486 }
1487 break;
1488 case SVC_SERVERDATA:
1489 {
1490 if (DM2_ReadServerdata(&in, svd) < 0)
1491 return -1;
1492 }
1493 break;
1494 case SVC_CONFIGSTRING:
1495 {
1496 char string[MAX_MSGLEN];
1497 int index;
1498
1499 if (DM2_ReadConfigstring(&in, &index, string) < 0)
1500 return -1;
1501
1502 strcpy(configstrings[index], string);
1503 }
1504 break;
1505 case SVC_SPAWNBASELINE:
1506 {
1507 int entity;
1508
1509 entity = DM2_ReadBaselineEntity(&in, baselines);
1510 if (entity < 0)
1511 return -1;
1512 }
1513 break;
1514 default:
1515 return -1;
1516 }
1517 }
1518 }
1519
1520 return numblocks;
1521 }
1522
1523 //
1524 // DM2_WritePreFrame
1525 // Writes the SVC_SERVERDATA, SVC_CONFIGSTRING, SVC_SPAWNBASELINE, and SVC_STUFFTEXT
1526 // that precedes all frame information
1527 //
DM2_WritePreFrame(const serverdata_t * svd,const relayinfo_t * relayinfo,const char (* configstrings)[64],const state_t * baselines,PFILE * fd)1528 int DM2_WritePreFrame(const serverdata_t *svd, const relayinfo_t *relayinfo, const char (*configstrings)[64], const state_t *baselines, PFILE *fd)
1529 {
1530 block_t out;
1531 char out_buffer[MAX_SVSLEN];
1532 int i, numblocks;
1533
1534 BlockInit(&out, out_buffer, sizeof(out_buffer));
1535 numblocks = 0;
1536
1537 WriteByte(&out, SVC_SERVERDATA);
1538 DM2_WriteServerdata(&out, svd);
1539
1540 i = 0;
1541 for(;;)
1542 {
1543 i = DM2_WriteConfigstrings(&out, configstrings, i, 1024);
1544 if (i == MAX_CONFIGSTRINGS)
1545 break;
1546
1547 if (WriteOverflow(&out))
1548 return -1;
1549 DM2_WriteBlock(&out, fd);
1550 BlockRewind(&out);
1551 numblocks++;
1552 }
1553
1554 i = 1;
1555 for (;;)
1556 {
1557 i = DM2_WriteBaselines(&out, baselines, i, 1024);
1558 if (i == MAX_EDICTS)
1559 break;
1560
1561 if (WriteOverflow(&out))
1562 return -1;
1563 DM2_WriteBlock(&out, fd);
1564 BlockRewind(&out);
1565 numblocks++;
1566 }
1567
1568 WriteByte(&out, SVC_STUFFTEXT);
1569 DM2_WriteStufftext(&out, "precache\n");
1570
1571 if (WriteOverflow(&out))
1572 return -1;
1573
1574 DM2_WriteBlock(&out, fd);
1575 numblocks++;
1576
1577 return numblocks;
1578 }
1579