1 #include "FileData.h"
2
3 #include "utils/FileSystemUtil.h"
4 #include "utils/StringUtil.h"
5 #include "utils/TimeUtil.h"
6 #include "AudioManager.h"
7 #include "CollectionSystemManager.h"
8 #include "FileFilterIndex.h"
9 #include "FileSorts.h"
10 #include "Log.h"
11 #include "MameNames.h"
12 #include "platform.h"
13 #include "Scripting.h"
14 #include "SystemData.h"
15 #include "VolumeControl.h"
16 #include "Window.h"
17 #include <assert.h>
18
FileData(FileType type,const std::string & path,SystemEnvironmentData * envData,SystemData * system)19 FileData::FileData(FileType type, const std::string& path, SystemEnvironmentData* envData, SystemData* system)
20 : mType(type), mPath(path), mSystem(system), mEnvData(envData), mSourceFileData(NULL), mParent(NULL), metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) // metadata is REALLY set in the constructor!
21 {
22 // metadata needs at least a name field (since that's what getName() will return)
23 if(metadata.get("name").empty())
24 metadata.set("name", getDisplayName());
25 mSystemName = system->getName();
26 metadata.resetChangedFlag();
27 }
28
~FileData()29 FileData::~FileData()
30 {
31 if(mParent)
32 mParent->removeChild(this);
33
34 if(mType == GAME)
35 mSystem->getIndex()->removeFromIndex(this);
36
37 mChildren.clear();
38 }
39
getDisplayName() const40 std::string FileData::getDisplayName() const
41 {
42 std::string stem = Utils::FileSystem::getStem(mPath);
43 if(mSystem && mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO))
44 stem = MameNames::getInstance()->getRealName(stem);
45
46 return stem;
47 }
48
getCleanName() const49 std::string FileData::getCleanName() const
50 {
51 return Utils::String::removeParenthesis(this->getDisplayName());
52 }
53
getThumbnailPath() const54 const std::string FileData::getThumbnailPath() const
55 {
56 std::string thumbnail = metadata.get("thumbnail");
57
58 // no thumbnail, try image
59 if(thumbnail.empty())
60 {
61 thumbnail = metadata.get("image");
62
63 // no image, try to use local image
64 if(thumbnail.empty() && Settings::getInstance()->getBool("LocalArt"))
65 {
66 const char* extList[2] = { ".png", ".jpg" };
67 for(int i = 0; i < 2; i++)
68 {
69 if(thumbnail.empty())
70 {
71 std::string path = mEnvData->mStartPath + "/images/" + getDisplayName() + "-image" + extList[i];
72 if(Utils::FileSystem::exists(path))
73 thumbnail = path;
74 }
75 }
76 }
77 }
78
79 return thumbnail;
80 }
81
getName()82 const std::string& FileData::getName()
83 {
84 return metadata.get("name");
85 }
86
getSortName()87 const std::string& FileData::getSortName()
88 {
89 if (metadata.get("sortname").empty())
90 return metadata.get("name");
91 else
92 return metadata.get("sortname");
93 }
94
getChildrenListToDisplay()95 const std::vector<FileData*>& FileData::getChildrenListToDisplay() {
96
97 FileFilterIndex* idx = CollectionSystemManager::get()->getSystemToView(mSystem)->getIndex();
98 if (idx->isFiltered()) {
99 mFilteredChildren.clear();
100 for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
101 {
102 if (idx->showFile((*it))) {
103 mFilteredChildren.push_back(*it);
104 }
105 }
106
107 return mFilteredChildren;
108 }
109 else
110 {
111 return mChildren;
112 }
113 }
114
getVideoPath() const115 const std::string FileData::getVideoPath() const
116 {
117 std::string video = metadata.get("video");
118
119 // no video, try to use local video
120 if(video.empty() && Settings::getInstance()->getBool("LocalArt"))
121 {
122 std::string path = mEnvData->mStartPath + "/images/" + getDisplayName() + "-video.mp4";
123 if(Utils::FileSystem::exists(path))
124 video = path;
125 }
126
127 return video;
128 }
129
getMarqueePath() const130 const std::string FileData::getMarqueePath() const
131 {
132 std::string marquee = metadata.get("marquee");
133
134 // no marquee, try to use local marquee
135 if(marquee.empty() && Settings::getInstance()->getBool("LocalArt"))
136 {
137 const char* extList[2] = { ".png", ".jpg" };
138 for(int i = 0; i < 2; i++)
139 {
140 if(marquee.empty())
141 {
142 std::string path = mEnvData->mStartPath + "/images/" + getDisplayName() + "-marquee" + extList[i];
143 if(Utils::FileSystem::exists(path))
144 marquee = path;
145 }
146 }
147 }
148
149 return marquee;
150 }
151
getImagePath() const152 const std::string FileData::getImagePath() const
153 {
154 std::string image = metadata.get("image");
155
156 // no image, try to use local image
157 if(image.empty())
158 {
159 const char* extList[2] = { ".png", ".jpg" };
160 for(int i = 0; i < 2; i++)
161 {
162 if(image.empty())
163 {
164 std::string path = mEnvData->mStartPath + "/images/" + getDisplayName() + "-image" + extList[i];
165 if(Utils::FileSystem::exists(path))
166 image = path;
167 }
168 }
169 }
170
171 return image;
172 }
173
getFilesRecursive(unsigned int typeMask,bool displayedOnly) const174 std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask, bool displayedOnly) const
175 {
176 std::vector<FileData*> out;
177 FileFilterIndex* idx = mSystem->getIndex();
178
179 for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
180 {
181 if((*it)->getType() & typeMask)
182 {
183 if (!displayedOnly || !idx->isFiltered() || idx->showFile(*it))
184 out.push_back(*it);
185 }
186
187 if((*it)->getChildren().size() > 0)
188 {
189 std::vector<FileData*> subchildren = (*it)->getFilesRecursive(typeMask, displayedOnly);
190 out.insert(out.cend(), subchildren.cbegin(), subchildren.cend());
191 }
192 }
193
194 return out;
195 }
196
getKey()197 std::string FileData::getKey() {
198 return getFileName();
199 }
200
isArcadeAsset()201 const bool FileData::isArcadeAsset()
202 {
203 const std::string stem = Utils::FileSystem::getStem(mPath);
204 return (
205 (mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO)))
206 &&
207 (MameNames::getInstance()->isBios(stem) || MameNames::getInstance()->isDevice(stem))
208 );
209 }
210
getSourceFileData()211 FileData* FileData::getSourceFileData()
212 {
213 return this;
214 }
215
addChild(FileData * file)216 void FileData::addChild(FileData* file)
217 {
218 assert(mType == FOLDER);
219 assert(file->getParent() == NULL);
220
221 const std::string key = file->getKey();
222 if (mChildrenByFilename.find(key) == mChildrenByFilename.cend())
223 {
224 mChildrenByFilename[key] = file;
225 mChildren.push_back(file);
226 file->mParent = this;
227 }
228 }
229
removeChild(FileData * file)230 void FileData::removeChild(FileData* file)
231 {
232 assert(mType == FOLDER);
233 assert(file->getParent() == this);
234 mChildrenByFilename.erase(file->getKey());
235 for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
236 {
237 if(*it == file)
238 {
239 file->mParent = NULL;
240 mChildren.erase(it);
241 return;
242 }
243 }
244
245 // File somehow wasn't in our children.
246 assert(false);
247
248 }
249
sort(ComparisonFunction & comparator,bool ascending)250 void FileData::sort(ComparisonFunction& comparator, bool ascending)
251 {
252 std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
253
254 for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
255 {
256 if((*it)->getChildren().size() > 0)
257 (*it)->sort(comparator, ascending);
258 }
259
260 if(!ascending)
261 std::reverse(mChildren.begin(), mChildren.end());
262 }
263
sort(const SortType & type)264 void FileData::sort(const SortType& type)
265 {
266 sort(*type.comparisonFunction, type.ascending);
267 }
268
launchGame(Window * window)269 void FileData::launchGame(Window* window)
270 {
271 LOG(LogInfo) << "Attempting to launch game...";
272
273 AudioManager::getInstance()->deinit();
274 VolumeControl::getInstance()->deinit();
275 window->deinit();
276
277 std::string command = mEnvData->mLaunchCommand;
278
279 const std::string rom = Utils::FileSystem::getEscapedPath(getPath());
280 const std::string basename = Utils::FileSystem::getStem(getPath());
281 const std::string rom_raw = Utils::FileSystem::getPreferredPath(getPath());
282
283 command = Utils::String::replace(command, "%ROM%", rom);
284 command = Utils::String::replace(command, "%BASENAME%", basename);
285 command = Utils::String::replace(command, "%ROM_RAW%", rom_raw);
286
287 Scripting::fireEvent("game-start", rom, basename);
288
289 LOG(LogInfo) << " " << command;
290 int exitCode = runSystemCommand(command);
291
292 if(exitCode != 0)
293 {
294 LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!";
295 }
296
297 Scripting::fireEvent("game-end");
298
299 window->init();
300 VolumeControl::getInstance()->init();
301 window->normalizeNextUpdate();
302
303 //update number of times the game has been launched
304
305 FileData* gameToUpdate = getSourceFileData();
306
307 int timesPlayed = gameToUpdate->metadata.getInt("playcount") + 1;
308 gameToUpdate->metadata.set("playcount", std::to_string(static_cast<long long>(timesPlayed)));
309
310 //update last played time
311 gameToUpdate->metadata.set("lastplayed", Utils::Time::DateTime(Utils::Time::now()));
312 CollectionSystemManager::get()->refreshCollectionSystems(gameToUpdate);
313
314 gameToUpdate->mSystem->onMetaDataSavePoint();
315 }
316
CollectionFileData(FileData * file,SystemData * system)317 CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
318 : FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), file->getSourceFileData()->getSystemEnvData(), system)
319 {
320 // we use this constructor to create a clone of the filedata, and change its system
321 mSourceFileData = file->getSourceFileData();
322 refreshMetadata();
323 mParent = NULL;
324 metadata = mSourceFileData->metadata;
325 mSystemName = mSourceFileData->getSystem()->getName();
326 }
327
~CollectionFileData()328 CollectionFileData::~CollectionFileData()
329 {
330 // need to remove collection file data at the collection object destructor
331 if(mParent)
332 mParent->removeChild(this);
333 mParent = NULL;
334 }
335
getKey()336 std::string CollectionFileData::getKey() {
337 return getFullPath();
338 }
339
getSourceFileData()340 FileData* CollectionFileData::getSourceFileData()
341 {
342 return mSourceFileData;
343 }
344
refreshMetadata()345 void CollectionFileData::refreshMetadata()
346 {
347 metadata = mSourceFileData->metadata;
348 mDirty = true;
349 }
350
getName()351 const std::string& CollectionFileData::getName()
352 {
353 if (mDirty) {
354 mCollectionFileName = Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
355 mCollectionFileName += " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
356 mDirty = false;
357 }
358
359 if (Settings::getInstance()->getBool("CollectionShowSystemInfo"))
360 return mCollectionFileName;
361 return mSourceFileData->metadata.get("name");
362 }
363
364 // returns Sort Type based on a string description
getSortTypeFromString(std::string desc)365 FileData::SortType getSortTypeFromString(std::string desc) {
366 std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
367 // find it
368 for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++)
369 {
370 const FileData::SortType& sort = FileSorts::SortTypes.at(i);
371 if(sort.description == desc)
372 {
373 return sort;
374 }
375 }
376 // if not found default to name, ascending
377 return FileSorts::SortTypes.at(0);
378 }
379