#include "dzip.h" void dem_nop(void) { copy_msg(1); } void dem_disconnect(void) { copy_msg(1); } void dem_updatestat(void) { copy_msg(6); } void dem_version(void) { copy_msg(5); } void dem_setview(void) { copy_msg(3); } void dem_sound(void) { uchar mask = inptr[1]; int len = 11; uInt chanent, c, entity; if (mask & 0x01) len++; if (mask & 0x02) len++; inptr[1] = 0x38+mask; chanent = getshort(inptr+len-9) & 0xffff; entity = chanent >> 3; chanent = (entity << 3) | ((2-(chanent & 0x07)) & 0x07); if (entity < MAX_ENT) { c = getshort(inptr+len-6) - oldent[entity].org0; c = cnvlong(c); memcpy(inptr+len-6,&c,2); c = getshort(inptr+len-4) - oldent[entity].org1; c = cnvlong(c); memcpy(inptr+len-4,&c,2); c = getshort(inptr+len-2) - oldent[entity].org2; c = cnvlong(c); memcpy(inptr+len-2,&c,2); } chanent = cnvlong(chanent); memcpy(inptr+len-9,&chanent,2); discard_msg(1); copy_msg(len-1); } void dem_time(void) { long tmp = getlong(inptr+1) - dem_gametime; dem_gametime = getlong(inptr+1); tmp = cnvlong(tmp); memcpy(inptr+1,&tmp,4); if (inptr[3] || inptr[4]) { *inptr = DZ_longtime; copy_msg(5); } else { copy_msg(3); discard_msg(2); } } /* used by lots of msgs */ void dem_string(void) { uchar *ptr = inptr + 1; while (*ptr++); copy_msg(ptr-inptr); } void dem_setangle(void) { copy_msg(4); } void dem_serverinfo(void) { uchar *ptr = inptr + 7; uchar *start_ptr; while (*ptr++); do { start_ptr = ptr; while (*ptr++); } while (ptr - start_ptr > 1); do { start_ptr = ptr; while (*ptr++); } while (ptr - start_ptr > 1); copy_msg(ptr-inptr); sble = 0; } void dem_lightstyle(void) { uchar *ptr = inptr + 2; while (*ptr++); copy_msg(ptr-inptr); } void dem_updatename(void) { uchar *ptr = inptr + 2; while (*ptr++); copy_msg(ptr-inptr); } void dem_updatefrags(void) { copy_msg(4); } uchar bdiff(int x, int y) { int d = x - y; if (d < 0) d += 256; return d & 0xff; } void dem_clientdata(void) { uchar buf[32]; uchar *ptr = inptr+3; uInt mask = getshort(inptr+1); long tmp; memset(&newcd,0,sizeof(cdata_t)); newcd.voz = 22; newcd.items = 0x4001; #define CFDIFF(x,def,bit) if (newcd.x == def) newcd.force |= bit; if (mask & 0x0001) { newcd.voz = *ptr++; CFDIFF(voz,22,0x0800); } if (mask & 0x0002) { newcd.pax = *ptr++; CFDIFF(pax,0,0x1000); } if (mask & 0x0004) { newcd.ang0 = *ptr++; CFDIFF(ang0,0,0x0100); } if (mask & 0x0020) { newcd.vel0 = *ptr++; CFDIFF(vel0,0,1); } if (mask & 0x0008) { newcd.ang1 = *ptr++; CFDIFF(ang1,0,0x0200); } if (mask & 0x0040) { newcd.vel1 = *ptr++; CFDIFF(vel1,0,2); } if (mask & 0x0010) { newcd.ang2 = *ptr++; CFDIFF(ang2,0,0x0400); } if (mask & 0x0080) { newcd.vel2 = *ptr++; CFDIFF(vel2,0,4); } newcd.items = getlong(ptr); ptr += 4; newcd.invbit = !(mask & 0x0200); newcd.uk10 = !!(mask & 0x0400); newcd.uk11 = !!(mask & 0x0800); if (mask & 0x1000) { newcd.wpf = *ptr++; CFDIFF(wpf,0,0x2000); } if (mask & 0x2000) { newcd.av = *ptr++; CFDIFF(av,0,0x4000); } if (mask & 0x4000) { newcd.wpm = *ptr++; CFDIFF(wpm,0,0x8000); } newcd.health = getshort(ptr); ptr += 2; newcd.am = *ptr++; newcd.sh = *ptr++; newcd.nl = *ptr++; newcd.rk = *ptr++; newcd.ce = *ptr++; newcd.wp = *ptr++; discard_msg(ptr-inptr); mask = 0x00000040; ptr = buf+4; #define CDIFF(x,b) \ if (oldcd.x != newcd.x) \ { mask |= b; *ptr++ = bdiff(newcd.x,oldcd.x); } CDIFF(vel2,0x00000001); CDIFF(vel0,0x00000002); CDIFF(vel1,0x00000004); CDIFF(wpf,0x00000100); if (oldcd.uk10 != newcd.uk10) mask |= 0x00000200; CDIFF(ang0,0x00000400); CDIFF(am,0x00000800); if (oldcd.health != newcd.health) { mask |= 0x00001000; tmp = newcd.health - oldcd.health; tmp = cnvlong(tmp); memcpy(ptr,&tmp,2); ptr += 2; } if (oldcd.items != newcd.items) { mask |= 0x00002000; tmp = newcd.items ^ oldcd.items; tmp = cnvlong(tmp); memcpy(ptr,&tmp,4); ptr += 4; } CDIFF(av,0x00004000); CDIFF(pax,0x00010000); CDIFF(sh,0x00020000); CDIFF(nl,0x00040000); CDIFF(rk,0x00080000); CDIFF(wpm,0x00100000); CDIFF(wp,0x00200000); if (oldcd.uk11 != newcd.uk11) mask |= 0x00400000; CDIFF(voz,0x01000000); CDIFF(ce,0x02000000); CDIFF(ang1,0x04000000); CDIFF(ang2,0x08000000); if (oldcd.invbit != newcd.invbit) mask |= 0x10000000; if (mask & 0xffffff00) mask |= 0x08; if (mask & 0xffff0000) mask |= 0x8000; if (mask & 0xff000000) mask |= 0x800000; tmp = cnvlong(mask); memcpy(buf,&tmp,4); if (!(mask & 0x08)) { memcpy(buf+1,buf+4,ptr-buf-4); ptr -= 3; } else if (!(mask & 0x8000)) { memcpy(buf+2,buf+4,ptr-buf-4); ptr -= 2; } else if (!(mask & 0x800000)) { memcpy(buf+3,buf+4,ptr-buf-4); ptr -= 1; } insert_msg(buf,ptr-buf); if (newcd.force != oldcd.force) { mask = 0x50 | (newcd.force ^ oldcd.force); if (mask & 0xff00) mask |= 8; buf[0] = mask & 0xff; buf[1] = (mask >> 8) & 0xff; insert_msg(buf,1+!!buf[1]); } oldcd = newcd; } void dem_stopsound(void) { copy_msg(3); } void dem_updatecolors(void) { copy_msg(3); } void dem_particle(void) { copy_msg(12); } void dem_damage(void) { copy_msg(9); } void dem_spawnstatic(void) { copy_msg(14); } void dem_spawnbinary(void) { copy_msg(1); } void dem_spawnbaseline(void) { uchar buf[16], *ptr; ent_t ent; int index = getshort(inptr+1); int diff; memset(&ent,0,sizeof(ent_t)); ent.modelindex = inptr[3]; ent.frame = inptr[4]; ent.colormap = inptr[5]; ent.skin = inptr[6]; ent.org0 = getshort(inptr+7); ent.ang0 = inptr[9]; ent.org1 = getshort(inptr+10); ent.ang1 = inptr[12]; ent.org2 = getshort(inptr+13); ent.ang2 = inptr[15]; discard_msg(16); buf[0] = DEM_spawnbaseline; diff = (index - sble + 0x400) % 0x400; buf[1] = diff & 0xff; buf[2] = diff >> 8; ptr = buf+3; *ptr++ = ent.modelindex; if (ent.frame) { buf[2] |= 0x04; *ptr++ = ent.frame; } if (ent.colormap) { buf[2] |= 0x08; *ptr++ = ent.colormap; } if (ent.skin) { buf[2] |= 0x10; *ptr++ = ent.skin; } if (ent.org0 || ent.org1 || ent.org2) { int tmp; buf[2] |= 0x20; tmp = ent.org0; tmp = cnvlong(tmp); memcpy(ptr,&tmp,2); tmp = ent.org1; tmp = cnvlong(tmp); memcpy(ptr+2,&tmp,2); tmp = ent.org2; tmp = cnvlong(tmp); memcpy(ptr+4,&tmp,2); ptr += 6; } if (ent.ang1) { buf[2] |= 0x40; *ptr++ = ent.ang1; } if (ent.ang0 || ent.ang2) { buf[2] |= 0x80; *ptr++ = ent.ang0; *ptr++ = ent.ang2; } insert_msg(buf,ptr-buf); base[index] = ent; sble = index; copybaseline = 1; } const uchar te_size[] = {8, 8, 8, 8, 8, 16, 16, 8, 8, 16, 8, 8, 10, 16, 8, 8, 14}; void dem_temp_entity(void) { uchar entitytype = inptr[1]; if (entitytype >= 16) { directory[numfiles].type = TYPE_NEHAHRA; if (entitytype == 17) { copy_msg(strlen((char *)inptr + 2) + 17); return; } if (entitytype > 17) /* this should be a bailout */ error("entitytype is %i", entitytype); } copy_msg(te_size[entitytype]); } void dem_setpause(void) { copy_msg(2); } void dem_signonnum(void) { copy_msg(2); } void dem_killedmonster(void) { copy_msg(1); } void dem_foundsecret(void) { copy_msg(1); } void dem_spawnstaticsound(void) { copy_msg(10); } void dem_intermission(void) { copy_msg(1); } void dem_cdtrack(void) { copy_msg(3); } void dem_sellscreen(void) { copy_msg(1); } /* nehahra */ void dem_showlmp(void) { uchar *ptr = inptr + 1; while (*ptr++); while (*ptr++); ptr += 2; *inptr = DZ_showlmp; /* DEM_showlmp (35) is used by DZ_longtime */ copy_msg(ptr-inptr); directory[numfiles].type = TYPE_NEHAHRA; } void dem_hidelmp(void) { uchar *ptr = inptr + 1; while (*ptr++); copy_msg(ptr-inptr); directory[numfiles].type = TYPE_NEHAHRA; } void dem_skybox(void) { uchar *ptr = inptr + 1; while (*ptr++); copy_msg(ptr-inptr); directory[numfiles].type = TYPE_NEHAHRA; } /* end nehahra */ void dem_copy_ue(void) { uchar mask = inptr[0] & 0x7f; uchar topmask; int len = 1; topmask = (mask & 0x01)? inptr[len++] : 0x00; if (topmask & 0x40) len += 2; else len++; if (topmask & 0x04) len++; if (mask & 0x40) len++; if (topmask & 0x08) len++; if (topmask & 0x10) len++; if (topmask & 0x20) len++; if (mask & 0x02) len += 2; if (topmask & 0x01) len++; if (mask & 0x04) len += 2; if (mask & 0x10) len++; if (mask & 0x08) len += 2; if (topmask & 0x02) len++; if (topmask & 0x80) /* this should be a bailout */ error("dem_copy_ue(): topmask & 0x80"); copy_msg(len); } void diff_entities(void); void dem_updateentity(void) { uchar mask = *inptr & 0x7f; uchar topmask; short entity; static short lastentity; int len = 1; if (!dem_updateframe) { dem_updateframe = 1; lastentity = 0; } if (dem_updateframe == 2) { dem_copy_ue(); return; } topmask = (mask & 0x01)? inptr[len++] : 0; if (topmask & 0x40) { entity = getshort(inptr+len); len += 2; } else { entity = inptr[len++]; } if (entity <= lastentity) { diff_entities(); dem_updateframe = 2; dem_copy_ue(); return; } lastentity = entity; memcpy(newent + entity, base + entity, sizeof(ent_t)); if (topmask & 0x40 && entity <= 0xff) newent[entity].force |= 0x400000; #define FDIFF(x,bit) if (newent[entity].x == base[entity].x) \ newent[entity].force |= bit; if (topmask & 0x04) { newent[entity].modelindex = inptr[len++]; FDIFF(modelindex,0x040000); } if (mask & 0x40) { newent[entity].frame = inptr[len++]; FDIFF(frame,0x4000); } if (topmask & 0x08) { newent[entity].colormap=inptr[len++]; FDIFF(colormap,0x080000); } if (topmask & 0x10) { newent[entity].skin = inptr[len++]; FDIFF(skin,0x100000); } if (topmask & 0x20) { newent[entity].effects = inptr[len++]; FDIFF(effects,0x200000); } if (mask & 0x02) { newent[entity].org0 = getshort(inptr+len); len += 2; FDIFF(org0,0x010000); } if (topmask & 0x01) { newent[entity].ang0 = inptr[len++]; FDIFF(ang0,0x0800); } if (mask & 0x04) { newent[entity].org1 = getshort(inptr+len); len += 2; FDIFF(org1,0x0400); } if (mask & 0x10) { newent[entity].ang1 = inptr[len++]; FDIFF(ang1,0x1000); } if (mask & 0x08) { newent[entity].org2 = getshort(inptr+len); len += 2; FDIFF(org2,0x020000); } if (topmask & 0x02) { newent[entity].ang2 = inptr[len++]; FDIFF(ang2,0x2000); } /* nehahra */ if (topmask & 0x80) { float tmp = getfloat(inptr + len); directory[numfiles].type = TYPE_NEHAHRA; if (tmp != 2 && tmp != 1) /* this should be a bailout */ error("nehahra: tmp is %f\n", tmp); newent[entity].alpha = getfloat(inptr + len + 4); len += 8; if (tmp == 2) { newent[entity].fullbright = 1 + (int)getfloat(inptr + len); if (newent[entity].fullbright != 2 && newent[entity].fullbright != 1) /* this should be a bailout */ error("nehahra: fullbright is %f\n", getfloat(inptr + len)); len += 4; } else newent[entity].fullbright = 0; newent[entity].force |= 0x800000; } newent[entity].newbit = mask & 0x20; newent[entity].present = 1; newent[entity].od0 = newent[entity].org0 - oldent[entity].org0; newent[entity].od1 = newent[entity].org1 - oldent[entity].org1; newent[entity].od2 = newent[entity].org2 - oldent[entity].org2; while (entlink[lastent] <= entity) lastent = entlink[lastent]; if (lastent < entity) { entlink[entity] = entlink[lastent]; entlink[lastent] = entity; } discard_msg(len); } void (* const dem_message[])(void) = { dem_nop, dem_disconnect, dem_updatestat, dem_version, dem_setview, dem_sound, dem_time, dem_string, dem_string, dem_setangle, dem_serverinfo, dem_lightstyle, dem_updatename, dem_updatefrags, dem_clientdata, dem_stopsound, dem_updatecolors, dem_particle, dem_damage, dem_spawnstatic, dem_spawnbinary, dem_spawnbaseline, dem_temp_entity, dem_setpause, dem_signonnum, dem_string, dem_killedmonster, dem_foundsecret, dem_spawnstaticsound, dem_intermission, dem_string, dem_cdtrack, dem_sellscreen, dem_string, dem_showlmp, dem_hidelmp, dem_skybox /* nehahra */ }; void update_activate(int i, int *baseval, uchar *buf) { uchar *ptr = buf; while (i - *baseval >= 0xff) { *ptr++ = 0xff; *baseval += 0xfe; } *ptr++ = i - *baseval; insert_msg(buf,ptr-buf); } void diff_entities(void) { uchar buf[32]; uchar *ptr; long tmp; int i, prev, mask; int firstent; int baseval = 0; uchar *tptr = tmpblk; buf[0] = 0x30; insert_msg(buf,1); for (prev = 0, i = entlink[0]; i < MAX_ENT; i = entlink[i]) { ent_t n = newent[i], o = oldent[i]; if (!n.present) { *tptr++ = 0x80; if (!o.active) update_activate(i,&baseval,buf); oldent[i] = base[i]; entlink[prev] = entlink[i]; continue; } if (!o.present) update_activate(i,&baseval,buf); prev = i; ptr = tptr+4; mask = 0; if (o.od2 != n.od2) { int diff = n.od2 - o.od2; if (diff >= -128 && diff <= 127) { if (diff < 0) diff += 256; mask |= 0x000001; *ptr++ = diff; } else { mask |= 0x000800; tmp = cnvlong(n.org2); memcpy(ptr,&tmp,2); ptr += 2; } } if (o.od1 != n.od1) { int diff = n.od1 - o.od1; if (diff >= -128 && diff <= 127) { if (diff < 0) diff += 256; mask |= 0x000002; *ptr++ = diff; } else { mask |= 0x000400; tmp = cnvlong(n.org1); memcpy(ptr,&tmp,2); ptr += 2; } } if (o.od0 != n.od0) { int diff = n.od0 - o.od0; if (diff >= -128 && diff <= 127) { if (diff < 0) diff += 256; mask |= 0x000004; *ptr++ = diff; } else { mask |= 0x000200; tmp = cnvlong(n.org0); memcpy(ptr,&tmp,2); ptr += 2; } } if (o.ang0 != n.ang0) { mask |= 0x000008; *ptr++ = bdiff(n.ang0,o.ang0); } if (o.ang1 != n.ang1) { mask |= 0x000010; *ptr++ = bdiff(n.ang1,o.ang1); } if (o.ang2 != n.ang2) { mask |= 0x000020; *ptr++ = bdiff(n.ang2,o.ang2); } if (o.frame != n.frame) { if (n.frame - o.frame == 1) mask |= 0x000040; else { mask |= 0x000100; *ptr++ = bdiff(n.frame,o.frame); } } if (o.effects != n.effects) { mask |= 0x001000; *ptr++ = n.effects; } if (o.modelindex != n.modelindex) { mask |= 0x002000; *ptr++ = n.modelindex; } if (o.newbit != n.newbit) mask |= 0x004000; if (o.colormap != n.colormap) { mask |= 0x010000; *ptr++ = n.colormap; } if (o.skin != n.skin) { mask |= 0x020000; *ptr++ = n.skin; } /* nehahra */ if (n.alpha != o.alpha) { float ftmp; ftmp = getfloat((uchar *)&n.alpha); memcpy(ptr, &ftmp, 4); ptr += 4; mask |= 0x040000; } if (n.fullbright != o.fullbright) { mask |= 0x080000; *ptr++ = n.fullbright; } if ((mask & 0xffff00) && !(mask & 0x0000ff)) { mask |= 0x01; tptr[3] = 0; } else { memcpy(tptr+3,tptr+4,ptr-tptr-4); ptr--; } if (mask & 0xffff00) mask |= 0x80; if (mask & 0xff0000) mask |= 0x8000; if (!mask) { if (o.present && !o.active) continue; *tptr++ = 0x00; continue; } newent[i].active = 1; if (!o.active && o.present) update_activate(i,&baseval,buf); tmp = cnvlong(mask); memcpy(tptr,&tmp,3); if (!(mask & 0xffff00)) { memcpy(tptr+1,tptr+3,ptr-tptr-3); ptr -= 2; } else if (!(mask & 0xff0000)) { memcpy(tptr+2,tptr+3,ptr-tptr-3); ptr--; } tptr = ptr; } buf[0] = 0; insert_msg(buf,1); insert_msg(tmpblk,tptr-tmpblk); firstent = -1; for (i = entlink[0]; i < MAX_ENT; i = entlink[i]) { if (oldent[i].force == newent[i].force) continue; if (firstent < 0) { firstent = i; buf[0] = 0x31; insert_msg(buf,1); } mask = i | (oldent[i].force ^ newent[i].force); if (mask & 0xff0000) mask |= 0x8000; buf[0] = mask & 0xff; buf[1] = (mask >> 8) & 0xff; buf[2] = (mask >> 16) & 0xff; insert_msg(buf,2+!!buf[2]); } if (firstent > 0) { buf[0] = buf[1] = 0; insert_msg(buf,2); } for (i = entlink[0]; i < MAX_ENT; i = entlink[i]) { oldent[i] = newent[i]; newent[i].present = 0; } } void dem_compress (uInt start, uInt stop) { unsigned int inlen, pos, crc_cheat; int cfields, clen; char cdstring[12]; uchar *ptr; long a1,a2,a3,o1,o2,o3; #define bail(s) { fprintf(stderr,"\nwarning: %s\n",s); goto bailout; } /* 12 is the max length of a real quake-demo's cd string */ if (stop - start > 13) for (clen = 0; clen < 12; clen++) { Infile_Read(cdstring + clen, 1); if (cdstring[clen] == '\n') { dzWrite(cdstring, clen + 1); goto tryit; } else if (cdstring[clen] == '-') /* ok */; else if (cdstring[clen] < '0' || cdstring[clen] > '9') break; } /* failed the test */ directory[numfiles].type = TYPE_NORMAL; Infile_Seek(start); crcval = INITCRC; normal_compress(stop - start); return; tryit: memset(&base,0,sizeof(ent_t)*MAX_ENT); memset(&oldent,0,sizeof(ent_t)*MAX_ENT); memset(&oldcd,0,sizeof(cdata_t)); oldcd.voz = 22; oldcd.items = 0x4001; entlink[0] = MAX_ENT; o1 = o2 = o3 = 0; copybaseline = 0; dem_gametime = 0; sble = 0; for (pos = start + clen + 1; pos < stop; pos += 4 + inlen) { cfields = 0, clen = 0; crc_cheat = crcval; if (pos + 4 > stop) bail("unexpected EOF"); Infile_Read(&inlen,4); inlen = cnvlong(inlen) + 12; if (inlen > p_blocksize / 2) bail("block too long"); if (pos + inlen + 4 > stop) bail("unexpected EOF"); Infile_Read(inblk, inlen); outlen = 1; ptr = inptr = inblk; a1 = getlong(inptr); a2 = getlong(inptr + 4); a3 = getlong(inptr + 8); o1 = a1 - o1; o1 = cnvlong(o1); o2 = a2 - o2; o2 = cnvlong(o2); o3 = a3 - o3; o3 = cnvlong(o3); if (o1) { cfields |= 1; memcpy(ptr,&o1,4); ptr += 4; } if (o2) { cfields |= 2; memcpy(ptr,&o2,4); ptr += 4; } if (o3) { cfields |= 4; memcpy(ptr,&o3,4); ptr += 4; } o1 = a1; o2 = a2; o3 = a3; clen = ptr - inptr; copy_msg(clen); discard_msg(12-clen); dem_updateframe = lastent = 0; while (inptr < inblk + inlen) { if (*inptr && *inptr <= DEM_skybox) { if (dem_updateframe == 1) { diff_entities(); dem_updateframe = 2; } dem_message[(int)(*inptr - 1)](); } else if (*inptr & 0x80) dem_updateentity(); else bail("bad message encountered"); } if (dem_updateframe == 1) diff_entities(); if (inptr > inblk + inlen) bail("parse error"); if (outlen > p_blocksize / 2) bail("block too large in output"); *outblk = cfields; cfields = 0; insert_msg(&cfields,1); dzWrite(outblk,outlen); if (copybaseline) { memcpy(oldent,base,sizeof(ent_t)*MAX_ENT); copybaseline = 0; } if (AbortOp) return; } return; bailout: Infile_Seek(pos); inlen = stop - pos; crcval = crc_cheat; while (inlen && !AbortOp) { int toread = (inlen > p_blocksize-5)? p_blocksize-5 : inlen; toread = cnvlong(toread); *inblk = 0xff; memcpy(inblk+1,&toread,4); toread = cnvlong(toread); Infile_Read(inblk+5,toread); dzWrite(inblk,toread+5); inlen -= toread; } }