1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//
17// Based on code from ScummVM - Scumm Interpreter
18// Copyright (C) 2002-2004 The ScummVM project
19//============================================================================
20
21#include "FSNodeFactory.hxx"
22#include "FSNode.hxx"
23
24// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25FilesystemNode::FilesystemNode()
26{
27}
28
29// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
30FilesystemNode::FilesystemNode(AbstractFSNodePtr realNode)
31 : _realNode(realNode)
32{
33}
34
35// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36FilesystemNode::FilesystemNode(const string& p)
37{
38 // Is this potentially a ZIP archive?
39#if defined(ZIP_SUPPORT)
40 if (BSPF::containsIgnoreCase(p, ".zip"))
41 _realNode = FilesystemNodeFactory::create(p, FilesystemNodeFactory::Type::ZIP);
42 else
43#endif
44 _realNode = FilesystemNodeFactory::create(p, FilesystemNodeFactory::Type::SYSTEM);
45}
46
47// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48bool FilesystemNode::exists() const
49{
50 return _realNode ? _realNode->exists() : false;
51}
52
53// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
54bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
55 const NameFilter& filter) const
56{
57 if (!_realNode || !_realNode->isDirectory())
58 return false;
59
60 AbstractFSList tmp;
61 tmp.reserve(fslist.capacity());
62
63 if (!_realNode->getChildren(tmp, mode))
64 return false;
65
66 std::sort(tmp.begin(), tmp.end(),
67 [](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2)
68 {
69 if (node1->isDirectory() != node2->isDirectory())
70 return node1->isDirectory();
71 else
72 return BSPF::compareIgnoreCase(node1->getName(), node2->getName()) < 0;
73 }
74 );
75
76 // Add parent node, if it is valid to do so
77 if (hasParent())
78 {
79 FilesystemNode parent = getParent();
80 parent.setName(" [..]");
81 fslist.emplace_back(parent);
82 }
83
84 // And now add the rest of the entries
85 for (const auto& i: tmp)
86 {
87 #if defined(ZIP_SUPPORT)
88 if (BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
89 {
90 // Force ZIP c'tor to be called
91 AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i->getPath(),
92 FilesystemNodeFactory::Type::ZIP);
93 FilesystemNode node(ptr);
94 if (filter(node))
95 fslist.emplace_back(node);
96 }
97 else
98 #endif
99 {
100 // Make directories stand out
101 if (i->isDirectory())
102 i->setName(" [" + i->getName() + "]");
103
104 FilesystemNode node(i);
105 if (filter(node))
106 fslist.emplace_back(node);
107 }
108 }
109
110 return true;
111}
112
113// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114const string& FilesystemNode::getName() const
115{
116 return _realNode ? _realNode->getName() : EmptyString;
117}
118
119// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120void FilesystemNode::setName(const string& name)
121{
122 if (_realNode)
123 _realNode->setName(name);
124}
125
126// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
127const string& FilesystemNode::getPath() const
128{
129 return _realNode ? _realNode->getPath() : EmptyString;
130}
131
132// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133string FilesystemNode::getShortPath() const
134{
135 return _realNode ? _realNode->getShortPath() : EmptyString;
136}
137
138// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
139string FilesystemNode::getNameWithExt(const string& ext) const
140{
141 if (!_realNode)
142 return EmptyString;
143
144 size_t pos = _realNode->getName().find_last_of("/\\");
145 string s = pos == string::npos ? _realNode->getName() :
146 _realNode->getName().substr(pos+1);
147
148 pos = s.find_last_of(".");
149 return (pos != string::npos) ? s.replace(pos, string::npos, ext) : s + ext;
150}
151
152// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
153string FilesystemNode::getPathWithExt(const string& ext) const
154{
155 if (!_realNode)
156 return EmptyString;
157
158 string s = _realNode->getPath();
159
160 size_t pos = s.find_last_of(".");
161 return (pos != string::npos) ? s.replace(pos, string::npos, ext) : s + ext;
162}
163
164// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165bool FilesystemNode::hasParent() const
166{
167 return _realNode ? _realNode->hasParent() : false;
168}
169
170// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
171FilesystemNode FilesystemNode::getParent() const
172{
173 if (!_realNode)
174 return *this;
175
176 AbstractFSNodePtr node = _realNode->getParent();
177 return node ? FilesystemNode(node) : *this;
178}
179
180// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
181bool FilesystemNode::isDirectory() const
182{
183 return _realNode ? _realNode->isDirectory() : false;
184}
185
186// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
187bool FilesystemNode::isFile() const
188{
189 return _realNode ? _realNode->isFile() : false;
190}
191
192// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193bool FilesystemNode::isReadable() const
194{
195 return _realNode ? _realNode->isReadable() : false;
196}
197
198// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
199bool FilesystemNode::isWritable() const
200{
201 return _realNode ? _realNode->isWritable() : false;
202}
203
204// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
205bool FilesystemNode::makeDir()
206{
207 return (_realNode && !_realNode->exists()) ? _realNode->makeDir() : false;
208}
209
210// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
211bool FilesystemNode::rename(const string& newfile)
212{
213 return (_realNode && _realNode->exists()) ? _realNode->rename(newfile) : false;
214}
215
216// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
217uInt32 FilesystemNode::read(ByteBuffer& image) const
218{
219 uInt32 size = 0;
220
221 // File must actually exist
222 if (!(exists() && isReadable()))
223 throw runtime_error("File not found/readable");
224
225 // First let the private subclass attempt to open the file
226 if (_realNode && (size = _realNode->read(image)) > 0)
227 return size;
228
229 // Otherwise, the default behaviour is to read from a normal C++ ifstream
230 image = make_unique<uInt8[]>(512 * 1024);
231 ifstream in(getPath(), std::ios::binary);
232 if (in)
233 {
234 in.seekg(0, std::ios::end);
235 std::streampos length = in.tellg();
236 in.seekg(0, std::ios::beg);
237
238 if (length == 0)
239 throw runtime_error("Zero-byte file");
240
241 size = std::min(uInt32(length), 512u * 1024u);
242 in.read(reinterpret_cast<char*>(image.get()), size);
243 }
244 else
245 throw runtime_error("File open/read error");
246
247 return size;
248}
249