Ejemplo n.º 1
0
/* <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);
}
Ejemplo n.º 2
0
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 );
}