2008-08-31 13:58:17 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
2021-12-26 17:47:58 +00:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2014-02-18 01:34:18 +00:00
*
2008-08-31 13:58:17 +00:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2014-02-18 01:34:18 +00:00
*
2008-08-31 13:58:17 +00:00
* You should have received a copy of the GNU General Public License
2021-12-26 17:47:58 +00:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2008-08-31 13:58:17 +00:00
*
*/
# include "common/archive.h"
# include "common/fs.h"
2008-09-27 18:32:01 +00:00
# include "common/system.h"
2011-04-24 08:34:27 +00:00
# include "common/textconsole.h"
2008-08-31 13:58:17 +00:00
namespace Common {
2011-12-13 16:20:25 +00:00
GenericArchiveMember : : GenericArchiveMember ( const String & name , const Archive * parent )
2008-10-03 16:57:40 +00:00
: _parent ( parent ) , _name ( name ) {
}
String GenericArchiveMember : : getName ( ) const {
return _name ;
}
2009-01-23 04:36:18 +00:00
SeekableReadStream * GenericArchiveMember : : createReadStream ( ) const {
2009-01-23 04:45:44 +00:00
return _parent - > createReadStreamForMember ( _name ) ;
2008-10-03 16:57:40 +00:00
}
2008-08-31 13:58:17 +00:00
2021-08-09 20:23:36 +00:00
int Archive : : listMatchingMembers ( ArchiveMemberList & list , const Path & pattern ) const {
2008-09-05 18:24:41 +00:00
// Get all "names" (TODO: "files" ?)
2008-10-03 16:57:40 +00:00
ArchiveMemberList allNames ;
listMembers ( allNames ) ;
2008-09-05 18:24:41 +00:00
2021-08-09 20:23:36 +00:00
String patternString = pattern . toString ( ) ;
2008-09-05 18:24:41 +00:00
int matches = 0 ;
2011-12-13 16:20:25 +00:00
ArchiveMemberList : : const_iterator it = allNames . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = allNames . end ( ) ; + + it ) {
2009-11-21 20:19:15 +00:00
// TODO: We match case-insenstivie for now, our API does not define whether that's ok or not though...
// For our use case case-insensitive is probably what we want to have though.
2021-08-09 20:23:36 +00:00
if ( ( * it ) - > getName ( ) . matchString ( patternString , true , " / " ) ) {
2008-09-05 18:24:41 +00:00
list . push_back ( * it ) ;
matches + + ;
}
}
return matches ;
}
2008-08-31 13:58:17 +00:00
2009-03-09 22:26:02 +00:00
SearchSet : : ArchiveNodeList : : iterator SearchSet : : find ( const String & name ) {
2008-12-15 12:55:13 +00:00
ArchiveNodeList : : iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it ) {
2008-11-21 17:49:19 +00:00
if ( it - > _name = = name )
2008-08-31 13:58:17 +00:00
break ;
}
return it ;
}
2009-03-09 22:26:02 +00:00
SearchSet : : ArchiveNodeList : : const_iterator SearchSet : : find ( const String & name ) const {
ArchiveNodeList : : const_iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it ) {
2009-03-09 22:26:02 +00:00
if ( it - > _name = = name )
break ;
}
return it ;
}
2008-08-31 13:58:17 +00:00
/*
2021-04-15 19:20:04 +00:00
Keep the nodes sorted according to descending priorities .
In case two or node nodes have the same priority , insertion
order prevails .
2008-08-31 13:58:17 +00:00
*/
void SearchSet : : insert ( const Node & node ) {
2008-12-15 12:55:13 +00:00
ArchiveNodeList : : iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it ) {
2008-11-21 17:49:19 +00:00
if ( it - > _priority < node . _priority )
2008-08-31 13:58:17 +00:00
break ;
}
_list . insert ( it , node ) ;
}
2008-11-07 13:42:05 +00:00
void SearchSet : : add ( const String & name , Archive * archive , int priority , bool autoFree ) {
2008-08-31 13:58:17 +00:00
if ( find ( name ) = = _list . end ( ) ) {
2008-10-22 17:44:12 +00:00
Node node ( priority , name , archive , autoFree ) ;
2008-08-31 13:58:17 +00:00
insert ( node ) ;
} else {
2008-11-07 13:42:05 +00:00
if ( autoFree )
delete archive ;
2008-08-31 13:58:17 +00:00
warning ( " SearchSet::add: archive '%s' already present " , name . c_str ( ) ) ;
}
}
2009-06-01 00:01:32 +00:00
void SearchSet : : addDirectory ( const String & name , const String & directory , int priority , int depth , bool flat ) {
2008-12-27 17:16:48 +00:00
FSNode dir ( directory ) ;
2009-06-01 00:01:32 +00:00
addDirectory ( name , dir , priority , depth , flat ) ;
2008-12-27 17:16:48 +00:00
}
2009-06-01 00:01:32 +00:00
void SearchSet : : addDirectory ( const String & name , const FSNode & dir , int priority , int depth , bool flat ) {
2008-12-27 17:16:48 +00:00
if ( ! dir . exists ( ) | | ! dir . isDirectory ( ) )
return ;
2019-10-20 15:37:18 +00:00
add ( name , new FSDirectory ( dir , depth , flat , _ignoreClashes ) , priority ) ;
2008-12-27 17:16:48 +00:00
}
2013-06-06 19:41:14 +00:00
void SearchSet : : addSubDirectoriesMatching ( const FSNode & directory , String origPattern , bool ignoreCase , int priority , int depth , bool flat ) {
2009-09-23 00:15:00 +00:00
FSList subDirs ;
if ( ! directory . getChildren ( subDirs ) )
return ;
2009-10-03 20:12:44 +00:00
String nextPattern , pattern ;
String : : const_iterator sep = Common : : find ( origPattern . begin ( ) , origPattern . end ( ) , ' / ' ) ;
if ( sep ! = origPattern . end ( ) ) {
pattern = String ( origPattern . begin ( ) , sep ) ;
2009-09-23 00:15:00 +00:00
+ + sep ;
2009-10-03 20:12:44 +00:00
if ( sep ! = origPattern . end ( ) )
nextPattern = String ( sep , origPattern . end ( ) ) ;
2016-02-09 11:10:04 +00:00
} else {
2009-10-03 20:12:44 +00:00
pattern = origPattern ;
2009-09-23 00:15:00 +00:00
}
// TODO: The code we have for displaying all matches, which vary only in case, might
// be a bit overhead, but as long as we want to display all useful information to the
// user we will need to keep track of all directory names added so far. We might
// want to reconsider this though.
typedef HashMap < String , bool , IgnoreCase_Hash , IgnoreCase_EqualTo > MatchList ;
MatchList multipleMatches ;
MatchList : : iterator matchIter ;
for ( FSList : : const_iterator i = subDirs . begin ( ) ; i ! = subDirs . end ( ) ; + + i ) {
String name = i - > getName ( ) ;
2011-08-06 07:47:19 +00:00
if ( matchString ( name . c_str ( ) , pattern . c_str ( ) , ignoreCase ) ) {
2009-09-23 00:15:00 +00:00
matchIter = multipleMatches . find ( name ) ;
if ( matchIter = = multipleMatches . end ( ) ) {
multipleMatches [ name ] = true ;
} else {
if ( matchIter - > _value ) {
warning ( " Clash in case for match of pattern \" %s \" found in directory \" %s \" : \" %s \" " , pattern . c_str ( ) , directory . getPath ( ) . c_str ( ) , matchIter - > _key . c_str ( ) ) ;
matchIter - > _value = false ;
}
warning ( " Clash in case for match of pattern \" %s \" found in directory \" %s \" : \" %s \" " , pattern . c_str ( ) , directory . getPath ( ) . c_str ( ) , name . c_str ( ) ) ;
}
if ( nextPattern . empty ( ) )
2013-06-06 19:41:14 +00:00
addDirectory ( name , * i , priority , depth , flat ) ;
2009-09-23 00:15:00 +00:00
else
2013-06-06 19:41:14 +00:00
addSubDirectoriesMatching ( * i , nextPattern , ignoreCase , priority , depth , flat ) ;
2009-09-23 00:15:00 +00:00
}
}
}
2008-12-27 17:16:48 +00:00
2008-11-07 13:42:05 +00:00
void SearchSet : : remove ( const String & name ) {
2008-12-15 12:55:13 +00:00
ArchiveNodeList : : iterator it = find ( name ) ;
2008-08-31 13:58:17 +00:00
if ( it ! = _list . end ( ) ) {
2008-10-22 17:44:12 +00:00
if ( it - > _autoFree )
delete it - > _arc ;
2008-08-31 13:58:17 +00:00
_list . erase ( it ) ;
}
}
2008-09-06 22:09:34 +00:00
bool SearchSet : : hasArchive ( const String & name ) const {
return ( find ( name ) ! = _list . end ( ) ) ;
}
2008-08-31 13:58:17 +00:00
void SearchSet : : clear ( ) {
2008-12-15 12:55:13 +00:00
for ( ArchiveNodeList : : iterator i = _list . begin ( ) ; i ! = _list . end ( ) ; + + i ) {
2008-10-22 17:44:12 +00:00
if ( i - > _autoFree )
delete i - > _arc ;
}
2008-08-31 13:58:17 +00:00
_list . clear ( ) ;
}
2008-11-07 13:42:05 +00:00
void SearchSet : : setPriority ( const String & name , int priority ) {
2008-12-15 12:55:13 +00:00
ArchiveNodeList : : iterator it = find ( name ) ;
2008-08-31 13:58:17 +00:00
if ( it = = _list . end ( ) ) {
warning ( " SearchSet::setPriority: archive '%s' is not present " , name . c_str ( ) ) ;
return ;
}
2008-11-21 17:49:19 +00:00
if ( priority = = it - > _priority )
2008-08-31 13:58:17 +00:00
return ;
Node node ( * it ) ;
_list . erase ( it ) ;
node . _priority = priority ;
insert ( node ) ;
}
2021-08-01 23:33:22 +00:00
bool SearchSet : : hasFile ( const Path & path ) const {
if ( path . empty ( ) )
2008-08-31 13:58:17 +00:00
return false ;
2011-12-13 16:20:25 +00:00
ArchiveNodeList : : const_iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it ) {
2021-08-01 23:33:22 +00:00
if ( it - > _arc - > hasFile ( path ) )
2008-08-31 13:58:17 +00:00
return true ;
}
return false ;
}
2021-08-09 20:23:36 +00:00
int SearchSet : : listMatchingMembers ( ArchiveMemberList & list , const Path & pattern ) const {
2008-08-31 13:58:17 +00:00
int matches = 0 ;
2011-12-13 16:20:25 +00:00
ArchiveNodeList : : const_iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it )
2008-11-21 17:49:19 +00:00
matches + = it - > _arc - > listMatchingMembers ( list , pattern ) ;
2008-08-31 13:58:17 +00:00
return matches ;
}
2011-12-13 16:20:25 +00:00
int SearchSet : : listMembers ( ArchiveMemberList & list ) const {
2008-09-18 08:19:00 +00:00
int matches = 0 ;
2011-12-13 16:20:25 +00:00
ArchiveNodeList : : const_iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it )
2008-11-21 17:49:19 +00:00
matches + = it - > _arc - > listMembers ( list ) ;
2008-09-18 08:19:00 +00:00
return matches ;
}
2021-08-01 23:33:22 +00:00
const ArchiveMemberPtr SearchSet : : getMember ( const Path & path ) const {
if ( path . empty ( ) )
2008-11-01 12:49:29 +00:00
return ArchiveMemberPtr ( ) ;
2011-12-13 16:20:25 +00:00
ArchiveNodeList : : const_iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it ) {
2021-08-01 23:33:22 +00:00
if ( it - > _arc - > hasFile ( path ) )
return it - > _arc - > getMember ( path ) ;
2008-11-01 12:49:29 +00:00
}
return ArchiveMemberPtr ( ) ;
}
2021-08-01 23:33:22 +00:00
SeekableReadStream * SearchSet : : createReadStreamForMember ( const Path & path ) const {
if ( path . empty ( ) )
2018-04-05 18:25:28 +00:00
return nullptr ;
2008-08-31 13:58:17 +00:00
2009-03-09 22:26:02 +00:00
ArchiveNodeList : : const_iterator it = _list . begin ( ) ;
2016-02-09 11:10:04 +00:00
for ( ; it ! = _list . end ( ) ; + + it ) {
2021-08-01 23:33:22 +00:00
SeekableReadStream * stream = it - > _arc - > createReadStreamForMember ( path ) ;
2009-02-22 16:48:02 +00:00
if ( stream )
return stream ;
2008-08-31 13:58:17 +00:00
}
2018-04-05 18:25:28 +00:00
return nullptr ;
2008-08-31 13:58:17 +00:00
}
2008-09-11 13:24:01 +00:00
2008-09-27 18:32:01 +00:00
SearchManager : : SearchManager ( ) {
2018-04-15 12:00:56 +00:00
clear ( ) ; // Force a reset
2008-09-27 18:32:01 +00:00
}
void SearchManager : : clear ( ) {
SearchSet : : clear ( ) ;
// Always keep system specific archives in the SearchManager.
// But we give them a lower priority than the default priority (which is 0),
// so that archives added by client code are searched first.
2009-01-29 22:09:06 +00:00
if ( g_system )
g_system - > addSysArchivesToSearchSet ( * this , - 1 ) ;
2008-10-02 17:52:29 +00:00
2019-12-16 20:58:26 +00:00
# ifndef __ANDROID__
2008-10-02 17:52:29 +00:00
// Add the current dir as a very last resort.
2021-02-28 09:28:23 +00:00
// See also bug #3984.
2019-12-16 20:58:26 +00:00
// But don't do this for Android platform, since it may lead to crashes
2008-10-22 17:08:17 +00:00
addDirectory ( " . " , " . " , - 2 ) ;
2019-12-16 20:58:26 +00:00
# endif
2008-09-27 18:32:01 +00:00
}
2008-09-11 13:24:01 +00:00
2017-07-10 19:17:41 +00:00
DECLARE_SINGLETON ( SearchManager ) ;
2011-06-27 23:06:23 +00:00
} // namespace Common