/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "precompiled.h" #pragma hdrstop /* ================================================================================================ Contains external code for building ZipFiles. ================================================================================================ */ #include "Zip.h" // #undef STDC idCVar zip_verbosity( "zip_verbosity", "0", CVAR_BOOL, "1 = verbose logging when building zip files" ); #define DEFAULT_COMPRESSION_LEVEL (5) /* 1 == Compress faster, 9 == Compress better */ #define DEFAULT_WRITEBUFFERSIZE (16384) /* * DG: all the zip implementation has been updated to minizip 1.1 and is in framework/minizip/zip.cpp * In the D3 BFG implementation there were some things specific to Doom3: * * FILE was replaced by idFile, thus * - fseek(), fread(), fwrite(), ftell() were replaced by idFile functions * - fopen() was replaced by fileSystem->OpenExplicitFileWrite() * - fclose() was replaced by fileSystem->CloseFile() * * As this isn't important for the code to work I haven't done these changes, but using the * Doom3 specific functions could be quite easily done by using zipOpen2() with an appropriate * zlib_filefunc_def. * * TODO: Doom3 should already support zip64 for unzipping, maybe it would make sense to use the * corresponding functions here as well.. but then we can't use idFile, because it only * supports files up to 2GB (Length() and Tell() return ints) */ /* ======================== idZipBuilder::AddFileFilters ======================== */ void idZipBuilder::AddFileFilters( const char* filters ) { #if 0 idStrList exts; idStrListBreakupString( exts, filters, "|" ); if( ( exts.Num() > 0 ) && ( exts[ exts.Num() - 1 ] == "" ) ) { exts.RemoveIndex( exts.Num() - 1 ); } filterExts.Append( exts ); #endif } /* ======================== idZipBuilder::AddUncompressedFileFilters ======================== */ void idZipBuilder::AddUncompressedFileFilters( const char* filters ) { #if 0 idStrList exts; idStrListBreakupString( exts, filters, "|" ); if( ( exts.Num() > 0 ) && ( exts[ exts.Num() - 1 ] == "" ) ) { exts.RemoveIndex( exts.Num() - 1 ); } uncompressedFilterExts.Append( exts ); #endif } /* ======================== idZipBuilder::Build builds a zip file of all the files in the specified folder, overwriting if necessary ======================== */ bool idZipBuilder::Build( const char* zipPath, const char* folder, bool cleanFolder ) { zipFileName = zipPath; sourceFolderName = folder; if( !CreateZipFile( false ) ) { // don't clean the folder if the zip fails return false; } if( cleanFolder ) { CleanSourceFolder(); } return true; } /* ======================== idZipBuilder::Update updates a zip file with the files in the specified folder ======================== */ bool idZipBuilder::Update( const char* zipPath, const char* folder, bool cleanFolder ) { // if this file doesn't exist, just build it if( fileSystem->GetTimestamp( zipPath ) == FILE_NOT_FOUND_TIMESTAMP ) { return Build( zipPath, folder, cleanFolder ); } zipFileName = zipPath; sourceFolderName = folder; if( !CreateZipFile( true ) ) { // don't clean the folder if the zip fails return false; } if( cleanFolder ) { CleanSourceFolder(); } return true; } /* ======================== idZipBuilder::GetFileTime ======================== */ bool idZipBuilder::GetFileTime( const idStr& filename, unsigned long* dostime ) const { // RB: FIXME #if defined(_WIN32) { FILETIME filetime; WIN32_FIND_DATA fileData; HANDLE findHandle = FindFirstFile( filename.c_str(), &fileData ); if( findHandle != INVALID_HANDLE_VALUE ) { FileTimeToLocalFileTime( &( fileData.ftLastWriteTime ), &filetime ); FileTimeToDosDateTime( &filetime, ( ( LPWORD )dostime ) + 1, ( ( LPWORD )dostime ) + 0 ); FindClose( findHandle ); return true; } FindClose( findHandle ); } #endif // RB end return false; } /* ======================== idZipBuilder::IsFiltered ======================== */ bool idZipBuilder::IsFiltered( const idStr& filename ) const { if( filterExts.Num() == 0 && uncompressedFilterExts.Num() == 0 ) { return false; } for( int j = 0; j < filterExts.Num(); j++ ) { idStr fileExt = idStr( "." + filterExts[j] ); if( filename.Right( fileExt.Length() ).Icmp( fileExt ) == 0 ) { return false; } } for( int j = 0; j < uncompressedFilterExts.Num(); j++ ) { idStr fileExt = idStr( "." + uncompressedFilterExts[j] ); if( filename.Right( fileExt.Length() ).Icmp( fileExt ) == 0 ) { return false; } } return true; } /* ======================== idZipBuilder::IsUncompressed ======================== */ bool idZipBuilder::IsUncompressed( const idStr& filename ) const { if( uncompressedFilterExts.Num() == 0 ) { return false; } for( int j = 0; j < uncompressedFilterExts.Num(); j++ ) { idStr fileExt = idStr( "." + uncompressedFilterExts[j] ); if( filename.Right( fileExt.Length() ).Icmp( fileExt ) == 0 ) { return true; } } return false; } /* ======================== idZipBuilder::CreateZipFile ======================== */ bool idZipBuilder::CreateZipFile( bool appendFiles ) { #if 0 //#ifdef ID_PC if( zipFileName.IsEmpty() || sourceFolderName.IsEmpty() ) { idLib::Warning( "[%s] - invalid parameters!", __FUNCTION__ ); return false; } // need to clear the filesystem's zip cache before we can open and write //fileSystem->ClearZipCache(); idLib::Printf( "Building zip file: '%s'\n", zipFileName.c_str() ); sourceFolderName.StripTrailing( "\\" ); sourceFolderName.StripTrailing( "/" ); #if 0 // attempt to check the file out if( !Sys_IsFileWritable( zipFileName ) ) { if( ( idLib::sourceControl == NULL ) || !idLib::sourceControl->CheckOut( zipFileName ) ) { idLib::Warning( "READONLY zip file couldn't be checked out: %s", zipFileName.c_str() ); } else { idLib::Printf( "Checked out: %s\n", zipFileName.c_str() ); } } #endif // if not appending, set the file size to zero to "create it from scratch" if( !appendFiles ) { idLib::PrintfIf( zip_verbosity.GetBool(), "Overwriting zip file: '%s'\n", zipFileName.c_str() ); idFile* zipFile = fileSystem->OpenExplicitFileWrite( zipFileName ); if( zipFile != NULL ) { delete zipFile; zipFile = NULL; } } else { idLib::PrintfIf( zip_verbosity.GetBool(), "Appending to zip file: '%s'\n", zipFileName.c_str() ); } // enumerate the files to zip up in the source folder idStrStatic< MAX_OSPATH > relPath; relPath = fileSystem->OSPathToRelativePath( sourceFolderName ); idFileList* files = fileSystem->ListFilesTree( relPath, "*.*" ); // check to make sure that at least one file will be added to the package int atLeastOneFilteredFile = false; for( int i = 0; i < files->GetNumFiles(); i++ ) { idStr filename = files->GetFile( i ); if( !IsFiltered( filename ) ) { atLeastOneFilteredFile = true; break; } } if( !atLeastOneFilteredFile ) { // although we didn't actually update/create a zip file, it's because no files would be added anyway, which would result in a corrupted zip idLib::Printf( "Skipping zip creation/modification, no additional changes need to be made...\n" ); return true; } // open the zip file zipFile zf = zipOpen( zipFileName, appendFiles ? APPEND_STATUS_ADDINZIP : 0 ); if( zf == NULL ) { idLib::Warning( "[%s] - error opening file '%s'!", __FUNCTION__, zipFileName.c_str() ); return false; } // add the files to the zip file for( int i = 0; i < files->GetNumFiles(); i++ ) { // add each file to the zip file zip_fileinfo zi; memset( &zi, 0, sizeof( zip_fileinfo ) ); idStr filename = files->GetFile( i ); if( IsFiltered( filename ) ) { idLib::PrintfIf( zip_verbosity.GetBool(), "...Skipping: '%s'\n", filename.c_str() ); continue; } idStr filenameInZip = filename; filenameInZip.Strip( relPath ); filenameInZip.StripLeading( "/" ); idStrStatic< MAX_OSPATH > ospath; ospath = fileSystem->RelativePathToOSPath( filename ); GetFileTime( ospath, &zi.dosDate ); idLib::PrintfIf( zip_verbosity.GetBool(), "...Adding: '%s' ", filenameInZip.c_str() ); int compressionMethod = Z_DEFLATED; if( IsUncompressed( filenameInZip ) ) { compressionMethod = 0; } int errcode = zipOpenNewFileInZip3( zf, filenameInZip, &zi, NULL, 0, NULL, 0, NULL /* comment*/, compressionMethod, DEFAULT_COMPRESSION_LEVEL, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL /*password*/, 0 /*fileCRC*/ ); if( errcode != ZIP_OK ) { idLib::Warning( "Error opening file in zipfile!" ); continue; } else { // open the source file idFile_Permanent src( filename, ospath, FS_READ ); if( !src.IsOpen() ) { idLib::Warning( "Error opening source file!" ); continue; } // copy the file data into the zip file idTempArray buffer( DEFAULT_WRITEBUFFERSIZE ); size_t total = 0; while( size_t bytesRead = src.Read( buffer.Ptr(), buffer.Size() ) ) { if( bytesRead > 0 ) { errcode = zipWriteInFileInZip( zf, buffer.Ptr(), ( unsigned int )bytesRead ); if( errcode != ZIP_OK ) { idLib::Warning( "Error writing to zipfile (%i bytes)!", bytesRead ); continue; } } total += bytesRead; } assert( total == ( size_t )src.Length() ); } errcode = zipCloseFileInZip( zf ); if( errcode != ZIP_OK ) { idLib::Warning( "Error zipping source file!" ); continue; } idLib::PrintfIf( zip_verbosity.GetBool(), "\n" ); } // free the file list if( files != NULL ) { fileSystem->FreeFileList( files ); } // close the zip file int closeError = zipClose( zf, NULL ); if( closeError != ZIP_OK ) { idLib::Warning( "[%s] - error closing file '%s'!", __FUNCTION__, zipFileName.c_str() ); return false; } idLib::Printf( "Done.\n" ); return true; #else return false; #endif } /* ======================== idZipBuilder::CreateZipFileFromFileList ======================== */ bool idZipBuilder::CreateZipFileFromFileList( const char* name, const idList< idFile_Memory* >& srcFiles ) { zipFileName = name; return CreateZipFileFromFiles( srcFiles ); } /* ======================== idZipBuilder::CreateZipFileFromFiles ======================== */ bool idZipBuilder::CreateZipFileFromFiles( const idList< idFile_Memory* >& srcFiles ) { if( zipFileName.IsEmpty() ) { idLib::Warning( "[%s] - invalid parameters!", __FUNCTION__ ); return false; } // need to clear the filesystem's zip cache before we can open and write //fileSystem->ClearZipCache(); idLib::Printf( "Building zip file: '%s'\n", zipFileName.c_str() ); // do not allow overwrite as this should be a tempfile attempt to check the file out if( !Sys_IsFileWritable( zipFileName ) ) { idLib::PrintfIf( zip_verbosity.GetBool(), "File %s not writable, cannot proceed.\n", zipFileName.c_str() ); return false; } // open the zip file zipFile zf = zipOpen( zipFileName, 0 ); if( zf == NULL ) { idLib::Warning( "[%s] - error opening file '%s'!", __FUNCTION__, zipFileName.c_str() ); return false; } // add the files to the zip file for( int i = 0; i < srcFiles.Num(); i++ ) { // add each file to the zip file zip_fileinfo zi; memset( &zi, 0, sizeof( zip_fileinfo ) ); idFile_Memory* src = srcFiles[i]; src->MakeReadOnly(); idLib::PrintfIf( zip_verbosity.GetBool(), "...Adding: '%s' ", src->GetName() ); int compressionMethod = Z_DEFLATED; if( IsUncompressed( src->GetName() ) ) { compressionMethod = 0; } int errcode = zipOpenNewFileInZip3( zf, src->GetName(), &zi, NULL, 0, NULL, 0, NULL /* comment*/, compressionMethod, DEFAULT_COMPRESSION_LEVEL, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL /*password*/, 0 /*fileCRC*/ ); if( errcode != ZIP_OK ) { idLib::Warning( "Error opening file in zipfile!" ); continue; } else { // copy the file data into the zip file idTempArray buffer( DEFAULT_WRITEBUFFERSIZE ); size_t total = 0; while( size_t bytesRead = src->Read( buffer.Ptr(), buffer.Size() ) ) { if( bytesRead > 0 ) { errcode = zipWriteInFileInZip( zf, buffer.Ptr(), ( unsigned int )bytesRead ); if( errcode != ZIP_OK ) { idLib::Warning( "Error writing to zipfile (%" PRIuSIZE " bytes)!", bytesRead ); continue; } } total += bytesRead; } assert( total == ( size_t )src->Length() ); } errcode = zipCloseFileInZip( zf ); if( errcode != ZIP_OK ) { idLib::Warning( "Error zipping source file!" ); continue; } idLib::PrintfIf( zip_verbosity.GetBool(), "\n" ); } // close the zip file int closeError = zipClose( zf, zipFileName ); if( closeError != ZIP_OK ) { idLib::Warning( "[%s] - error closing file '%s'!", __FUNCTION__, zipFileName.c_str() ); return false; } idLib::PrintfIf( zip_verbosity.GetBool(), "Done.\n" ); return true; } /* ======================== idZipBuilder::CleanSourceFolder this folder is assumed to be a path under FSPATH_BASE ======================== */ zipFile idZipBuilder::CreateZipFile( const char* name ) { idLib::Printf( "Creating zip file: '%s'\n", name ); // do not allow overwrite as this should be a tempfile attempt to check the file out if( !Sys_IsFileWritable( name ) ) { idLib::PrintfIf( zip_verbosity.GetBool(), "File %s not writable, cannot proceed.\n", name ); return NULL; } // open the zip file zipFile zf = zipOpen( name, 0 ); if( zf == NULL ) { idLib::Warning( "[%s] - error opening file '%s'!", __FUNCTION__, name ); } return zf; } /* ======================== idZipBuilder::CleanSourceFolder this folder is assumed to be a path under FSPATH_BASE ======================== */ bool idZipBuilder::AddFile( zipFile zf, idFile_Memory* src, bool deleteFile ) { // add each file to the zip file zip_fileinfo zi; memset( &zi, 0, sizeof( zip_fileinfo ) ); src->MakeReadOnly(); idLib::PrintfIf( zip_verbosity.GetBool(), "...Adding: '%s' ", src->GetName() ); int compressionMethod = Z_DEFLATED; if( IsUncompressed( src->GetName() ) ) { compressionMethod = Z_NO_COMPRESSION; } int errcode = zipOpenNewFileInZip3( zf, src->GetName(), &zi, NULL, 0, NULL, 0, NULL /* comment*/, compressionMethod, DEFAULT_COMPRESSION_LEVEL, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL /*password*/, 0 /*fileCRC*/ ); if( errcode != ZIP_OK ) { idLib::Warning( "Error opening file in zipfile!" ); if( deleteFile ) { src->Clear( true ); delete src; } return false; } else { // copy the file data into the zip file idTempArray buffer( DEFAULT_WRITEBUFFERSIZE ); size_t total = 0; while( size_t bytesRead = src->Read( buffer.Ptr(), buffer.Size() ) ) { if( bytesRead > 0 ) { errcode = zipWriteInFileInZip( zf, buffer.Ptr(), ( unsigned int )bytesRead ); if( errcode != ZIP_OK ) { idLib::Warning( "Error writing to zipfile (%" PRIuSIZE " bytes)!", bytesRead ); continue; } } total += bytesRead; } assert( total == ( size_t )src->Length() ); } errcode = zipCloseFileInZip( zf ); if( errcode != ZIP_OK ) { idLib::Warning( "Error zipping source file!" ); if( deleteFile ) { src->Clear( true ); delete src; } return false; } idLib::PrintfIf( zip_verbosity.GetBool(), "\n" ); if( deleteFile ) { src->Clear( true ); delete src; } return true; } /* ======================== idZipBuilder::CleanSourceFolder this folder is assumed to be a path under FSPATH_BASE ======================== */ void idZipBuilder::CloseZipFile( zipFile zf ) { // close the zip file int closeError = zipClose( zf, zipFileName ); if( closeError != ZIP_OK ) { idLib::Warning( "[%s] - error closing file '%s'!", __FUNCTION__, zipFileName.c_str() ); } idLib::PrintfIf( zip_verbosity.GetBool(), "Done.\n" ); } /* ======================== idZipBuilder::CleanSourceFolder this folder is assumed to be a path under FSPATH_BASE ======================== */ void idZipBuilder::CleanSourceFolder() { #if 0 //#ifdef ID_PC_WIN idStrList deletedFiles; // make sure this is a valid path, we don't want to go nuking // some user path or something else unintentionally idStr ospath = sourceFolderName; ospath.SlashesToBackSlashes(); ospath.ToLower(); char relPath[MAX_OSPATH]; fileSystem->OSPathToRelativePath( ospath, relPath, MAX_OSPATH ); // get the game's base path idStr basePath = fileSystem->GetBasePathStr( FSPATH_BASE ); basePath.AppendPath( BASE_GAMEDIR ); basePath.AppendPath( "maps" ); basePath.SlashesToBackSlashes(); basePath.ToLower(); // path must be off of our base path, ospath can't have .map on the end, and // do some additional sanity checks if( ( ospath.Find( basePath ) == 0 ) && ( ospath.Right( 4 ) != ".map" ) && ( ospath != "c:\\" ) && ( ospath.Length() > basePath.Length() ) ) { // get the files in the current directory idFileList* files = fileSystem->ListFilesTree( relPath, "*.*" ); if( files->GetNumFiles() && zip_verbosity.GetBool() ) { idLib::Printf( "Deleting files in '%s'...\n", relPath ); } for( int i = 0; i < files->GetNumFiles(); i++ ) { if( IsFiltered( files->GetFile( i ) ) ) { continue; } // nuke 'em if( zip_verbosity.GetBool() ) { idLib::Printf( "\t...%s\n", files->GetFile( i ) ); } fileSystem->RemoveFile( files->GetFile( i ) ); char ospath2[MAX_OSPATH]; fileSystem->RelativePathToOSPath( files->GetFile( i ), ospath2, MAX_OSPATH ); deletedFiles.Append( ospath2 ); } fileSystem->FreeFileList( files ); fileSystem->RemoveDir( relPath ); } else { idLib::Printf( "Warning: idZipBuilder::CleanSourceFolder - Non-standard path: '%s'!\n", ospath.c_str() ); return; } // figure out which deleted files need to be removed from source control, and then remove those files idStrList filesToRemoveFromSourceControl; for( int i = 0; i < deletedFiles.Num(); i++ ) { scFileStatus_t fileStatus = idLib::sourceControl->GetFileStatus( deletedFiles[ i ] ); if( SCF_IS_IN_SOURCE_CONTROL( fileStatus ) ) { filesToRemoveFromSourceControl.Append( deletedFiles[ i ] ); } } if( filesToRemoveFromSourceControl.Num() > 0 ) { idLib::sourceControl->Delete( filesToRemoveFromSourceControl ); } #endif } /* ======================== idZipBuilder::BuildMapFolderZip ======================== */ const char* ZIP_FILE_EXTENSION = "pk4"; bool idZipBuilder::BuildMapFolderZip( const char* mapFileName ) { idStr zipFileName = mapFileName; zipFileName.SetFileExtension( ZIP_FILE_EXTENSION ); idStr pathToZip = mapFileName; pathToZip.StripFileExtension(); idZipBuilder zip; zip.AddFileFilters( "bcm|bmodel|proc|" ); zip.AddUncompressedFileFilters( "genmodel|sbcm|tbcm|" ); bool success = zip.Build( zipFileName, pathToZip, true ); // even if the zip build failed we want to clear the source folder so no contributing files are left around if( !success ) { zip.CleanSourceFolder(); } return success; } /* ======================== idZipBuilder::UpdateMapFolderZip ======================== */ bool idZipBuilder::UpdateMapFolderZip( const char* mapFileName ) { idStr zipFileName = mapFileName; zipFileName.SetFileExtension( ZIP_FILE_EXTENSION ); idStr pathToZip = mapFileName; pathToZip.StripFileExtension(); idZipBuilder zip; zip.AddFileFilters( "bcm|bmodel|proc|" ); zip.AddUncompressedFileFilters( "genmodel|sbcm|tbcm|" ); bool success = zip.Update( zipFileName, pathToZip, true ); // even if the zip build failed we want to clear the source folder so no contributing files are left around if( !success ) { zip.CleanSourceFolder(); } return success; } /* ======================== idZipBuilder::CombineFiles ======================== */ idFile_Memory* idZipBuilder::CombineFiles( const idList< idFile_Memory* >& srcFiles ) { idFile_Memory* destFile = NULL; #if 0 //#ifdef ID_PC // create a new temp file so we can zip into it without refactoring the zip routines char ospath[MAX_OSPATH]; const char* tempName = "temp.tmp"; fileSystem->RelativePathToOSPath( tempName, ospath, MAX_OSPATH, FSPATH_SAVE ); fileSystem->RemoveFile( ospath ); // combine src files into dest filename just specified idZipBuilder zip; zip.zipFileName = ospath; bool ret = zip.CreateZipFileFromFiles( srcFiles ); // read the temp file created into a memory file to return if( ret ) { destFile = new idFile_Memory(); if( !destFile->Load( tempName, ospath ) ) { assert( false && "couldn't read the combined file" ); delete destFile; destFile = NULL; } // delete the temp file fileSystem->RemoveFile( ospath ); // make the new file readable destFile->MakeReadOnly(); } #endif return destFile; } CONSOLE_COMMAND( testZipBuilderCombineFiles, "test routine for memory zip file building", 0 ) { #if 0 idList< idFile_Memory* > list; const char* testString = "test"; int numFiles = 2; if( args.Argc() > 2 ) { idLib::Printf( "usage: testZipBuilderExtractFiles [numFiles]\n" ); return; } for( int arg = 1; arg < args.Argc(); arg++ ) { numFiles = atoi( args.Argv( arg ) ); } // allocate all the test files for( int i = 0; i < numFiles; i++ ) { idFile_Memory* file = new idFile_Memory( va( "%s%d.txt", testString, i + 1 ) ); file->MakeWritable(); idStr str = va( "%s%d", testString, i + 1 ); file->WriteString( str ); list.Append( file ); } // combine the files into a single memory file idZipBuilder zip; idFile_Memory* file = zip.CombineFiles( list ); if( file != NULL ) { file->MakeReadOnly(); char ospath[MAX_OSPATH]; const char* tempName = "temp.zip"; fileSystem->RelativePathToOSPath( tempName, ospath, MAX_OSPATH, FSPATH_SAVE ); // remove previous file if it exists fileSystem->RemoveFile( ospath ); if( file->Save( tempName, ospath ) ) { idLib::PrintfIf( zip_verbosity.GetBool(), va( "File written: %s.\n", ospath ) ); } else { idLib::Error( "Could not save the file." ); } delete file; } list.DeleteContents(); #endif // Now look at the temp.zip, unzip it to see if it works } /* ======================== idZipBuilder::ExtractFiles ======================== */ bool idZipBuilder::ExtractFiles( idFile_Memory*& srcFile, idList< idFile_Memory* >& destFiles ) { bool ret = false; #if 0 //#ifdef ID_PC destFiles.Clear(); // write the memory file to temp storage so we can unzip it without refactoring the unzip routines char ospath[MAX_OSPATH]; const char* tempName = "temp.tmp"; fileSystem->RelativePathToOSPath( tempName, ospath, MAX_OSPATH, FSPATH_SAVE ); ret = srcFile->Save( tempName, ospath ); assert( ret && "couldn't create temp file" ); if( ret ) { idLib::PrintfIf( zip_verbosity.GetBool(), "Opening archive %s:\n", ospath ); unzFile zip = unzOpen( ospath ); int numFiles = 0; int result = unzGoToFirstFile( zip ); while( result == UNZ_OK ) { numFiles++; unz_file_info curFileInfo; char fileName[MAX_OSPATH]; unzGetCurrentFileInfo( zip, &curFileInfo, fileName, MAX_OSPATH, NULL, 0, NULL, 0 ); idLib::PrintfIf( zip_verbosity.GetBool(), "%d: %s, size: %d \\ %d\n", numFiles, fileName, curFileInfo.compressed_size, curFileInfo.uncompressed_size ); // create a buffer big enough to hold the entire uncompressed file void* buff = Mem_Alloc( curFileInfo.uncompressed_size, TAG_TEMP ); result = unzOpenCurrentFile( zip ); if( result == UNZ_OK ) { result = unzReadCurrentFile( zip, buff, curFileInfo.uncompressed_size ); unzCloseCurrentFile( zip ); } // create the new memory file idFile_Memory* outFile = new idFile_Memory( fileName ); outFile->SetReadOnlyData( ( const char* )buff, curFileInfo.uncompressed_size ); destFiles.Append( outFile ); result = unzGoToNextFile( zip ); } // close it so we can delete the zip file and create a new one unzClose( zip ); // delete the temp zipfile fileSystem->RemoveFile( ospath ); } #endif return ret; } CONSOLE_COMMAND( testZipBuilderExtractFiles, "test routine for memory zip file extraction", 0 ) { #if 0 idList< idFile_Memory* > list; idFile_Memory* zipfile; const char* testString = "test"; int numFiles = 2; bool overallSuccess = true; bool success = true; if( args.Argc() > 2 ) { idLib::Printf( "usage: testZipBuilderExtractFiles [numFiles]\n" ); return; } for( int arg = 1; arg < args.Argc(); arg++ ) { numFiles = atoi( args.Argv( arg ) ); } // create a temp.zip file with string files { // allocate all the test files for( int i = 0; i < numFiles; i++ ) { idFile_Memory* file = new idFile_Memory( va( "%s%d.txt", testString, i + 1 ) ); file->MakeWritable(); idStr str = va( "%s%d", testString, i + 1 ); file->WriteString( str ); list.Append( file ); } // combine the files into a single memory file idZipBuilder zip; zipfile = zip.CombineFiles( list ); success = ( zipfile != NULL ); overallSuccess &= success; idLib::Printf( "Zip file created: %s\n", success ? "^2PASS" : "^1FAIL" ); // destroy all the test files list.DeleteContents(); } // unzip the file into separate memory files if( overallSuccess ) { // extract all the test files using the single zip file from above idZipBuilder zip; if( !zip.ExtractFiles( zipfile, list ) ) { idLib::Error( "Could not extract files." ); } success = ( list.Num() == numFiles ); overallSuccess &= success; idLib::Printf( "Number of files: %s\n", success ? "^2PASS" : "^1FAIL" ); for( int i = 0; i < list.Num(); i++ ) { idStr str; idFile_Memory* file = list[i]; file->MakeReadOnly(); file->ReadString( str ); idStr filename = va( "%s%d.txt", testString, i + 1 ); idStr contents = va( "%s%d", testString, i + 1 ); // test the filename bool nameSuccess = ( file->GetName() == filename ); overallSuccess &= nameSuccess; // test the string bool contentSuccess = ( str == contents ); overallSuccess &= contentSuccess; idLib::Printf( "Extraction of file, %s: %s^0, contents check: %s\n", filename.c_str(), nameSuccess ? "^2PASS" : "^1FAIL", contentSuccess ? "^2PASS" : "^1FAIL" ); } list.DeleteContents(); } if( zipfile != NULL ) { delete zipfile; } idLib::Printf( "[%s] overall tests: %s\n", __FUNCTION__, overallSuccess ? "^2PASS" : "^1FAIL" ); #endif }