1 /* ScummVM - Graphic Adventure Engine
2  *
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.
6  *
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.
11  *
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.
16  *
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.
20  *
21  */
22 
23 #include "common/archive.h"
24 #include "common/debug.h"
25 #include "common/file.h"
26 #include "common/fs.h"
27 #include "common/textconsole.h"
28 #include "common/system.h"
29 #include "backends/fs/fs-factory.h"
30 
31 namespace Common {
32 
File()33 File::File()
34 	: _handle(nullptr) {
35 }
36 
~File()37 File::~File() {
38 	close();
39 }
40 
open(const String & filename)41 bool File::open(const String &filename) {
42 	return open(filename, SearchMan);
43 }
44 
open(const String & filename,Archive & archive)45 bool File::open(const String &filename, Archive &archive) {
46 	assert(!filename.empty());
47 	assert(!_handle);
48 
49 	SeekableReadStream *stream = nullptr;
50 
51 	if ((stream = archive.createReadStreamForMember(filename))) {
52 		debug(8, "Opening hashed: %s", filename.c_str());
53 	} else if ((stream = archive.createReadStreamForMember(filename + "."))) {
54 		// WORKAROUND: Bug #1458388: "SIMON1: Game Detection fails"
55 		// sometimes instead of "GAMEPC" we get "GAMEPC." (note trailing dot)
56 		debug(8, "Opening hashed: %s.", filename.c_str());
57 	}
58 
59 	return open(stream, filename);
60 }
61 
open(const FSNode & node)62 bool File::open(const FSNode &node) {
63 	assert(!_handle);
64 
65 	if (!node.exists()) {
66 		warning("File::open: '%s' does not exist", node.getPath().c_str());
67 		return false;
68 	} else if (node.isDirectory()) {
69 		warning("File::open: '%s' is a directory", node.getPath().c_str());
70 		return false;
71 	}
72 
73 	SeekableReadStream *stream = node.createReadStream();
74 	return open(stream, node.getPath());
75 }
76 
open(SeekableReadStream * stream,const String & name)77 bool File::open(SeekableReadStream *stream, const String &name) {
78 	assert(!_handle);
79 
80 	if (stream) {
81 		_handle = stream;
82 		_name = name;
83 	} else {
84 		debug(2, "File::open: opening '%s' failed", name.c_str());
85 	}
86 	return _handle != nullptr;
87 }
88 
89 
exists(const String & filename)90 bool File::exists(const String &filename) {
91 	if (SearchMan.hasFile(filename)) {
92 		return true;
93 	} else if (SearchMan.hasFile(filename + ".")) {
94 		// WORKAROUND: Bug #1458388: "SIMON1: Game Detection fails"
95 		// sometimes instead of "GAMEPC" we get "GAMEPC." (note trailing dot)
96 		return true;
97 	}
98 
99 	return false;
100 }
101 
close()102 void File::close() {
103 	delete _handle;
104 	_handle = nullptr;
105 }
106 
isOpen() const107 bool File::isOpen() const {
108 	return _handle != nullptr;
109 }
110 
err() const111 bool File::err() const {
112 	assert(_handle);
113 	return _handle->err();
114 }
115 
clearErr()116 void File::clearErr() {
117 	assert(_handle);
118 	_handle->clearErr();
119 }
120 
eos() const121 bool File::eos() const {
122 	assert(_handle);
123 	return _handle->eos();
124 }
125 
pos() const126 int32 File::pos() const {
127 	assert(_handle);
128 	return _handle->pos();
129 }
130 
size() const131 int32 File::size() const {
132 	assert(_handle);
133 	return _handle->size();
134 }
135 
seek(int32 offs,int whence)136 bool File::seek(int32 offs, int whence) {
137 	assert(_handle);
138 	return _handle->seek(offs, whence);
139 }
140 
read(void * ptr,uint32 len)141 uint32 File::read(void *ptr, uint32 len) {
142 	assert(_handle);
143 	return _handle->read(ptr, len);
144 }
145 
146 
DumpFile()147 DumpFile::DumpFile() : _handle(nullptr) {
148 }
149 
~DumpFile()150 DumpFile::~DumpFile() {
151 	close();
152 }
153 
open(const String & filename,bool createPath)154 bool DumpFile::open(const String &filename, bool createPath) {
155 	assert(!filename.empty());
156 	assert(!_handle);
157 
158 	if (createPath) {
159 		for (uint32 i = 0; i < filename.size(); ++i) {
160 			if (filename[i] == '/' || filename[i] == '\\') {
161 				Common::String subpath = filename;
162 				subpath.erase(i);
163 				if (subpath.empty()) continue;
164 				AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(subpath);
165 				if (node->exists()) {
166 					delete node;
167 					continue;
168 				}
169 				if (!node->createDirectory()) warning("DumpFile: unable to create directories from path prefix");
170 				delete node;
171 			}
172 		}
173 	}
174 
175 	FSNode node(filename);
176 	return open(node);
177 }
178 
open(const FSNode & node)179 bool DumpFile::open(const FSNode &node) {
180 	assert(!_handle);
181 
182 	if (node.isDirectory()) {
183 		warning("DumpFile::open: FSNode is a directory");
184 		return false;
185 	}
186 
187 	_handle = node.createWriteStream();
188 
189 	if (_handle == nullptr)
190 		debug(2, "File %s not found", node.getName().c_str());
191 
192 	return _handle != nullptr;
193 }
194 
close()195 void DumpFile::close() {
196 	delete _handle;
197 	_handle = nullptr;
198 }
199 
isOpen() const200 bool DumpFile::isOpen() const {
201 	return _handle != nullptr;
202 }
203 
err() const204 bool DumpFile::err() const {
205 	assert(_handle);
206 	return _handle->err();
207 }
208 
clearErr()209 void DumpFile::clearErr() {
210 	assert(_handle);
211 	_handle->clearErr();
212 }
213 
write(const void * ptr,uint32 len)214 uint32 DumpFile::write(const void *ptr, uint32 len) {
215 	assert(_handle);
216 	return _handle->write(ptr, len);
217 }
218 
flush()219 bool DumpFile::flush() {
220 	assert(_handle);
221 	return _handle->flush();
222 }
223 
pos() const224 int32 DumpFile::pos() const { return _handle->pos(); }
225 
seek(int32 offset,int whence)226 bool DumpFile::seek(int32 offset, int whence) {
227 	SeekableWriteStream *ws = dynamic_cast<SeekableWriteStream *>(_handle);
228 	return ws ? ws->seek(offset, whence) : false;
229 }
230 
size() const231 int32 DumpFile::size() const {
232 	SeekableWriteStream *ws = dynamic_cast<SeekableWriteStream *>(_handle);
233 	return ws ? ws->size() : -1;
234 }
235 
236 } // End of namespace Common
237