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