/* <2ae8e> ../engine/hashpak.c:1599 */ NOXREF char *HPAK_GetItem(int item) { NOXREFCHECK; int nCurrent; hash_pack_header_t header; hash_pack_directory_t directory; hash_pack_entry_t *entry; static char name[MAX_PATH]; char szFileName[MAX_PATH]; FileHandle_t fp; HPAK_FlushHostQueue(); Q_snprintf(name, ARRAYSIZE(name), "%s", "custom"); COM_DefaultExtension(name, HASHPAK_EXTENSION); fp = FS_Open(name, "rb"); if (!fp) return ""; FS_Read(&header, sizeof(hash_pack_header_t), 1, fp); if (Q_strncmp(header.szFileStamp, "HPAK", sizeof(header.szFileStamp))) { Con_Printf("%s is not an HPAK file\n", name); FS_Close(fp); return ""; } if (header.version != HASHPAK_VERSION) { Con_Printf("HPAK_List: version mismatch\n"); FS_Close(fp); return ""; } FS_Seek(fp, header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); FS_Read(&directory.nEntries, 4, 1, fp); if (directory.nEntries < 1 || (unsigned int)directory.nEntries > MAX_FILE_ENTRIES) { Con_Printf("ERROR: HPAK had bogus # of directory entries: %i\n", directory.nEntries); FS_Close(fp); return ""; } directory.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * directory.nEntries); FS_Read(directory.p_rgEntries, sizeof(hash_pack_entry_t) * directory.nEntries, 1, fp); nCurrent = directory.nEntries - 1; if (nCurrent > item) nCurrent = item; entry = &directory.p_rgEntries[nCurrent]; COM_FileBase(entry->resource.szFileName, szFileName); Q_snprintf(name, sizeof(name), "!MD5%s", MD5_Print(entry->resource.rgucMD5_hash)); FS_Close(fp); Mem_Free(directory.p_rgEntries); return name; }
/* <2ab62> ../engine/hashpak.c:1200 */ void HPAK_Remove_f(void) { int nIndex; char *pakname; resource_t resource; if (cmd_source != src_command) return; HPAK_FlushHostQueue(); if (Cmd_Argc() != 3) { Con_Printf("Usage: hpkremove <hpk> <index>\n"); return; } pakname = (char *)Cmd_Argv(1); nIndex = Q_atoi(Cmd_Argv(2)); if (HPAK_ResourceForIndex(pakname, nIndex, &resource)) HPAK_RemoveLump(pakname, &resource); else Con_Printf("Could not locate resource %i in %s\n", nIndex, pakname); }
/* <2a3cb> ../engine/hashpak.c:1235 */ void HPAK_Validate_f(void) { hash_pack_header_t header; hash_pack_directory_t directory; hash_pack_entry_t *entry; char name[MAX_PATH]; char szFileName[MAX_PATH]; char type[32]; FileHandle_t fp; byte *pData; int nDataSize; byte md5[16]; MD5Context_t ctx; if (cmd_source != src_command) return; HPAK_FlushHostQueue(); if (Cmd_Argc() != 2) { Con_Printf("Usage: hpkval hpkname\n"); return; } Q_snprintf(name, ARRAYSIZE(name), "%s", Cmd_Argv(1)); #ifdef REHLDS_FIXES name[ARRAYSIZE(name) - 1] = 0; #endif // REHLDS_FIXES COM_DefaultExtension(name, HASHPAK_EXTENSION); Con_Printf("Validating %s.\n", name); fp = FS_Open(name, "rb"); if (!fp) { Con_Printf("ERROR: couldn't open %s.\n", name); return; } FS_Read(&header, sizeof(hash_pack_header_t), 1, fp); if (Q_strncmp(header.szFileStamp, "HPAK", sizeof(header.szFileStamp))) { Con_Printf("%s is not an HPAK file\n", name); FS_Close(fp); return; } if (header.version != HASHPAK_VERSION) { Con_Printf("hpkval: version mismatch\n"); FS_Close(fp); return; } FS_Seek(fp, header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); FS_Read(&directory.nEntries, 4, 1, fp); if (directory.nEntries < 1 || (unsigned int)directory.nEntries > MAX_FILE_ENTRIES) { Con_Printf("ERROR: HPAK had bogus # of directory entries: %i\n", directory.nEntries); FS_Close(fp); return; } Con_Printf("# of Entries: %i\n", directory.nEntries); Con_Printf("# Type Size FileName : MD5 Hash\n"); directory.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * directory.nEntries); FS_Read(directory.p_rgEntries, sizeof(hash_pack_entry_t) * directory.nEntries, 1, fp); for (int nCurrent = 0; nCurrent < directory.nEntries; nCurrent++) { entry = &directory.p_rgEntries[nCurrent]; COM_FileBase(entry->resource.szFileName, szFileName); switch (entry->resource.type) { case t_sound: Q_strcpy(type, "sound"); break; case t_skin: Q_strcpy(type, "skin"); break; case t_model: Q_strcpy(type, "model"); break; case t_decal: Q_strcpy(type, "decal"); break; case t_generic: Q_strcpy(type, "generic"); break; case t_eventscript: Q_strcpy(type, "event"); break; default: Q_strcpy(type, "?"); break; } Con_Printf("%i: %10s %.2fK %s: ", nCurrent + 1, type, entry->resource.nDownloadSize / 1024.0f, szFileName); nDataSize = entry->nFileLength; if (nDataSize < 1 || (unsigned int)nDataSize >= MAX_FILE_SIZE) Con_Printf("Unable to MD5 hash data, size invalid: %i\n", nDataSize); else { pData = (byte *)Mem_Malloc(nDataSize + 1); Q_memset(pData, 0, nDataSize); FS_Seek(fp, entry->nOffset, FILESYSTEM_SEEK_HEAD); FS_Read(pData, nDataSize, 1, fp); Q_memset(&ctx, 0, sizeof(MD5Context_t)); MD5Init(&ctx); MD5Update(&ctx, pData, nDataSize); MD5Final(md5, &ctx); if (Q_memcmp(entry->resource.rgucMD5_hash, md5, sizeof(md5)) == 0) Con_Printf(" OK\n"); else { Con_Printf(" MISMATCHED\n"); Con_Printf("--------------------\n"); Con_Printf(" File : %s\n", MD5_Print(entry->resource.rgucMD5_hash)); Con_Printf(" Actual: %s\n", MD5_Print(md5)); Con_Printf("--------------------\n"); } if (pData) Mem_Free(pData); } } FS_Close(fp); Mem_Free(directory.p_rgEntries); }
/* <2a644> ../engine/hashpak.c:945 */ void HPAK_List_f(void) { hash_pack_header_t header; hash_pack_directory_t directory; hash_pack_entry_t *entry; char name[MAX_PATH]; char szFileName[MAX_PATH]; char type[32]; FileHandle_t fp; if (cmd_source != src_command) return; HPAK_FlushHostQueue(); Q_snprintf(name, ARRAYSIZE(name), "%s", Cmd_Argv(1)); #ifdef REHLDS_FIXES name[ARRAYSIZE(name) - 1] = 0; #endif // REHLDS_FIXES COM_DefaultExtension(name, HASHPAK_EXTENSION); Con_Printf("Contents for %s.\n", name); fp = FS_Open(name, "rb"); if (!fp) { Con_Printf("ERROR: couldn't open %s.\n", name); return; } FS_Read(&header, sizeof(hash_pack_header_t), 1, fp); if (Q_strncmp(header.szFileStamp, "HPAK", sizeof(header.szFileStamp))) { Con_Printf("%s is not an HPAK file\n", name); FS_Close(fp); return; } if (header.version != HASHPAK_VERSION) { Con_Printf("HPAK_List: version mismatch\n"); FS_Close(fp); return; } FS_Seek(fp, header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); FS_Read(&directory.nEntries, 4, 1, fp); if (directory.nEntries < 1 || (unsigned int)directory.nEntries > MAX_FILE_ENTRIES) { Con_Printf("ERROR: HPAK had bogus # of directory entries: %i\n", directory.nEntries); FS_Close(fp); return; } Con_Printf("# of Entries: %i\n", directory.nEntries); Con_Printf("# Type Size FileName : MD5 Hash\n"); directory.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * directory.nEntries); FS_Read(directory.p_rgEntries, sizeof(hash_pack_entry_t) * directory.nEntries, 1, fp); for (int nCurrent = 0; nCurrent < directory.nEntries; nCurrent++) { entry = &directory.p_rgEntries[nCurrent]; COM_FileBase(entry->resource.szFileName, szFileName); switch (entry->resource.type) { case t_sound: Q_strcpy(type, "sound"); break; case t_skin: Q_strcpy(type, "skin"); break; case t_model: Q_strcpy(type, "model"); break; case t_decal: Q_strcpy(type, "decal"); break; case t_generic: Q_strcpy(type, "generic"); break; case t_eventscript: Q_strcpy(type, "event"); break; default: Q_strcpy(type, "?"); break; } Con_Printf("%i: %10s %.2fK %s\n : %s\n", nCurrent + 1, type, entry->resource.nDownloadSize / 1024.0f, szFileName, MD5_Print(entry->resource.rgucMD5_hash)); } FS_Close(fp); Mem_Free(directory.p_rgEntries); }
/* <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); }
/* <2afb5> ../engine/hashpak.c:1728 */ void HPAK_ValidatePak(char *fullpakname) { hash_pack_header_t header; hash_pack_directory_t directory; hash_pack_entry_t *entry; char szFileName[MAX_PATH]; FileHandle_t fp; byte *pData; byte md5[16]; MD5Context_t ctx; HPAK_FlushHostQueue(); fp = FS_Open(fullpakname, "rb"); if (!fp) return; FS_Read(&header, sizeof(hash_pack_header_t), 1, fp); if (header.version != HASHPAK_VERSION || Q_strncmp(header.szFileStamp, "HPAK", sizeof(header.szFileStamp)) != 0) { Con_Printf("%s is not a PAK file, deleting\n", fullpakname); FS_Close(fp); FS_RemoveFile(fullpakname, 0); return; } FS_Seek(fp, header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); FS_Read(&directory, 4, 1, fp); if (directory.nEntries < 1 || (unsigned int)directory.nEntries > MAX_FILE_ENTRIES) { Con_Printf("ERROR: HPAK %s had bogus # of directory entries: %i, deleting\n", fullpakname, directory.nEntries); FS_Close(fp); FS_RemoveFile(fullpakname, 0); return; } directory.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * directory.nEntries); FS_Read(directory.p_rgEntries, sizeof(hash_pack_entry_t) * directory.nEntries, 1, fp); for (int nCurrent = 0; nCurrent < directory.nEntries; nCurrent++) { entry = &directory.p_rgEntries[nCurrent]; COM_FileBase(entry->resource.szFileName, szFileName); if ((unsigned int)entry->nFileLength >= MAX_FILE_SIZE) { Con_Printf("Mismatched data in HPAK file %s, deleting\n", fullpakname); Con_Printf("Unable to MD5 hash data lump %i, size invalid: %i\n", nCurrent + 1, entry->nFileLength); FS_Close(fp); FS_RemoveFile(fullpakname, 0); Mem_Free(directory.p_rgEntries); return; } pData = (byte *)Mem_Malloc(entry->nFileLength + 1); Q_memset(pData, 0, entry->nFileLength); FS_Seek(fp, entry->nOffset, FILESYSTEM_SEEK_HEAD); FS_Read(pData, entry->nFileLength, 1, fp); Q_memset(&ctx, 0, sizeof(MD5Context_t)); MD5Init(&ctx); MD5Update(&ctx, pData, entry->nFileLength); MD5Final(md5, &ctx); if (pData) Mem_Free(pData); if (Q_memcmp(entry->resource.rgucMD5_hash, md5, sizeof(md5)) != 0) { Con_Printf("Mismatched data in HPAK file %s, deleting\n", fullpakname); FS_Close(fp); FS_RemoveFile(fullpakname, 0); Mem_Free(directory.p_rgEntries); return; } } FS_Close(fp); Mem_Free(directory.p_rgEntries); }
/* <2a33c> ../engine/hashpak.c:1401 */ void HPAK_Extract_f(void) { hash_pack_header_t header; hash_pack_directory_t directory; hash_pack_entry_t *entry; char name[MAX_PATH]; char type[32]; FileHandle_t fp; int nIndex; byte *pData; int nDataSize; FileHandle_t fpOutput; char szFileOut[MAX_PATH]; if (cmd_source != src_command) return; HPAK_FlushHostQueue(); if (Cmd_Argc() != 3) { Con_Printf("Usage: hpkextract hpkname [all | single index]\n"); return; } if (Q_stricmp(Cmd_Argv(2),"all") != 0) { nIndex = Q_atoi(Cmd_Argv(2)); #ifdef REHLDS_FIXES Q_snprintf(name, sizeof(name), "%s", Cmd_Argv(1)); #else Q_snprintf(name, 256, "%s", Cmd_Argv(1)); #endif // REHLDS_FIXES if (nIndex != -1) Con_Printf("Extracting lump %i from %s\n", nIndex, name); } else { nIndex = -1; Q_snprintf(name, ARRAYSIZE(name), "%s", Cmd_Argv(1)); #ifdef REHLDS_FIXES name[ARRAYSIZE(name) - 1] = 0; #endif // REHLDS_FIXES COM_DefaultExtension(name, HASHPAK_EXTENSION); Con_Printf("Extracting all lumps from %s.\n", name); } fp = FS_Open(name, "rb"); if (!fp) { Con_Printf("ERROR: couldn't open %s.\n", name); return; } FS_Read(&header, sizeof(hash_pack_header_t), 1, fp); if (Q_strncmp(header.szFileStamp, "HPAK", sizeof(header.szFileStamp))) { Con_Printf("%s is not an HPAK file\n", name); FS_Close(fp); return; } if (header.version != HASHPAK_VERSION) { Con_Printf("hpkextract: version mismatch\n"); FS_Close(fp); return; } FS_Seek(fp, header.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); FS_Read(&directory.nEntries, 4, 1, fp); if (directory.nEntries < 1 || (unsigned int)directory.nEntries > MAX_FILE_ENTRIES) { Con_Printf("ERROR: HPAK had bogus # of directory entries: %i\n", directory.nEntries); FS_Close(fp); return; } Con_Printf("# of Entries: %i\n", directory.nEntries); Con_Printf("# Type Size FileName : MD5 Hash\n"); directory.p_rgEntries = (hash_pack_entry_t *)Mem_Malloc(sizeof(hash_pack_entry_t) * directory.nEntries); FS_Read(directory.p_rgEntries, sizeof(hash_pack_entry_t) * directory.nEntries, 1, fp); for (int nCurrent = 0; nCurrent < directory.nEntries; nCurrent++) { entry = &directory.p_rgEntries[nCurrent]; if (nIndex == -1 || nIndex == nCurrent) { COM_FileBase(entry->resource.szFileName, szFileOut); switch (entry->resource.type) { case t_sound: Q_strcpy(type, "sound"); break; case t_skin: Q_strcpy(type, "skin"); break; case t_model: Q_strcpy(type, "model"); break; case t_decal: Q_strcpy(type, "decal"); break; case t_generic: Q_strcpy(type, "generic"); break; case t_eventscript: Q_strcpy(type, "event"); break; default: Q_strcpy(type, "?"); break; } Con_Printf("Extracting %i: %10s %.2fK %s\n", nCurrent, type, entry->resource.nDownloadSize / 1024.0f, szFileOut); nDataSize = entry->nFileLength; if (nDataSize < 1 || (unsigned int)nDataSize >= MAX_FILE_SIZE) Con_Printf("Unable to extract data, size invalid: %s\n", nDataSize); else { pData = (byte *)Mem_Malloc(nDataSize + 1); Q_memset(pData, 0, nDataSize); FS_Seek(fp, entry->nOffset, FILESYSTEM_SEEK_HEAD); FS_Read(pData, nDataSize, 1, fp); Q_snprintf(szFileOut, sizeof(szFileOut), "hpklmps\\lmp%04i.wad", nCurrent); COM_FixSlashes(szFileOut); COM_CreatePath(szFileOut); fpOutput = FS_Open(szFileOut, "wb"); if (fpOutput) { FS_Write(pData, nDataSize, 1, fpOutput); FS_Close(fpOutput); } else Con_Printf("Error creating lump file %s\n", szFileOut); if (pData) Mem_Free(pData); } } } FS_Close(fp); Mem_Free(directory.p_rgEntries); }
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 ); }
static qboolean HPAK_Validate( const char *filename, qboolean quiet ) { file_t *f; hpak_dir_t *dataDir; hpak_header_t hdr; byte *dataPak; int i, num_lumps; MD5Context_t MD5_Hash; string pakname; resource_t *pRes; char md5[16]; if( quiet ) HPAK_FlushHostQueue(); // not an error - just flush queue if( !filename || !*filename ) return true; Q_strncpy( pakname, filename, sizeof( pakname )); FS_StripExtension( pakname ); FS_DefaultExtension( pakname, ".hpk" ); f = FS_Open( pakname, "rb", false ); if( !f ) { MsgDev( D_INFO, "Couldn't find %s.\n", pakname ); return true; } if( !quiet ) MsgDev( D_INFO, "Validating %s\n", pakname ); FS_Read( f, &hdr, sizeof( hdr )); if( hdr.ident != IDCUSTOMHEADER || hdr.version != IDCUSTOM_VERSION ) { MsgDev( D_ERROR, "HPAK_ValidatePak: %s does not have a valid HPAK header.\n", pakname ); FS_Close( f ); return false; } FS_Seek( f, hdr.seek, SEEK_SET ); FS_Read( f, &num_lumps, sizeof( num_lumps )); if( num_lumps < 1 || num_lumps > MAX_FILES_IN_WAD ) { MsgDev( D_ERROR, "HPAK_ValidatePak: %s has too many lumps %u.\n", pakname, num_lumps ); FS_Close( f ); return false; } if( !quiet ) MsgDev( D_INFO, "# of Entries: %i\n", num_lumps ); dataDir = Z_Malloc( sizeof( hpak_dir_t ) * num_lumps ); FS_Read( f, dataDir, sizeof( hpak_dir_t ) * num_lumps ); if( !quiet ) MsgDev( D_INFO, "# Type Size FileName : MD5 Hash\n" ); for( i = 0; i < num_lumps; i++ ) { if( dataDir[i].size < 1 || dataDir[i].size > 131071 ) { // odd max size MsgDev( D_ERROR, "HPAK_ValidatePak: lump %i has invalid size %s\n", i, Q_pretifymem( dataDir[i].size, 2 )); Mem_Free( dataDir ); FS_Close(f); return false; } dataPak = Z_Malloc( dataDir[i].size ); FS_Seek( f, dataDir[i].seek, SEEK_SET ); FS_Read( f, dataPak, dataDir[i].size ); Q_memset( &MD5_Hash, 0, sizeof( MD5Context_t )); MD5Init( &MD5_Hash ); MD5Update( &MD5_Hash, dataPak, dataDir[i].size ); MD5Final( md5, &MD5_Hash ); pRes = &dataDir[i].DirectoryResource; MsgDev( D_INFO, "%i: %s %s %s: ", i, HPAK_TypeFromIndex( pRes->type ), Q_pretifymem( pRes->nDownloadSize, 2 ), pRes->szFileName ); if( Q_memcmp( md5, pRes->rgucMD5_hash, 0x10 )) { if( quiet ) { MsgDev( D_ERROR, "HPAK_ValidatePak: %s has invalid checksum.\n", pakname ); Mem_Free( dataPak ); Mem_Free( dataDir ); FS_Close( f ); return false; } else MsgDev( D_INFO, "failed\n" ); } else { if( !quiet ) MsgDev( D_INFO, "OK\n" ); } // at this point, it's passed our checks. Mem_Free( dataPak ); } Mem_Free( dataDir ); FS_Close( f ); return true; }