2009-05-08 13:28:41 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 1998 - 2000 , Matthes Bender
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
* Copyright ( c ) 2009 - 2013 , The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2009-05-08 13:28:41 +00:00
*/
/* Handles group files */
/* Needs to be compilable as Objective C++ on OS X */
# include <C4Include.h>
# include <C4Group.h>
# include <C4Components.h>
# include <C4InputValidation.h>
# include <zlib.h>
2009-08-12 12:13:16 +00:00
2009-05-08 13:28:41 +00:00
//------------------------------ File Sort Lists -------------------------------------------
const char * C4CFN_FLS [ ] =
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4CFN_System , C4FLS_System ,
2010-03-28 18:58:01 +00:00
C4CFN_Material , C4FLS_Material ,
C4CFN_Graphics , C4FLS_Graphics ,
C4CFN_DefFiles , C4FLS_Def ,
C4CFN_PlayerFiles , C4FLS_Player ,
C4CFN_ObjectInfoFiles , C4FLS_Object ,
C4CFN_ScenarioFiles , C4FLS_Scenario ,
C4CFN_FolderFiles , C4FLS_Folder ,
2009-05-08 13:28:41 +00:00
C4CFN_ScenarioSections , C4FLS_Section ,
2015-02-07 12:59:45 +00:00
C4CFN_Sound , C4FLS_Sound ,
2010-03-28 18:58:01 +00:00
C4CFN_Music , C4FLS_Music ,
2009-05-08 13:28:41 +00:00
NULL , NULL
2010-03-28 18:58:01 +00:00
} ;
2009-05-08 13:28:41 +00:00
# ifdef _DEBUG
2015-02-07 12:59:45 +00:00
const char * szCurrAccessedEntry = NULL ;
2009-05-08 13:28:41 +00:00
int iC4GroupRewindFilePtrNoWarn = 0 ;
# endif
# ifdef _DEBUG
//#define C4GROUP_DUMP_ACCESS
# endif
//---------------------------- Global C4Group_Functions -------------------------------------------
char C4Group_TempPath [ _MAX_PATH + 1 ] = " " ;
2012-10-13 15:18:25 +00:00
char C4Group_Ignore [ _MAX_PATH + 1 ] = " cvs;CVS;Thumbs.db;.orig;.svn " ;
2009-05-08 13:28:41 +00:00
const char * * C4Group_SortList = NULL ;
time_t C4Group_AssumeTimeOffset = 0 ;
2009-08-15 18:50:32 +00:00
bool ( * C4Group_ProcessCallback ) ( const char * , int ) = NULL ;
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
void C4Group_SetProcessCallback ( bool ( * fnCallback ) ( const char * , int ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Group_ProcessCallback = fnCallback ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Group_SetSortList ( const char * * ppSortList )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Group_SortList = ppSortList ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Group_SetTempPath ( const char * szPath )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( ! szPath | | ! szPath [ 0 ] ) C4Group_TempPath [ 0 ] = 0 ;
else { SCopy ( szPath , C4Group_TempPath , _MAX_PATH ) ; AppendBackslash ( C4Group_TempPath ) ; }
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
const char * C4Group_GetTempPath ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return C4Group_TempPath ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group_TestIgnore ( const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2010-04-15 20:24:34 +00:00
if ( ! * szFilename ) return true ; //poke out empty strings
const char * name = GetFilename ( szFilename ) ;
return * name = = ' . ' //no hidden files and the directory itself
| | name [ strlen ( name ) - 1 ] = = ' ~ ' //no temp files
| | SIsModule ( C4Group_Ignore , name ) ; //not on Blacklist
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4Group_IsGroup ( const char * szFilename )
2010-03-28 18:58:01 +00:00
{
C4Group hGroup ; if ( hGroup . Open ( szFilename ) ) { hGroup . Close ( ) ; return true ; }
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4Group_CopyItem ( const char * szSource , const char * szTarget1 , bool fNoSort , bool fResetAttributes )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Parameter check
2009-08-15 18:50:32 +00:00
if ( ! szSource | | ! szTarget1 | | ! szSource [ 0 ] | | ! szTarget1 [ 0 ] ) return false ;
2009-05-08 13:28:41 +00:00
char szTarget [ _MAX_PATH + 1 ] ; SCopy ( szTarget1 , szTarget , _MAX_PATH ) ;
// Backslash terminator indicates target is a path only (append filename)
if ( szTarget [ SLen ( szTarget ) - 1 ] = = DirectorySeparator ) SAppend ( GetFilename ( szSource ) , szTarget ) ;
// Check for identical source and target
// Note that attributes aren't reset here
2009-08-15 18:50:32 +00:00
if ( ItemIdentical ( szSource , szTarget ) ) return true ;
2009-05-08 13:28:41 +00:00
// Source and target are simple items
2010-03-28 18:58:01 +00:00
if ( ItemExists ( szSource ) & & CreateItem ( szTarget ) ) return CopyItem ( szSource , szTarget , fResetAttributes ) ;
2009-05-08 13:28:41 +00:00
// For items within groups, attribute resetting isn't needed, because packing/unpacking will kill all
// attributes anyway
// Source & target
C4Group hSourceParent , hTargetParent ;
char szSourceParentPath [ _MAX_PATH + 1 ] , szTargetParentPath [ _MAX_PATH + 1 ] ;
GetParentPath ( szSource , szSourceParentPath ) ; GetParentPath ( szTarget , szTargetParentPath ) ;
// Temp filename
char szTempFilename [ _MAX_PATH + 1 ] ;
SCopy ( C4Group_TempPath , szTempFilename , _MAX_PATH ) ;
SAppend ( GetFilename ( szSource ) , szTempFilename ) ;
MakeTempFilename ( szTempFilename ) ;
// Extract source to temp file
if ( ! hSourceParent . Open ( szSourceParentPath )
2010-03-28 18:58:01 +00:00
| | ! hSourceParent . Extract ( GetFilename ( szSource ) , szTempFilename )
| | ! hSourceParent . Close ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// Move temp file to target
if ( ! hTargetParent . Open ( szTargetParentPath )
2010-03-28 18:58:01 +00:00
| | ! hTargetParent . SetNoSort ( fNoSort )
| | ! hTargetParent . Move ( szTempFilename , GetFilename ( szTarget ) )
| | ! hTargetParent . Close ( ) ) { EraseItem ( szTempFilename ) ; return false ; }
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4Group_MoveItem ( const char * szSource , const char * szTarget1 , bool fNoSort )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Parameter check
2009-08-15 18:50:32 +00:00
if ( ! szSource | | ! szTarget1 | | ! szSource [ 0 ] | | ! szTarget1 [ 0 ] ) return false ;
2009-05-08 13:28:41 +00:00
char szTarget [ _MAX_PATH + 1 ] ; SCopy ( szTarget1 , szTarget , _MAX_PATH ) ;
// Backslash terminator indicates target is a path only (append filename)
if ( szTarget [ SLen ( szTarget ) - 1 ] = = DirectorySeparator ) SAppend ( GetFilename ( szSource ) , szTarget ) ;
// Check for identical source and target
2009-08-15 18:50:32 +00:00
if ( ItemIdentical ( szSource , szTarget ) ) return true ;
2009-05-08 13:28:41 +00:00
// Source and target are simple items
if ( ItemExists ( szSource ) & & CreateItem ( szTarget ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// erase test file, because it may block moving a directory
EraseItem ( szTarget ) ;
return MoveItem ( szSource , szTarget ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Source & target
C4Group hSourceParent , hTargetParent ;
char szSourceParentPath [ _MAX_PATH + 1 ] , szTargetParentPath [ _MAX_PATH + 1 ] ;
GetParentPath ( szSource , szSourceParentPath ) ; GetParentPath ( szTarget , szTargetParentPath ) ;
// Temp filename
char szTempFilename [ _MAX_PATH + 1 ] ;
SCopy ( C4Group_TempPath , szTempFilename , _MAX_PATH ) ;
SAppend ( GetFilename ( szSource ) , szTempFilename ) ;
MakeTempFilename ( szTempFilename ) ;
// Extract source to temp file
if ( ! hSourceParent . Open ( szSourceParentPath )
2010-03-28 18:58:01 +00:00
| | ! hSourceParent . Extract ( GetFilename ( szSource ) , szTempFilename )
| | ! hSourceParent . Close ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// Move temp file to target
if ( ! hTargetParent . Open ( szTargetParentPath )
2010-03-28 18:58:01 +00:00
| | ! hTargetParent . SetNoSort ( fNoSort )
| | ! hTargetParent . Move ( szTempFilename , GetFilename ( szTarget ) )
| | ! hTargetParent . Close ( ) ) { EraseItem ( szTempFilename ) ; return false ; }
2009-05-08 13:28:41 +00:00
// Delete original file
if ( ! hSourceParent . Open ( szSourceParentPath )
2010-03-28 18:58:01 +00:00
| | ! hSourceParent . DeleteEntry ( GetFilename ( szSource ) )
| | ! hSourceParent . Close ( ) ) return false ;
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4Group_DeleteItem ( const char * szItem , bool fRecycle )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Parameter check
2009-08-15 18:50:32 +00:00
if ( ! szItem | | ! szItem [ 0 ] ) return false ;
2009-05-08 13:28:41 +00:00
// simple item?
if ( ItemExists ( szItem ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( fRecycle )
return EraseItemSafe ( szItem ) ;
else
return EraseItem ( szItem ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// delete from parent
C4Group hParent ;
char szParentPath [ _MAX_PATH + 1 ] ;
GetParentPath ( szItem , szParentPath ) ;
// Delete original file
if ( ! hParent . Open ( szParentPath )
2010-03-28 18:58:01 +00:00
| | ! hParent . DeleteEntry ( GetFilename ( szItem ) , fRecycle )
| | ! hParent . Close ( ) ) return false ;
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group_PackDirectoryTo ( const char * szFilename , const char * szFilenameTo )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Check file type
if ( ! DirectoryExists ( szFilename ) ) return false ;
// Target mustn't exist
if ( FileExists ( szFilenameTo ) ) return false ;
// Ignore
if ( C4Group_TestIgnore ( szFilename ) )
return true ;
// Process message
if ( C4Group_ProcessCallback )
C4Group_ProcessCallback ( szFilename , 0 ) ;
// Create group file
C4Group hGroup ;
2009-08-15 18:50:32 +00:00
if ( ! hGroup . Open ( szFilenameTo , true ) )
2009-05-08 13:28:41 +00:00
return false ;
// Add folder contents to group
DirectoryIterator i ( szFilename ) ;
2010-03-28 18:58:01 +00:00
for ( ; * i ; i + + )
{
2009-05-08 13:28:41 +00:00
// Ignore
if ( C4Group_TestIgnore ( * i ) )
continue ;
// Must pack?
if ( DirectoryExists ( * i ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Find temporary filename
char szTempFilename [ _MAX_PATH + 1 ] ;
// At C4Group temp path
SCopy ( C4Group_TempPath , szTempFilename , _MAX_PATH ) ;
SAppend ( GetFilename ( * i ) , szTempFilename , _MAX_PATH ) ;
// Make temporary filename
MakeTempFilename ( szTempFilename ) ;
// Pack and move into group
if ( ! C4Group_PackDirectoryTo ( * i , szTempFilename ) ) break ;
if ( ! hGroup . Move ( szTempFilename , GetFilename ( * i ) ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
EraseFile ( szTempFilename ) ;
break ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Add normally otherwise
else if ( ! hGroup . Add ( * i , NULL ) )
break ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Something went wrong?
if ( * i )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Close group and remove temporary file
hGroup . Close ( ) ;
EraseItem ( szFilenameTo ) ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Reset iterator
i . Reset ( ) ;
// Close group
hGroup . SortByList ( C4Group_SortList , szFilename ) ;
if ( ! hGroup . Close ( ) )
return false ;
// Done
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group_PackDirectory ( const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Make temporary filename
char szTempFilename [ _MAX_PATH + 1 ] ;
SCopy ( szFilename , szTempFilename , _MAX_PATH ) ;
MakeTempFilename ( szTempFilename ) ;
// Pack directory
if ( ! C4Group_PackDirectoryTo ( szFilename , szTempFilename ) )
return false ;
// Rename folder
char szTempFilename2 [ _MAX_PATH + 1 ] ;
SCopy ( szFilename , szTempFilename2 , _MAX_PATH ) ;
MakeTempFilename ( szTempFilename2 ) ;
if ( ! RenameFile ( szFilename , szTempFilename2 ) )
return false ;
// Name group file
if ( ! RenameFile ( szTempFilename , szFilename ) )
return false ;
// Last: Delete folder
return EraseDirectory ( szTempFilename2 ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4Group_UnpackDirectory ( const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Already unpacked: success
2009-08-15 18:50:32 +00:00
if ( DirectoryExists ( szFilename ) ) return true ;
2009-05-08 13:28:41 +00:00
// Not a real file: unpack parent directory first
char szParentFilename [ _MAX_PATH + 1 ] ;
if ( ! FileExists ( szFilename ) )
if ( GetParentPath ( szFilename , szParentFilename ) )
if ( ! C4Group_UnpackDirectory ( szParentFilename ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
// Open group
2010-03-27 16:05:02 +00:00
C4Group hGroup ;
if ( ! hGroup . Open ( szFilename ) ) return false ;
2009-05-08 13:28:41 +00:00
// Process message
2010-03-27 16:05:02 +00:00
if ( C4Group_ProcessCallback )
2009-05-08 13:28:41 +00:00
C4Group_ProcessCallback ( szFilename , 0 ) ;
2010-03-27 16:05:02 +00:00
// Create target directory
2009-05-08 13:28:41 +00:00
char szFoldername [ _MAX_PATH + 1 ] ;
2010-03-27 16:05:02 +00:00
SCopy ( szFilename , szFoldername , _MAX_PATH ) ;
MakeTempFilename ( szFoldername ) ;
if ( ! CreatePath ( szFoldername ) ) { hGroup . Close ( ) ; return false ; }
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Extract files to folder
2009-08-15 18:50:32 +00:00
if ( ! hGroup . Extract ( " * " , szFoldername ) ) { hGroup . Close ( ) ; return false ; }
2009-05-08 13:28:41 +00:00
// Close group
2010-03-27 16:05:02 +00:00
hGroup . Close ( ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Rename group file
2009-05-08 13:28:41 +00:00
char szTempFilename [ _MAX_PATH + 1 ] ;
2010-03-27 16:05:02 +00:00
SCopy ( szFilename , szTempFilename , _MAX_PATH ) ;
MakeTempFilename ( szTempFilename ) ;
2009-08-15 18:50:32 +00:00
if ( ! RenameFile ( szFilename , szTempFilename ) ) return false ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Rename target directory
if ( ! RenameFile ( szFoldername , szFilename ) ) return false ;
2009-05-08 13:28:41 +00:00
// Delete renamed group file
2010-03-27 16:05:02 +00:00
return EraseItem ( szTempFilename ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group_ExplodeDirectory ( const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
// Ignore
2009-08-15 18:50:32 +00:00
if ( C4Group_TestIgnore ( szFilename ) ) return true ;
2009-05-08 13:28:41 +00:00
// Unpack this directory
2009-08-15 18:50:32 +00:00
if ( ! C4Group_UnpackDirectory ( szFilename ) ) return false ;
2009-05-08 13:28:41 +00:00
// Explode all children
ForEachFile ( szFilename , C4Group_ExplodeDirectory ) ;
// Success
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group_ReadFile ( const char * szFile , char * * pData , size_t * iSize )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// security
2010-03-28 18:58:01 +00:00
if ( ! szFile | | ! pData ) return false ;
2009-05-08 13:28:41 +00:00
// get mother path & file name
char szPath [ _MAX_PATH + 1 ] ;
GetParentPath ( szFile , szPath ) ;
const char * pFileName = GetFilename ( szFile ) ;
// open mother group
C4Group MotherGroup ;
2010-03-28 18:58:01 +00:00
if ( ! MotherGroup . Open ( szPath ) ) return false ;
2009-05-08 13:28:41 +00:00
// access the file
size_t iFileSize ;
2010-03-28 18:58:01 +00:00
if ( ! MotherGroup . AccessEntry ( pFileName , & iFileSize ) ) return false ;
2009-05-08 13:28:41 +00:00
// create buffer
* pData = new char [ iFileSize ] ;
// read it
2010-03-28 18:58:01 +00:00
if ( ! MotherGroup . Read ( * pData , iFileSize ) ) { delete [ ] * pData ; * pData = NULL ; return false ; }
2009-05-08 13:28:41 +00:00
// ok
MotherGroup . Close ( ) ;
2010-03-28 18:58:01 +00:00
if ( iSize ) * iSize = iFileSize ;
2009-05-08 13:28:41 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void MemScramble ( BYTE * bypBuffer , int iSize )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int cnt ; BYTE temp ;
// XOR deface
for ( cnt = 0 ; cnt < iSize ; cnt + + )
bypBuffer [ cnt ] ^ = 237 ;
// BYTE swap
for ( cnt = 0 ; cnt + 2 < iSize ; cnt + = 3 )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
temp = bypBuffer [ cnt ] ;
bypBuffer [ cnt ] = bypBuffer [ cnt + 2 ] ;
bypBuffer [ cnt + 2 ] = temp ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
//---------------------------------- C4Group ---------------------------------------------
C4GroupHeader : : C4GroupHeader ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
ZeroMem ( this , sizeof ( C4GroupHeader ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GroupHeader : : Init ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
SCopy ( C4GroupFileID , id , sizeof ( id ) - 1 ) ;
Ver1 = C4GroupFileVer1 ; Ver2 = C4GroupFileVer2 ;
Entries = 0 ;
2011-05-01 11:37:36 +00:00
std : : memset ( reserved , ' \0 ' , sizeof ( reserved ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4GroupEntryCore : : C4GroupEntryCore ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
ZeroMem ( this , sizeof ( C4GroupEntryCore ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4GroupEntry : : C4GroupEntry ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
ZeroMem ( this , sizeof ( C4GroupEntry ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4GroupEntry : : ~ C4GroupEntry ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( HoldBuffer )
if ( bpMemBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( BufferIsStdbuf )
StdBuf : : DeletePointer ( bpMemBuf ) ;
else
delete [ ] bpMemBuf ;
2010-03-28 18:58:01 +00:00
}
}
2010-01-25 15:54:38 +00:00
2009-05-08 13:28:41 +00:00
void C4GroupEntry : : Set ( const DirectoryIterator & iter , const char * path )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
ZeroMem ( this , sizeof ( C4GroupEntry ) ) ;
SCopy ( GetFilename ( * iter ) , FileName , _MAX_FNAME ) ;
SCopy ( * iter , DiskPath , _MAX_PATH - 1 ) ;
2011-08-11 13:45:27 +00:00
Size = FileSize ( * iter ) ;
2009-05-08 13:28:41 +00:00
Status = C4GRES_OnDisk ;
2009-08-15 18:50:32 +00:00
Packed = false ;
2015-03-25 18:04:04 +00:00
ChildGroup = false ;
2009-05-08 13:28:41 +00:00
// Notice folder entries are not checked for ChildGroup status.
// This would cause extreme performance loss and be good for
// use in entry list display only.
2010-03-28 18:58:01 +00:00
}
2010-01-25 15:54:38 +00:00
2009-05-08 13:28:41 +00:00
C4Group : : C4Group ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
Init ( ) ;
StdOutput = false ;
2009-05-08 13:28:41 +00:00
fnProcessCallback = NULL ;
2009-08-15 18:50:32 +00:00
NoSort = false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Group : : Init ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
// General
Status = GRPF_Inactive ;
FileName [ 0 ] = 0 ;
// Child status
Mother = NULL ;
ExclusiveChild = false ;
// File only
FilePtr = 0 ;
EntryOffset = 0 ;
Modified = false ;
Head . Init ( ) ;
FirstEntry = NULL ;
SearchPtr = NULL ;
2015-02-07 12:59:45 +00:00
pInMemEntry = NULL ; iInMemEntrySize = 0u ;
2010-03-27 16:05:02 +00:00
// Folder only
2012-11-17 18:44:04 +00:00
FolderSearch . Clear ( ) ;
2010-03-27 16:05:02 +00:00
// Error status
SCopy ( " No Error " , ErrorString , C4GroupMaxError ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Group : : ~ C4Group ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
Clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Error ( const char * szStatus )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
SCopy ( szStatus , ErrorString , C4GroupMaxError ) ;
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
const char * C4Group : : GetError ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
return ErrorString ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
void C4Group : : SetStdOutput ( bool fStatus )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
StdOutput = fStatus ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4Group : : Open ( const char * szGroupName , bool fCreate )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( ! szGroupName ) return Error ( " Open: Null filename " ) ;
if ( ! szGroupName [ 0 ] ) return Error ( " Open: Empty filename " ) ;
2009-05-08 13:28:41 +00:00
char szGroupNameN [ _MAX_FNAME ] ;
SCopy ( szGroupName , szGroupNameN , _MAX_FNAME ) ;
// Convert to native path
SReplaceChar ( szGroupNameN , ' \\ ' , DirectorySeparator ) ;
2010-03-27 16:05:02 +00:00
// Real reference
2009-05-08 13:28:41 +00:00
if ( FileExists ( szGroupNameN ) )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
// Init
Init ( ) ;
// Open group or folder
2009-05-08 13:28:41 +00:00
return OpenReal ( szGroupNameN ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// If requested, try creating a new group file
if ( fCreate )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
CStdFile temp ;
2009-08-15 18:50:32 +00:00
if ( temp . Create ( szGroupNameN , false ) )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
// Temporary file has been created
temp . Close ( ) ;
// Init
Init ( ) ;
Status = GRPF_File ; Modified = true ;
2009-05-08 13:28:41 +00:00
SCopy ( szGroupNameN , FileName , _MAX_FNAME ) ;
2010-03-27 16:05:02 +00:00
return true ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// While not a real reference (child group), trace back to mother group or folder.
2009-05-08 13:28:41 +00:00
// Open mother and child in exclusive mode.
2010-03-27 16:05:02 +00:00
char szRealGroup [ _MAX_FNAME ] ;
2009-05-08 13:28:41 +00:00
SCopy ( szGroupNameN , szRealGroup , _MAX_FNAME ) ;
2010-03-27 16:05:02 +00:00
do
2014-05-26 00:36:40 +00:00
{ if ( ! TruncatePath ( szRealGroup ) ) return Error ( FormatString ( " Open( \" %s \" ): File not found " , szGroupNameN ) . getData ( ) ) ; }
2010-03-27 16:05:02 +00:00
while ( ! FileExists ( szRealGroup ) ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Open mother and child in exclusive mode
2015-02-13 18:46:11 +00:00
C4Group * pMother = new C4Group ;
2009-05-08 13:28:41 +00:00
pMother - > SetStdOutput ( StdOutput ) ;
if ( ! pMother - > Open ( szRealGroup ) )
2014-05-26 00:36:40 +00:00
{ Clear ( ) ; Error ( pMother - > ErrorString ) ; delete pMother ; return false ; }
2009-08-15 18:50:32 +00:00
if ( ! OpenAsChild ( pMother , szGroupNameN + SLen ( szRealGroup ) + 1 , true ) )
2014-05-26 00:36:40 +00:00
{ Clear ( ) ; return false ; }
2009-05-08 13:28:41 +00:00
// Success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : OpenReal ( const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Get original filename
2009-08-15 18:50:32 +00:00
if ( ! szFilename ) return false ;
2009-05-08 13:28:41 +00:00
SCopy ( szFilename , FileName , _MAX_FNAME ) ;
// Folder
if ( DirectoryExists ( FileName ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Ignore
if ( C4Group_TestIgnore ( szFilename ) )
2011-03-04 02:49:39 +00:00
return Error ( FormatString ( " OpenReal: filename '%s' ignored " , szFilename ) . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
// OpenReal: Simply set status and return
Status = GRPF_Folder ;
ResetSearch ( ) ;
// Success
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// File: Try reading header and entries
if ( OpenRealGrpFile ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Status = GRPF_File ;
ResetSearch ( ) ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
return Error ( " OpenReal: Not a valid group " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : OpenRealGrpFile ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
int cnt , file_entries ;
C4GroupEntryCore corebuf ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Open StdFile
if ( ! StdFile . Open ( FileName , true ) ) return Error ( " OpenRealGrpFile: Cannot open standard file " ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Read header
if ( ! StdFile . Read ( ( BYTE * ) & Head , sizeof ( C4GroupHeader ) ) ) return Error ( " OpenRealGrpFile: Error reading header " ) ;
2009-05-08 13:28:41 +00:00
MemScramble ( ( BYTE * ) & Head , sizeof ( C4GroupHeader ) ) ;
2010-03-27 16:05:02 +00:00
EntryOffset + = sizeof ( C4GroupHeader ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Check Header
if ( ! SEqual ( Head . id , C4GroupFileID )
2010-03-28 18:58:01 +00:00
| | ( Head . Ver1 ! = C4GroupFileVer1 ) | | ( Head . Ver2 > C4GroupFileVer2 ) )
return Error ( " OpenRealGrpFile: Invalid header " ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Read Entries
2009-05-08 13:28:41 +00:00
file_entries = Head . Entries ;
Head . Entries = 0 ; // Reset, will be recounted by AddEntry
for ( cnt = 0 ; cnt < file_entries ; cnt + + )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( ! StdFile . Read ( ( BYTE * ) & corebuf , sizeof ( C4GroupEntryCore ) ) ) return Error ( " OpenRealGrpFile: Error reading entries " ) ;
// New C4Groups have filenames in UTF-8
StdStrBuf entryname ( corebuf . FileName ) ;
2009-06-28 20:53:32 +00:00
entryname . EnsureUnicode ( ) ;
2009-05-08 13:28:41 +00:00
// Prevent overwriting of user stuff by malicuous groups
C4InVal : : ValidateFilename ( const_cast < char * > ( entryname . getData ( ) ) , entryname . getLength ( ) ) ;
EntryOffset + = sizeof ( C4GroupEntryCore ) ;
2015-02-13 17:49:23 +00:00
if ( ! AddEntry ( C4GroupEntry : : C4GRES_InGroup , ! ! corebuf . ChildGroup ,
2011-05-01 11:37:36 +00:00
corebuf . FileName , corebuf . Size ,
2012-02-07 23:12:44 +00:00
entryname . getData ( ) ,
2010-03-28 18:58:01 +00:00
NULL , false , false ,
! ! corebuf . Executable ) )
2009-05-08 13:28:41 +00:00
return Error ( " OpenRealGrpFile: Cannot add entry " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2015-02-13 17:49:23 +00:00
bool C4Group : : AddEntry ( C4GroupEntry : : EntryStatus status ,
2010-03-28 18:58:01 +00:00
bool childgroup ,
2009-05-08 13:28:41 +00:00
const char * fname ,
2010-03-28 18:58:01 +00:00
long size ,
2009-05-08 13:28:41 +00:00
const char * entryname ,
BYTE * membuf ,
2010-03-28 18:58:01 +00:00
bool fDeleteOnDisk ,
bool fHoldBuffer ,
bool fExecutable ,
bool fBufferIsStdbuf )
{
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Folder: add file to folder immediately
if ( Status = = GRPF_Folder )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Close open StdFile
StdFile . Close ( ) ;
2010-03-27 16:05:02 +00:00
// Get path to target folder file
char tfname [ _MAX_FNAME ] ;
SCopy ( FileName , tfname , _MAX_FNAME ) ;
AppendBackslash ( tfname ) ;
if ( entryname ) SAppend ( entryname , tfname ) ;
else SAppend ( GetFilename ( fname ) , tfname ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
switch ( status )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_OnDisk : // Copy/move file to folder
2015-02-08 21:22:55 +00:00
if ( ! CopyItem ( fname , tfname ) )
return false ;
2015-02-08 21:25:16 +00:00
// Reset directory iterator to reflect new file
ResetSearch ( true ) ;
2015-02-08 21:22:55 +00:00
if ( fDeleteOnDisk & & ! EraseItem ( fname ) )
return false ;
return true ;
2009-05-08 13:28:41 +00:00
2015-08-31 18:52:39 +00:00
case C4GroupEntry : : C4GRES_InMemory : { // Save buffer to file in folder
2010-03-28 18:58:01 +00:00
CStdFile hFile ;
bool fOkay = false ;
if ( hFile . Create ( tfname , ! ! childgroup ) )
fOkay = ! ! hFile . Write ( membuf , size ) ;
hFile . Close ( ) ;
2015-02-08 21:25:16 +00:00
ResetSearch ( true ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
if ( fHoldBuffer ) { if ( fBufferIsStdbuf ) StdBuf : : DeletePointer ( membuf ) ; else delete [ ] membuf ; }
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
return fOkay ;
2015-08-31 18:52:39 +00:00
}
2009-05-08 13:28:41 +00:00
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return Error ( " Add to folder: Invalid request " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Group file: add to virtual entry list
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
C4GroupEntry * nentry , * lentry , * centry ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Delete existing entries of same name
centry = GetEntry ( GetFilename ( entryname ? entryname : fname ) ) ;
2015-02-13 17:49:23 +00:00
if ( centry ) { centry - > Status = C4GroupEntry : : C4GRES_Deleted ; Head . Entries - - ; }
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Allocate memory for new entry
2015-02-13 18:46:11 +00:00
nentry = new C4GroupEntry ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Find end of list
for ( lentry = FirstEntry ; lentry & & lentry - > Next ; lentry = lentry - > Next ) { }
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Init entry core data
if ( entryname ) SCopy ( entryname , nentry - > FileName , _MAX_FNAME ) ;
else SCopy ( GetFilename ( fname ) , nentry - > FileName , _MAX_FNAME ) ;
nentry - > Size = size ;
nentry - > ChildGroup = childgroup ;
nentry - > Offset = 0 ;
2009-05-08 13:28:41 +00:00
nentry - > Executable = fExecutable ;
nentry - > DeleteOnDisk = fDeleteOnDisk ;
nentry - > HoldBuffer = fHoldBuffer ;
nentry - > BufferIsStdbuf = fBufferIsStdbuf ;
2010-03-27 16:05:02 +00:00
if ( lentry ) nentry - > Offset = lentry - > Offset + lentry - > Size ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Init list entry data
SCopy ( fname , nentry - > DiskPath , _MAX_FNAME ) ;
nentry - > Status = status ;
nentry - > bpMemBuf = membuf ;
nentry - > Next = NULL ;
2009-05-08 13:28:41 +00:00
nentry - > NoSort = NoSort ;
2010-03-27 16:05:02 +00:00
// Append entry to list
if ( lentry ) lentry - > Next = nentry ;
else FirstEntry = nentry ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Increase virtual file count of group
Head . Entries + + ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4GroupEntry * C4Group : : GetEntry ( const char * szName )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( Status = = GRPF_Folder ) return NULL ;
C4GroupEntry * centry ;
for ( centry = FirstEntry ; centry ; centry = centry - > Next )
2015-02-13 17:49:23 +00:00
if ( centry - > Status ! = C4GroupEntry : : C4GRES_Deleted )
2010-03-27 16:05:02 +00:00
if ( WildcardMatch ( szName , centry - > FileName ) )
return centry ;
return NULL ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Close ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
C4GroupEntry * centry ;
bool fRewrite = false ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( Status = = GRPF_Inactive ) return false ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Folder: just close
if ( Status = = GRPF_Folder )
{ CloseExclusiveMother ( ) ; Clear ( ) ; return true ; }
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Rewrite check
for ( centry = FirstEntry ; centry ; centry = centry - > Next )
2015-02-13 17:49:23 +00:00
if ( centry - > Status ! = C4GroupEntry : : C4GRES_InGroup )
2010-03-27 16:05:02 +00:00
fRewrite = true ;
if ( Modified ) fRewrite = true ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// No rewrite: just close
if ( ! fRewrite )
{ CloseExclusiveMother ( ) ; Clear ( ) ; return true ; }
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( StdOutput ) printf ( " Writing group file... \n " ) ;
2009-05-08 13:28:41 +00:00
// Set new version
Head . Ver1 = C4GroupFileVer1 ;
Head . Ver2 = C4GroupFileVer2 ;
// Automatic sort
SortByList ( C4Group_SortList ) ;
// Save group contents to disk
2009-08-15 18:50:32 +00:00
bool fSuccess = Save ( false ) ;
2009-05-08 13:28:41 +00:00
// Close exclusive mother
CloseExclusiveMother ( ) ;
// Close file
Clear ( ) ;
return ! ! fSuccess ;
}
2009-08-15 18:50:32 +00:00
bool C4Group : : Save ( bool fReOpen )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
int cscore ;
C4GroupEntryCore * save_core ;
C4GroupEntry * centry ;
char szTempFileName [ _MAX_FNAME + 1 ] , szGrpFileName [ _MAX_FNAME + 1 ] ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Create temporary core list with new actual offsets to be saved
2009-05-08 13:28:41 +00:00
int32_t iContentsSize = 0 ;
2010-03-27 16:05:02 +00:00
save_core = new C4GroupEntryCore [ Head . Entries ] ;
cscore = 0 ;
for ( centry = FirstEntry ; centry ; centry = centry - > Next )
2015-02-13 17:49:23 +00:00
if ( centry - > Status ! = C4GroupEntry : : C4GRES_Deleted )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
save_core [ cscore ] = ( C4GroupEntryCore ) * centry ;
// Make actual offset
save_core [ cscore ] . Offset = iContentsSize ;
2009-05-08 13:28:41 +00:00
iContentsSize + = centry - > Size ;
2010-03-27 16:05:02 +00:00
cscore + + ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Hold contents in memory?
bool fToMemory = ! fReOpen & & Mother & & iContentsSize < C4GroupSwapThreshold ;
2010-03-28 18:58:01 +00:00
if ( ! fToMemory )
{
2009-05-08 13:28:41 +00:00
// Create target temp file (in temp directory!)
SCopy ( FileName , szGrpFileName , _MAX_FNAME ) ;
2009-06-17 19:45:41 +00:00
if ( C4Group_TempPath [ 0 ] ) { SCopy ( C4Group_TempPath , szTempFileName , _MAX_FNAME ) ; SAppend ( GetFilename ( FileName ) , szTempFileName , _MAX_FNAME ) ; }
else SCopy ( FileName , szTempFileName , _MAX_FNAME ) ;
2009-05-08 13:28:41 +00:00
MakeTempFilename ( szTempFileName ) ;
// (Temp file must not have the same name as the group.)
if ( SEqual ( szTempFileName , szGrpFileName ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
SAppend ( " .tmp " , szTempFileName ) ; // Add a second temp extension
MakeTempFilename ( szTempFileName ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Create the new (temp) group file
2010-03-27 16:05:02 +00:00
CStdFile tfile ;
if ( ! tfile . Create ( szTempFileName , true , false , fToMemory ) )
{ delete [ ] save_core ; return Error ( " Close: ... " ) ; }
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Save header and core list
2009-05-08 13:28:41 +00:00
C4GroupHeader headbuf = Head ;
MemScramble ( ( BYTE * ) & headbuf , sizeof ( C4GroupHeader ) ) ;
2010-03-27 16:05:02 +00:00
if ( ! tfile . Write ( ( BYTE * ) & headbuf , sizeof ( C4GroupHeader ) )
2010-03-28 18:58:01 +00:00
| | ! tfile . Write ( ( BYTE * ) save_core , Head . Entries * sizeof ( C4GroupEntryCore ) ) )
2010-03-27 16:05:02 +00:00
{ tfile . Close ( ) ; delete [ ] save_core ; return Error ( " Close: ... " ) ; }
delete [ ] save_core ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Save Entries to temp file
2009-05-08 13:28:41 +00:00
int iTotalSize = 0 , iSizeDone = 0 ;
for ( centry = FirstEntry ; centry ; centry = centry - > Next ) iTotalSize + = centry - > Size ;
for ( centry = FirstEntry ; centry ; centry = centry - > Next )
if ( AppendEntry2StdFile ( centry , tfile ) )
{ iSizeDone + = centry - > Size ; if ( iTotalSize & & fnProcessCallback ) fnProcessCallback ( centry - > FileName , 100 * iSizeDone / iTotalSize ) ; }
else
2010-03-28 18:58:01 +00:00
{
2009-08-15 18:50:32 +00:00
tfile . Close ( ) ; return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Write
StdBuf * pBuf ;
tfile . Close ( fToMemory ? & pBuf : NULL ) ;
// Child: move temp file to mother
if ( Mother )
2010-03-28 18:58:01 +00:00
{
if ( fToMemory )
2009-05-08 13:28:41 +00:00
{
if ( ! Mother - > Add ( GetFilename ( FileName ) , * pBuf , true , true ) )
{ delete pBuf ; CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " Close: Cannot move rewritten child data to mother " ) ; }
delete pBuf ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( ! Mother - > Move ( szTempFileName , GetFilename ( FileName ) ) )
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " Close: Cannot move rewritten child temp file to mother " ) ; }
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
Clear ( ) ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Clear (close file)
2010-03-27 16:05:02 +00:00
Clear ( ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Delete old group file, rename new file
2009-05-08 13:28:41 +00:00
if ( ! EraseFile ( szGrpFileName ) )
return Error ( " Close: Cannot erase temp file " ) ;
if ( ! RenameFile ( szTempFileName , szGrpFileName ) )
return Error ( " Close: Cannot rename group file " ) ;
// Should reopen the file?
2010-03-28 18:58:01 +00:00
if ( fReOpen )
2009-05-08 13:28:41 +00:00
OpenReal ( szGrpFileName ) ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Group : : Default ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
FirstEntry = NULL ;
StdFile . Default ( ) ;
Mother = NULL ;
ExclusiveChild = 0 ;
Init ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Group : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
// Delete entries
C4GroupEntry * next ;
while ( FirstEntry )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
next = FirstEntry - > Next ;
delete FirstEntry ;
FirstEntry = next ;
2010-03-28 18:58:01 +00:00
}
2010-03-27 16:05:02 +00:00
// Close std file
StdFile . Close ( ) ;
// Delete mother
if ( Mother & & ExclusiveChild )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
delete Mother ;
Mother = NULL ;
2010-03-28 18:58:01 +00:00
}
2010-03-27 16:05:02 +00:00
// Reset
Init ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : AppendEntry2StdFile ( C4GroupEntry * centry , CStdFile & hTarget )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
CStdFile hSource ;
long csize ;
BYTE fbuf ;
switch ( centry - > Status )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_InGroup : // Copy from group to std file
2010-03-28 18:58:01 +00:00
if ( ! SetFilePtr ( centry - > Offset ) )
return Error ( " AE2S: Cannot set file pointer " ) ;
for ( csize = centry - > Size ; csize > 0 ; csize - - )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! Read ( & fbuf , 1 ) )
return Error ( " AE2S: Cannot read entry from group file " ) ;
if ( ! hTarget . Write ( & fbuf , 1 ) )
return Error ( " AE2S: Cannot write to target file " ) ;
}
break ;
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_OnDisk : // Copy/move from disk item to std file
2010-03-28 18:58:01 +00:00
{
char szFileSource [ _MAX_FNAME + 1 ] ;
SCopy ( centry - > DiskPath , szFileSource , _MAX_FNAME ) ;
// Disk item is a directory
if ( DirectoryExists ( centry - > DiskPath ) )
return Error ( " AE2S: Cannot add directory to group file " ) ;
// Resort group if neccessary
// (The group might be renamed by adding, forcing a resort)
bool fTempFile = false ;
if ( centry - > ChildGroup )
if ( ! centry - > NoSort )
if ( ! SEqual ( GetFilename ( szFileSource ) , centry - > FileName ) )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// copy group
MakeTempFilename ( szFileSource ) ;
if ( ! CopyItem ( centry - > DiskPath , szFileSource ) )
return Error ( " AE2S: Cannot copy item " ) ;
// open group and resort
C4Group SortGrp ;
if ( ! SortGrp . Open ( szFileSource ) )
return Error ( " AE2S: Cannot open group " ) ;
if ( ! SortGrp . SortByList ( C4Group_SortList , centry - > FileName ) )
return Error ( " AE2S: Cannot resort group " ) ;
fTempFile = true ;
// close group (won't be saved if the sort didn't change)
SortGrp . Close ( ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// Append disk source to target file
if ( ! hSource . Open ( szFileSource , ! ! centry - > ChildGroup ) )
return Error ( " AE2S: Cannot open on-disk file " ) ;
for ( csize = centry - > Size ; csize > 0 ; csize - - )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! hSource . Read ( & fbuf , 1 ) )
{ hSource . Close ( ) ; return Error ( " AE2S: Cannot read on-disk file " ) ; }
if ( ! hTarget . Write ( & fbuf , 1 ) )
{ hSource . Close ( ) ; return Error ( " AE2S: Cannot write to target file " ) ; }
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
hSource . Close ( ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
// Erase temp file
if ( fTempFile )
EraseItem ( szFileSource ) ;
// Erase disk source if requested
if ( centry - > DeleteOnDisk )
EraseItem ( centry - > DiskPath ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
break ;
}
2009-05-08 13:28:41 +00:00
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_InMemory : // Copy from mem to std file
2010-03-28 18:58:01 +00:00
if ( ! centry - > bpMemBuf ) return Error ( " AE2S: no buffer " ) ;
if ( ! hTarget . Write ( centry - > bpMemBuf , centry - > Size ) ) return Error ( " AE2S: writing error " ) ;
break ;
2009-05-08 13:28:41 +00:00
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_Deleted : // Don't save
2010-03-28 18:58:01 +00:00
break ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
default : // Unknown file status
return Error ( " AE2S: Unknown file status " ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2012-11-17 18:44:04 +00:00
void C4Group : : ResetSearch ( bool reload_contents )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
switch ( Status )
2010-03-28 18:58:01 +00:00
{
case GRPF_Folder :
SearchPtr = NULL ;
2012-11-17 18:44:04 +00:00
FolderSearch . Reset ( FileName , reload_contents ) ;
2010-03-28 18:58:01 +00:00
if ( * FolderSearch )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
FolderSearchEntry . Set ( FolderSearch , FileName ) ;
SearchPtr = & FolderSearchEntry ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
break ;
case GRPF_File :
SearchPtr = FirstEntry ;
break ;
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4GroupEntry * C4Group : : GetNextFolderEntry ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( * + + FolderSearch )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
FolderSearchEntry . Set ( FolderSearch , FileName ) ;
return & FolderSearchEntry ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return NULL ;
2010-03-27 16:05:02 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4GroupEntry * C4Group : : SearchNextEntry ( const char * szName )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Wildcard "*.*" is expected to find all files: substitute correct wildcard "*"
if ( SEqual ( szName , " *.* " ) )
szName = " * " ;
// Search by group type
2010-03-27 16:05:02 +00:00
C4GroupEntry * pEntry ;
switch ( Status )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2010-03-28 18:58:01 +00:00
case GRPF_File :
for ( pEntry = SearchPtr ; pEntry ; pEntry = pEntry - > Next )
2015-02-13 17:49:23 +00:00
if ( pEntry - > Status ! = C4GroupEntry : : C4GRES_Deleted )
2010-03-27 16:05:02 +00:00
if ( WildcardMatch ( szName , pEntry - > FileName ) )
2010-03-28 18:58:01 +00:00
{
SearchPtr = pEntry - > Next ;
return pEntry ;
}
break ;
2009-05-08 13:28:41 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2010-03-28 18:58:01 +00:00
case GRPF_Folder :
for ( pEntry = SearchPtr ; pEntry ; pEntry = GetNextFolderEntry ( ) )
if ( WildcardMatch ( szName , pEntry - > FileName ) )
if ( ! C4Group_TestIgnore ( pEntry - > FileName ) )
{
LastFolderSearchEntry = ( * pEntry ) ;
pEntry = & LastFolderSearchEntry ;
SearchPtr = GetNextFolderEntry ( ) ;
return pEntry ;
}
break ;
2009-05-08 13:28:41 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// No entry found: reset search pointer
2010-03-27 16:05:02 +00:00
SearchPtr = NULL ;
return NULL ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : SetFilePtr ( int iOffset )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( Status = = GRPF_Folder )
2009-05-08 13:28:41 +00:00
return Error ( " SetFilePtr not implemented for Folders " ) ;
// ensure mother is at correct pos
if ( Mother ) Mother - > EnsureChildFilePtr ( this ) ;
2010-03-27 16:05:02 +00:00
// Rewind if necessary
if ( FilePtr > iOffset )
if ( ! RewindFilePtr ( ) ) return false ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Advance to target pointer
if ( FilePtr < iOffset )
if ( ! AdvanceFilePtr ( iOffset - FilePtr ) ) return false ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Advance ( int iOffset )
2010-03-28 18:58:01 +00:00
{
2015-02-07 12:59:45 +00:00
assert ( iOffset > = 0 ) ;
// cached advance
if ( pInMemEntry )
{
if ( iInMemEntrySize < size_t ( iOffset ) ) return false ;
iInMemEntrySize - = iOffset ;
pInMemEntry + = iOffset ;
return true ;
}
// uncached advance
2009-05-08 13:28:41 +00:00
if ( Status = = GRPF_Folder ) return ! ! StdFile . Advance ( iOffset ) ;
// FIXME: reading the file one byte at a time sounds just slow.
BYTE buf ;
for ( ; iOffset > 0 ; iOffset - - )
2009-08-15 18:50:32 +00:00
if ( ! Read ( & buf , 1 ) ) return false ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Read ( void * pBuffer , size_t iSize )
2010-03-28 18:58:01 +00:00
{
2015-02-07 12:59:45 +00:00
// Access cached entry from memory?
if ( pInMemEntry )
{
if ( iInMemEntrySize < iSize ) return Error ( " ReadCached: " ) ;
memcpy ( pBuffer , pInMemEntry , iSize ) ;
iInMemEntrySize - = iSize ;
pInMemEntry + = iSize ;
return true ;
}
// Not cached. Read from file.
2010-03-27 16:05:02 +00:00
switch ( Status )
2010-03-28 18:58:01 +00:00
{
case GRPF_File :
// Child group: read from mother group
if ( Mother )
2010-03-27 16:05:02 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! Mother - > Read ( pBuffer , iSize ) )
{ RewindFilePtr ( ) ; return Error ( " Read: " ) ; }
2010-03-27 16:05:02 +00:00
}
2010-03-28 18:58:01 +00:00
// Regular group: read from standard file
else
{
if ( ! StdFile . Read ( pBuffer , iSize ) )
{ RewindFilePtr ( ) ; return Error ( " Read: " ) ; }
}
FilePtr + = iSize ;
break ;
case GRPF_Folder :
if ( ! StdFile . Read ( pBuffer , iSize ) ) return Error ( " Read: Error reading from folder contents " ) ;
break ;
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2010-03-28 18:58:01 +00:00
}
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : AdvanceFilePtr ( int iOffset , C4Group * pByChild )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
// Child group file: pass command to mother
if ( ( Status = = GRPF_File ) & & Mother )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Ensure mother file ptr for it may have been moved by foreign access to mother
if ( ! Mother - > EnsureChildFilePtr ( this ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( ! Mother - > AdvanceFilePtr ( iOffset , this ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2010-03-27 16:05:02 +00:00
// Regular group
else if ( Status = = GRPF_File )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( ! StdFile . Advance ( iOffset ) )
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Open folder
else
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( ! StdFile . Advance ( iOffset ) )
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Advanced
2010-03-27 16:05:02 +00:00
FilePtr + = iOffset ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : RewindFilePtr ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
# ifdef _DEBUG
if ( szCurrAccessedEntry & & ! iC4GroupRewindFilePtrNoWarn )
2010-03-28 18:58:01 +00:00
{
2015-02-07 12:59:45 +00:00
LogF ( " C4Group::RewindFilePtr() for %s (%s) after %s " , szCurrAccessedEntry ? szCurrAccessedEntry : " ??? " , FileName , sPrevAccessedEntry . getLength ( ) ? sPrevAccessedEntry . getData ( ) : " ??? " ) ;
2009-05-08 13:28:41 +00:00
szCurrAccessedEntry = NULL ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
# endif
2010-03-27 16:05:02 +00:00
// Child group file: pass command to mother
if ( ( Status = = GRPF_File ) & & Mother )
2010-03-28 18:58:01 +00:00
{
2011-03-28 17:31:28 +00:00
if ( ! Mother - > SetFilePtr2Entry ( FileName , true ) ) // Set to group file start
2009-08-15 18:50:32 +00:00
return false ;
2010-03-27 16:05:02 +00:00
if ( ! Mother - > AdvanceFilePtr ( EntryOffset , this ) ) // Advance data offset
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2010-03-27 16:05:02 +00:00
// Regular group or open folder: rewind standard file
else
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( ! StdFile . Rewind ( ) ) // Set to group file start
2009-08-15 18:50:32 +00:00
return false ;
2010-03-27 16:05:02 +00:00
if ( ! StdFile . Advance ( EntryOffset ) ) // Advance data offset
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
FilePtr = 0 ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Merge ( const char * szFolders )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fMove = true ;
if ( StdOutput ) printf ( " %s... \n " , fMove ? " Moving " : " Adding " ) ;
// Add files & directories
char szFileName [ _MAX_FNAME + 1 ] ;
int iFileCount = 0 ;
DirectoryIterator i ;
// Process segmented path & search wildcards
char cSeparator = ( SCharCount ( ' ; ' , szFolders ) ? ' ; ' : ' | ' ) ;
for ( int cseg = 0 ; SCopySegment ( szFolders , cseg , szFileName , cSeparator ) ; cseg + + )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
i . Reset ( szFileName ) ;
2010-03-28 18:58:01 +00:00
while ( * i )
{
2009-05-08 13:28:41 +00:00
// File count
2010-03-28 18:58:01 +00:00
iFileCount + + ;
2009-05-08 13:28:41 +00:00
// Process output & callback
if ( StdOutput ) printf ( " %s \n " , GetFilename ( * i ) ) ;
2010-03-28 18:58:01 +00:00
if ( fnProcessCallback )
fnProcessCallback ( GetFilename ( * i ) , 0 ) ; // cbytes/tbytes
2009-05-08 13:28:41 +00:00
// AddEntryOnDisk
AddEntryOnDisk ( * i , NULL , fMove ) ;
+ + i ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if ( StdOutput ) printf ( " %d file(s) %s. \n " , iFileCount , fMove ? " moved " : " added " ) ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : AddEntryOnDisk ( const char * szFilename ,
2010-03-28 18:58:01 +00:00
const char * szAddAs ,
bool fMove )
{
2009-05-08 13:28:41 +00:00
// Do not process yourself
2009-08-15 18:50:32 +00:00
if ( ItemIdentical ( szFilename , FileName ) ) return true ;
2009-05-08 13:28:41 +00:00
// File is a directory: copy to temp path, pack, and add packed file
if ( DirectoryExists ( szFilename ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Ignore
2009-08-15 18:50:32 +00:00
if ( C4Group_TestIgnore ( szFilename ) ) return true ;
2009-05-08 13:28:41 +00:00
// Temp filename
char szTempFilename [ _MAX_PATH + 1 ] ;
if ( C4Group_TempPath [ 0 ] ) { SCopy ( C4Group_TempPath , szTempFilename , _MAX_PATH ) ; SAppend ( GetFilename ( szFilename ) , szTempFilename , _MAX_PATH ) ; }
else SCopy ( szFilename , szTempFilename , _MAX_PATH ) ;
MakeTempFilename ( szTempFilename ) ;
// Copy or move item to temp file (moved items might be killed if later process fails)
if ( fMove ) { if ( ! MoveItem ( szFilename , szTempFilename ) ) return Error ( " AddEntryOnDisk: Move failure " ) ; }
else { if ( ! CopyItem ( szFilename , szTempFilename ) ) return Error ( " AddEntryOnDisk: Copy failure " ) ; }
// Pack temp file
if ( ! C4Group_PackDirectory ( szTempFilename ) ) return Error ( " AddEntryOnDisk: Pack directory failure " ) ;
// Add temp file
if ( ! szAddAs ) szAddAs = GetFilename ( szFilename ) ;
szFilename = szTempFilename ;
2009-08-15 18:50:32 +00:00
fMove = true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Determine size
bool fIsGroup = ! ! C4Group_IsGroup ( szFilename ) ;
int iSize = fIsGroup ? UncompressedFileSize ( szFilename ) : FileSize ( szFilename ) ;
// Determine executable bit (linux only)
bool fExecutable = false ;
# ifdef __linux__
fExecutable = ( access ( szFilename , X_OK ) = = 0 ) ;
# endif
// AddEntry
2015-02-13 17:49:23 +00:00
return AddEntry ( C4GroupEntry : : C4GRES_OnDisk ,
2010-03-28 18:58:01 +00:00
fIsGroup ,
szFilename ,
iSize ,
szAddAs ,
NULL ,
fMove ,
false ,
fExecutable ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Add ( const char * szFile , const char * szAddAs )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fMove = false ;
2010-03-27 16:05:02 +00:00
if ( StdOutput ) printf ( " %s %s as %s... \n " , fMove ? " Moving " : " Adding " , GetFilename ( szFile ) , szAddAs ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return AddEntryOnDisk ( szFile , szAddAs , fMove ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Move ( const char * szFile , const char * szAddAs )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fMove = true ;
2010-03-27 16:05:02 +00:00
if ( StdOutput ) printf ( " %s %s as %s... \n " , fMove ? " Moving " : " Adding " , GetFilename ( szFile ) , szAddAs ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return AddEntryOnDisk ( szFile , szAddAs , fMove ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Delete ( const char * szFiles , bool fRecursive )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
int fcount = 0 ;
C4GroupEntry * tentry ;
2009-05-08 13:28:41 +00:00
// Segmented file specs
if ( SCharCount ( ' ; ' , szFiles ) | | SCharCount ( ' | ' , szFiles ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
char cSeparator = ( SCharCount ( ' ; ' , szFiles ) ? ' ; ' : ' | ' ) ;
bool success = true ;
char filespec [ _MAX_FNAME + 1 ] ;
for ( int cseg = 0 ; SCopySegment ( szFiles , cseg , filespec , cSeparator , _MAX_FNAME ) ; cseg + + )
if ( ! Delete ( filespec , fRecursive ) )
success = false ;
return success ; // Would be nicer to return the file count and add up all counts from recursive actions...
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Delete all matching Entries
ResetSearch ( ) ;
while ( ( tentry = SearchNextEntry ( szFiles ) ) )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
// StdOutput
if ( StdOutput ) printf ( " %s \n " , tentry - > FileName ) ;
2009-05-08 13:28:41 +00:00
if ( ! DeleteEntry ( tentry - > FileName ) )
return Error ( " Delete: Could not delete entry " ) ;
fcount + + ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Recursive: process sub groups
if ( fRecursive )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Group hChild ;
ResetSearch ( ) ;
while ( ( tentry = SearchNextEntry ( " * " ) ) )
if ( tentry - > ChildGroup )
if ( hChild . OpenAsChild ( this , tentry - > FileName ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
hChild . SetStdOutput ( StdOutput ) ;
hChild . Delete ( szFiles , fRecursive ) ;
hChild . Close ( ) ;
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// StdOutput
if ( StdOutput )
2009-05-08 13:28:41 +00:00
printf ( " %d file(s) deleted. \n " , fcount ) ;
2010-03-27 16:05:02 +00:00
return true ; // Would be nicer to return the file count and add up all counts from recursive actions...
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : DeleteEntry ( const char * szFilename , bool fRecycle )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
switch ( Status )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
case GRPF_File :
// Get entry
C4GroupEntry * pEntry ;
if ( ! ( pEntry = GetEntry ( szFilename ) ) ) return false ;
// Delete moved source files
2015-02-13 17:49:23 +00:00
if ( pEntry - > Status = = C4GroupEntry : : C4GRES_OnDisk )
2010-03-28 18:58:01 +00:00
if ( pEntry - > DeleteOnDisk )
{
EraseItem ( pEntry - > DiskPath ) ;
}
// (moved buffers are deleted by ~C4GroupEntry)
// Delete status and update virtual file count
2015-02-13 17:49:23 +00:00
pEntry - > Status = C4GroupEntry : : C4GRES_Deleted ;
2010-03-28 18:58:01 +00:00
Head . Entries - - ;
break ;
case GRPF_Folder :
StdFile . Close ( ) ;
char szPath [ _MAX_FNAME + 1 ] ;
sprintf ( szPath , " %s%c%s " , FileName , DirectorySeparator , szFilename ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
if ( fRecycle )
{
if ( ! EraseItemSafe ( szPath ) ) return false ;
2010-03-27 16:05:02 +00:00
}
2010-03-28 18:58:01 +00:00
else
{
if ( ! EraseItem ( szPath ) ) return false ;
}
break ;
2012-11-17 18:44:04 +00:00
// refresh file list
ResetSearch ( true ) ;
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2010-03-27 16:05:02 +00:00
}
2010-03-28 18:58:01 +00:00
return true ;
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Rename ( const char * szFile , const char * szNewName )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( StdOutput ) printf ( " Renaming %s to %s... \n " , szFile , szNewName ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
switch ( Status )
2010-03-28 18:58:01 +00:00
{
case GRPF_File :
// Get entry
C4GroupEntry * pEntry ;
if ( ! ( pEntry = GetEntry ( szFile ) ) ) return Error ( " Rename: File not found " ) ;
// Check double name
if ( GetEntry ( szNewName ) & & ! SEqualNoCase ( szNewName , szFile ) ) return Error ( " Rename: File exists already " ) ;
// Rename
SCopy ( szNewName , pEntry - > FileName , _MAX_FNAME ) ;
Modified = true ;
break ;
case GRPF_Folder :
StdFile . Close ( ) ;
char path [ _MAX_FNAME + 1 ] ; SCopy ( FileName , path , _MAX_PATH - 1 ) ;
AppendBackslash ( path ) ; SAppend ( szFile , path , _MAX_PATH ) ;
char path2 [ _MAX_FNAME + 1 ] ; SCopy ( FileName , path2 , _MAX_PATH - 1 ) ;
AppendBackslash ( path2 ) ; SAppend ( szNewName , path2 , _MAX_PATH ) ;
if ( ! RenameFile ( path , path2 ) ) return Error ( " Rename: Failure " ) ;
2012-11-17 18:44:04 +00:00
// refresh file list
ResetSearch ( true ) ;
2010-03-28 18:58:01 +00:00
break ;
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group_IsExcluded ( const char * szFile , const char * szExcludeList )
{
// No file or no exclude list
2009-08-15 18:50:32 +00:00
if ( ! szFile | | ! szFile [ 0 ] | | ! szExcludeList | | ! szExcludeList [ 0 ] ) return false ;
2009-05-08 13:28:41 +00:00
// Process segmented exclude list
char cSeparator = ( SCharCount ( ' ; ' , szExcludeList ) ? ' ; ' : ' | ' ) ;
char szSegment [ _MAX_PATH + 1 ] ;
2010-03-27 16:05:02 +00:00
for ( int i = 0 ; SCopySegment ( szExcludeList , i , szSegment , cSeparator ) ; i + + )
2009-05-08 13:28:41 +00:00
if ( WildcardMatch ( szSegment , GetFilename ( szFile ) ) )
2009-08-15 18:50:32 +00:00
return true ;
2009-05-08 13:28:41 +00:00
// No match
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
}
bool C4Group : : Extract ( const char * szFiles , const char * szExtractTo , const char * szExclude )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// StdOutput
if ( StdOutput )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
printf ( " Extracting " ) ;
if ( szExtractTo ) printf ( " to %s " , szExtractTo ) ;
printf ( " ... \n " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
int fcount = 0 ;
2009-05-08 13:28:41 +00:00
int cbytes , tbytes ;
2010-03-27 16:05:02 +00:00
C4GroupEntry * tentry ;
2009-05-08 13:28:41 +00:00
cbytes = 0 ; tbytes = EntrySize ( ) ;
// Process segmented list
char cSeparator = ( SCharCount ( ' ; ' , szFiles ) ? ' ; ' : ' | ' ) ;
char szFileName [ _MAX_PATH + 1 ] ;
2010-03-27 16:05:02 +00:00
for ( int cseg = 0 ; SCopySegment ( szFiles , cseg , szFileName , cSeparator ) ; cseg + + )
2009-05-08 13:28:41 +00:00
{
// Search all entries
ResetSearch ( ) ;
2010-01-25 04:00:59 +00:00
while ( ( tentry = SearchNextEntry ( szFileName ) ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// skip?
if ( C4Group_IsExcluded ( tentry - > FileName , szExclude ) ) continue ;
// Process data & output
if ( StdOutput ) printf ( " %s \n " , tentry - > FileName ) ;
cbytes + = tentry - > Size ;
if ( fnProcessCallback )
fnProcessCallback ( tentry - > FileName , 100 * cbytes / Max ( tbytes , 1 ) ) ;
// Extract
if ( ! ExtractEntry ( tentry - > FileName , szExtractTo ) )
return Error ( " Extract: Could not extract entry " ) ;
fcount + + ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
2010-03-27 16:05:02 +00:00
if ( StdOutput ) printf ( " %d file(s) extracted. \n " , fcount ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : ExtractEntry ( const char * szFilename , const char * szExtractTo )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
CStdFile tfile ;
CStdFile hDummy ;
char szTempFName [ _MAX_FNAME + 1 ] , szTargetFName [ _MAX_FNAME + 1 ] ;
// Target file name
if ( szExtractTo )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
SCopy ( szExtractTo , szTargetFName , _MAX_FNAME - 1 ) ;
if ( DirectoryExists ( szTargetFName ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
AppendBackslash ( szTargetFName ) ;
SAppend ( szFilename , szTargetFName , _MAX_FNAME ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
SCopy ( szFilename , szTargetFName , _MAX_FNAME ) ;
// Extract
switch ( Status )
2010-03-28 18:58:01 +00:00
{
case GRPF_File : // Copy entry to target
// Get entry
C4GroupEntry * pEntry ;
2012-11-19 21:45:19 +00:00
if ( ! ( pEntry = GetEntry ( szFilename ) ) ) return Error ( " Extract: Entry not found " ) ;
2010-03-28 18:58:01 +00:00
// Create dummy file to reserve target file name
2011-06-19 14:35:06 +00:00
hDummy . Create ( szTargetFName , false ) ;
hDummy . Write ( " Dummy " , 5 ) ;
hDummy . Close ( ) ;
2010-03-28 18:58:01 +00:00
// Make temp target file name
SCopy ( szTargetFName , szTempFName , _MAX_FNAME ) ;
MakeTempFilename ( szTempFName ) ;
// Create temp target file
if ( ! tfile . Create ( szTempFName , ! ! pEntry - > ChildGroup , ! ! pEntry - > Executable ) )
return Error ( " Extract: Cannot create target file " ) ;
// Write entry file to temp target file
if ( ! AppendEntry2StdFile ( pEntry , tfile ) )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// Failure: close and erase temp target file
2009-05-08 13:28:41 +00:00
tfile . Close ( ) ;
2010-03-28 18:58:01 +00:00
EraseItem ( szTempFName ) ;
// Also erase reservation target file
EraseItem ( szTargetFName ) ;
// Failure
return false ;
}
// Close target file
tfile . Close ( ) ;
// Make temp file to original file
if ( ! EraseItem ( szTargetFName ) )
return Error ( " Extract: Cannot erase temporary file " ) ;
if ( ! RenameItem ( szTempFName , szTargetFName ) )
return Error ( " Extract: Cannot rename temporary file " ) ;
break ;
case GRPF_Folder : // Copy item from folder to target
char szPath [ _MAX_FNAME + 1 ] ;
sprintf ( szPath , " %s%c%s " , FileName , DirectorySeparator , szFilename ) ;
if ( ! CopyItem ( szPath , szTargetFName ) )
return Error ( " ExtractEntry: Cannot copy item " ) ;
break ;
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return true ;
}
2009-05-08 13:28:41 +00:00
bool C4Group : : OpenAsChild ( C4Group * pMother ,
2010-03-28 18:58:01 +00:00
const char * szEntryName , bool fExclusive , bool fCreate )
{
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( ! pMother ) return Error ( " OpenAsChild: No mother specified " ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( SCharCount ( ' * ' , szEntryName ) ) return Error ( " OpenAsChild: No wildcards allowed " ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Open nested child group check: If szEntryName is a reference to
// a nested group, open the first mother (in specified mode), then open the child
// in exclusive mode
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( SCharCount ( DirectorySeparator , szEntryName ) )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
char mothername [ _MAX_FNAME + 1 ] ;
SCopyUntil ( szEntryName , mothername , DirectorySeparator , _MAX_FNAME ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
C4Group * pMother2 ;
pMother2 = new C4Group ;
pMother2 - > SetStdOutput ( StdOutput ) ;
if ( ! pMother2 - > OpenAsChild ( pMother , mothername , fExclusive ) )
2009-05-08 13:28:41 +00:00
{
delete pMother2 ;
return Error ( " OpenAsChild: Cannot open mother " ) ;
}
2010-03-27 16:05:02 +00:00
return OpenAsChild ( pMother2 , szEntryName + SLen ( mothername ) + 1 , true ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Init
2009-05-08 13:28:41 +00:00
Init ( ) ;
2010-03-27 16:05:02 +00:00
SCopy ( szEntryName , FileName , _MAX_FNAME ) ;
Mother = pMother ;
ExclusiveChild = fExclusive ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Folder: Simply set status and return
char path [ _MAX_FNAME + 1 ] ;
2009-05-08 13:28:41 +00:00
SCopy ( GetFullName ( ) . getData ( ) , path , _MAX_FNAME ) ;
2010-03-27 16:05:02 +00:00
if ( DirectoryExists ( path ) )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
SCopy ( path , FileName , _MAX_FNAME ) ;
Status = GRPF_Folder ;
ResetSearch ( ) ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Get original entry name
C4GroupEntry * centry ;
2010-01-25 04:00:59 +00:00
if ( ( centry = Mother - > GetEntry ( FileName ) ) )
2009-05-08 13:28:41 +00:00
SCopy ( centry - > FileName , FileName , _MAX_PATH ) ;
2010-03-27 16:05:02 +00:00
// Access entry in mother group
2009-05-08 13:28:41 +00:00
size_t iSize ;
2011-03-05 01:45:27 +00:00
if ( ( ! Mother - > AccessEntry ( FileName , & iSize , NULL , true ) ) )
2010-03-28 18:58:01 +00:00
{
if ( ! fCreate )
2009-05-08 13:28:41 +00:00
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " OpenAsChild: Entry not in mother group " ) ; }
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Create - will be added to mother in Close()
2009-08-15 18:50:32 +00:00
Status = GRPF_File ; Modified = true ;
return true ;
2010-01-25 04:00:59 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Child Group?
2010-03-28 18:58:01 +00:00
if ( centry & & ! centry - > ChildGroup )
2009-05-08 13:28:41 +00:00
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " OpenAsChild: Is not a child group " ) ; }
2010-03-27 16:05:02 +00:00
// Read header
2009-05-08 13:28:41 +00:00
// Do not do size checks for packed subgroups of unpacked groups (there will be no entry),
// because that would be the PACKED size which can actually be smaller than sizeof(C4GroupHeader)!
if ( iSize < sizeof ( C4GroupHeader ) & & centry )
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " OpenAsChild: Entry too small " ) ; }
2010-03-27 16:05:02 +00:00
if ( ! Mother - > Read ( & Head , sizeof ( C4GroupHeader ) ) )
2009-05-08 13:28:41 +00:00
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " OpenAsChild: Entry reading error " ) ; }
MemScramble ( ( BYTE * ) & Head , sizeof ( C4GroupHeader ) ) ;
2010-03-27 16:05:02 +00:00
EntryOffset + = sizeof ( C4GroupHeader ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Check Header
if ( ! SEqual ( Head . id , C4GroupFileID )
2010-03-28 18:58:01 +00:00
| | ( Head . Ver1 ! = C4GroupFileVer1 ) | | ( Head . Ver2 > C4GroupFileVer2 ) )
2009-05-08 13:28:41 +00:00
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " OpenAsChild: Invalid Header " ) ; }
2010-03-27 16:05:02 +00:00
// Read Entries
C4GroupEntryCore corebuf ;
2009-05-08 13:28:41 +00:00
int file_entries = Head . Entries ;
Head . Entries = 0 ; // Reset, will be recounted by AddEntry
2010-03-27 16:05:02 +00:00
for ( int cnt = 0 ; cnt < file_entries ; cnt + + )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( ! Mother - > Read ( & corebuf , sizeof ( C4GroupEntryCore ) ) )
2009-05-08 13:28:41 +00:00
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " OpenAsChild: Entry reading error " ) ; }
2010-03-27 16:05:02 +00:00
EntryOffset + = sizeof ( C4GroupEntryCore ) ;
2015-02-13 17:49:23 +00:00
if ( ! AddEntry ( C4GroupEntry : : C4GRES_InGroup , ! ! corebuf . ChildGroup ,
2011-05-01 11:37:36 +00:00
corebuf . FileName , corebuf . Size ,
2010-03-28 18:58:01 +00:00
NULL , NULL , false , false ,
! ! corebuf . Executable ) )
2009-05-08 13:28:41 +00:00
{ CloseExclusiveMother ( ) ; Clear ( ) ; return Error ( " OpenAsChild: Insufficient memory " ) ; }
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
ResetSearch ( ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// File
Status = GRPF_File ;
2009-05-08 13:28:41 +00:00
// save position in mother group
if ( centry ) MotherOffset = centry - > Offset ;
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : AccessEntry ( const char * szWildCard ,
2010-03-28 18:58:01 +00:00
size_t * iSize , char * sFileName ,
2011-03-05 01:45:27 +00:00
bool NeedsToBeAGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
# ifdef C4GROUP_DUMP_ACCESS
LogF ( " Group access in %s: %s " , GetFullName ( ) . getData ( ) , szWildCard ) ;
# endif
2011-03-05 01:45:27 +00:00
StdStrBuf fname ;
if ( ! FindEntry ( szWildCard , & fname , & iCurrFileSize ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
# ifdef _DEBUG
2011-03-05 01:45:27 +00:00
szCurrAccessedEntry = fname . getMData ( ) ;
2009-05-08 13:28:41 +00:00
# endif
2011-03-28 17:31:28 +00:00
bool fResult = SetFilePtr2Entry ( fname . getData ( ) , NeedsToBeAGroup ) ;
2009-05-08 13:28:41 +00:00
# ifdef _DEBUG
2015-02-07 12:59:45 +00:00
sPrevAccessedEntry . Copy ( szCurrAccessedEntry ) ;
2009-05-08 13:28:41 +00:00
szCurrAccessedEntry = NULL ;
# endif
2009-08-15 18:50:32 +00:00
if ( ! fResult ) return false ;
2011-03-05 01:45:27 +00:00
if ( sFileName ) SCopy ( fname . getData ( ) , sFileName ) ;
2009-05-08 13:28:41 +00:00
if ( iSize ) * iSize = iCurrFileSize ;
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : AccessNextEntry ( const char * szWildCard ,
2010-03-28 18:58:01 +00:00
size_t * iSize , char * sFileName ,
2011-05-22 22:02:10 +00:00
bool fStartAtFilename )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
char fname [ _MAX_FNAME + 1 ] ;
2011-05-22 22:02:10 +00:00
if ( ! FindNextEntry ( szWildCard , fname , & iCurrFileSize , fStartAtFilename ) ) return false ;
2009-05-08 13:28:41 +00:00
# ifdef _DEBUG
szCurrAccessedEntry = fname ;
# endif
2010-03-27 16:05:02 +00:00
bool fResult = SetFilePtr2Entry ( fname ) ;
2009-05-08 13:28:41 +00:00
# ifdef _DEBUG
szCurrAccessedEntry = NULL ;
# endif
2009-08-15 18:50:32 +00:00
if ( ! fResult ) return false ;
2010-03-27 16:05:02 +00:00
if ( sFileName ) SCopy ( fname , sFileName ) ;
2009-05-08 13:28:41 +00:00
if ( iSize ) * iSize = iCurrFileSize ;
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2011-03-28 17:31:28 +00:00
bool C4Group : : SetFilePtr2Entry ( const char * szName , bool NeedsToBeAGroup )
2010-03-28 18:58:01 +00:00
{
2015-02-07 12:59:45 +00:00
C4GroupEntry * centry = GetEntry ( szName ) ;
// Read cached entries directly from memory (except child groups. that is not supported.)
if ( centry & & centry - > bpMemBuf & & ! NeedsToBeAGroup )
{
pInMemEntry = centry - > bpMemBuf ;
iInMemEntrySize = centry - > Size ;
return true ;
}
else
{
pInMemEntry = NULL ;
}
// Not cached. Access from disk.
2010-03-27 16:05:02 +00:00
switch ( Status )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case GRPF_File :
2015-02-13 17:49:23 +00:00
if ( ( ! centry ) | | ( centry - > Status ! = C4GroupEntry : : C4GRES_InGroup ) ) return false ;
2010-03-28 18:58:01 +00:00
return SetFilePtr ( centry - > Offset ) ;
2009-05-08 13:28:41 +00:00
2015-08-31 18:52:39 +00:00
case GRPF_Folder : {
2010-03-28 18:58:01 +00:00
StdFile . Close ( ) ;
char path [ _MAX_FNAME + 1 ] ; SCopy ( FileName , path , _MAX_FNAME ) ;
AppendBackslash ( path ) ; SAppend ( szName , path ) ;
bool fSuccess = StdFile . Open ( path , NeedsToBeAGroup ) ;
return fSuccess ;
2015-08-31 18:52:39 +00:00
}
2009-05-08 13:28:41 +00:00
2015-08-31 18:52:39 +00:00
default : break ; // InGrp & Deleted ignored
2010-03-27 16:05:02 +00:00
}
2010-03-28 18:58:01 +00:00
return false ;
}
2009-05-08 13:28:41 +00:00
2011-03-05 01:45:27 +00:00
bool C4Group : : FindEntry ( const char * szWildCard , StdStrBuf * sFileName , size_t * iSize )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
ResetSearch ( ) ;
2011-03-05 01:45:27 +00:00
return FindNextEntry ( szWildCard , sFileName , iSize ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : FindNextEntry ( const char * szWildCard ,
2011-03-05 01:45:27 +00:00
StdStrBuf * sFileName ,
2010-03-28 18:58:01 +00:00
size_t * iSize ,
bool fStartAtFilename )
{
2010-03-27 16:05:02 +00:00
C4GroupEntry * centry ;
if ( ! szWildCard ) return false ;
2009-05-08 13:28:41 +00:00
// Reset search to specified position
2011-03-05 01:45:27 +00:00
if ( fStartAtFilename ) FindEntry ( sFileName - > getData ( ) ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( ! ( centry = SearchNextEntry ( szWildCard ) ) ) return false ;
2011-03-05 01:45:27 +00:00
if ( sFileName ) sFileName - > Copy ( centry - > FileName ) ;
2010-03-27 16:05:02 +00:00
if ( iSize ) * iSize = centry - > Size ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2011-05-01 11:37:36 +00:00
bool C4Group : : Add ( const char * szName , void * pBuffer , int iSize , bool fChild , bool fHoldBuffer , bool fExecutable )
2010-03-28 18:58:01 +00:00
{
2015-02-13 17:49:23 +00:00
return AddEntry ( C4GroupEntry : : C4GRES_InMemory ,
2010-03-28 18:58:01 +00:00
fChild ,
szName ,
iSize ,
szName ,
( BYTE * ) pBuffer ,
false ,
fHoldBuffer ,
fExecutable ) ;
}
2009-05-08 13:28:41 +00:00
2011-05-01 11:37:36 +00:00
bool C4Group : : Add ( const char * szName , StdBuf & pBuffer , bool fChild , bool fHoldBuffer , bool fExecutable )
2010-03-28 18:58:01 +00:00
{
2015-02-13 17:49:23 +00:00
if ( ! AddEntry ( C4GroupEntry : : C4GRES_InMemory ,
2010-03-28 18:58:01 +00:00
fChild ,
szName ,
pBuffer . getSize ( ) ,
szName ,
2015-08-31 18:52:39 +00:00
( BYTE * ) pBuffer . getMData ( ) ,
2010-03-28 18:58:01 +00:00
false ,
fHoldBuffer ,
fExecutable ,
true ) ) return false ;
2009-05-08 13:28:41 +00:00
// Pointer is now owned and released by C4Group!
if ( fHoldBuffer ) pBuffer . GrabPointer ( ) ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2011-05-01 11:37:36 +00:00
bool C4Group : : Add ( const char * szName , StdStrBuf & pBuffer , bool fChild , bool fHoldBuffer , bool fExecutable )
2010-03-28 18:58:01 +00:00
{
2015-02-13 17:49:23 +00:00
if ( ! AddEntry ( C4GroupEntry : : C4GRES_InMemory ,
2010-03-28 18:58:01 +00:00
fChild ,
szName ,
pBuffer . getLength ( ) ,
szName ,
2015-08-31 18:52:39 +00:00
( BYTE * ) pBuffer . getMData ( ) ,
2010-03-28 18:58:01 +00:00
false ,
fHoldBuffer ,
fExecutable ,
true ) ) return false ;
2009-05-08 13:28:41 +00:00
// Pointer is now owned and released by C4Group!
if ( fHoldBuffer ) pBuffer . GrabPointer ( ) ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
const char * C4Group : : GetName ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
return FileName ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int C4Group : : EntryCount ( const char * szWildCard )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
int fcount ;
C4GroupEntry * tentry ;
// All files if no wildcard
if ( ! szWildCard ) szWildCard = " * " ;
// Match wildcard
ResetSearch ( ) ; fcount = 0 ;
while ( ( tentry = SearchNextEntry ( szWildCard ) ) ) fcount + + ;
return fcount ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-04-20 16:20:24 +00:00
size_t C4Group : : EntrySize ( const char * szWildCard )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
int fsize ;
C4GroupEntry * tentry ;
// All files if no wildcard
if ( ! szWildCard ) szWildCard = " * " ;
// Match wildcard
ResetSearch ( ) ; fsize = 0 ;
while ( ( tentry = SearchNextEntry ( szWildCard ) ) )
2009-05-08 13:28:41 +00:00
fsize + = tentry - > Size ;
2010-03-27 16:05:02 +00:00
return fsize ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
unsigned int C4Group : : EntryCRC32 ( const char * szWildCard )
2010-03-28 18:58:01 +00:00
{
if ( ! szWildCard ) szWildCard = " * " ;
2009-05-08 13:28:41 +00:00
// iterate thorugh child
C4GroupEntry * pEntry ; unsigned int iCRC = 0 ;
ResetSearch ( ) ;
2010-03-28 18:58:01 +00:00
while ( ( pEntry = SearchNextEntry ( szWildCard ) ) )
{
2012-02-07 23:12:44 +00:00
iCRC ^ = CalcCRC32 ( pEntry ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// return
return iCRC ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : LoadEntry ( const char * szEntryName , char * * lpbpBuf , size_t * ipSize , int iAppendZeros )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
size_t size ;
// Access entry, allocate buffer, read data
( * lpbpBuf ) = NULL ; if ( ipSize ) * ipSize = 0 ;
if ( ! AccessEntry ( szEntryName , & size ) ) return Error ( " LoadEntry: Not found " ) ;
2015-02-13 18:46:11 +00:00
* lpbpBuf = new char [ size + iAppendZeros ] ;
2010-03-27 16:05:02 +00:00
if ( ! Read ( * lpbpBuf , size ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
delete [ ] ( * lpbpBuf ) ; * lpbpBuf = NULL ;
return Error ( " LoadEntry: Reading error " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if ( ipSize ) * ipSize = size ;
if ( iAppendZeros )
ZeroMem ( ( * lpbpBuf ) + size , iAppendZeros ) ;
2010-03-27 16:05:02 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2011-03-05 01:44:26 +00:00
bool C4Group : : LoadEntry ( const char * szEntryName , StdBuf * Buf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
size_t size ;
// Access entry, allocate buffer, read data
if ( ! AccessEntry ( szEntryName , & size ) ) return Error ( " LoadEntry: Not found " ) ;
// Allocate memory
2011-03-05 01:44:26 +00:00
Buf - > New ( size ) ;
2009-05-08 13:28:41 +00:00
// Load data
2011-03-05 01:44:26 +00:00
if ( ! Read ( Buf - > getMData ( ) , size ) )
2010-03-28 18:58:01 +00:00
{
2011-03-05 01:44:26 +00:00
Buf - > Clear ( ) ;
2009-05-08 13:28:41 +00:00
return Error ( " LoadEntry: Reading error " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// ok
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2011-03-05 01:44:26 +00:00
bool C4Group : : LoadEntryString ( const char * szEntryName , StdStrBuf * Buf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
size_t size ;
// Access entry, allocate buffer, read data
if ( ! AccessEntry ( szEntryName , & size ) ) return Error ( " LoadEntry: Not found " ) ;
// Allocate memory
2011-03-05 01:44:26 +00:00
Buf - > SetLength ( size ) ;
2009-05-08 13:28:41 +00:00
// other parts crash when they get a zero length buffer, so fail here
if ( ! size ) return false ;
// Load data
2011-03-05 01:44:26 +00:00
if ( ! Read ( Buf - > getMData ( ) , size ) )
2010-03-28 18:58:01 +00:00
{
2011-03-05 01:44:26 +00:00
Buf - > Clear ( ) ;
2009-05-08 13:28:41 +00:00
return Error ( " LoadEntry: Reading error " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// ok
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int SortRank ( const char * szElement , const char * szSortList )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int cnt ;
char csegment [ _MAX_FNAME + 1 ] ;
for ( cnt = 0 ; SCopySegment ( szSortList , cnt , csegment , ' | ' , _MAX_FNAME ) ; cnt + + )
if ( WildcardMatch ( csegment , szElement ) )
return ( SCharCount ( ' | ' , szSortList ) + 1 ) - cnt ;
return 0 ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : Sort ( const char * szSortList )
2010-03-28 18:58:01 +00:00
{
2009-08-15 18:50:32 +00:00
bool fBubble ;
2009-05-08 13:28:41 +00:00
C4GroupEntry * centry , * prev , * next , * nextnext ;
2009-08-15 18:50:32 +00:00
if ( ! szSortList | | ! szSortList [ 0 ] ) return false ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
if ( StdOutput ) printf ( " Sorting... \n " ) ;
2009-05-08 13:28:41 +00:00
do
2010-03-28 18:58:01 +00:00
{
2009-08-15 18:50:32 +00:00
fBubble = false ;
2009-05-08 13:28:41 +00:00
for ( prev = NULL , centry = FirstEntry ; centry ; prev = centry , centry = next )
2010-01-25 04:00:59 +00:00
if ( ( next = centry - > Next ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// primary sort by file list
int iS1 = SortRank ( centry - > FileName , szSortList ) ;
int iS2 = SortRank ( next - > FileName , szSortList ) ;
if ( iS1 > iS2 ) continue ;
// secondary sort by filename
if ( iS1 = = iS2 )
if ( stricmp ( centry - > FileName , next - > FileName ) < = 0 ) continue ;
// wrong order: Swap!
nextnext = next - > Next ;
if ( prev ) prev - > Next = next ;
else FirstEntry = next ;
next - > Next = centry ;
centry - > Next = nextnext ;
next = nextnext ;
2009-08-15 18:50:32 +00:00
fBubble = true ;
Modified = true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
while ( fBubble ) ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Group * C4Group : : GetMother ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return Mother ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : CloseExclusiveMother ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( Mother & & ExclusiveChild )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Mother - > Close ( ) ;
delete Mother ;
Mother = NULL ;
2009-08-15 18:50:32 +00:00
return true ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return false ;
}
2009-05-08 13:28:41 +00:00
bool C4Group : : SortByList ( const char * * ppSortList , const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// No sort list specified
2009-08-15 18:50:32 +00:00
if ( ! ppSortList ) return false ;
2009-05-08 13:28:41 +00:00
// No group name specified, use own
if ( ! szFilename ) szFilename = FileName ;
szFilename = GetFilename ( szFilename ) ;
// Find matching filename entry in sort list
const char * * ppListEntry ;
for ( ppListEntry = ppSortList ; * ppListEntry ; ppListEntry + = 2 )
if ( WildcardMatch ( * ppListEntry , szFilename ) )
break ;
// Sort by sort list entry
if ( * ppListEntry & & * ( ppListEntry + 1 ) )
Sort ( * ( ppListEntry + 1 ) ) ;
// Success
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Group : : ProcessOut ( const char * szMessage , int iProcess )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( fnProcessCallback ) fnProcessCallback ( szMessage , iProcess ) ;
if ( C4Group_ProcessCallback ) C4Group_ProcessCallback ( szMessage , iProcess ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : EnsureChildFilePtr ( C4Group * pChild )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// group file
2010-03-28 18:58:01 +00:00
if ( Status = = GRPF_File )
2009-05-08 13:28:41 +00:00
{
// check if FilePtr has to be moved
2010-03-28 18:58:01 +00:00
if ( FilePtr ! = pChild - > MotherOffset + pChild - > EntryOffset + pChild - > FilePtr )
2009-05-08 13:28:41 +00:00
// move it to the position the child thinks it is
2010-03-28 18:58:01 +00:00
if ( ! SetFilePtr ( pChild - > MotherOffset + pChild - > EntryOffset + pChild - > FilePtr ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
// ok
2009-08-15 18:50:32 +00:00
return true ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// Open standard file is not the child file ...or StdFile ptr does not match pChild->FilePtr
2009-05-08 13:28:41 +00:00
char szChildPath [ _MAX_PATH + 1 ] ; sprintf ( szChildPath , " %s%c%s " , FileName , DirectorySeparator , GetFilename ( pChild - > FileName ) ) ;
if ( ! ItemIdentical ( StdFile . Name , szChildPath ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Reopen correct child stdfile
2013-07-04 21:18:52 +00:00
if ( ! SetFilePtr2Entry ( GetFilename ( pChild - > FileName ) , true ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
// Advance to child's old file ptr
if ( ! AdvanceFilePtr ( pChild - > EntryOffset + pChild - > FilePtr ) )
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Looks okay
2009-08-15 18:50:32 +00:00
return true ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
StdStrBuf C4Group : : GetFullName ( ) const
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
char str [ _MAX_PATH + 1 ] ; * str = ' \0 ' ;
char sep [ ] = " / " ; sep [ 0 ] = DirectorySeparator ;
for ( const C4Group * pGroup = this ; pGroup ; pGroup = pGroup - > Mother )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( * str ) SInsert ( str , sep , 0 , _MAX_PATH ) ;
2009-09-23 02:07:19 +00:00
// Avoid double slash
2012-02-12 18:41:48 +00:00
if ( pGroup = = this | | SLen ( pGroup - > FileName ) > 1 | | pGroup - > FileName [ 0 ] ! = ' / ' )
2009-09-23 02:07:19 +00:00
SInsert ( str , pGroup - > FileName , 0 , _MAX_PATH ) ;
2009-05-08 13:28:41 +00:00
if ( pGroup - > Status = = GRPF_Folder ) break ; // Folder is assumed to have full path
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
StdStrBuf sResult ; sResult . Copy ( str ) ;
return sResult ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2012-02-07 23:12:44 +00:00
uint32_t C4Group : : CalcCRC32 ( C4GroupEntry * pEntry )
2010-03-28 18:58:01 +00:00
{
2012-02-07 23:12:44 +00:00
uint32_t CRC ;
2009-05-08 13:28:41 +00:00
// child group?
2015-02-13 17:49:23 +00:00
if ( pEntry - > ChildGroup | | ( pEntry - > Status = = C4GroupEntry : : C4GRES_OnDisk & & ( DirectoryExists ( pEntry - > DiskPath ) | | C4Group_IsGroup ( pEntry - > DiskPath ) ) ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// open
C4Group Child ;
2010-03-28 18:58:01 +00:00
switch ( pEntry - > Status )
{
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_InGroup :
2010-03-28 18:58:01 +00:00
if ( ! Child . OpenAsChild ( this , pEntry - > FileName ) )
2009-05-08 13:28:41 +00:00
return 0 ;
2010-03-28 18:58:01 +00:00
break ;
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_OnDisk :
2010-03-28 18:58:01 +00:00
if ( ! Child . Open ( pEntry - > DiskPath ) )
return 0 ;
break ;
default :
return 0 ;
2009-05-08 13:28:41 +00:00
}
// get checksum
2012-02-07 23:12:44 +00:00
CRC = Child . EntryCRC32 ( ) ;
2010-03-28 18:58:01 +00:00
}
else if ( ! pEntry - > Size )
2012-02-07 23:12:44 +00:00
CRC = 0 ;
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2012-02-07 23:12:44 +00:00
BYTE * pData = NULL ; bool fOwnData ; CStdFile f ;
// get data
switch ( pEntry - > Status )
2009-05-08 13:28:41 +00:00
{
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_InGroup :
2012-02-07 23:12:44 +00:00
// create buffer
pData = new BYTE [ pEntry - > Size ] ; fOwnData = true ;
// go to entry
if ( ! SetFilePtr2Entry ( pEntry - > FileName ) ) { delete [ ] pData ; return false ; }
// read
if ( ! Read ( pData , pEntry - > Size ) ) { delete [ ] pData ; return false ; }
break ;
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_OnDisk :
2012-02-07 23:12:44 +00:00
// create buffer
pData = new BYTE [ pEntry - > Size ] ; fOwnData = true ;
// open
if ( ! f . Open ( pEntry - > DiskPath ) ) { delete [ ] pData ; return false ; }
// read
if ( ! f . Read ( pData , pEntry - > Size ) ) { delete [ ] pData ; return false ; }
break ;
2015-02-13 17:49:23 +00:00
case C4GroupEntry : : C4GRES_InMemory :
2012-02-07 23:12:44 +00:00
// set
pData = pEntry - > bpMemBuf ; fOwnData = false ;
break ;
default :
return false ;
2009-05-08 13:28:41 +00:00
}
2012-02-07 23:12:44 +00:00
if ( ! pData ) return false ;
// calc crc
CRC = crc32 ( 0 , pData , pEntry - > Size ) ;
// discard buffer
if ( fOwnData ) delete [ ] pData ;
2009-05-08 13:28:41 +00:00
// add file name
2012-02-07 23:12:44 +00:00
CRC = crc32 ( CRC , reinterpret_cast < BYTE * > ( pEntry - > FileName ) , SLen ( pEntry - > FileName ) ) ;
2009-05-08 13:28:41 +00:00
}
// ok
2012-02-07 23:12:44 +00:00
return CRC ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Group : : OpenChild ( const char * strEntry )
{
// hack: The seach-handle would be closed twice otherwise
FolderSearch . Reset ( ) ;
// Create a memory copy of ourselves
C4Group * pOurselves = new C4Group ;
* pOurselves = * this ;
// Open a child from the memory copy
C4Group hChild ;
2009-08-15 18:50:32 +00:00
if ( ! hChild . OpenAsChild ( pOurselves , strEntry , false ) )
2009-05-08 13:28:41 +00:00
{
// Silently delete our memory copy
pOurselves - > Default ( ) ; delete pOurselves ;
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
}
// hack: The seach-handle would be closed twice otherwise
FolderSearch . Reset ( ) ;
hChild . FolderSearch . Reset ( ) ;
// We now become our own child
* this = hChild ;
// Make ourselves exclusive (until we hit our memory copy parent)
for ( C4Group * pGroup = this ; pGroup ! = pOurselves ; pGroup = pGroup - > Mother )
2009-08-15 18:50:32 +00:00
pGroup - > ExclusiveChild = true ;
2009-05-08 13:28:41 +00:00
// Reset the temporary child variable so it doesn't delete anything
hChild . Default ( ) ;
// Yeehaw
2009-08-15 18:50:32 +00:00
return true ;
2009-05-08 13:28:41 +00:00
}
bool C4Group : : OpenMother ( )
{
// This only works if we are an exclusive child
2009-08-15 18:50:32 +00:00
if ( ! Mother | | ! ExclusiveChild ) return false ;
2009-05-08 13:28:41 +00:00
// Store a pointer to our mother
C4Group * pMother = Mother ;
// Clear ourselves without deleting our mother
2009-08-15 18:50:32 +00:00
ExclusiveChild = false ;
2009-05-08 13:28:41 +00:00
Clear ( ) ;
// hack: The seach-handle would be closed twice otherwise
pMother - > FolderSearch . Reset ( ) ;
FolderSearch . Reset ( ) ;
// We now become our own mother (whoa!)
* this = * pMother ;
// Now silently delete our former mother
pMother - > Default ( ) ;
delete pMother ;
// Yeehaw
2009-08-15 18:50:32 +00:00
return true ;
2009-05-08 13:28:41 +00:00
}
2015-02-07 12:59:45 +00:00
int C4Group : : PreCacheEntries ( const char * szSearchPattern )
{
assert ( szSearchPattern ) ;
int result = 0 ;
// pre-load entries to memory. return number of loaded entries.
for ( C4GroupEntry * p = FirstEntry ; p ; p = p - > Next )
{
// skip some stuff that can not be cached
if ( p - > ChildGroup | | p - > bpMemBuf | | ! p - > Size ) continue ;
// is this to be cached?
if ( ! WildcardListMatch ( szSearchPattern , p - > FileName ) ) continue ;
// then load it!
StdBuf buf ;
if ( ! this - > LoadEntry ( p - > FileName , & buf ) ) continue ;
p - > HoldBuffer = true ;
p - > BufferIsStdbuf = true ;
p - > Size = buf . getSize ( ) ; // update size in case group changed on disk between calls
p - > bpMemBuf = static_cast < BYTE * > ( buf . GrabPointer ( ) ) ;
}
return result ;
}