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 // Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
24 #define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
25 
26 #include "common/scummsys.h"
27 
28 #if defined(RISCOS)
29 
30 #include "backends/platform/sdl/riscos/riscos-utils.h"
31 #include "backends/fs/riscos/riscos-fs.h"
32 #include "backends/fs/stdiostream.h"
33 #include "common/algorithm.h"
34 
35 // TODO: Replace use of access()
36 #include <unistd.h>
37 
38 #include <kernel.h>
39 #include <swis.h>
40 
exists() const41 bool RISCOSFilesystemNode::exists() const {
42 	return access(_path.c_str(), F_OK) == 0;
43 }
44 
isReadable() const45 bool RISCOSFilesystemNode::isReadable() const {
46 	return access(_path.c_str(), R_OK) == 0;
47 }
48 
isWritable() const49 bool RISCOSFilesystemNode::isWritable() const {
50 	return access(_path.c_str(), W_OK) == 0;
51 }
52 
setFlags()53 void RISCOSFilesystemNode::setFlags() {
54 	_kernel_swi_regs regs;
55 	regs.r[0] = 23;
56 	regs.r[1] = (int)_nativePath.c_str();
57 	_kernel_swi(OS_File, &regs, &regs);
58 
59 	if (regs.r[0] == 0) {
60 		_isDirectory = false;
61 		_isValid = false;
62 	} else if (regs.r[0] == 2) {
63 		_isDirectory = true;
64 		_isValid = true;
65 	} else {
66 		_isDirectory = false;
67 		_isValid = true;
68 	}
69 }
70 
RISCOSFilesystemNode(const Common::String & p)71 RISCOSFilesystemNode::RISCOSFilesystemNode(const Common::String &p) {
72 	_path = p;
73 	if (p == "/") {
74 		_nativePath = "";
75 		_isDirectory = true;
76 		_isValid = true;
77 	} else {
78 		_nativePath = RISCOS_Utils::toRISCOS(p);
79 		setFlags();
80 	}
81 }
82 
getChild(const Common::String & n) const83 AbstractFSNode *RISCOSFilesystemNode::getChild(const Common::String &n) const {
84 	assert(!_path.empty());
85 	assert(_isDirectory);
86 
87 	// Make sure the string contains no slashes
88 	assert(!n.contains('/'));
89 
90 	// We assume here that _path is already normalized (hence don't bother to call
91 	//  Common::normalizePath on the final path).
92 	Common::String newPath(_path);
93 	if (_path.lastChar() != '/')
94 		newPath += '/';
95 	newPath += n;
96 
97 	return makeNode(newPath);
98 }
99 
getChildren(AbstractFSList & myList,ListMode mode,bool hidden) const100 bool RISCOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
101 	assert(_isDirectory);
102 
103 	if (_path == "/") {
104 		// Special case for the root dir: List all drives
105 		char fsname[MAXPATHLEN] = "";
106 		for (int fsNum = 0; fsNum < 256; fsNum += 1) {
107 			if (fsNum == 33 || fsNum == 46 || fsNum == 53 || fsNum == 99)
108 				continue;
109 
110 			_kernel_swi_regs regs;
111 			regs.r[0] = 33;
112 			regs.r[1] = fsNum;
113 			regs.r[2] = (int)fsname;
114 			regs.r[3] = sizeof(fsname);
115 			_kernel_swi(OS_FSControl, &regs, &regs);
116 			if (fsname[0] == 0)
117 				continue;
118 
119 			int drives = (fsNum == 193) ? 23 : 9;
120 
121 			for (int discnum = 0; discnum <= drives; discnum += 1) {
122 				const Common::String path = Common::String::format("%s::%d.$", fsname, discnum);
123 				char outpath[MAXPATHLEN] = "";
124 				regs.r[0] = 37;
125 				regs.r[1] = (int)path.c_str();
126 				regs.r[2] = (int)outpath;
127 				regs.r[3] = 0;
128 				regs.r[4] = 0;
129 				regs.r[5] = sizeof(outpath);
130 				if (_kernel_swi(OS_FSControl, &regs, &regs) != NULL)
131 					continue;
132 
133 				RISCOSFilesystemNode *entry = new RISCOSFilesystemNode();
134 				entry->_nativePath = outpath;
135 				entry->_path = Common::String('/') + outpath;
136 				entry->_displayName = outpath;
137 				entry->setFlags();
138 				if (entry->_isDirectory)
139 					myList.push_back(entry);
140 			}
141 		}
142 		return true;
143 	}
144 
145 	char file[MAXPATHLEN];
146 	_kernel_swi_regs regs;
147 	regs.r[0] = 9;
148 	regs.r[1] = (int)_nativePath.c_str();
149 	regs.r[2] = (int)file;
150 	regs.r[3] = 1;
151 	regs.r[4] = 0;
152 	regs.r[5] = sizeof(file);
153 	regs.r[6] = 0;
154 	while (regs.r[4] != -1) {
155 		_kernel_swi(OS_GBPB, &regs, &regs);
156 
157 		if (regs.r[4] == -1)
158 			break;
159 
160 		// Start with a clone of this node, with the correct path set
161 		RISCOSFilesystemNode entry(*this);
162 		entry._displayName = file;
163 		entry._displayName = RISCOS_Utils::toUnix(entry._displayName);
164 		if (_path.lastChar() != '/')
165 			entry._path += '/';
166 		entry._path += entry._displayName;
167 		entry._nativePath = RISCOS_Utils::toRISCOS(entry._path);
168 		entry.setFlags();
169 		if (!entry._isValid)
170 			continue;
171 
172 		// Honor the chosen mode
173 		if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
174 			(mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory))
175 			continue;
176 
177 		myList.push_back(new RISCOSFilesystemNode(entry));
178 	}
179 
180 	return true;
181 }
182 
getParent() const183 AbstractFSNode *RISCOSFilesystemNode::getParent() const {
184 	if (_path == "/")
185 		return 0;	// The filesystem root has no parent
186 
187 	const char *start = _path.c_str();
188 	const char *end = start + _path.size();
189 
190 	// Strip of the last component. We make use of the fact that at this
191 	// point, _path is guaranteed to be normalized
192 	while (end > start && *(end-1) != '/')
193 		end--;
194 
195 	if (end == start) {
196 		// This only happens if we were called with a relative path, for which
197 		// there simply is no parent.
198 		// TODO: We could also resolve this by assuming that the parent is the
199 		//       current working directory, and returning a node referring to that.
200 		return 0;
201 	}
202 
203 	if (*(end-1) == '/' && end != start + 1)
204 		end--;
205 
206 	return makeNode(Common::String(start, end));
207 }
208 
createReadStream()209 Common::SeekableReadStream *RISCOSFilesystemNode::createReadStream() {
210 	return StdioStream::makeFromPath(getPath(), false);
211 }
212 
createWriteStream()213 Common::SeekableWriteStream *RISCOSFilesystemNode::createWriteStream() {
214 	return StdioStream::makeFromPath(getPath(), true);
215 }
216 
createDirectory()217 bool RISCOSFilesystemNode::createDirectory() {
218 	_kernel_swi_regs regs;
219 	regs.r[0] = 8;
220 	regs.r[1] = (int)_nativePath.c_str();
221 	if (_kernel_swi(OS_File, &regs, &regs) == NULL)
222 		setFlags();
223 
224 	return _isValid && _isDirectory;
225 }
226 
227 namespace Riscos {
228 
assureDirectoryExists(const Common::String & dir,const char * prefix)229 bool assureDirectoryExists(const Common::String &dir, const char *prefix) {
230 	AbstractFSNode *node;
231 
232 	// Check whether the prefix exists if one is supplied.
233 	if (prefix) {
234 		node = new RISCOSFilesystemNode(prefix);
235 		if (!node->isDirectory()) {
236 			return false;
237 		}
238 	}
239 
240 	// Obtain absolute path.
241 	Common::String path;
242 	if (prefix) {
243 		path = prefix;
244 		path += '/';
245 		path += dir;
246 	} else {
247 		path = dir;
248 	}
249 
250 	path = Common::normalizePath(path, '/');
251 
252 	const Common::String::iterator end = path.end();
253 	Common::String::iterator cur = path.begin();
254 	if (*cur == '/')
255 		++cur;
256 
257 	do {
258 		if (cur + 1 != end) {
259 			if (*cur != '/') {
260 				continue;
261 			}
262 
263 			// It is kind of ugly and against the purpose of Common::String to
264 			// insert 0s inside, but this is just for a local string and
265 			// simplifies the code a lot.
266 			*cur = '\0';
267 		}
268 
269 		node = new RISCOSFilesystemNode(path);
270 		if (!node->createDirectory()) {
271 			if (node->exists()) {
272 				if (!node->isDirectory()) {
273 					return false;
274 				}
275 			} else {
276 				return false;
277 			}
278 		}
279 
280 		*cur = '/';
281 	} while (cur++ != end);
282 
283 	return true;
284 }
285 
286 } // End of namespace RISCOS
287 
288 #endif //#if defined(RISCOS)
289