static long ovc_tell (void *datasource) { bgTrack_t *track = (bgTrack_t *)datasource; #ifdef OGG_DIRECT_FILE return ftell(track->file); #else Com_Printf("Calling FS_Tell\n"); return FS_Tell(track->file); #endif }
/* * SCR_PlayCinematic */ static void SCR_PlayCinematic( char *arg ) { int len; size_t name_size; static cinematics_t clientCin; cinematics_t *cin = cl.cin = &clientCin; roq_chunk_t *chunk = &cin->chunk; name_size = strlen( "video/" ) + strlen( arg ) + strlen( ".roq" ) + 1; cin->name = Mem_ZoneMalloc( name_size ); Q_snprintfz( cin->name, name_size, "video/%s", arg ); COM_DefaultExtension( cin->name, ".roq", name_size ); // nasty hack cin->s_rate = 22050; cin->s_width = 2; cin->width = cin->height = 0; cin->frame = 0; len = FS_FOpenFile( cin->name, &cin->file, FS_READ ); if( !cin->file || len < 1 ) { Com_Printf( "Couldn't find %s\n", cin->name ); SCR_StopCinematic(); return; } // read header RoQ_ReadChunk( cin ); if( chunk->id != RoQ_HEADER1 || chunk->size != RoQ_HEADER2 || chunk->argument != RoQ_HEADER3 ) { Com_Printf( "Invalid video file %s\n", cin->name ); SCR_StopCinematic(); return; } SCR_EndLoadingPlaque(); cin->headerlen = FS_Tell( cin->file ); cin->frame = 0; cin->pic = cin->pic_pending = SCR_ReadNextCinematicFrame(); cin->time = cls.realtime + 1; // add 1 msec so SCR_GetCinematicTime is also valid for early commands CL_SetClientState( CA_ACTIVE ); CL_SoundModule_StopAllSounds(); }
/* ================= CL_DrawDemoRecording ================= */ void CL_DrawDemoRecording( void ) { char string[64]; rgba_t color = { 255, 255, 255, 255 }; fs_offset_t pos; int len; if(!( host.developer && cls.demorecording )) return; pos = FS_Tell( cls.demofile ); Q_snprintf( string, sizeof( string ), "RECORDING %s: %ik", cls.demoname, (int)(pos / 1024) ); Con_DrawStringLen( string, &len, NULL ); Con_DrawString(( scr_width->integer - len) >> 1, scr_height->integer >> 2, string, color ); }
/* ================= CL_StopRecord finish recording demo ================= */ void CL_StopRecord( void ) { int i, curpos; float stoptime; if( !cls.demorecording ) return; // demo playback should read this as an incoming message. CL_WriteDemoCmdHeader( dem_stop, cls.demofile ); stoptime = CL_GetDemoRecordClock(); // close down the hud for now. // g-cont. is this need??? if( clgame.hInstance ) clgame.dllFuncs.pfnReset(); curpos = FS_Tell( cls.demofile ); demo.entry->length = curpos - demo.entry->offset; demo.entry->playback_time = stoptime - demo.realstarttime; demo.entry->playback_frames = demo.framecount; // Now write out the directory and free it and touch up the demo header. FS_Write( cls.demofile, &demo.directory.numentries, sizeof( int )); for( i = 0; i < demo.directory.numentries; i++ ) { FS_Write( cls.demofile, &demo.directory.entries[i], sizeof( demoentry_t )); } Mem_Free( demo.directory.entries ); demo.directory.numentries = 0; demo.header.directory_offset = curpos; FS_Seek( cls.demofile, 0, SEEK_SET ); FS_Write( cls.demofile, &demo.header, sizeof( demo.header )); FS_Close( cls.demofile ); cls.demofile = NULL; cls.demorecording = false; cls.demoname[0] = '\0'; menu.globals->demoname[0] = '\0'; Msg( "Completed demo\n" ); MsgDev( D_INFO, "Recording time %.2f\n", cls.demotime ); cls.demotime = 0.0; }
/* ==================== CheckPendingDownloads checks if there are free download slots to start new downloads in. To not start too many downloads at once, only one download is added at a time, up to a maximum number of cl_curl_maxdownloads are running. ==================== */ static void CheckPendingDownloads(void) { const char *h; if(!curl_dll) return; if(numdownloads < cl_curl_maxdownloads.integer) { downloadinfo *di; for(di = downloads; di; di = di->next) { if(!di->started) { if(!di->buffer) { Con_Printf("Downloading %s -> %s", CleanURL(di->url), di->filename); di->stream = FS_OpenRealFile(di->filename, "ab", false); if(!di->stream) { Con_Printf("\nFAILED: Could not open output file %s\n", di->filename); Curl_EndDownload(di, CURL_DOWNLOAD_FAILED, CURLE_OK); return; } FS_Seek(di->stream, 0, SEEK_END); di->startpos = FS_Tell(di->stream); if(di->startpos > 0) Con_Printf(", resuming from position %ld", (long) di->startpos); Con_Print("...\n"); } else { Con_DPrintf("Downloading %s -> memory\n", CleanURL(di->url)); di->startpos = 0; } di->curle = qcurl_easy_init(); di->slist = NULL; qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url); qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion); qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer); qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos); qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1); qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite); qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_LIMIT, (long) 256); qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_TIME, (long) 45); qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di); qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di); qcurl_easy_setopt(di->curle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP); if(qcurl_easy_setopt(di->curle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP) != CURLE_OK) { Con_Printf("^1WARNING:^7 for security reasons, please upgrade to libcurl 7.19.4 or above. In a later version of DarkPlaces, HTTP redirect support will be disabled for this libcurl version.\n"); //qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 0); } if(di->post_content_type) { qcurl_easy_setopt(di->curle, CURLOPT_POST, 1); qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDS, di->postbuf); qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDSIZE, di->postbufsize); di->slist = qcurl_slist_append(di->slist, va("Content-Type: %s", di->post_content_type)); } // parse extra headers into slist // \n separated list! h = di->extraheaders; while(h) { const char *hh = strchr(h, '\n'); if(hh) { char *buf = (char *) Mem_Alloc(tempmempool, hh - h + 1); memcpy(buf, h, hh - h); buf[hh - h] = 0; di->slist = qcurl_slist_append(di->slist, buf); h = hh + 1; } else { di->slist = qcurl_slist_append(di->slist, h); h = NULL; } } qcurl_easy_setopt(di->curle, CURLOPT_HTTPHEADER, di->slist); qcurl_multi_add_handle(curlm, di->curle); di->started = true; ++numdownloads; if(numdownloads >= cl_curl_maxdownloads.integer) break; } } } }
/* <2a121> ../engine/hashpak.c:1060 */ void HPAK_CreatePak(char *pakname, struct resource_s *pResource, void *pData, FileHandle_t fpSource) { char name[MAX_PATH]; int32 curpos; FileHandle_t fp; hash_pack_entry_t *pCurrentEntry; byte md5[16]; MD5Context_t ctx; byte *pDiskData; if ((!fpSource && !pData) || (fpSource && pData)) { Con_Printf("HPAK_CreatePak, must specify one of pData or fpSource\n"); return; } Q_snprintf(name, ARRAYSIZE(name), "%s", pakname); #ifdef REHLDS_FIXES name[ARRAYSIZE(name) - 1] = 0; #endif // REHLDS_FIXES COM_DefaultExtension(name, HASHPAK_EXTENSION); Con_Printf("Creating HPAK %s.\n", name); fp = FS_Open(name, "wb"); if (!fp) { Con_Printf("ERROR: couldn't open new .hpk, check access rights to %s.\n", name); return; } Q_memset(&ctx, 0, sizeof(MD5Context_t)); MD5Init(&ctx); if (pData) MD5Update(&ctx, (byte *)pData, pResource->nDownloadSize); else { curpos = FS_Tell(fpSource); pDiskData = (byte *)Mem_Malloc(pResource->nDownloadSize + 1); Q_memset(pDiskData, 0, pResource->nDownloadSize); FS_Read(pDiskData, pResource->nDownloadSize, 1, fp); FS_Seek(fpSource, curpos, FILESYSTEM_SEEK_HEAD); MD5Update(&ctx, pDiskData, pResource->nDownloadSize); Mem_Free(pDiskData); } MD5Final(md5, &ctx); if (Q_memcmp(pResource->rgucMD5_hash, md5, sizeof(md5)) != 0) { Con_Printf("HPAK_CreatePak called with bogus lump, md5 mismatch\n"); Con_Printf("Purported: %s\n", MD5_Print(pResource->rgucMD5_hash)); Con_Printf("Actual : %s\n", MD5_Print(md5)); Con_Printf("Ignoring lump addition\n"); return; } Q_memset(&hash_pack_header, 0, sizeof(hash_pack_header_t)); Q_memcpy(hash_pack_header.szFileStamp, "HPAK", sizeof(hash_pack_header.szFileStamp)); hash_pack_header.version = HASHPAK_VERSION; hash_pack_header.nDirectoryOffset = 0; FS_Write(&hash_pack_header, sizeof(hash_pack_header_t), 1, fp); Q_memset(&hash_pack_dir, 0, sizeof(hash_pack_directory_t)); hash_pack_dir.nEntries = 1; hash_pack_dir.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t)); Q_memset(hash_pack_dir.p_rgEntries, 0, sizeof(hash_pack_entry_t) * hash_pack_dir.nEntries); pCurrentEntry = &hash_pack_dir.p_rgEntries[0]; Q_memcpy(&pCurrentEntry->resource, pResource, sizeof(resource_t)); pCurrentEntry->nOffset = FS_Tell(fp); pCurrentEntry->nFileLength = pResource->nDownloadSize; if (pData) FS_Write(pData, pResource->nDownloadSize, 1, fp); else COM_CopyFileChunk(fp, fpSource, pResource->nDownloadSize); curpos = FS_Tell(fp); FS_Write(&hash_pack_dir.nEntries, 4, 1, fp); FS_Write(hash_pack_dir.p_rgEntries, sizeof(hash_pack_entry_t), 1, fp); if (hash_pack_dir.p_rgEntries) { Mem_Free(hash_pack_dir.p_rgEntries); hash_pack_dir.p_rgEntries = NULL; } hash_pack_dir.nEntries = 0; hash_pack_header.nDirectoryOffset = curpos; FS_Seek(fp, 0, FILESYSTEM_SEEK_HEAD); FS_Write(&hash_pack_header, sizeof(hash_pack_header_t), 1, fp); FS_Close(fp); }
/* <2a974> ../engine/hashpak.c:598 */ void HPAK_RemoveLump(char *pakname, resource_t *pResource) { FileHandle_t fp; FileHandle_t tmp; char szTempName[MAX_PATH]; char szOriginalName[MAX_PATH]; hash_pack_directory_t olddir; hash_pack_directory_t newdir; hash_pack_entry_t *oldentry; hash_pack_entry_t *newentry; int n; int i; if (pakname == NULL || *pakname == '\0' || pResource == NULL) { Con_Printf(__FUNCTION__ ": Invalid arguments\n"); return; } HPAK_FlushHostQueue(); #ifdef REHLDS_FIXES Q_strncpy(szOriginalName, pakname, ARRAYSIZE(szOriginalName) - 1); szOriginalName[ARRAYSIZE(szOriginalName) - 1] = 0; COM_DefaultExtension(szOriginalName, HASHPAK_EXTENSION); #else //TODO: Not sure why Cmd_Argv(1) is used since function receives pakname parameter char name[MAX_PATH]; Q_snprintf(name, ARRAYSIZE(name), "%s", Cmd_Argv(1)); COM_DefaultExtension(name, HASHPAK_EXTENSION); Q_strncpy(szOriginalName, name, ARRAYSIZE(szOriginalName) - 1); szOriginalName[ARRAYSIZE(szOriginalName) - 1] = 0; #endif // REHLDS_FIXES fp = FS_Open(szOriginalName, "rb"); if (!fp) { Con_Printf("Error: couldn't open HPAK file %s for removal.\n", szOriginalName); return; } COM_StripExtension(szOriginalName, szTempName); COM_DefaultExtension(szTempName, ".hp2"); tmp = FS_Open(szTempName, "w+b"); if (!tmp) { FS_Close(fp); Con_Printf("ERROR: couldn't create %s.\n", szTempName); return; } FS_Seek(fp, 0, FILESYSTEM_SEEK_HEAD); FS_Seek(tmp, 0, FILESYSTEM_SEEK_HEAD); FS_Read(&hash_pack_header, sizeof(hash_pack_header_t), 1, fp); FS_Write(&hash_pack_header, sizeof(hash_pack_header_t), 1, tmp); if (Q_strncmp(hash_pack_header.szFileStamp, "HPAK", sizeof(hash_pack_header.szFileStamp))) { FS_Close(fp); FS_Close(tmp); FS_Unlink(szTempName); Con_Printf("%s is not an HPAK file\n", szOriginalName); return; } if (hash_pack_header.version != HASHPAK_VERSION) { FS_Close(fp); FS_Close(tmp); FS_Unlink(szTempName); Con_Printf("ERROR: HPAK version outdated\n"); return; } FS_Seek(fp, hash_pack_header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); FS_Read(&olddir.nEntries, 4, 1, fp); if (olddir.nEntries < 1 || (unsigned int)olddir.nEntries > MAX_FILE_ENTRIES) { FS_Close(fp); FS_Close(tmp); FS_Unlink(szTempName); Con_Printf("ERROR: HPAK had bogus # of directory entries: %i\n", olddir.nEntries); return; } if (olddir.nEntries == 1) { FS_Close(fp); FS_Close(tmp); FS_Unlink(szOriginalName); FS_Unlink(szTempName); Con_Printf("Removing final lump from HPAK, deleting HPAK:\n %s\n", szOriginalName); return; } olddir.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * olddir.nEntries); FS_Read(olddir.p_rgEntries, sizeof(hash_pack_entry_t) * olddir.nEntries, 1, fp); newdir.nEntries = olddir.nEntries - 1; newdir.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * newdir.nEntries); if (!HPAK_FindResource(&olddir, pResource->rgucMD5_hash, NULL)) { FS_Close(fp); FS_Close(tmp); FS_Unlink(szTempName); Mem_Free(olddir.p_rgEntries); Mem_Free(newdir.p_rgEntries); Con_Printf("ERROR: HPAK doesn't contain specified lump: %s\n", pResource->szFileName); return; } Con_Printf("Removing %s from HPAK %s.\n", pResource->szFileName, szOriginalName); for (i = 0, n = 0; i < olddir.nEntries; i++) { oldentry = &olddir.p_rgEntries[i]; if (Q_memcmp(olddir.p_rgEntries[i].resource.rgucMD5_hash, pResource->rgucMD5_hash, 16)) { newentry = &newdir.p_rgEntries[n++]; Q_memcpy(newentry, oldentry, sizeof(hash_pack_entry_t)); newentry->nOffset = FS_Tell(tmp); FS_Seek(fp, oldentry->nOffset, FILESYSTEM_SEEK_HEAD); COM_CopyFileChunk(tmp, fp, newentry->nFileLength); } } hash_pack_header.nDirectoryOffset = FS_Tell(tmp); FS_Write(&newdir.nEntries, 4, 1, tmp); for (i = 0; i < newdir.nEntries; i++) FS_Write(&newdir.p_rgEntries[i], sizeof(hash_pack_entry_t), 1, tmp); FS_Seek(tmp, 0, FILESYSTEM_SEEK_HEAD); FS_Write(&hash_pack_header, sizeof(hash_pack_header_t), 1, tmp); FS_Close(fp); FS_Close(tmp); FS_Unlink(szOriginalName); FS_Rename(szTempName, szOriginalName); Mem_Free(olddir.p_rgEntries); Mem_Free(newdir.p_rgEntries); }
/* <2a494> ../engine/hashpak.c:322 */ void HPAK_AddLump(qboolean bUseQueue, char *pakname, struct resource_s *pResource, void *pData, FileHandle_t fpSource) { FileHandle_t iRead; FileHandle_t iWrite; char name[MAX_PATH]; char szTempName[MAX_PATH]; char szOriginalName[MAX_PATH]; hash_pack_directory_t olddirectory; hash_pack_directory_t newdirectory; hash_pack_entry_t *pNewEntry; byte md5[16]; MD5Context_t ctx; byte *pDiskData; if (pakname == NULL) { Con_Printf("HPAK_AddLump called with invalid arguments: no .pak filename\n"); return; } if (!pResource) { Con_Printf("HPAK_AddLump called with invalid arguments: no lump to add\n"); return; } if (!pData && !fpSource) { Con_Printf("HPAK_AddLump called with invalid arguments: no file handle\n"); return; } if (pResource->nDownloadSize < 1024 || (unsigned int)pResource->nDownloadSize > MAX_FILE_SIZE) { Con_Printf("HPAK_AddLump called with bogus lump, size: %i\n", pResource->nDownloadSize); return; } Q_memset(&ctx, 0, sizeof(MD5Context_t)); MD5Init(&ctx); if (pData) MD5Update(&ctx, (byte *)pData, pResource->nDownloadSize); else { pDiskData = (byte *)Mem_Malloc(pResource->nDownloadSize + 1); Q_memset(pDiskData, 0, pResource->nDownloadSize); FS_Read(pDiskData, pResource->nDownloadSize, 1, fpSource); FS_Seek(fpSource, FS_Tell(fpSource), FILESYSTEM_SEEK_HEAD); MD5Update(&ctx, pDiskData, pResource->nDownloadSize); Mem_Free(pDiskData); } MD5Final(md5, &ctx); if (Q_memcmp(pResource->rgucMD5_hash, md5, sizeof(md5)) != 0) { Con_Printf("HPAK_AddLump called with bogus lump, md5 mismatch\n"); Con_Printf("Purported: %s\n", MD5_Print(pResource->rgucMD5_hash)); Con_Printf("Actual : %s\n", MD5_Print(md5)); Con_Printf("Ignoring lump addition\n"); return; } if (bUseQueue) { HPAK_AddToQueue(pakname, pResource, pData, fpSource); return; } Q_snprintf(name, ARRAYSIZE(name), "%s", pakname); #ifdef REHLDS_FIXES name[ARRAYSIZE(name) - 1] = 0; #endif // REHLDS_FIXES COM_DefaultExtension(name, HASHPAK_EXTENSION); COM_FixSlashes(name); Q_strncpy(szOriginalName, name, ARRAYSIZE(szOriginalName) - 1); szOriginalName[ARRAYSIZE(szOriginalName) - 1] = 0; iRead = FS_Open(name, "rb"); if (!iRead) { HPAK_CreatePak(pakname, pResource, pData, fpSource); return; } COM_StripExtension(name, szTempName); COM_DefaultExtension(szTempName, ".hp2"); iWrite = FS_Open(szTempName, "w+b"); if (!iWrite) { FS_Close(iRead); Con_Printf("ERROR: couldn't open %s.\n", szTempName); return; } FS_Read(&hash_pack_header, sizeof(hash_pack_header_t), 1, iRead); if (hash_pack_header.version != HASHPAK_VERSION) { FS_Close(iRead); FS_Close(iWrite); FS_Unlink(szTempName); Con_Printf("Invalid .hpk version in HPAK_AddLump\n"); return; } FS_Seek(iRead, 0, FILESYSTEM_SEEK_HEAD); COM_CopyFileChunk(iWrite, iRead, FS_Size(iRead)); FS_Seek(iRead, hash_pack_header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); FS_Read(&olddirectory.nEntries, 4, 1, iRead); if (olddirectory.nEntries < 1 || (unsigned int)olddirectory.nEntries > MAX_FILE_ENTRIES) { FS_Close(iRead); FS_Close(iWrite); FS_Unlink(szTempName); Con_Printf("ERROR: .hpk had bogus # of directory entries: %i\n", olddirectory.nEntries); return; } olddirectory.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * olddirectory.nEntries); FS_Read(olddirectory.p_rgEntries, sizeof(hash_pack_entry_t) * olddirectory.nEntries, 1, iRead); FS_Close(iRead); if (HPAK_FindResource(&olddirectory, pResource->rgucMD5_hash, NULL) != FALSE) { FS_Close(iWrite); FS_Unlink(szTempName); Mem_Free(olddirectory.p_rgEntries); return; } newdirectory.nEntries = olddirectory.nEntries + 1; newdirectory.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * newdirectory.nEntries); Q_memset(newdirectory.p_rgEntries, 0, sizeof(hash_pack_entry_t) * newdirectory.nEntries); Q_memcpy(newdirectory.p_rgEntries, olddirectory.p_rgEntries, sizeof(hash_pack_entry_t) * olddirectory.nEntries); pNewEntry = NULL; for (int i = 0; i < olddirectory.nEntries; i++) { if (Q_memcmp(pResource->rgucMD5_hash, olddirectory.p_rgEntries[i].resource.rgucMD5_hash, 16) >= 0) { pNewEntry = &newdirectory.p_rgEntries[i]; #ifndef REHLDS_FIXES while (i < olddirectory.nEntries) { Q_memcpy(&newdirectory.p_rgEntries[i + 1], &olddirectory.p_rgEntries[i], sizeof(hash_pack_entry_t)); i++; } #else Q_memcpy(&newdirectory.p_rgEntries[i + 1], &olddirectory.p_rgEntries[i], (olddirectory.nEntries - i) * sizeof(hash_pack_entry_t)); #endif break; } } if (pNewEntry == NULL) { pNewEntry = &newdirectory.p_rgEntries[newdirectory.nEntries - 1]; } Q_memset(pNewEntry, 0, sizeof(hash_pack_entry_t)); FS_Seek(iWrite, hash_pack_header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); Q_memcpy(&pNewEntry->resource, pResource, sizeof(resource_t)); pNewEntry->nOffset = FS_Tell(iWrite); pNewEntry->nFileLength = pResource->nDownloadSize; if (pData) FS_Write(pData, pResource->nDownloadSize, 1, iWrite); else COM_CopyFileChunk(iWrite, fpSource, pResource->nDownloadSize); hash_pack_header.nDirectoryOffset = FS_Tell(iWrite); FS_Write(&newdirectory.nEntries, 4, 1, iWrite); for (int j = 0; j < newdirectory.nEntries; j++) FS_Write(&newdirectory.p_rgEntries[j], sizeof(hash_pack_entry_t), 1, iWrite); if (newdirectory.p_rgEntries) Mem_Free(newdirectory.p_rgEntries); if (olddirectory.p_rgEntries) Mem_Free(olddirectory.p_rgEntries); FS_Seek(iWrite, 0, FILESYSTEM_SEEK_HEAD); FS_Write(&hash_pack_header, sizeof(hash_pack_header_t), 1, iWrite); FS_Close(iWrite); FS_Unlink(szOriginalName); FS_Rename(szTempName, szOriginalName); }
/* ================= CL_DemoReadMessage reads demo data and write it to client ================= */ qboolean CL_DemoReadMessage( byte *buffer, size_t *length ) { float f = 0.0f; int curpos = 0; float fElapsedTime = 0.0f; qboolean swallowmessages = true; byte *userbuf = NULL; size_t size; byte cmd; if( !cls.demofile ) { MsgDev( D_ERROR, "tried to read a demo message with no demo file\n" ); CL_DemoCompleted(); return false; } // HACKHACK: changedemo issues if( !cls.netchan.remote_address.type ) cls.netchan.remote_address.type = NA_LOOPBACK; if(( !cl.background && ( cl.refdef.paused || cls.key_dest != key_game )) || cls.key_dest == key_console ) { demo.starttime += host.frametime; return false; // paused } do { qboolean bSkipMessage = false; if( !cls.demofile ) break; curpos = FS_Tell( cls.demofile ); CL_ReadDemoCmdHeader( &cmd, &f ); fElapsedTime = CL_GetDemoPlaybackClock() - demo.starttime; bSkipMessage = (f >= fElapsedTime) ? true : false; if( cls.changelevel ) demo.framecount = 1; // HACKHACK: changelevel issues if( demo.framecount <= 10 && ( fElapsedTime - f ) > host.frametime ) demo.starttime = CL_GetDemoPlaybackClock(); // not ready for a message yet, put it back on the file. if( cmd != dem_norewind && cmd != dem_stop && bSkipMessage ) { // never skip first message if( demo.framecount != 0 ) { FS_Seek( cls.demofile, curpos, SEEK_SET ); return false; // not time yet. } } // COMMAND HANDLERS switch( cmd ) { case dem_jumptime: demo.starttime = CL_GetDemoPlaybackClock(); break; case dem_stop: CL_DemoMoveToNextSection(); break; case dem_userdata: FS_Read( cls.demofile, &size, sizeof( int )); userbuf = Mem_Alloc( cls.mempool, size ); FS_Read( cls.demofile, userbuf, size ); if( clgame.hInstance ) clgame.dllFuncs.pfnDemo_ReadBuffer( size, userbuf ); Mem_Free( userbuf ); userbuf = NULL; break; case dem_usercmd: CL_ReadDemoUserCmd( false ); break; default: swallowmessages = false; break; } } while( swallowmessages ); if( !cls.demofile ) return false; // if not on "LOADING" section, check a few things if( demo.entryIndex ) { // We are now on the second frame of a new section, // if so, reset start time (unless in a timedemo) if( demo.framecount == 1 ) { // cheat by moving the relative start time forward. demo.starttime = CL_GetDemoPlaybackClock(); } } demo.framecount++; CL_ReadDemoSequence( false ); return CL_ReadRawNetworkData( buffer, length ); }
/* ==================== CL_WriteDemoHeader Write demo header ==================== */ void CL_WriteDemoHeader( const char *name ) { fs_offset_t copysize; fs_offset_t savepos; fs_offset_t curpos; MsgDev( D_INFO, "recording to %s.\n", name ); cls.demofile = FS_Open( name, "wb", false ); cls.demotime = 0.0; if( !cls.demofile ) { MsgDev( D_ERROR, "couldn't open %s.\n", name ); return; } cls.demorecording = true; cls.demowaiting = true; // don't start saving messages until a non-delta compressed message is received Q_memset( &demo.header, 0, sizeof( demo.header )); demo.header.id = IDEMOHEADER; demo.header.dem_protocol = DEMO_PROTOCOL; demo.header.net_protocol = PROTOCOL_VERSION; Q_strncpy( demo.header.mapname, clgame.mapname, sizeof( demo.header.mapname )); Q_strncpy( demo.header.gamedir, FS_Gamedir(), sizeof( demo.header.gamedir )); // write header FS_Write( cls.demofile, &demo.header, sizeof( demo.header )); demo.directory.numentries = 2; demo.directory.entries = Mem_Alloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); // DIRECTORY ENTRY # 0 demo.entry = &demo.directory.entries[0]; // only one here. demo.entry->entrytype = DEMO_STARTUP; demo.entry->playback_time = 0.0f; // startup takes 0 time. demo.entry->offset = FS_Tell( cls.demofile ); // position for this chunk. // finish off the startup info. CL_WriteDemoCmdHeader( dem_stop, cls.demoheader ); // now copy the stuff we cached from the server. copysize = savepos = FS_Tell( cls.demoheader ); FS_Seek( cls.demoheader, 0, SEEK_SET ); FS_FileCopy( cls.demofile, cls.demoheader, copysize ); // jump back to end, in case we record another demo for this session. FS_Seek( cls.demoheader, savepos, SEEK_SET ); demo.starttime = CL_GetDemoRecordClock(); // setup the demo starttime demo.realstarttime = demo.starttime; demo.framecount = 0; // now move on to entry # 1, the first data chunk. curpos = FS_Tell( cls.demofile ); demo.entry->length = curpos - demo.entry->offset; // now we are writing the first real lump. demo.entry = &demo.directory.entries[1]; // first real data lump demo.entry->entrytype = DEMO_NORMAL; demo.entry->playback_time = 0.0f; // startup takes 0 time. demo.entry->offset = FS_Tell( cls.demofile ); // demo playback should read this as an incoming message. // write the client's realtime value out so we can synchronize the reads. CL_WriteDemoCmdHeader( dem_jumptime, cls.demofile ); if( clgame.hInstance ) clgame.dllFuncs.pfnReset(); Cbuf_InsertText( "fullupdate\n" ); Cbuf_Execute(); }
void HPAK_CreatePak( const char *filename, resource_t *DirEnt, byte *data, file_t *f ) { int filelocation; string pakname; char md5[16]; char *temp; MD5Context_t MD5_Hash; file_t *fout; if( !filename || !filename[0] ) { MsgDev( D_ERROR, "HPAK_CreatePak: NULL name\n" ); return; } if(( f != NULL && data != NULL ) || ( f == NULL && data == NULL )) { MsgDev( D_ERROR, "HPAK_CreatePak: too many sources, please leave one.\n" ); return; } Q_strncpy( pakname, filename, sizeof( pakname )); FS_StripExtension( pakname ); FS_DefaultExtension( pakname, ".hpk" ); MsgDev( D_INFO, "creating HPAK %s.\n", pakname ); fout = FS_Open( pakname, "wb", false ); if( !fout ) { MsgDev( D_ERROR, "HPAK_CreatePak: can't write %s.\n", pakname ); return; } // let's hash it. Q_memset( &MD5_Hash, 0, sizeof( MD5Context_t )); MD5Init( &MD5_Hash ); if( data == NULL ) { // there are better ways filelocation = FS_Tell( f ); temp = Z_Malloc( DirEnt->nDownloadSize ); FS_Read( f, temp, DirEnt->nDownloadSize ); FS_Seek( f, filelocation, SEEK_SET ); MD5Update( &MD5_Hash, temp, DirEnt->nDownloadSize ); Mem_Free( temp ); } else { MD5Update( &MD5_Hash, data, DirEnt->nDownloadSize ); } MD5Final( md5, &MD5_Hash ); if( Q_memcmp( md5, DirEnt->rgucMD5_hash, 16 )) { MsgDev( D_ERROR, "HPAK_CreatePak: bad checksum for %s. Ignored\n", pakname ); return; } hash_pack_header.ident = IDCUSTOMHEADER; hash_pack_header.version = IDCUSTOM_VERSION; hash_pack_header.seek = 0; FS_Write( fout, &hash_pack_header, sizeof( hash_pack_header )); hash_pack_dir.count = 1; hash_pack_dir.dirs = Z_Malloc( sizeof( hpak_dir_t )); hash_pack_dir.dirs[0].DirectoryResource = *DirEnt; hash_pack_dir.dirs[0].seek = FS_Tell( fout ); hash_pack_dir.dirs[0].size = DirEnt->nDownloadSize; if( data == NULL ) { HPAK_FileCopy( fout, f, hash_pack_dir.dirs[0].size ); } else { FS_Write( fout, data, hash_pack_dir.dirs[0].size ); } filelocation = FS_Tell( fout ); FS_Write( fout, &hash_pack_dir.count, sizeof( hash_pack_dir.count )); FS_Write( fout, &hash_pack_dir.dirs[0], sizeof( hpak_dir_t )); Mem_Free( hash_pack_dir.dirs ); Q_memset( &hash_pack_dir, 0, sizeof( hpak_container_t )); hash_pack_header.seek = filelocation; FS_Seek( fout, 0, SEEK_SET ); FS_Write( fout, &hash_pack_header, sizeof( hpak_header_t )); FS_Close( fout ); }
void HPAK_RemoveLump( const char *name, resource_t *resource ) { string read_path; string save_path; file_t *f1, *f2; hpak_container_t hpak_read; hpak_container_t hpak_save; int i, j; if( !name || !name[0] || !resource ) return; HPAK_FlushHostQueue(); Q_strncpy( read_path, name, sizeof( read_path )); FS_StripExtension( read_path ); FS_DefaultExtension( read_path, ".hpk" ); f1 = FS_Open( read_path, "rb", false ); if( !f1 ) { MsgDev( D_ERROR, "HPAK_RemoveLump: %s couldn't open.\n", read_path ); return; } Q_strncpy( save_path, read_path, sizeof( save_path )); FS_StripExtension( save_path ); FS_DefaultExtension( save_path, ".hp2" ); f2 = FS_Open( save_path, "w+b", false ); if( !f2 ) { MsgDev( D_ERROR, "HPAK_RemoveLump: %s couldn't open.\n", save_path ); FS_Close( f1 ); return; } FS_Seek( f1, 0, SEEK_SET ); FS_Seek( f2, 0, SEEK_SET ); // header copy FS_Read( f1, &hash_pack_header, sizeof( hpak_header_t )); FS_Write( f2, &hash_pack_header, sizeof( hpak_header_t )); if( hash_pack_header.ident != IDCUSTOMHEADER || hash_pack_header.version != IDCUSTOM_VERSION ) { MsgDev( D_ERROR, "HPAK_RemoveLump: %s has invalid header.\n", read_path ); FS_Close( f1 ); FS_Close( f2 ); FS_Delete( save_path ); // delete temp file return; } FS_Seek( f1, hash_pack_header.seek, SEEK_SET ); FS_Read( f1, &hpak_read.count, sizeof( hpak_read.count )); if( hpak_read.count < 1 || hpak_read.count > MAX_FILES_IN_WAD ) { MsgDev( D_ERROR, "HPAK_RemoveLump: %s has invalid number of lumps.\n", read_path ); FS_Close( f1 ); FS_Close( f2 ); FS_Delete( save_path ); // delete temp file return; } if( hpak_read.count == 1 ) { MsgDev( D_ERROR, "HPAK_RemoveLump: %s only has one element, so it's not deleted.\n", read_path ); FS_Close( f1 ); FS_Close( f2 ); FS_Delete( read_path ); FS_Delete( save_path ); return; } hpak_save.count = hpak_read.count - 1; hpak_read.dirs = Z_Malloc( sizeof( hpak_dir_t ) * hpak_read.count ); hpak_save.dirs = Z_Malloc( sizeof( hpak_dir_t ) * hpak_save.count ); FS_Read( f1, hpak_read.dirs, sizeof( hpak_dir_t ) * hpak_read.count ); if( !HPAK_FindResource( &hpak_read, resource->rgucMD5_hash, NULL )) { MsgDev( D_ERROR, "HPAK_RemoveLump: Couldn't find the lump %s in hpak %s.n", resource->szFileName, read_path ); Mem_Free( hpak_read.dirs ); Mem_Free( hpak_save.dirs ); FS_Close( f1 ); FS_Close( f2 ); FS_Delete( save_path ); return; } MsgDev( D_INFO, "Removing lump %s from %s.\n", resource->szFileName, read_path ); // If there's a collision, we've just corrupted this hpak. for( i = 0, j = 0; i < hpak_read.count; i++ ) { if( !Q_memcmp( hpak_read.dirs[i].DirectoryResource.rgucMD5_hash, resource->rgucMD5_hash, 16 )) continue; hpak_save.dirs[j] = hpak_read.dirs[i]; hpak_save.dirs[j].seek = FS_Tell( f2 ); FS_Seek( f1, hpak_read.dirs[j].seek, SEEK_SET ); HPAK_FileCopy( f2, f1, hpak_save.dirs[j].size ); j++; } hash_pack_header.seek = FS_Tell( f2 ); FS_Write( f2, &hpak_save.count, ( hpak_save.count )); for( i = 0; i < hpak_save.count; i++ ) { FS_Write( f2, &hpak_save.dirs[i], sizeof( hpak_dir_t )); } FS_Seek( f2, 0, SEEK_SET ); FS_Write( f2, &hash_pack_header, sizeof( hpak_header_t )); Mem_Free( hpak_read.dirs ); Mem_Free( hpak_save.dirs ); FS_Close( f1 ); FS_Close( f2 ); FS_Delete( read_path ); FS_Rename( save_path, read_path ); }
void HPAK_AddLump( qboolean add_to_queue, const char *name, resource_t *DirEnt, byte *data, file_t *f ) { int i, position, length; string pakname1, pakname2; char md5[16]; MD5Context_t MD5_Hash; hpak_container_t hpak1, hpak2; file_t *f1, *f2; hpak_dir_t *dirs; byte *temp; if( !name || !name[0] ) { MsgDev( D_ERROR, "HPAK_AddLump: NULL name\n" ); return; } if( !DirEnt ) { MsgDev( D_ERROR, "HPAK_AddLump: invalid lump\n" ); return; } if( data == NULL && f == NULL ) { MsgDev( D_ERROR, "HPAK_AddLump: missing lump data\n" ); return; } if( DirEnt->nDownloadSize < 1024 || DirEnt->nDownloadSize > 131072 ) { MsgDev( D_ERROR, "HPAK_AddLump: invalid size %s\n", Q_pretifymem( DirEnt->nDownloadSize, 2 )); return; } // hash it Q_memset( &MD5_Hash, 0, sizeof( MD5Context_t )); MD5Init( &MD5_Hash ); if( data == NULL ) { // there are better ways position = FS_Tell( f ); temp = Z_Malloc( DirEnt->nDownloadSize ); FS_Read( f, temp, DirEnt->nDownloadSize ); FS_Seek( f, position, SEEK_SET ); MD5Update( &MD5_Hash, temp, DirEnt->nDownloadSize ); Mem_Free( temp ); } else { MD5Update( &MD5_Hash, data, DirEnt->nDownloadSize ); } MD5Final( md5, &MD5_Hash ); if( Q_memcmp( md5, DirEnt->rgucMD5_hash, 0x10 )) { MsgDev( D_ERROR, "HPAK_AddLump: bad checksum for %s. Ignored\n", DirEnt->szFileName ); return; } if( add_to_queue ) { HPAK_AddToQueue( name, DirEnt, data, f ); return; } Q_strncpy( pakname1, name, sizeof( pakname1 )); FS_StripExtension( pakname1 ); FS_DefaultExtension( pakname1, ".hpk" ); f1 = FS_Open( pakname1, "rb", false ); if( !f1 ) { // create new pack HPAK_CreatePak( name, DirEnt, data, f ); return; } Q_strncpy( pakname2, pakname1, sizeof( pakname2 )); FS_StripExtension( pakname2 ); FS_DefaultExtension( pakname2, ".hp2" ); f2 = FS_Open( pakname2, "w+b", false ); if( !f2 ) { MsgDev( D_ERROR, "HPAK_AddLump: couldn't open %s.\n", pakname2 ); FS_Close( f1 ); return; } // load headers FS_Read( f1, &hash_pack_header, sizeof( hpak_header_t )); if( hash_pack_header.version != IDCUSTOM_VERSION ) { // we don't check the HPAK bit for some reason. MsgDev( D_ERROR, "HPAK_AddLump: %s does not have a valid header.\n", pakname2 ); FS_Close( f1 ); FS_Close( f2 ); } length = FS_FileLength( f1 ); HPAK_FileCopy( f2, f1, length ); FS_Seek( f1, hash_pack_header.seek, SEEK_SET ); FS_Read( f1, &hpak1.count, sizeof( hpak1.count )); if( hpak1.count < 1 || hpak1.count > MAX_FILES_IN_WAD ) { MsgDev( D_ERROR, "HPAK_AddLump: %s contain too many lumps.\n", pakname1 ); FS_Close( f1 ); FS_Close( f2 ); return; } // load the data hpak1.dirs = Z_Malloc( sizeof( hpak_dir_t ) * hpak1.count ); FS_Read( f1, hpak1.dirs, sizeof( hpak_dir_t ) * hpak1.count ); FS_Close( f1 ); if( HPAK_FindResource( &hpak1, DirEnt->rgucMD5_hash, NULL )) { Mem_Free( hpak1.dirs ); FS_Close( f2 ); } // make a new container hpak2.count = hpak1.count; hpak2.dirs = Z_Malloc( sizeof( hpak_dir_t ) * hpak2.count ); Q_memcpy( hpak2.dirs, hpak1.dirs, hpak1.count ); for( i = 0, dirs = NULL; i < hpak1.count; i++ ) { if( Q_memcmp( hpak1.dirs[i].DirectoryResource.rgucMD5_hash, DirEnt->rgucMD5_hash, 16 ) < 0 ) { dirs = &hpak1.dirs[i]; while( i < hpak1.count ) { hpak2.dirs[i+1] = hpak1.dirs[i]; i++; } break; } } if( dirs == NULL ) dirs = &hpak2.dirs[hpak2.count-1]; Q_memset( dirs, 0, sizeof( hpak_dir_t )); FS_Seek( f2, hash_pack_header.seek, SEEK_SET ); dirs->DirectoryResource = *DirEnt; dirs->seek = FS_Tell( f2 ); dirs->size = DirEnt->nDownloadSize; if( !data ) HPAK_FileCopy( f2, f, dirs->size ); else FS_Write( f2, data, dirs->size ); hash_pack_header.seek = FS_Tell( f2 ); FS_Write( f2, &hpak2.count, sizeof( hpak2.count )); for( i = 0; i < hpak2.count; i++ ) { FS_Write( f2, &hpak2.dirs[i], sizeof( hpak_dir_t )); } // finalize Mem_Free( hpak1.dirs ); Mem_Free( hpak2.dirs ); FS_Seek( f2, 0, SEEK_SET ); FS_Write( f2, &hash_pack_header, sizeof( hpak_header_t )); FS_Close( f2 ); FS_Delete( pakname1 ); FS_Rename( pakname2, pakname1 ); }