1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "common/util.h"
26 #include "common/system.h"
27 #include "backends/fs/abstract-fs.h"
28 #include "backends/fs/fs-factory.h"
35 FSNode::FSNode(AbstractFSNode
*realNode
)
36 : _realNode(realNode
) {
39 FSNode::FSNode(const Common::String
&p
) {
41 FilesystemFactory
*factory
= g_system
->getFilesystemFactory();
42 AbstractFSNode
*tmp
= 0;
44 if (p
.empty() || p
== ".")
45 tmp
= factory
->makeCurrentDirectoryFileNode();
47 tmp
= factory
->makeFileNodePath(p
);
48 _realNode
= Common::SharedPtr
<AbstractFSNode
>(tmp
);
51 bool FSNode::operator<(const FSNode
& node
) const {
52 if (isDirectory() != node
.isDirectory())
55 return getDisplayName().compareToIgnoreCase(node
.getDisplayName()) < 0;
58 bool FSNode::exists() const {
62 return _realNode
->exists();
65 FSNode
FSNode::getChild(const Common::String
&n
) const {
66 // If this node is invalid or not a directory, return an invalid node
67 if (_realNode
== 0 || !_realNode
->isDirectory())
70 AbstractFSNode
*node
= _realNode
->getChild(n
);
74 bool FSNode::getChildren(FSList
&fslist
, ListMode mode
, bool hidden
) const {
75 if (!_realNode
|| !_realNode
->isDirectory())
80 if (!_realNode
->getChildren(tmp
, mode
, hidden
))
84 for (AbstractFSList::iterator i
= tmp
.begin(); i
!= tmp
.end(); ++i
) {
85 fslist
.push_back(FSNode(*i
));
91 Common::String
FSNode::getDisplayName() const {
93 return _realNode
->getDisplayName();
96 Common::String
FSNode::getName() const {
98 return _realNode
->getName();
101 FSNode
FSNode::getParent() const {
105 AbstractFSNode
*node
= _realNode
->getParent();
113 Common::String
FSNode::getPath() const {
115 return _realNode
->getPath();
118 bool FSNode::isDirectory() const {
122 return _realNode
->isDirectory();
125 bool FSNode::isReadable() const {
129 return _realNode
->isReadable();
132 bool FSNode::isWritable() const {
136 return _realNode
->isWritable();
139 Common::SeekableReadStream
*FSNode::createReadStream() const {
143 if (!_realNode
->exists()) {
144 warning("FSNode::createReadStream: FSNode does not exist");
146 } else if (_realNode
->isDirectory()) {
147 warning("FSNode::createReadStream: FSNode is a directory");
151 return _realNode
->createReadStream();
154 Common::WriteStream
*FSNode::createWriteStream() const {
158 if (_realNode
->isDirectory()) {
159 warning("FSNode::createWriteStream: FSNode is a directory");
163 return _realNode
->createWriteStream();
166 FSDirectory::FSDirectory(const FSNode
&node
, int depth
, bool flat
)
167 : _node(node
), _cached(false), _depth(depth
), _flat(flat
) {
170 FSDirectory::FSDirectory(const String
&prefix
, const FSNode
&node
, int depth
, bool flat
)
171 : _node(node
), _cached(false), _depth(depth
), _flat(flat
) {
176 FSDirectory::FSDirectory(const String
&name
, int depth
, bool flat
)
177 : _node(name
), _cached(false), _depth(depth
), _flat(flat
) {
180 FSDirectory::FSDirectory(const String
&prefix
, const String
&name
, int depth
, bool flat
)
181 : _node(name
), _cached(false), _depth(depth
), _flat(flat
) {
186 FSDirectory::~FSDirectory() {
189 void FSDirectory::setPrefix(const String
&prefix
) {
192 if (!_prefix
.empty() && !_prefix
.hasSuffix("/"))
196 FSNode
FSDirectory::getFSNode() const {
200 FSNode
*FSDirectory::lookupCache(NodeCache
&cache
, const String
&name
) const {
201 // make caching as lazy as possible
205 if (cache
.contains(name
))
212 bool FSDirectory::hasFile(const String
&name
) {
213 if (name
.empty() || !_node
.isDirectory())
216 FSNode
*node
= lookupCache(_fileCache
, name
);
217 return node
&& node
->exists();
220 ArchiveMemberPtr
FSDirectory::getMember(const String
&name
) {
221 if (name
.empty() || !_node
.isDirectory())
222 return ArchiveMemberPtr();
224 FSNode
*node
= lookupCache(_fileCache
, name
);
226 if (!node
|| !node
->exists()) {
227 warning("FSDirectory::getMember: FSNode does not exist");
228 return ArchiveMemberPtr();
229 } else if (node
->isDirectory()) {
230 warning("FSDirectory::getMember: FSNode is a directory");
231 return ArchiveMemberPtr();
234 return ArchiveMemberPtr(new FSNode(*node
));
237 SeekableReadStream
*FSDirectory::createReadStreamForMember(const String
&name
) const {
238 if (name
.empty() || !_node
.isDirectory())
241 FSNode
*node
= lookupCache(_fileCache
, name
);
244 SeekableReadStream
*stream
= node
->createReadStream();
246 warning("FSDirectory::createReadStreamForMember: Can't create stream for file '%s'", name
.c_str());
251 FSDirectory
*FSDirectory::getSubDirectory(const String
&name
, int depth
, bool flat
) {
252 return getSubDirectory(String::emptyString
, name
, depth
, flat
);
255 FSDirectory
*FSDirectory::getSubDirectory(const String
&prefix
, const String
&name
, int depth
, bool flat
) {
256 if (name
.empty() || !_node
.isDirectory())
259 FSNode
*node
= lookupCache(_subDirCache
, name
);
263 return new FSDirectory(prefix
, *node
, depth
, flat
);
266 void FSDirectory::cacheDirectoryRecursive(FSNode node
, int depth
, const String
& prefix
) const {
271 node
.getChildren(list
, FSNode::kListAll
, false);
273 FSList::iterator it
= list
.begin();
274 for ( ; it
!= list
.end(); ++it
) {
275 String name
= prefix
+ it
->getName();
277 // don't touch name as it might be used for warning messages
278 String lowercaseName
= name
;
279 lowercaseName
.toLowercase();
281 // since the hashmap is case insensitive, we need to check for clashes when caching
282 if (it
->isDirectory()) {
283 if (!_flat
&& _subDirCache
.contains(lowercaseName
)) {
284 warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring sub-directory '%s'", name
.c_str());
286 if (_subDirCache
.contains(lowercaseName
)) {
287 warning("FSDirectory::cacheDirectory: name clash when building subDirCache with subdirectory '%s'", name
.c_str());
289 cacheDirectoryRecursive(*it
, depth
- 1, _flat
? prefix
: lowercaseName
+ "/");
290 _subDirCache
[lowercaseName
] = *it
;
293 if (_fileCache
.contains(lowercaseName
)) {
294 warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring file '%s'", name
.c_str());
296 _fileCache
[lowercaseName
] = *it
;
303 void FSDirectory::ensureCached() const {
306 cacheDirectoryRecursive(_node
, _depth
, _prefix
);
310 int FSDirectory::listMatchingMembers(ArchiveMemberList
&list
, const String
&pattern
) {
311 if (!_node
.isDirectory())
317 String
lowercasePattern(pattern
);
318 lowercasePattern
.toLowercase();
321 NodeCache::iterator it
= _fileCache
.begin();
322 for ( ; it
!= _fileCache
.end(); ++it
) {
323 if (it
->_key
.matchString(lowercasePattern
, true)) {
324 list
.push_back(ArchiveMemberPtr(new FSNode(it
->_value
)));
331 int FSDirectory::listMembers(ArchiveMemberList
&list
) {
332 if (!_node
.isDirectory())
339 for (NodeCache::iterator it
= _fileCache
.begin(); it
!= _fileCache
.end(); ++it
) {
340 list
.push_back(ArchiveMemberPtr(new FSNode(it
->_value
)));
348 } // End of namespace Common