2009-05-08 13:28:41 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright ( c ) 2009 - 2016 , 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
*/
2016-04-03 18:07:56 +00:00
# include "C4Include.h"
# include "network/C4Network2Res.h"
2009-05-08 13:28:41 +00:00
2016-04-03 18:07:56 +00:00
# include "c4group/C4Components.h"
2017-04-30 08:49:09 +00:00
# include "c4group/C4Group.h"
2016-04-03 18:07:56 +00:00
# include "control/C4GameControl.h"
2017-04-30 08:49:09 +00:00
# include "lib/C4Random.h"
# include "game/C4Application.h"
2009-05-08 13:28:41 +00:00
# include <sys/types.h>
# include <sys/stat.h>
# ifdef _WIN32
# include <direct.h>
# endif
# ifdef _MSC_VER
# define snprintf _snprintf
# pragma warning (disable : 4355)
# endif
// compile debug options
// #define C4NET2RES_LOAD_ALL
# define C4NET2RES_DEBUG_LOG
// helper
2010-03-28 18:58:01 +00:00
class DirSizeHelper
{
2009-05-08 13:28:41 +00:00
static uint32_t iSize , iMaxSize ;
static bool Helper ( const char * szPath )
{
2010-03-28 18:58:01 +00:00
if ( szPath [ SLen ( szPath ) - 1 ] = = ' . ' )
2009-05-08 13:28:41 +00:00
return false ;
2010-03-28 18:58:01 +00:00
if ( iSize > iMaxSize )
2009-05-08 13:28:41 +00:00
return false ;
2010-03-28 18:58:01 +00:00
if ( DirectoryExists ( szPath ) )
2009-05-08 13:28:41 +00:00
ForEachFile ( szPath , & Helper ) ;
2010-03-28 18:58:01 +00:00
else if ( FileExists ( szPath ) )
2009-05-08 13:28:41 +00:00
iSize + = FileSize ( szPath ) ;
return true ;
}
public :
static bool GetDirSize ( const char * szPath , uint32_t * pSize , uint32_t inMaxSize = ~ 0 )
{
// Security
2010-03-28 18:58:01 +00:00
if ( ! pSize ) return false ;
2009-05-08 13:28:41 +00:00
// Fold it
iSize = 0 ; iMaxSize = inMaxSize ;
ForEachFile ( szPath , & Helper ) ;
// Return
* pSize = iSize ;
return true ;
}
} ;
uint32_t DirSizeHelper : : iSize , DirSizeHelper : : iMaxSize ;
// *** C4Network2ResCore
C4Network2ResCore : : C4Network2ResCore ( )
2017-05-07 11:50:00 +00:00
: iFileSize ( ~ 0u ) , iFileCRC ( ~ 0u ) , iContentsCRC ( ~ 0u ) ,
2010-01-25 04:00:59 +00:00
iChunkSize ( C4NetResChunkSize )
2009-05-08 13:28:41 +00:00
{
}
2010-12-05 17:57:06 +00:00
void C4Network2ResCore : : Set ( C4Network2ResType enType , int32_t iResID , const char * strFileName , uint32_t inContentsCRC )
2009-05-08 13:28:41 +00:00
{
// Initialize base data
eType = enType ; iID = iResID ; iDerID = - 1 ;
fLoadable = false ;
iFileSize = iFileCRC = ~ 0 ; iContentsCRC = inContentsCRC ;
iChunkSize = C4NetResChunkSize ;
FileName . Copy ( strFileName ) ;
}
void C4Network2ResCore : : SetLoadable ( uint32_t iSize , uint32_t iCRC )
{
fLoadable = true ;
iFileSize = iSize ;
iFileCRC = iCRC ;
}
void C4Network2ResCore : : Clear ( )
{
eType = NRT_Null ;
iID = iDerID = - 1 ;
fLoadable = false ;
FileName . Clear ( ) ;
iFileSize = iFileCRC = iContentsCRC = ~ 0 ;
fHasFileSHA = false ;
}
// C4PacketBase virtuals
void C4Network2ResCore : : CompileFunc ( StdCompiler * pComp )
{
2010-03-27 16:05:02 +00:00
pComp - > Value ( mkNamingAdapt ( mkEnumAdaptT < uint8_t > ( eType , C4Network2ResType_EnumMap ) , " Type " , NRT_Null ) ) ;
pComp - > Value ( mkNamingAdapt ( iID , " ID " , - 1 ) ) ;
pComp - > Value ( mkNamingAdapt ( iDerID , " DerID " , - 1 ) ) ;
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkNamingAdapt ( fLoadable , " Loadable " , true ) ) ;
2010-03-28 18:58:01 +00:00
if ( fLoadable )
2009-05-08 13:28:41 +00:00
{
pComp - > Value ( mkNamingAdapt ( iFileSize , " FileSize " , 0U ) ) ;
pComp - > Value ( mkNamingAdapt ( iFileCRC , " FileCRC " , 0U ) ) ;
2010-03-28 18:58:01 +00:00
pComp - > Value ( mkNamingAdapt ( iChunkSize , " ChunkSize " , C4NetResChunkSize ) ) ;
if ( ! iChunkSize ) pComp - > excCorrupt ( " zero chunk size " ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-27 16:05:02 +00:00
pComp - > Value ( mkNamingAdapt ( iContentsCRC , " ContentsCRC " , 0U ) ) ;
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkNamingCountAdapt ( fHasFileSHA , " FileSHA " ) ) ;
2010-03-28 18:58:01 +00:00
if ( fHasFileSHA )
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkNamingAdapt ( mkHexAdapt ( FileSHA ) , " FileSHA " ) ) ;
2010-03-27 16:05:02 +00:00
pComp - > Value ( mkNamingAdapt ( mkNetFilenameAdapt ( FileName ) , " Filename " , " " ) ) ;
2009-05-08 13:28:41 +00:00
}
// *** C4Network2ResLoad
C4Network2ResLoad : : C4Network2ResLoad ( int32_t inChunk , int32_t inByClient )
2016-11-02 23:58:02 +00:00
: iChunk ( inChunk ) , Timestamp ( time ( nullptr ) ) , iByClient ( inByClient ) , pNext ( nullptr )
2009-05-08 13:28:41 +00:00
{
}
2017-05-03 18:28:00 +00:00
C4Network2ResLoad : : ~ C4Network2ResLoad ( ) = default ;
2009-05-08 13:28:41 +00:00
bool C4Network2ResLoad : : CheckTimeout ( )
{
2016-11-02 23:58:02 +00:00
return difftime ( time ( nullptr ) , Timestamp ) > = C4NetResLoadTimeout ;
2009-05-08 13:28:41 +00:00
}
// *** C4Network2ResChunkData
2017-05-07 11:50:00 +00:00
C4Network2ResChunkData : : C4Network2ResChunkData ( ) = default ;
2009-05-08 13:28:41 +00:00
C4Network2ResChunkData : : C4Network2ResChunkData ( const C4Network2ResChunkData & Data2 )
2010-03-28 18:58:01 +00:00
: C4PacketBase ( Data2 ) ,
2017-05-07 11:50:00 +00:00
iChunkCnt ( Data2 . getChunkCnt ( ) )
2009-05-08 13:28:41 +00:00
{
// add ranges
Merge ( Data2 ) ;
}
C4Network2ResChunkData : : ~ C4Network2ResChunkData ( )
{
Clear ( ) ;
}
C4Network2ResChunkData & C4Network2ResChunkData : : operator = ( const C4Network2ResChunkData & Data2 )
{
// clear, merge
SetIncomplete ( Data2 . getChunkCnt ( ) ) ;
Merge ( Data2 ) ;
return * this ;
}
void C4Network2ResChunkData : : SetIncomplete ( int32_t inChunkCnt )
{
Clear ( ) ;
// just set total chunk count
iChunkCnt = inChunkCnt ;
}
void C4Network2ResChunkData : : SetComplete ( int32_t inChunkCnt )
{
Clear ( ) ;
// set total chunk count
iPresentChunkCnt = iChunkCnt = inChunkCnt ;
// create one range
ChunkRange * pRange = new ChunkRange ;
pRange - > Start = 0 ; pRange - > Length = iChunkCnt ;
2016-11-02 23:58:02 +00:00
pRange - > Next = nullptr ;
2009-05-08 13:28:41 +00:00
pChunkRanges = pRange ;
}
void C4Network2ResChunkData : : AddChunk ( int32_t iChunk )
{
AddChunkRange ( iChunk , 1 ) ;
}
void C4Network2ResChunkData : : AddChunkRange ( int32_t iStart , int32_t iLength )
{
// security
2010-03-28 18:58:01 +00:00
if ( iStart < 0 | | iStart + iLength > iChunkCnt | | iLength < = 0 ) return ;
2009-05-08 13:28:41 +00:00
// find position
ChunkRange * pRange , * pPrev ;
2016-11-02 23:58:02 +00:00
for ( pRange = pChunkRanges , pPrev = nullptr ; pRange ; pPrev = pRange , pRange = pRange - > Next )
2010-03-28 18:58:01 +00:00
if ( pRange - > Start > = iStart )
2009-05-08 13:28:41 +00:00
break ;
// create new
ChunkRange * pNew = new ChunkRange ;
pNew - > Start = iStart ; pNew - > Length = iLength ;
// add to list
pNew - > Next = pRange ;
( pPrev ? pPrev - > Next : pChunkRanges ) = pNew ;
// counts
iPresentChunkCnt + = iLength ; iChunkRangeCnt + + ;
// check merges
2010-03-28 18:58:01 +00:00
if ( pPrev & & MergeRanges ( pPrev ) )
while ( MergeRanges ( pPrev ) ) { }
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
while ( MergeRanges ( pNew ) ) { }
2009-05-08 13:28:41 +00:00
}
void C4Network2ResChunkData : : Merge ( const C4Network2ResChunkData & Data2 )
{
// must have same basis chunk count
assert ( iChunkCnt = = Data2 . getChunkCnt ( ) ) ;
// add ranges
2010-03-28 18:58:01 +00:00
for ( ChunkRange * pRange = Data2 . pChunkRanges ; pRange ; pRange = pRange - > Next )
2009-05-08 13:28:41 +00:00
AddChunkRange ( pRange - > Start , pRange - > Length ) ;
}
void C4Network2ResChunkData : : Clear ( )
{
iChunkCnt = iPresentChunkCnt = iChunkRangeCnt = 0 ;
// remove all ranges
2010-03-28 18:58:01 +00:00
while ( pChunkRanges )
2009-05-08 13:28:41 +00:00
{
ChunkRange * pDelete = pChunkRanges ;
pChunkRanges = pDelete - > Next ;
delete pDelete ;
}
}
int32_t C4Network2ResChunkData : : GetChunkToRetrieve ( const C4Network2ResChunkData & Available , int32_t iLoadingCnt , int32_t * pLoading ) const
{
// (this version is highly calculation-intensitive, yet the most satisfactory
// solution I could find)
// find everything that should not be retrieved
C4Network2ResChunkData ChData ; Available . GetNegative ( ChData ) ;
ChData . Merge ( * this ) ;
2010-03-28 18:58:01 +00:00
for ( int32_t i = 0 ; i < iLoadingCnt ; i + + )
2009-05-08 13:28:41 +00:00
ChData . AddChunk ( pLoading [ i ] ) ;
// nothing to retrieve?
2010-03-28 18:58:01 +00:00
if ( ChData . isComplete ( ) ) return - 1 ;
2009-05-08 13:28:41 +00:00
// invert to get everything that should be retrieved
C4Network2ResChunkData ChData2 ; ChData . GetNegative ( ChData2 ) ;
// select chunk (random)
2016-04-25 15:32:23 +00:00
int32_t iRetrieveChunk = UnsyncedRandom ( ChData2 . getPresentChunkCnt ( ) ) ;
2009-05-08 13:28:41 +00:00
// return
return ChData2 . getPresentChunk ( iRetrieveChunk ) ;
}
bool C4Network2ResChunkData : : MergeRanges ( ChunkRange * pRange )
{
// no next entry?
2010-03-28 18:58:01 +00:00
if ( ! pRange | | ! pRange - > Next ) return false ;
2009-05-08 13:28:41 +00:00
// do merge?
ChunkRange * pNext = pRange - > Next ;
2010-03-28 18:58:01 +00:00
if ( pRange - > Start + pRange - > Length < pNext - > Start ) return false ;
2009-05-08 13:28:41 +00:00
// get overlap
2015-11-15 12:53:01 +00:00
int32_t iOverlap = std : : min ( ( pRange - > Start + pRange - > Length ) - pNext - > Start , pNext - > Length ) ;
2009-05-08 13:28:41 +00:00
// set new chunk range
pRange - > Length + = pNext - > Length - iOverlap ;
// remove range
pRange - > Next = pNext - > Next ;
delete pNext ;
// counts
iChunkRangeCnt - - ; iPresentChunkCnt - = iOverlap ;
// ok
return true ;
}
void C4Network2ResChunkData : : GetNegative ( C4Network2ResChunkData & Target ) const
{
// clear target
Target . SetIncomplete ( iChunkCnt ) ;
// add all ranges that are missing
int32_t iFreeStart = 0 ;
2010-03-28 18:58:01 +00:00
for ( ChunkRange * pRange = pChunkRanges ; pRange ; pRange = pRange - > Next )
2009-05-08 13:28:41 +00:00
{
// add range
Target . AddChunkRange ( iFreeStart , pRange - > Start - iFreeStart ) ;
// safe new start
iFreeStart = pRange - > Start + pRange - > Length ;
}
// add last range
Target . AddChunkRange ( iFreeStart , iChunkCnt - iFreeStart ) ;
}
int32_t C4Network2ResChunkData : : getPresentChunk ( int32_t iNr ) const
{
2010-03-28 18:58:01 +00:00
for ( ChunkRange * pRange = pChunkRanges ; pRange ; pRange = pRange - > Next )
if ( iNr < pRange - > Length )
2009-05-08 13:28:41 +00:00
return iNr + pRange - > Start ;
else
iNr - = pRange - > Length ;
return - 1 ;
}
void C4Network2ResChunkData : : CompileFunc ( StdCompiler * pComp )
{
2017-03-11 14:05:41 +00:00
bool deserializing = pComp - > isDeserializer ( ) ;
if ( deserializing ) Clear ( ) ;
2010-03-27 16:05:02 +00:00
// Data
pComp - > Value ( mkNamingAdapt ( mkIntPackAdapt ( iChunkCnt ) , " ChunkCnt " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( mkIntPackAdapt ( iChunkRangeCnt ) , " ChunkRangeCnt " , 0 ) ) ;
2009-05-08 13:28:41 +00:00
// Ranges
2010-03-28 18:58:01 +00:00
if ( ! pComp - > Name ( " Ranges " ) )
2010-03-27 16:05:02 +00:00
pComp - > excCorrupt ( " ResChunk ranges expected! " ) ;
2016-11-02 23:58:02 +00:00
ChunkRange * pRange = nullptr ;
2010-03-28 18:58:01 +00:00
for ( int32_t i = 0 ; i < iChunkRangeCnt ; i + + )
2009-05-08 13:28:41 +00:00
{
2010-03-27 16:05:02 +00:00
// Create new range / go to next range
2017-03-11 14:05:41 +00:00
if ( deserializing )
2010-03-27 16:05:02 +00:00
pRange = ( pRange ? pRange - > Next : pChunkRanges ) = new ChunkRange ;
else
pRange = pRange ? pRange - > Next : pChunkRanges ;
2010-04-01 21:08:06 +00:00
// Separate
if ( i ) pComp - > Separator ( ) ;
2010-03-27 16:05:02 +00:00
// Compile range
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkIntPackAdapt ( pRange - > Start ) ) ;
2010-04-01 21:08:06 +00:00
pComp - > Separator ( StdCompiler : : SEP_PART2 ) ;
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkIntPackAdapt ( pRange - > Length ) ) ;
}
2010-03-27 16:05:02 +00:00
// Terminate list
2017-03-11 14:05:41 +00:00
if ( deserializing )
2016-11-02 23:58:02 +00:00
( pRange ? pRange - > Next : pChunkRanges ) = nullptr ;
2010-03-27 16:05:02 +00:00
pComp - > NameEnd ( ) ;
2009-05-08 13:28:41 +00:00
}
// *** C4Network2Res
C4Network2Res : : C4Network2Res ( C4Network2ResList * pnParent )
2010-03-28 18:58:01 +00:00
: fDirty ( false ) ,
2009-05-08 13:28:41 +00:00
fTempFile ( false ) , fStandaloneFailed ( false ) ,
iRefCnt ( 0 ) , fRemoved ( false ) ,
iLastReqTime ( 0 ) ,
fLoading ( false ) ,
2016-11-02 23:58:02 +00:00
pCChunks ( nullptr ) , iDiscoverStartTime ( 0 ) , pLoads ( nullptr ) , iLoadCnt ( 0 ) ,
pNext ( nullptr ) ,
2009-05-08 13:28:41 +00:00
pParent ( pnParent )
{
szFile [ 0 ] = szStandalone [ 0 ] = ' \0 ' ;
}
C4Network2Res : : ~ C4Network2Res ( )
{
2010-03-27 16:05:02 +00:00
assert ( ! pNext ) ;
2009-05-08 13:28:41 +00:00
Clear ( ) ;
}
bool C4Network2Res : : SetByFile ( const char * strFilePath , bool fTemp , C4Network2ResType eType , int32_t iResID , const char * szResName , bool fSilent )
{
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2012-10-21 20:20:43 +00:00
// default resource name: relative path
2010-03-28 18:58:01 +00:00
if ( ! szResName ) szResName = Config . AtRelativePath ( strFilePath ) ;
2009-05-08 13:28:41 +00:00
SCopy ( strFilePath , szFile , sizeof ( szFile ) - 1 ) ;
// group?
C4Group Grp ;
2011-01-06 20:18:13 +00:00
if ( Reloc . Open ( Grp , strFilePath ) )
2009-05-08 13:28:41 +00:00
return SetByGroup ( & Grp , fTemp , eType , iResID , szResName , fSilent ) ;
// so it needs to be a file
2011-01-06 20:18:13 +00:00
StdStrBuf szFullFile ;
if ( ! Reloc . LocateItem ( szFile , szFullFile ) )
2010-03-28 18:58:01 +00:00
{ if ( ! fSilent ) LogF ( " SetByFile: file %s not found! " , strFilePath ) ; return false ; }
2009-05-08 13:28:41 +00:00
// calc checksum
uint32_t iCRC32 ;
2011-09-15 18:16:00 +00:00
if ( ! GetFileCRC ( szFullFile . getData ( ) , & iCRC32 ) ) return false ;
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_DEBUG_LOG
// log
LogSilentF ( " Network: Resource: complete %d:%s is file %s (%s) " , iResID , szResName , szFile , fTemp ? " temp " : " static " ) ;
# endif
// set core
2011-01-06 20:18:13 +00:00
Core . Set ( eType , iResID , Config . AtRelativePath ( szFullFile . getData ( ) ) , iCRC32 ) ;
2009-05-08 13:28:41 +00:00
// set own data
fDirty = true ;
fTempFile = fTemp ;
fStandaloneFailed = false ;
fRemoved = false ;
2016-11-02 23:58:02 +00:00
iLastReqTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
fLoading = false ;
// ok
return true ;
}
bool C4Network2Res : : SetByGroup ( C4Group * pGrp , bool fTemp , C4Network2ResType eType , int32_t iResID , const char * szResName , bool fSilent ) // by main thread
{
Clear ( ) ;
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2012-10-21 20:20:43 +00:00
// default resource name: relative path
2009-05-08 13:28:41 +00:00
StdStrBuf sResName ;
if ( szResName )
sResName = szResName ;
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
StdStrBuf sFullName = pGrp - > GetFullName ( ) ;
sResName . Copy ( Config . AtRelativePath ( sFullName . getData ( ) ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
SCopy ( pGrp - > GetFullName ( ) . getData ( ) , szFile , sizeof ( szFile ) - 1 ) ;
// set core
2010-12-05 17:57:06 +00:00
Core . Set ( eType , iResID , sResName . getData ( ) , pGrp - > EntryCRC32 ( ) ) ;
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_DEBUG_LOG
// log
LogSilentF ( " Network: Resource: complete %d:%s is file %s (%s) " , iResID , sResName . getData ( ) , szFile , fTemp ? " temp " : " static " ) ;
# endif
// set data
fDirty = true ;
fTempFile = fTemp ;
fStandaloneFailed = false ;
fRemoved = false ;
2016-11-02 23:58:02 +00:00
iLastReqTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
fLoading = false ;
// ok
return true ;
}
bool C4Network2Res : : SetByCore ( const C4Network2ResCore & nCore , bool fSilent , const char * szAsFilename , int32_t iRecursion ) // by main thread
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
StdStrBuf sFilename ;
// try open local file
const char * szFilename = szAsFilename ? szAsFilename : GetC4Filename ( nCore . getFileName ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( SetByFile ( szFilename , false , nCore . getType ( ) , nCore . getID ( ) , nCore . getFileName ( ) , fSilent ) )
{
2009-05-08 13:28:41 +00:00
// check contents checksum
2010-03-28 18:58:01 +00:00
if ( Core . getContentsCRC ( ) = = nCore . getContentsCRC ( ) )
{
2009-05-08 13:28:41 +00:00
// set core
fDirty = true ;
Core = nCore ;
// ok then
return true ;
}
2010-03-28 18:58:01 +00:00
}
2011-03-13 15:38:33 +00:00
// get and search for filename without specified folder (e.g., Castle.ocs when the opened game is Easy.ocf\Castle.ocs)
2009-05-08 13:28:41 +00:00
const char * szFilenameOnly = GetFilename ( szFilename ) ;
const char * szFilenameC4 = GetC4Filename ( szFilename ) ;
if ( szFilenameOnly ! = szFilenameC4 )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
sFilename . Copy ( szFilename , SLen ( szFilename ) - SLen ( szFilenameC4 ) ) ;
sFilename . Append ( szFilenameOnly ) ;
if ( SetByCore ( nCore , fSilent , szFilenameOnly , Config . Network . MaxResSearchRecursion ) ) return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// if it could still not be set, try within all folders of root (ignoring special folders), and try as file outside the folder
// but do not recurse any deeper than set by config (default: One folder)
if ( iRecursion > = Config . Network . MaxResSearchRecursion ) return false ;
StdStrBuf sSearchPath ; const char * szSearchPath ;
if ( ! iRecursion )
2011-04-02 20:50:54 +00:00
szSearchPath = Config . General . ExePath . getData ( ) ;
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
sSearchPath . Copy ( szFilename , SLen ( szFilename ) - SLen ( szFilenameC4 ) ) ;
szSearchPath = sSearchPath . getData ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
StdStrBuf sNetPath ; sNetPath . Copy ( Config . Network . WorkPath ) ;
char * szNetPath = sNetPath . GrabPointer ( ) ;
TruncateBackslash ( szNetPath ) ;
sNetPath . Take ( szNetPath ) ;
for ( DirectoryIterator i ( szSearchPath ) ; * i ; + + i )
if ( DirectoryExists ( * i ) )
if ( ! * GetExtension ( * i ) ) // directories without extension only
if ( ! szNetPath | | ! * szNetPath | | ! ItemIdentical ( * i , szNetPath ) ) // ignore network path
2010-03-28 18:58:01 +00:00
{
2011-03-13 15:38:33 +00:00
// search for complete name at subpath (e.g. MyFolder\Easy.ocf\Castle.ocs)
2009-05-08 13:28:41 +00:00
sFilename . Format ( " %s%c%s " , * i , DirectorySeparator , szFilenameC4 ) ;
if ( SetByCore ( nCore , fSilent , sFilename . getData ( ) , iRecursion + 1 ) )
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// file could not be found locally
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2Res : : SetLoad ( const C4Network2ResCore & nCore ) // by main thread
{
Clear ( ) ;
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2009-05-08 13:28:41 +00:00
// must be loadable
2010-03-28 18:58:01 +00:00
if ( ! nCore . isLoadable ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// save core, set chunks
Core = nCore ;
Chunks . SetIncomplete ( Core . getChunkCnt ( ) ) ;
// create temporary file
2010-03-28 18:58:01 +00:00
if ( ! pParent - > FindTempResFileName ( Core . getFileName ( ) , szFile ) )
2009-05-08 13:28:41 +00:00
return false ;
# ifdef C4NET2RES_DEBUG_LOG
// log
Application . InteractiveThread . ThreadLogS ( " Network: Resource: loading %d:%s to file %s " , Core . getID ( ) , Core . getFileName ( ) , szFile ) ;
# endif
// set standalone (result is going to be binary-compatible)
SCopy ( szFile , szStandalone , sizeof ( szStandalone ) - 1 ) ;
// set flags
fDirty = false ;
fTempFile = true ;
fStandaloneFailed = false ;
fRemoved = false ;
2016-11-02 23:58:02 +00:00
iLastReqTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
fLoading = true ;
// No discovery yet
iDiscoverStartTime = 0 ;
return true ;
}
bool C4Network2Res : : SetDerived ( const char * strName , const char * strFilePath , bool fTemp , C4Network2ResType eType , int32_t iDResID )
{
2010-03-27 16:05:02 +00:00
Clear ( ) ;
CStdLock FileLock ( & FileCSec ) ;
// set core
2010-12-05 17:57:06 +00:00
Core . Set ( eType , C4NetResIDAnonymous , strName , ~ 0 ) ;
2010-03-27 16:05:02 +00:00
Core . SetDerived ( iDResID ) ;
// save file path
SCopy ( strFilePath , szFile , _MAX_PATH ) ;
* szStandalone = ' \0 ' ;
// set flags
fDirty = false ;
fTempFile = fTemp ;
fStandaloneFailed = false ;
fRemoved = false ;
2016-11-02 23:58:02 +00:00
iLastReqTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
fLoading = false ;
2012-10-21 20:20:43 +00:00
// Do not set any chunk data - anonymous resources are very likely to change.
2010-03-27 16:05:02 +00:00
// Wait for FinishDerived()-call.
return true ;
2009-05-08 13:28:41 +00:00
}
void C4Network2Res : : ChangeID ( int32_t inID )
{
Core . SetID ( inID ) ;
}
bool C4Network2Res : : IsBinaryCompatible ( )
{
2012-10-21 20:20:43 +00:00
// returns wether the standalone of this resource is binary compatible
2009-05-08 13:28:41 +00:00
// to the official version (means: matches the file checksum)
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2009-05-08 13:28:41 +00:00
// standalone set? ok then (see GetStandalone)
2010-03-28 18:58:01 +00:00
if ( szStandalone [ 0 ] ) return true ;
2009-05-08 13:28:41 +00:00
// is a directory?
2010-03-28 18:58:01 +00:00
if ( DirectoryExists ( szFile ) )
2009-05-08 13:28:41 +00:00
// forget it - if the file is packed now, the creation time and author
// won't match.
return false ;
// try to create the standalone
2016-11-02 23:58:02 +00:00
return GetStandalone ( nullptr , 0 , false , false , true ) ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2Res : : GetStandalone ( char * pTo , int32_t iMaxL , bool fSetOfficial , bool fAllowUnloadable , bool fSilent )
{
// already set?
2010-03-28 18:58:01 +00:00
if ( szStandalone [ 0 ] )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( pTo ) SCopy ( szStandalone , pTo , iMaxL ) ;
2009-05-08 13:28:41 +00:00
return true ;
}
// already tried and failed? No point in retrying
2010-03-28 18:58:01 +00:00
if ( fStandaloneFailed ) return false ;
2009-05-08 13:28:41 +00:00
// not loadable? Wo won't be able to check the standalone as the core will lack the needed information.
// the standalone won't be interesting in this case, anyway.
2010-03-28 18:58:01 +00:00
if ( ! fSetOfficial & & ! Core . isLoadable ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// set flag, so failure below will let future calls fail
fStandaloneFailed = true ;
2010-03-27 16:05:02 +00:00
// lock file
CStdLock FileLock ( & FileCSec ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// directory?
2009-05-08 13:28:41 +00:00
SCopy ( szFile , szStandalone , sizeof ( szStandalone ) - 1 ) ;
2010-03-28 18:58:01 +00:00
if ( DirectoryExists ( szFile ) )
2009-05-08 13:28:41 +00:00
{
// size check for the directory, if allowed
2010-03-28 18:58:01 +00:00
if ( fAllowUnloadable )
2009-05-08 13:28:41 +00:00
{
uint32_t iDirSize ;
2010-03-28 18:58:01 +00:00
if ( ! DirSizeHelper : : GetDirSize ( szFile , & iDirSize , Config . Network . MaxLoadFileSize ) )
{ if ( ! fSilent ) LogF ( " Network: could not get directory size of %s! " , szFile ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
if ( iDirSize > uint32_t ( Config . Network . MaxLoadFileSize ) )
{ if ( ! fSilent ) LogSilentF ( " Network: %s over size limit, will be marked unloadable! " , szFile ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
}
// log - this may take a few seconds
2010-03-28 18:58:01 +00:00
if ( ! fSilent ) LogF ( LoadResStr ( " IDS_PRC_NETPACKING " ) , GetFilename ( szFile ) ) ;
2009-05-08 13:28:41 +00:00
// pack inplace?
2010-03-28 18:58:01 +00:00
if ( ! fTempFile )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! pParent - > FindTempResFileName ( szFile , szStandalone ) )
{ if ( ! fSilent ) Log ( " GetStandalone: could not find free name for temporary file! " ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
if ( ! C4Group_PackDirectoryTo ( szFile , szStandalone ) )
{ if ( ! fSilent ) Log ( " GetStandalone: could not pack directory! " ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
else if ( ! C4Group_PackDirectory ( szStandalone ) )
{ if ( ! fSilent ) Log ( " GetStandalone: could not pack directory! " ) ; if ( ! SEqual ( szFile , szStandalone ) ) EraseDirectory ( szStandalone ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
// make sure directory is packed
2010-03-28 18:58:01 +00:00
if ( DirectoryExists ( szStandalone ) )
{ if ( ! fSilent ) Log ( " GetStandalone: directory hasn't been packed! " ) ; if ( ! SEqual ( szFile , szStandalone ) ) EraseDirectory ( szStandalone ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
// fallthru
}
// doesn't exist physically?
2010-03-28 18:58:01 +00:00
if ( ! FileExists ( szStandalone ) )
2009-05-08 13:28:41 +00:00
{
// try C4Group (might be packed)
2010-03-28 18:58:01 +00:00
if ( ! pParent - > FindTempResFileName ( szFile , szStandalone ) )
{ if ( ! fSilent ) Log ( " GetStandalone: could not find free name for temporary file! " ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
if ( ! C4Group_CopyItem ( szFile , szStandalone ) )
{ if ( ! fSilent ) Log ( " GetStandalone: could not copy to temporary file! " ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
}
// remains missing? give up.
2010-03-28 18:58:01 +00:00
if ( ! FileExists ( szStandalone ) )
{ if ( ! fSilent ) Log ( " GetStandalone: file not found! " ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
// do optimizations (delete unneeded entries)
2010-03-28 18:58:01 +00:00
if ( ! OptimizeStandalone ( fSilent ) )
2011-08-11 13:45:27 +00:00
{ if ( ! SEqual ( szFile , szStandalone ) ) EraseItem ( szStandalone ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
// get file size
size_t iSize = FileSize ( szStandalone ) ;
// size limit
2010-03-28 18:58:01 +00:00
if ( fAllowUnloadable )
if ( iSize > uint32_t ( Config . Network . MaxLoadFileSize ) )
{ if ( ! fSilent ) LogSilentF ( " Network: %s over size limit, will be marked unloadable! " , szFile ) ; szStandalone [ 0 ] = ' \0 ' ; return false ; }
2009-05-08 13:28:41 +00:00
// check
2010-03-28 18:58:01 +00:00
if ( ! fSetOfficial & & iSize ! = Core . getFileSize ( ) )
2009-05-08 13:28:41 +00:00
{
// remove file
2018-12-31 12:45:43 +00:00
if ( ! SEqual ( szFile , szStandalone ) ) EraseItem ( szStandalone ) ;
szStandalone [ 0 ] = ' \0 ' ;
2009-05-08 13:28:41 +00:00
// sorry, this version isn't good enough :(
return false ;
}
// calc checksum
uint32_t iCRC32 ;
2011-09-15 18:16:00 +00:00
if ( ! GetFileCRC ( szStandalone , & iCRC32 ) )
2010-03-28 18:58:01 +00:00
{ if ( ! fSilent ) Log ( " GetStandalone: could not calculate checksum! " ) ; return false ; }
2009-05-08 13:28:41 +00:00
// set / check
2010-03-28 18:58:01 +00:00
if ( ! fSetOfficial & & iCRC32 ! = Core . getFileCRC ( ) )
2009-05-08 13:28:41 +00:00
{
// remove file, return
2018-12-31 12:45:43 +00:00
if ( ! SEqual ( szFile , szStandalone ) ) EraseItem ( szStandalone ) ;
szStandalone [ 0 ] = ' \0 ' ;
2009-05-08 13:28:41 +00:00
return false ;
}
// we didn't fail
fStandaloneFailed = false ;
// mark resource as loadable and safe file information
Core . SetLoadable ( iSize , iCRC32 ) ;
// set up chunk data
Chunks . SetComplete ( Core . getChunkCnt ( ) ) ;
// ok
return true ;
}
bool C4Network2Res : : CalculateSHA ( )
{
// already present?
2010-03-28 18:58:01 +00:00
if ( Core . hasFileSHA ( ) ) return true ;
2009-05-08 13:28:41 +00:00
// get the file
char szStandalone [ _MAX_PATH + 1 ] ;
2010-03-28 18:58:01 +00:00
if ( ! GetStandalone ( szStandalone , _MAX_PATH , false ) )
2009-05-08 13:28:41 +00:00
SCopy ( szFile , szStandalone , _MAX_PATH ) ;
// get the hash
BYTE hash [ SHA_DIGEST_LENGTH ] ;
2011-09-15 18:16:00 +00:00
if ( ! GetFileSHA1 ( szStandalone , hash ) )
2009-05-08 13:28:41 +00:00
return false ;
// save it back
Core . SetFileSHA ( hash ) ;
// okay
return true ;
}
C4Network2Res : : Ref C4Network2Res : : Derive ( )
{
2010-03-27 16:05:02 +00:00
// Called before the file is changed. Rescues all files and creates a
2012-10-21 20:20:43 +00:00
// new resource for the file. This resource is flagged as "anonymous", as it
2010-03-27 16:05:02 +00:00
// has no official core (no res ID, to be exact).
// The resource gets its final core when FinishDerive() is called.
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// For security: This doesn't make much sense if the resource is currently being
// loaded. So better assume the caller doesn't know what he's doing and check.
2016-11-02 23:58:02 +00:00
if ( isLoading ( ) ) return nullptr ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
// Save back original file name
char szOrgFile [ _MAX_PATH + 1 ] ;
SCopy ( szFile , szOrgFile , _MAX_PATH ) ;
bool fOrgTempFile = fTempFile ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Create a copy of the file, if neccessary
2010-03-28 18:58:01 +00:00
if ( ! * szStandalone | | SEqual ( szStandalone , szFile ) )
2010-03-27 16:05:02 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! pParent - > FindTempResFileName ( szOrgFile , szFile ) )
2016-11-02 23:58:02 +00:00
{ Log ( " Derive: could not find free name for temporary file! " ) ; return nullptr ; }
2010-03-28 18:58:01 +00:00
if ( ! C4Group_CopyItem ( szOrgFile , szFile ) )
2016-11-02 23:58:02 +00:00
{ Log ( " Derive: could not copy to temporary file! " ) ; return nullptr ; }
2010-03-27 16:05:02 +00:00
// set standalone
2010-03-28 18:58:01 +00:00
if ( * szStandalone )
2010-03-27 16:05:02 +00:00
SCopy ( szFile , szStandalone , _MAX_PATH ) ;
fTempFile = true ;
}
else
{
// Standlone exists: Just set szFile to point on the standlone. It's
// assumed that the original file isn't of intrest after this point anyway.
SCopy ( szStandalone , szFile , _MAX_PATH ) ;
fTempFile = true ;
}
2009-05-08 13:28:41 +00:00
2012-10-21 20:20:43 +00:00
Application . InteractiveThread . ThreadLogS ( " Network: Resource: deriving from %d:%s, original at %s " , getResID ( ) , Core . getFileName ( ) , szFile ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// (note: should remove temp file if something fails after this point)
2009-05-08 13:28:41 +00:00
2012-10-21 20:20:43 +00:00
// create new resource
2009-05-08 13:28:41 +00:00
C4Network2Res : : Ref pDRes = new C4Network2Res ( pParent ) ;
2016-11-02 23:58:02 +00:00
if ( ! pDRes ) return nullptr ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// initialize
2010-03-28 18:58:01 +00:00
if ( ! pDRes - > SetDerived ( Core . getFileName ( ) , szOrgFile , fOrgTempFile , getType ( ) , getResID ( ) ) )
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// add to list
pParent - > Add ( pDRes ) ;
2009-05-08 13:28:41 +00:00
2012-10-21 20:20:43 +00:00
// return new resource
2010-03-27 16:05:02 +00:00
return pDRes ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2Res : : FinishDerive ( ) // by main thread
{
2012-10-21 20:20:43 +00:00
// All changes have been made. Register this resource with a new ID.
2010-03-27 16:05:02 +00:00
// security
2010-03-28 18:58:01 +00:00
if ( ! isAnonymous ( ) ) return false ;
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
// Save back data
int32_t iDerID = Core . getDerID ( ) ;
char szName [ _MAX_PATH + 1 ] ; SCopy ( Core . getFileName ( ) , szName , _MAX_PATH ) ;
char szFileC [ _MAX_PATH + 1 ] ; SCopy ( szFile , szFileC , _MAX_PATH ) ;
// Set by file
2010-03-28 18:58:01 +00:00
if ( ! SetByFile ( szFileC , fTempFile , getType ( ) , pParent - > nextResID ( ) , szName ) )
2010-03-27 16:05:02 +00:00
return false ;
// create standalone
2016-11-02 23:58:02 +00:00
if ( ! GetStandalone ( nullptr , 0 , true ) )
2010-03-27 16:05:02 +00:00
return false ;
// Set ID
Core . SetDerived ( iDerID ) ;
2009-05-08 13:28:41 +00:00
// announce derive
pParent - > getIOClass ( ) - > BroadcastMsg ( MkC4NetIOPacket ( PID_NetResDerive , Core ) ) ;
2010-03-27 16:05:02 +00:00
// derivation is dirty bussines
fDirty = true ;
// ok
return true ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2Res : : FinishDerive ( const C4Network2ResCore & nCore )
{
2010-03-27 16:05:02 +00:00
// security
2010-03-28 18:58:01 +00:00
if ( ! isAnonymous ( ) ) return false ;
2010-03-27 16:05:02 +00:00
// Set core
Core = nCore ;
2012-10-21 20:20:43 +00:00
// Set chunks (assume the resource is complete)
2010-03-27 16:05:02 +00:00
Chunks . SetComplete ( Core . getChunkCnt ( ) ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Note that the Contents-CRC is /not/ checked. Derivation needs to be
// synchronized outside of C4Network2Res.
2009-05-08 13:28:41 +00:00
2012-10-21 20:20:43 +00:00
// But note that the resource /might/ be binary compatible (though very
2010-03-27 16:05:02 +00:00
// unlikely), so do not set fNotBinaryCompatible.
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// ok
return true ;
2009-05-08 13:28:41 +00:00
}
C4Group * C4Network2Res : : OpenAsGrp ( ) const
{
C4Group * pnGrp = new C4Group ( ) ;
2010-03-28 18:58:01 +00:00
if ( ! pnGrp - > Open ( szFile ) )
2009-05-08 13:28:41 +00:00
{
delete pnGrp ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
}
return pnGrp ;
}
void C4Network2Res : : Remove ( )
{
// schedule for removal
fRemoved = true ;
}
bool C4Network2Res : : SendStatus ( C4Network2IOConnection * pTo )
{
// pack status
C4NetIOPacket Pkt = MkC4NetIOPacket ( PID_NetResStat , C4PacketResStatus ( Core . getID ( ) , Chunks ) ) ;
// to one client?
2010-03-28 18:58:01 +00:00
if ( pTo )
2009-05-08 13:28:41 +00:00
return pTo - > Send ( Pkt ) ;
else
{
// reset dirty flag
fDirty = false ;
// broadcast status
assert ( pParent & & pParent - > getIOClass ( ) ) ;
return pParent - > getIOClass ( ) - > BroadcastMsg ( Pkt ) ;
}
}
bool C4Network2Res : : SendChunk ( uint32_t iChunk , int32_t iToClient )
{
assert ( pParent & & pParent - > getIOClass ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( ! szStandalone [ 0 ] | | iChunk > = Core . getChunkCnt ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// find connection for given client (one of the rare uses of the data connection)
C4Network2IOConnection * pConn = pParent - > getIOClass ( ) - > GetDataConnection ( iToClient ) ;
2010-03-28 18:58:01 +00:00
if ( ! pConn ) return false ;
2009-05-08 13:28:41 +00:00
// save last request time
2016-11-02 23:58:02 +00:00
iLastReqTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
// create packet
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2009-05-08 13:28:41 +00:00
C4Network2ResChunk ResChunk ;
ResChunk . Set ( this , iChunk ) ;
// send
bool fSuccess = pConn - > Send ( MkC4NetIOPacket ( PID_NetResData , ResChunk ) ) ;
pConn - > DelRef ( ) ;
return fSuccess ;
}
void C4Network2Res : : AddRef ( )
{
2017-03-23 16:03:16 +00:00
+ + iRefCnt ;
2009-05-08 13:28:41 +00:00
}
void C4Network2Res : : DelRef ( )
{
2017-03-23 16:03:16 +00:00
if ( - - iRefCnt = = 0 )
2009-05-08 13:28:41 +00:00
delete this ;
}
void C4Network2Res : : OnDiscover ( C4Network2IOConnection * pBy )
{
2010-03-28 18:58:01 +00:00
if ( ! IsBinaryCompatible ( ) ) return ;
2009-05-08 13:28:41 +00:00
// discovered
2016-11-02 23:58:02 +00:00
iLastReqTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
// send status back
SendStatus ( pBy ) ;
}
void C4Network2Res : : OnStatus ( const C4Network2ResChunkData & rChunkData , C4Network2IOConnection * pBy )
{
2010-03-28 18:58:01 +00:00
if ( ! fLoading ) return ;
2009-05-08 13:28:41 +00:00
// discovered a source: reset timeout
iDiscoverStartTime = 0 ;
// check if the chunk data is valid
2010-03-28 18:58:01 +00:00
if ( rChunkData . getChunkCnt ( ) ! = Chunks . getChunkCnt ( ) )
2009-05-08 13:28:41 +00:00
return ;
// add chunk data
ClientChunks * pChunks ;
2010-03-28 18:58:01 +00:00
for ( pChunks = pCChunks ; pChunks ; pChunks = pChunks - > Next )
if ( pChunks - > ClientID = = pBy - > getClientID ( ) )
2009-05-08 13:28:41 +00:00
break ;
// not found? add
2010-03-28 18:58:01 +00:00
if ( ! pChunks )
2009-05-08 13:28:41 +00:00
{
pChunks = new ClientChunks ( ) ;
pChunks - > Next = pCChunks ;
pCChunks = pChunks ;
}
pChunks - > ClientID = pBy - > getClientID ( ) ;
pChunks - > Chunks = rChunkData ;
// check load
2010-03-28 18:58:01 +00:00
if ( ! StartLoad ( pChunks - > ClientID , pChunks - > Chunks ) )
2009-05-08 13:28:41 +00:00
RemoveCChunks ( pCChunks ) ;
}
void C4Network2Res : : OnChunk ( const C4Network2ResChunk & rChunk )
{
2010-03-28 18:58:01 +00:00
if ( ! fLoading ) return ;
2012-10-21 20:20:43 +00:00
// correct resource?
2010-03-28 18:58:01 +00:00
if ( rChunk . getResID ( ) ! = getResID ( ) ) return ;
2012-10-21 20:20:43 +00:00
// add resource data
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2009-05-08 13:28:41 +00:00
bool fSuccess = rChunk . AddTo ( this , pParent - > getIOClass ( ) ) ;
# ifdef C4NET2RES_DEBUG_LOG
// log
2012-10-21 20:20:43 +00:00
Application . InteractiveThread . ThreadLogS ( " Network: Res: %s chunk %d to resource %s (%s)%s " , fSuccess ? " added " : " could not add " , rChunk . getChunkNr ( ) , Core . getFileName ( ) , szFile , fSuccess ? " " : " ! " ) ;
2009-05-08 13:28:41 +00:00
# endif
2010-03-28 18:58:01 +00:00
if ( fSuccess )
2009-05-08 13:28:41 +00:00
{
// status changed
fDirty = true ;
// remove load waits
2012-06-15 16:16:49 +00:00
for ( C4Network2ResLoad * pLoad = pLoads , * pNext ; pLoad ; pLoad = pNext )
2009-05-08 13:28:41 +00:00
{
pNext = pLoad - > Next ( ) ;
2010-03-28 18:58:01 +00:00
if ( static_cast < uint32_t > ( pLoad - > getChunk ( ) ) = = rChunk . getChunkNr ( ) )
2009-05-08 13:28:41 +00:00
RemoveLoad ( pLoad ) ;
}
}
// complete?
2010-03-28 18:58:01 +00:00
if ( Chunks . isComplete ( ) )
2009-05-08 13:28:41 +00:00
EndLoad ( ) ;
// check: start new loads?
else
StartNewLoads ( ) ;
}
bool C4Network2Res : : DoLoad ( )
{
2010-03-28 18:58:01 +00:00
if ( ! fLoading ) return true ;
2009-05-08 13:28:41 +00:00
// any loads currently active?
2010-03-28 18:58:01 +00:00
if ( iLoadCnt )
2009-05-08 13:28:41 +00:00
{
// check for load timeouts
int32_t iLoadsRemoved = 0 ;
2010-03-28 18:58:01 +00:00
for ( C4Network2ResLoad * pLoad = pLoads , * pNext ; pLoad ; pLoad = pNext )
2009-05-08 13:28:41 +00:00
{
pNext = pLoad - > Next ( ) ;
2010-03-28 18:58:01 +00:00
if ( pLoad - > CheckTimeout ( ) )
2009-05-08 13:28:41 +00:00
{
RemoveLoad ( pLoad ) ;
iLoadsRemoved + + ;
}
}
// start new loads
2010-03-28 18:58:01 +00:00
if ( iLoadsRemoved ) StartNewLoads ( ) ;
2009-05-08 13:28:41 +00:00
}
else
{
// discover timeout?
2010-03-28 18:58:01 +00:00
if ( iDiscoverStartTime )
2016-11-02 23:58:02 +00:00
if ( difftime ( time ( nullptr ) , iDiscoverStartTime ) > C4NetResDiscoverTimeout )
2009-05-08 13:28:41 +00:00
return false ;
}
// ok
return true ;
}
bool C4Network2Res : : NeedsDiscover ( )
{
// loading, but no active load sources?
2010-03-28 18:58:01 +00:00
if ( fLoading & & ! iLoadCnt )
2009-05-08 13:28:41 +00:00
{
// set timeout, if this is the first discover
2010-03-28 18:58:01 +00:00
if ( ! iDiscoverStartTime )
2016-11-02 23:58:02 +00:00
iDiscoverStartTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
// do discover
return true ;
}
return false ;
}
void C4Network2Res : : Clear ( )
{
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2009-05-08 13:28:41 +00:00
// delete files
2010-03-28 18:58:01 +00:00
if ( fTempFile )
if ( FileExists ( szFile ) )
2011-08-11 13:45:27 +00:00
if ( ! EraseFile ( szFile ) )
2009-05-08 13:28:41 +00:00
LogSilentF ( " Network: Could not delete temporary resource file (%s) " , strerror ( errno ) ) ;
2010-03-28 18:58:01 +00:00
if ( szStandalone [ 0 ] & & ! SEqual ( szFile , szStandalone ) )
if ( FileExists ( szStandalone ) )
2011-08-11 13:45:27 +00:00
if ( ! EraseFile ( szStandalone ) )
2009-05-08 13:28:41 +00:00
LogSilentF ( " Network: Could not delete temporary resource file (%s) " , strerror ( errno ) ) ;
szFile [ 0 ] = szStandalone [ 0 ] = ' \0 ' ;
fDirty = false ;
fTempFile = false ;
Core . Clear ( ) ;
Chunks . Clear ( ) ;
fRemoved = false ;
ClearLoad ( ) ;
}
int32_t C4Network2Res : : OpenFileRead ( )
{
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2016-11-02 23:58:02 +00:00
if ( ! GetStandalone ( nullptr , 0 , false , false , true ) ) return - 1 ;
2011-08-11 13:46:06 +00:00
// FIXME: Use standard OC file access api here
# ifdef _WIN32
return _wopen ( GetWideChar ( szStandalone ) , _O_BINARY | O_RDONLY ) ;
# else
2012-03-05 00:33:02 +00:00
return open ( szStandalone , _O_BINARY | O_CLOEXEC | O_RDONLY ) ;
2011-08-11 13:46:06 +00:00
# endif
2009-05-08 13:28:41 +00:00
}
int32_t C4Network2Res : : OpenFileWrite ( )
{
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2011-08-11 13:46:06 +00:00
// FIXME: Use standard OC file access api here
# ifdef _WIN32
return _wopen ( GetWideChar ( szStandalone ) , _O_BINARY | O_CREAT | O_WRONLY , S_IREAD | S_IWRITE ) ;
# else
2012-03-05 00:33:02 +00:00
return open ( szStandalone , _O_BINARY | O_CLOEXEC | O_CREAT | O_WRONLY , S_IREAD | S_IWRITE ) ;
2011-08-11 13:46:06 +00:00
# endif
2009-05-08 13:28:41 +00:00
}
void C4Network2Res : : StartNewLoads ( )
{
2010-03-28 18:58:01 +00:00
if ( ! pCChunks ) return ;
2009-05-08 13:28:41 +00:00
// count clients
int32_t iCChunkCnt = 0 ; ClientChunks * pChunks ;
2010-03-28 18:58:01 +00:00
for ( pChunks = pCChunks ; pChunks ; pChunks = pChunks - > Next )
2009-05-08 13:28:41 +00:00
iCChunkCnt + + ;
// create array
ClientChunks * * pC = new ClientChunks * [ iCChunkCnt ] ;
// initialize
int32_t i ;
2016-11-02 23:58:02 +00:00
for ( i = 0 ; i < iCChunkCnt ; i + + ) pC [ i ] = nullptr ;
2009-05-08 13:28:41 +00:00
// create shuffled order
2010-03-28 18:58:01 +00:00
for ( pChunks = pCChunks , i = 0 ; i < iCChunkCnt ; i + + , pChunks = pChunks - > Next )
2009-05-08 13:28:41 +00:00
{
// determine position
2016-04-25 15:32:23 +00:00
int32_t iPos = UnsyncedRandom ( iCChunkCnt - i ) ;
2009-05-08 13:28:41 +00:00
// find & set
2010-03-28 18:58:01 +00:00
for ( int32_t j = 0 ; ; j + + )
if ( ! pC [ j ] & & ! iPos - - )
2009-05-08 13:28:41 +00:00
{
pC [ j ] = pChunks ;
break ;
}
}
// start new load until maximum count reached
2010-03-28 18:58:01 +00:00
while ( iLoadCnt + 1 < = C4NetResMaxLoad )
2009-05-08 13:28:41 +00:00
{
int32_t ioLoadCnt = iLoadCnt ;
// search someone
2010-03-28 18:58:01 +00:00
for ( i = 0 ; i < iCChunkCnt ; i + + )
if ( pC [ i ] )
2009-05-08 13:28:41 +00:00
{
// try to start load
2010-03-28 18:58:01 +00:00
if ( ! StartLoad ( pC [ i ] - > ClientID , pC [ i ] - > Chunks ) )
2016-11-02 23:58:02 +00:00
{ RemoveCChunks ( pC [ i ] ) ; pC [ i ] = nullptr ; continue ; }
2009-05-08 13:28:41 +00:00
// success?
2010-03-28 18:58:01 +00:00
if ( iLoadCnt > ioLoadCnt ) break ;
2009-05-08 13:28:41 +00:00
}
// not found?
2010-03-28 18:58:01 +00:00
if ( i > = iCChunkCnt )
2009-05-08 13:28:41 +00:00
break ;
}
// clear up
delete [ ] pC ;
}
bool C4Network2Res : : StartLoad ( int32_t iFromClient , const C4Network2ResChunkData & Available )
{
assert ( pParent & & pParent - > getIOClass ( ) ) ;
// all slots used? ignore
2010-03-28 18:58:01 +00:00
if ( iLoadCnt + 1 > = C4NetResMaxLoad ) return true ;
2009-05-08 13:28:41 +00:00
// is there already a load by this client? ignore
2010-03-28 18:58:01 +00:00
for ( C4Network2ResLoad * pPos = pLoads ; pPos ; pPos = pPos - > Next ( ) )
if ( pPos - > getByClient ( ) = = iFromClient )
2009-05-08 13:28:41 +00:00
return true ;
// find chunk to retrieve
int32_t iLoads [ C4NetResMaxLoad ] ; int32_t i = 0 ;
2010-03-28 18:58:01 +00:00
for ( C4Network2ResLoad * pLoad = pLoads ; pLoad ; pLoad = pLoad - > Next ( ) )
2009-05-08 13:28:41 +00:00
iLoads [ i + + ] = pLoad - > getChunk ( ) ;
int32_t iRetrieveChunk = Chunks . GetChunkToRetrieve ( Available , i , iLoads ) ;
// nothing? ignore
2010-03-28 18:58:01 +00:00
if ( iRetrieveChunk < 0 | | ( uint32_t ) iRetrieveChunk > = Core . getChunkCnt ( ) )
2009-05-08 13:28:41 +00:00
return true ;
// search message connection for client
C4Network2IOConnection * pConn = pParent - > getIOClass ( ) - > GetMsgConnection ( iFromClient ) ;
2010-03-28 18:58:01 +00:00
if ( ! pConn ) return false ;
2009-05-08 13:28:41 +00:00
// send request
2010-03-28 18:58:01 +00:00
if ( ! pConn - > Send ( MkC4NetIOPacket ( PID_NetResReq , C4PacketResRequest ( Core . getID ( ) , iRetrieveChunk ) ) ) )
2009-05-08 13:28:41 +00:00
{ pConn - > DelRef ( ) ; return false ; }
pConn - > DelRef ( ) ;
# ifdef C4NET2RES_DEBUG_LOG
// log
Application . InteractiveThread . ThreadLogS ( " Network: Res: requesting chunk %d of %d:%s (%s) from client %d " ,
2010-03-28 18:58:01 +00:00
iRetrieveChunk , Core . getID ( ) , Core . getFileName ( ) , szFile , iFromClient ) ;
2009-05-08 13:28:41 +00:00
# endif
// create load class
C4Network2ResLoad * pnLoad = new C4Network2ResLoad ( iRetrieveChunk , iFromClient ) ;
// add to list
pnLoad - > pNext = pLoads ;
pLoads = pnLoad ;
iLoadCnt + + ;
// ok
return true ;
}
void C4Network2Res : : EndLoad ( )
{
// clear loading data
ClearLoad ( ) ;
// set complete
fLoading = false ;
// call handler
assert ( pParent ) ;
pParent - > OnResComplete ( this ) ;
}
void C4Network2Res : : ClearLoad ( )
{
// remove client chunks and loads
fLoading = false ;
2010-03-28 18:58:01 +00:00
while ( pCChunks ) RemoveCChunks ( pCChunks ) ;
while ( pLoads ) RemoveLoad ( pLoads ) ;
2009-05-08 13:28:41 +00:00
iDiscoverStartTime = iLoadCnt = 0 ;
}
void C4Network2Res : : RemoveLoad ( C4Network2ResLoad * pLoad )
{
2010-03-28 18:58:01 +00:00
if ( pLoad = = pLoads )
2009-05-08 13:28:41 +00:00
pLoads = pLoad - > Next ( ) ;
else
{
// find previous entry
C4Network2ResLoad * pPrev ;
2010-03-28 18:58:01 +00:00
for ( pPrev = pLoads ; pPrev & & pPrev - > Next ( ) ! = pLoad ; pPrev = pPrev - > Next ( ) ) { }
2009-05-08 13:28:41 +00:00
// remove
2010-03-28 18:58:01 +00:00
if ( pPrev )
2009-05-08 13:28:41 +00:00
pPrev - > pNext = pLoad - > Next ( ) ;
}
// delete
delete pLoad ;
iLoadCnt - - ;
}
void C4Network2Res : : RemoveCChunks ( ClientChunks * pChunks )
{
2010-03-28 18:58:01 +00:00
if ( pChunks = = pCChunks )
2009-05-08 13:28:41 +00:00
pCChunks = pChunks - > Next ;
else
{
// find previous entry
ClientChunks * pPrev ;
2010-03-28 18:58:01 +00:00
for ( pPrev = pCChunks ; pPrev & & pPrev - > Next ! = pChunks ; pPrev = pPrev - > Next ) { }
2009-05-08 13:28:41 +00:00
// remove
2010-03-28 18:58:01 +00:00
if ( pPrev )
2009-05-08 13:28:41 +00:00
pPrev - > Next = pChunks - > Next ;
}
// delete
delete pChunks ;
}
bool C4Network2Res : : OptimizeStandalone ( bool fSilent )
{
2010-03-27 16:05:02 +00:00
CStdLock FileLock ( & FileCSec ) ;
2009-05-08 13:28:41 +00:00
// for now: player files only
2010-03-28 18:58:01 +00:00
if ( Core . getType ( ) = = NRT_Player )
2009-05-08 13:28:41 +00:00
{
// log - this may take a few seconds
2010-03-28 18:58:01 +00:00
if ( ! fSilent ) LogF ( LoadResStr ( " IDS_PRC_NETPREPARING " ) , GetFilename ( szFile ) ) ;
2009-05-08 13:28:41 +00:00
// copy to temp file, if needed
2010-03-28 18:58:01 +00:00
if ( ! fTempFile & & SEqual ( szFile , szStandalone ) )
2009-05-08 13:28:41 +00:00
{
char szNewStandalone [ _MAX_PATH + 1 ] ;
2010-03-28 18:58:01 +00:00
if ( ! pParent - > FindTempResFileName ( szStandalone , szNewStandalone ) )
{ if ( ! fSilent ) Log ( " OptimizeStandalone: could not find free name for temporary file! " ) ; return false ; }
if ( ! C4Group_CopyItem ( szStandalone , szNewStandalone ) )
{ if ( ! fSilent ) Log ( " OptimizeStandalone: could not copy to temporary file! " ) ; return false ; } /* TODO: Test failure */
2009-05-08 13:28:41 +00:00
SCopy ( szNewStandalone , szStandalone , sizeof ( szStandalone ) - 1 ) ;
}
// open as group
C4Group Grp ;
2010-03-28 18:58:01 +00:00
if ( ! Grp . Open ( szStandalone ) )
{ if ( ! fSilent ) Log ( " OptimizeStandalone: could not open player file! " ) ; return false ; }
2009-05-08 13:28:41 +00:00
// remove bigicon, if the file size is too large
size_t iBigIconSize = 0 ;
2016-11-02 23:58:02 +00:00
if ( Grp . FindEntry ( C4CFN_BigIcon , nullptr , & iBigIconSize ) )
2009-05-08 13:28:41 +00:00
if ( iBigIconSize > C4NetResMaxBigicon * 1024 )
Grp . Delete ( C4CFN_BigIcon ) ;
Grp . Close ( ) ;
}
return true ;
}
// *** C4Network2ResChunk
2017-05-03 18:28:00 +00:00
C4Network2ResChunk : : C4Network2ResChunk ( ) = default ;
2009-05-08 13:28:41 +00:00
2017-05-03 18:28:00 +00:00
C4Network2ResChunk : : ~ C4Network2ResChunk ( ) = default ;
2009-05-08 13:28:41 +00:00
bool C4Network2ResChunk : : Set ( C4Network2Res * pRes , uint32_t inChunk )
{
const C4Network2ResCore & Core = pRes - > getCore ( ) ;
iResID = pRes - > getResID ( ) ;
iChunk = inChunk ;
// calculate offset and size
int32_t iOffset = iChunk * Core . getChunkSize ( ) ,
2015-11-15 12:53:01 +00:00
iSize = std : : min < int32_t > ( Core . getFileSize ( ) - iOffset , C4NetResChunkSize ) ;
2009-05-08 13:28:41 +00:00
if ( iSize < 0 ) { LogF ( " Network: could not get chunk from offset %d from resource file %s: File size is only %d! " , iOffset , pRes - > getFile ( ) , Core . getFileSize ( ) ) ; return false ; }
// open file
int32_t f = pRes - > OpenFileRead ( ) ;
2010-03-28 18:58:01 +00:00
if ( f = = - 1 ) { LogF ( " Network: could not open resource file %s! " , pRes - > getFile ( ) ) ; return false ; }
2009-05-08 13:28:41 +00:00
// seek
2010-03-28 18:58:01 +00:00
if ( iOffset )
if ( lseek ( f , iOffset , SEEK_SET ) ! = iOffset )
2009-05-08 13:28:41 +00:00
{ close ( f ) ; LogF ( " Network: could not read resource file %s! " , pRes - > getFile ( ) ) ; return false ; }
// read chunk of data
2016-04-17 19:07:53 +00:00
char * pBuf = ( char * ) malloc ( iSize ) ;
2010-03-28 18:58:01 +00:00
if ( read ( f , pBuf , iSize ) ! = iSize )
2016-04-17 21:56:36 +00:00
{ free ( pBuf ) ; close ( f ) ; LogF ( " Network: could not read resource file %s! " , pRes - > getFile ( ) ) ; return false ; }
2009-05-08 13:28:41 +00:00
// set
Data . Take ( pBuf , iSize ) ;
// close
close ( f ) ;
// ok
return true ;
}
bool C4Network2ResChunk : : AddTo ( C4Network2Res * pRes , C4Network2IO * pIO ) const
{
assert ( pRes ) ; assert ( pIO ) ;
const C4Network2ResCore & Core = pRes - > getCore ( ) ;
// check
2010-03-28 18:58:01 +00:00
if ( iResID ! = pRes - > getResID ( ) )
{
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_DEBUG_LOG
2012-10-21 20:20:43 +00:00
Application . InteractiveThread . ThreadLogS ( " C4Network2ResChunk(%d)::AddTo(%s [%d]): Resource ID mismatch! " , ( int ) iResID , ( const char * ) Core . getFileName ( ) , ( int ) pRes - > getResID ( ) ) ;
2009-05-08 13:28:41 +00:00
# endif
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// calculate offset and size
int32_t iOffset = iChunk * Core . getChunkSize ( ) ;
2010-03-28 18:58:01 +00:00
if ( iOffset + Data . getSize ( ) > Core . getFileSize ( ) )
{
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_DEBUG_LOG
2010-01-25 04:00:59 +00:00
Application . InteractiveThread . ThreadLogS ( " C4Network2ResChunk(%d)::AddTo(%s [%d]): Adding %d bytes at offset %d exceeds expected file size of %d! " , ( int ) iResID , ( const char * ) Core . getFileName ( ) , ( int ) pRes - > getResID ( ) , ( int ) Data . getSize ( ) , ( int ) iOffset , ( int ) Core . getFileSize ( ) ) ;
2009-05-08 13:28:41 +00:00
# endif
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// open file
int32_t f = pRes - > OpenFileWrite ( ) ;
2010-03-28 18:58:01 +00:00
if ( f = = - 1 )
{
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_DEBUG_LOG
2010-01-25 04:00:59 +00:00
Application . InteractiveThread . ThreadLogS ( " C4Network2ResChunk(%d)::AddTo(%s [%d]): Open write file error: %s! " , ( int ) iResID , ( const char * ) Core . getFileName ( ) , ( int ) pRes - > getResID ( ) , strerror ( errno ) ) ;
2009-05-08 13:28:41 +00:00
# endif
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// seek
2010-03-28 18:58:01 +00:00
if ( iOffset )
if ( lseek ( f , iOffset , SEEK_SET ) ! = iOffset )
{
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_DEBUG_LOG
2010-01-25 04:00:59 +00:00
Application . InteractiveThread . ThreadLogS ( " C4Network2ResChunk(%d)::AddTo(%s [%d]): lseek file error: %s! " , ( int ) iResID , ( const char * ) Core . getFileName ( ) , ( int ) pRes - > getResID ( ) , strerror ( errno ) ) ;
2009-05-08 13:28:41 +00:00
# endif
close ( f ) ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// write
2010-03-28 18:58:01 +00:00
if ( write ( f , Data . getData ( ) , Data . getSize ( ) ) ! = int32_t ( Data . getSize ( ) ) )
{
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_DEBUG_LOG
2010-01-25 04:00:59 +00:00
Application . InteractiveThread . ThreadLogS ( " C4Network2ResChunk(%d)::AddTo(%s [%d]): write error: %s! " , ( int ) iResID , ( const char * ) Core . getFileName ( ) , ( int ) pRes - > getResID ( ) , strerror ( errno ) ) ;
2009-05-08 13:28:41 +00:00
# endif
close ( f ) ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// ok, add chunks
close ( f ) ;
pRes - > Chunks . AddChunk ( iChunk ) ;
return true ;
}
void C4Network2ResChunk : : CompileFunc ( StdCompiler * pComp )
{
// pack header
pComp - > Value ( mkNamingAdapt ( iResID , " ResID " , - 1 ) ) ;
2010-03-27 16:05:02 +00:00
pComp - > Value ( mkNamingAdapt ( iChunk , " Chunk " , ~ 0U ) ) ;
// Data
pComp - > Value ( mkNamingAdapt ( Data , " Data " ) ) ;
2009-05-08 13:28:41 +00:00
}
// *** C4Network2ResList
C4Network2ResList : : C4Network2ResList ( )
2017-05-07 11:50:00 +00:00
: ResListCSec ( this ) ,
iNextResID ( ( - 1 ) < < 16 )
2009-05-08 13:28:41 +00:00
{
}
C4Network2ResList : : ~ C4Network2ResList ( )
{
Clear ( ) ;
}
bool C4Network2ResList : : Init ( int32_t inClientID , C4Network2IO * pIOClass ) // by main thread
{
// clear old list
Clear ( ) ;
// safe IO class
pIO = pIOClass ;
// set client id
iNextResID = iClientID = 0 ;
SetLocalID ( inClientID ) ;
// create network path
2010-03-28 18:58:01 +00:00
if ( ! CreateNetworkFolder ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// ok
return true ;
}
void C4Network2ResList : : SetLocalID ( int32_t inClientID )
{
CStdLock ResIDLock ( & ResIDCSec ) ;
int32_t iOldClientID = iClientID ;
int32_t iIDDiff = ( inClientID - iClientID ) < < 16 ;
// set new counter
iClientID = inClientID ;
iNextResID + = iIDDiff ;
2012-10-21 20:20:43 +00:00
// change resource ids
2009-05-08 13:28:41 +00:00
CStdLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( pRes - > getResClient ( ) = = iOldClientID )
2009-05-08 13:28:41 +00:00
pRes - > ChangeID ( pRes - > getResID ( ) + iIDDiff ) ;
}
int32_t C4Network2ResList : : nextResID ( ) // by main thread
{
CStdLock ResIDLock ( & ResIDCSec ) ;
assert ( iNextResID > = ( iClientID < < 16 ) ) ;
2010-03-28 18:58:01 +00:00
if ( iNextResID > = ( ( iClientID + 1 ) < < 16 ) - 1 )
2015-11-15 12:53:01 +00:00
iNextResID = std : : max < int32_t > ( 0 , iClientID ) < < 16 ;
2009-05-08 13:28:41 +00:00
// find free
2010-03-28 18:58:01 +00:00
while ( getRes ( iNextResID ) )
2009-05-08 13:28:41 +00:00
iNextResID + + ;
return iNextResID + + ;
}
C4Network2Res * C4Network2ResList : : getRes ( int32_t iResID )
{
CStdShareLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pCur = pFirst ; pCur ; pCur = pCur - > pNext )
if ( pCur - > getResID ( ) = = iResID )
2009-05-08 13:28:41 +00:00
return pCur ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
}
C4Network2Res * C4Network2ResList : : getRes ( const char * szFile , bool fLocalOnly )
{
CStdShareLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pCur = pFirst ; pCur ; pCur = pCur - > pNext )
if ( ! pCur - > isAnonymous ( ) )
if ( SEqual ( pCur - > getFile ( ) , szFile ) )
2009-05-08 13:28:41 +00:00
if ( ! fLocalOnly | | pCur - > getResClient ( ) = = iClientID )
return pCur ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
}
C4Network2Res : : Ref C4Network2ResList : : getRefRes ( int32_t iResID )
{
CStdShareLock ResListLock ( & ResListCSec ) ;
return getRes ( iResID ) ;
}
C4Network2Res : : Ref C4Network2ResList : : getRefRes ( const char * szFile , bool fLocalOnly )
{
CStdShareLock ResListLock ( & ResListCSec ) ;
return getRes ( szFile , fLocalOnly ) ;
}
C4Network2Res : : Ref C4Network2ResList : : getRefNextRes ( int32_t iResID )
{
CStdShareLock ResListLock ( & ResListCSec ) ;
2016-11-02 23:58:02 +00:00
C4Network2Res * pRes = nullptr ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pCur = pFirst ; pCur ; pCur = pCur - > pNext )
if ( ! pCur - > isRemoved ( ) & & pCur - > getResID ( ) > = iResID )
if ( ! pRes | | pRes - > getResID ( ) > pCur - > getResID ( ) )
pRes = pCur ;
2009-05-08 13:28:41 +00:00
return pRes ;
}
void C4Network2ResList : : Add ( C4Network2Res * pRes )
{
// get locks
CStdShareLock ResListLock ( & ResListCSec ) ;
CStdLock ResListAddLock ( & ResListAddCSec ) ;
// reference
pRes - > AddRef ( ) ;
// add
pRes - > pNext = pFirst ;
pFirst = pRes ;
}
C4Network2Res : : Ref C4Network2ResList : : AddByFile ( const char * strFilePath , bool fTemp , C4Network2ResType eType , int32_t iResID , const char * szResName , bool fAllowUnloadable )
{
// already in list?
C4Network2Res : : Ref pRes = getRefRes ( strFilePath ) ;
2010-03-28 18:58:01 +00:00
if ( pRes ) return pRes ;
2012-10-21 20:20:43 +00:00
// get resource ID
2010-03-28 18:58:01 +00:00
if ( iResID < 0 ) iResID = nextResID ( ) ;
2016-11-02 23:58:02 +00:00
if ( iResID < 0 ) { Log ( " AddByFile: no more resource IDs available! " ) ; return nullptr ; }
2009-05-08 13:28:41 +00:00
// create new
pRes = new C4Network2Res ( this ) ;
// initialize
2016-11-02 23:58:02 +00:00
if ( ! pRes - > SetByFile ( strFilePath , fTemp , eType , iResID , szResName ) ) { return nullptr ; }
2009-05-08 13:28:41 +00:00
// create standalone for non-system files
// system files shouldn't create a standalone; they should never be marked loadable!
if ( eType ! = NRT_System )
2016-11-02 23:58:02 +00:00
if ( ! pRes - > GetStandalone ( nullptr , 0 , true , fAllowUnloadable ) )
2010-03-28 18:58:01 +00:00
if ( ! fAllowUnloadable )
{
2009-05-08 13:28:41 +00:00
delete pRes ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// add to list
Add ( pRes ) ;
return pRes ;
}
C4Network2Res : : Ref C4Network2ResList : : AddByGroup ( C4Group * pGrp , bool fTemp , C4Network2ResType eType , int32_t iResID , const char * szResName , bool fAllowUnloadable )
{
2012-10-21 20:20:43 +00:00
// get resource ID
2010-03-28 18:58:01 +00:00
if ( iResID < 0 ) iResID = nextResID ( ) ;
2016-11-02 23:58:02 +00:00
if ( iResID < 0 ) { Log ( " AddByGroup: no more resource IDs available! " ) ; return nullptr ; }
2009-05-08 13:28:41 +00:00
// create new
C4Network2Res : : Ref pRes = new C4Network2Res ( this ) ;
// initialize
2010-03-28 18:58:01 +00:00
if ( ! pRes - > SetByGroup ( pGrp , fTemp , eType , iResID , szResName ) )
{
2009-05-08 13:28:41 +00:00
delete pRes ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// create standalone
2016-11-02 23:58:02 +00:00
if ( ! pRes - > GetStandalone ( nullptr , 0 , true , fAllowUnloadable ) )
2010-03-28 18:58:01 +00:00
if ( ! fAllowUnloadable )
{
2009-05-08 13:28:41 +00:00
delete pRes ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// add to list
Add ( pRes ) ;
return pRes ;
}
C4Network2Res : : Ref C4Network2ResList : : AddByCore ( const C4Network2ResCore & Core , bool fLoad ) // by main thread
{
// already in list?
C4Network2Res : : Ref pRes = getRefRes ( Core . getID ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( pRes ) return pRes ;
2009-05-08 13:28:41 +00:00
# ifdef C4NET2RES_LOAD_ALL
// load without check (if possible)
2010-03-28 18:58:01 +00:00
if ( Core . isLoadable ( ) ) return AddLoad ( Core ) ;
2009-05-08 13:28:41 +00:00
# endif
// create new
pRes = new C4Network2Res ( this ) ;
// try set by core
2010-03-28 18:58:01 +00:00
if ( ! pRes - > SetByCore ( Core , true ) )
{
2010-03-27 16:05:02 +00:00
pRes . Clear ( ) ;
2009-05-08 13:28:41 +00:00
// try load (if specified)
2016-11-02 23:58:02 +00:00
return fLoad ? AddLoad ( Core ) : nullptr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// log
Application . InteractiveThread . ThreadLogS ( " Network: Found identical %s. Not loading. " , pRes - > getCore ( ) . getFileName ( ) ) ;
// add to list
Add ( pRes ) ;
// ok
return pRes ;
}
C4Network2Res : : Ref C4Network2ResList : : AddLoad ( const C4Network2ResCore & Core ) // by main thread
{
// marked unloadable by creator?
2010-03-28 18:58:01 +00:00
if ( ! Core . isLoadable ( ) )
2009-05-08 13:28:41 +00:00
{
// show error msg
Application . InteractiveThread . ThreadLog ( " Network: Cannot load %s (marked unloadable) " , Core . getFileName ( ) ) ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
}
// create new
C4Network2Res : : Ref pRes = new C4Network2Res ( this ) ;
// initialize
pRes - > SetLoad ( Core ) ;
// log
Application . InteractiveThread . ThreadLogS ( " Network: loading %s... " , Core . getFileName ( ) ) ;
// add to list
Add ( pRes ) ;
return pRes ;
}
void C4Network2ResList : : RemoveAtClient ( int32_t iClientID ) // by main thread
{
CStdShareLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( pRes - > getResClient ( ) = = iClientID )
2009-05-08 13:28:41 +00:00
pRes - > Remove ( ) ;
}
void C4Network2ResList : : Clear ( )
{
CStdShareLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pRes = pFirst ; pRes ; pRes = pRes - > pNext )
2009-05-08 13:28:41 +00:00
{
pRes - > Remove ( ) ;
pRes - > iLastReqTime = 0 ;
}
iClientID = C4ClientIDUnknown ;
iLastDiscover = iLastStatus = 0 ;
}
void C4Network2ResList : : OnClientConnect ( C4Network2IOConnection * pConn ) // by main thread
{
2012-10-21 20:20:43 +00:00
// discover resources
2009-05-08 13:28:41 +00:00
SendDiscover ( pConn ) ;
}
void C4Network2ResList : : HandlePacket ( char cStatus , const C4PacketBase * pPacket , C4Network2IOConnection * pConn )
{
// security
2010-03-28 18:58:01 +00:00
if ( ! pConn ) return ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
# define GETPKT(type, name) \
assert ( pPacket ) ; const type & name = \
2015-03-25 18:04:04 +00:00
static_cast < const type & > ( * pPacket ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
switch ( cStatus )
2009-05-08 13:28:41 +00:00
{
2012-10-21 20:20:43 +00:00
case PID_NetResDis : // resource discover
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! pConn - > isOpen ( ) ) break ;
2009-05-08 13:28:41 +00:00
GETPKT ( C4PacketResDiscover , Pkt ) ;
2012-10-21 20:20:43 +00:00
// search matching resources
2009-05-08 13:28:41 +00:00
CStdShareLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( Pkt . isIDPresent ( pRes - > getResID ( ) ) )
2009-05-08 13:28:41 +00:00
// must be binary compatible
2010-03-28 18:58:01 +00:00
if ( pRes - > IsBinaryCompatible ( ) )
2009-05-08 13:28:41 +00:00
pRes - > OnDiscover ( pConn ) ;
}
break ;
2012-10-21 20:20:43 +00:00
case PID_NetResStat : // resource status
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! pConn - > isOpen ( ) ) break ;
2009-05-08 13:28:41 +00:00
GETPKT ( C4PacketResStatus , Pkt ) ;
2012-10-21 20:20:43 +00:00
// matching resource?
2009-05-08 13:28:41 +00:00
CStdShareLock ResListLock ( & ResListCSec ) ;
C4Network2Res * pRes = getRes ( Pkt . getResID ( ) ) ;
// present / being loaded? call handler
2010-03-28 18:58:01 +00:00
if ( pRes )
2009-05-08 13:28:41 +00:00
pRes - > OnStatus ( Pkt . getChunks ( ) , pConn ) ;
}
break ;
2012-10-21 20:20:43 +00:00
case PID_NetResDerive : // resource derive
2009-05-08 13:28:41 +00:00
{
GETPKT ( C4Network2ResCore , Core ) ;
2010-03-28 18:58:01 +00:00
if ( Core . getDerID ( ) < 0 ) break ;
2012-10-21 20:20:43 +00:00
// Check if there is a anonymous derived resource with matching parent.
2009-05-08 13:28:41 +00:00
CStdShareLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( pRes - > isAnonymous ( ) & & pRes - > getCore ( ) . getDerID ( ) = = Core . getDerID ( ) )
2009-05-08 13:28:41 +00:00
pRes - > FinishDerive ( Core ) ;
}
break ;
2012-10-21 20:20:43 +00:00
case PID_NetResReq : // resource request
2009-05-08 13:28:41 +00:00
{
GETPKT ( C4PacketResRequest , Pkt ) ;
2012-10-21 20:20:43 +00:00
// find resource
2009-05-08 13:28:41 +00:00
CStdShareLock ResListLock ( & ResListCSec ) ;
C4Network2Res * pRes = getRes ( Pkt . getReqID ( ) ) ;
// send requested chunk
2010-03-28 18:58:01 +00:00
if ( pRes & & pRes - > IsBinaryCompatible ( ) ) pRes - > SendChunk ( Pkt . getReqChunk ( ) , pConn - > getClientID ( ) ) ;
2009-05-08 13:28:41 +00:00
}
break ;
case PID_NetResData : // a chunk of data is coming in
{
GETPKT ( C4Network2ResChunk , Chunk ) ;
2012-10-21 20:20:43 +00:00
// find resource
2009-05-08 13:28:41 +00:00
CStdShareLock ResListLock ( & ResListCSec ) ;
C4Network2Res * pRes = getRes ( Chunk . getResID ( ) ) ;
// send requested chunk
2010-03-28 18:58:01 +00:00
if ( pRes ) pRes - > OnChunk ( Chunk ) ;
2009-05-08 13:28:41 +00:00
}
break ;
}
2010-03-28 18:58:01 +00:00
# undef GETPKT
2009-05-08 13:28:41 +00:00
}
void C4Network2ResList : : OnTimer ( )
{
CStdShareLock ResListLock ( & ResListCSec ) ;
C4Network2Res * pRes ;
// do loads, check timeouts
2010-03-28 18:58:01 +00:00
for ( pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( pRes - > isLoading ( ) & & ! pRes - > isRemoved ( ) )
if ( ! pRes - > DoLoad ( ) )
2009-05-08 13:28:41 +00:00
pRes - > Remove ( ) ;
// discovery time?
2016-11-02 23:58:02 +00:00
if ( ! iLastDiscover | | difftime ( time ( nullptr ) , iLastDiscover ) > = C4NetResDiscoverInterval )
2009-05-08 13:28:41 +00:00
{
// needed?
bool fSendDiscover = false ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( pRes - > isLoading ( ) & & ! pRes - > isRemoved ( ) )
2009-05-08 13:28:41 +00:00
fSendDiscover | = pRes - > NeedsDiscover ( ) ;
// send
2010-03-28 18:58:01 +00:00
if ( fSendDiscover )
2009-05-08 13:28:41 +00:00
SendDiscover ( ) ;
}
// status update?
2016-11-02 23:58:02 +00:00
if ( ! iLastStatus | | difftime ( time ( nullptr ) , iLastStatus ) > = C4NetResStatusInterval )
2009-05-08 13:28:41 +00:00
{
// any?
bool fStatusUpdates = false ;
2010-03-28 18:58:01 +00:00
for ( pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( pRes - > isDirty ( ) & & ! pRes - > isRemoved ( ) )
2009-05-08 13:28:41 +00:00
fStatusUpdates | = pRes - > SendStatus ( ) ;
// set time accordingly
2016-11-02 23:58:02 +00:00
iLastStatus = fStatusUpdates ? time ( nullptr ) : 0 ;
2009-05-08 13:28:41 +00:00
}
}
void C4Network2ResList : : OnShareFree ( CStdCSecEx * pCSec )
{
2010-03-28 18:58:01 +00:00
if ( pCSec = = & ResListCSec )
2009-05-08 13:28:41 +00:00
{
// remove entries
2016-11-02 23:58:02 +00:00
for ( C4Network2Res * pRes = pFirst , * pNext , * pPrev = nullptr ; pRes ; pRes = pNext )
2009-05-08 13:28:41 +00:00
{
pNext = pRes - > pNext ;
2016-11-02 23:58:02 +00:00
if ( pRes - > isRemoved ( ) & & ( ! pRes - > getLastReqTime ( ) | | difftime ( time ( nullptr ) , pRes - > getLastReqTime ( ) ) > C4NetResDeleteTime ) )
2009-05-08 13:28:41 +00:00
{
// unlink
( pPrev ? pPrev - > pNext : pFirst ) = pNext ;
// remove
2016-11-02 23:58:02 +00:00
pRes - > pNext = nullptr ;
2009-05-08 13:28:41 +00:00
pRes - > DelRef ( ) ;
}
else
pPrev = pRes ;
}
}
}
bool C4Network2ResList : : SendDiscover ( C4Network2IOConnection * pTo ) // by both
{
// make packet
C4PacketResDiscover Pkt ;
// add special retrieves
CStdShareLock ResListLock ( & ResListCSec ) ;
2010-03-28 18:58:01 +00:00
for ( C4Network2Res * pRes = pFirst ; pRes ; pRes = pRes - > pNext )
if ( ! pRes - > isRemoved ( ) )
if ( pRes - > isLoading ( ) )
2009-05-08 13:28:41 +00:00
Pkt . AddDisID ( pRes - > getResID ( ) ) ;
ResListLock . Clear ( ) ;
// empty?
2010-03-28 18:58:01 +00:00
if ( ! Pkt . getDisIDCnt ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// broadcast?
2010-03-28 18:58:01 +00:00
if ( ! pTo )
2009-05-08 13:28:41 +00:00
{
// save time
2016-11-02 23:58:02 +00:00
iLastDiscover = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
// send
return pIO - > BroadcastMsg ( MkC4NetIOPacket ( PID_NetResDis , Pkt ) ) ;
}
else
return pTo - > Send ( MkC4NetIOPacket ( PID_NetResDis , Pkt ) ) ;
}
void C4Network2ResList : : OnResComplete ( C4Network2Res * pRes )
{
// log (network thread -> ThreadLog)
Application . InteractiveThread . ThreadLogS ( " Network: %s received. " , pRes - > getCore ( ) . getFileName ( ) ) ;
2012-10-21 20:20:43 +00:00
// call handler (ctrl might wait for this resource)
2009-06-15 22:06:37 +00:00
: : Control . Network . OnResComplete ( pRes ) ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2ResList : : CreateNetworkFolder ( )
{
// get network path without trailing backslash
char szNetworkPath [ _MAX_PATH + 1 ] ;
SCopy ( Config . AtNetworkPath ( " " ) , szNetworkPath , _MAX_PATH ) ;
TruncateBackslash ( szNetworkPath ) ;
// but make sure that the configured path has one
AppendBackslash ( Config . Network . WorkPath ) ;
// does not exist?
2011-08-11 13:45:27 +00:00
if ( ! DirectoryExists ( szNetworkPath ) )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! CreatePath ( szNetworkPath ) )
2009-05-08 13:28:41 +00:00
{ LogFatal ( " Network: could not create network path! " ) ; return false ; }
return true ;
}
return true ;
}
bool C4Network2ResList : : FindTempResFileName ( const char * szFilename , char * pTarget )
{
2010-03-28 18:58:01 +00:00
char safeFilename [ _MAX_PATH ] ;
char * safePos = safeFilename ;
while ( * szFilename )
{
if ( ( * szFilename > = ' a ' & & * szFilename < = ' z ' ) | |
( * szFilename > = ' A ' & & * szFilename < = ' Z ' ) | |
( * szFilename > = ' 0 ' & & * szFilename < = ' 9 ' ) | |
( * szFilename = = ' . ' ) | | ( * szFilename = = ' / ' ) )
* safePos = * szFilename ;
else
* safePos = ' _ ' ;
+ + safePos ;
+ + szFilename ;
}
* safePos = 0 ;
szFilename = safeFilename ;
2009-05-08 13:28:41 +00:00
// create temporary file
SCopy ( Config . AtNetworkPath ( GetFilename ( szFilename ) ) , pTarget , _MAX_PATH ) ;
// file name is free?
2011-08-11 13:45:27 +00:00
if ( ! ItemExists ( pTarget ) ) return true ;
2009-05-08 13:28:41 +00:00
// find another file name
char szFileMask [ _MAX_PATH + 1 ] ;
SCopy ( pTarget , szFileMask , GetExtension ( pTarget ) - 1 - pTarget ) ;
SAppend ( " _%d " , szFileMask , _MAX_PATH ) ;
SAppend ( GetExtension ( pTarget ) - 1 , szFileMask , _MAX_PATH ) ;
2010-03-28 18:58:01 +00:00
for ( int32_t i = 2 ; i < 1000 ; i + + )
2009-05-08 13:28:41 +00:00
{
snprintf ( pTarget , _MAX_PATH , szFileMask , i ) ;
// doesn't exist?
2011-08-11 13:45:27 +00:00
if ( ! ItemExists ( pTarget ) )
2009-05-08 13:28:41 +00:00
return true ;
}
// not found
return false ;
}