1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the FIFE team                              *
3  *   http://www.fifengine.net                                              *
4  *   This file is part of FIFE.                                            *
5  *                                                                         *
6  *   FIFE is free software; you can redistribute it and/or                 *
7  *   modify it under the terms of the GNU Lesser General Public            *
8  *   License as published by the Free Software Foundation; either          *
9  *   version 2.1 of the License, or (at your option) any later version.    *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the                 *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 
24 // 3rd party library includes
25 #include <tinyxml.h>
26 
27 // FIFE includes
28 // These includes are split up in two parts, separated by one empty line
29 // First block: files included from the FIFE root src directory
30 // Second block: files included from the same folder
31 #include "vfs/fife_boost_filesystem.h"
32 #include "vfs/vfs.h"
33 #include "vfs/raw/rawdata.h"
34 #include "video/imagemanager.h"
35 #include "video/animationmanager.h"
36 #include "video/image.h"
37 #include "video/animation.h"
38 #include "util/base/exception.h"
39 #include "util/log/logger.h"
40 #include "util/resource/resource.h"
41 #include "util/resource/resourcemanager.h"
42 
43 #include "animationloader.h"
44 
45 namespace FIFE {
46 	/** Logger to use for this source file.
47 	 *  @relates Logger
48 	 */
49 	static Logger _log(LM_NATIVE_LOADERS);
50 
AnimationLoader(VFS * vfs,ImageManager * imageManager,AnimationManager * animationManager)51 	AnimationLoader::AnimationLoader(VFS* vfs, ImageManager* imageManager, AnimationManager* animationManager)
52 	: m_vfs(vfs), m_imageManager(imageManager), m_animationManager(animationManager) {
53 
54 	}
55 
isLoadable(const std::string & filename)56 	bool AnimationLoader::isLoadable(const std::string& filename) {
57 		bfs::path animPath(filename);
58 
59 		std::string animationFilename = animPath.string();
60 		TiXmlDocument animFile;
61 
62 		try {
63 			RawData* data = m_vfs->open(animationFilename);
64 
65 			if (data) {
66 				if (data->getDataLength() != 0) {
67 					// TODO - this could be expanded to do more checks
68 					animFile.Parse(data->readString(data->getDataLength()).c_str());
69 
70 					if (animFile.Error()) {
71 						return false;
72 					}
73 				}
74 
75 				// done with data delete resource
76 				delete data;
77 				data = 0;
78 			}
79 		}
80 		catch (NotFound&) {
81 			return false;
82 		}
83 
84 		// if we get here then loading the file went well
85 		TiXmlElement* root = animFile.RootElement();
86 
87 		if (root && root->ValueStr() == "assets") {
88 			if (root->FirstChildElement("animation")) {
89 				return true;
90 			}
91 		}
92 
93 		return false;
94 	}
95 
load(const std::string & filename)96 	AnimationPtr AnimationLoader::load(const std::string& filename) {
97 		bfs::path animPath(filename);
98 
99 		std::string animationFilename = animPath.string();
100 
101 		TiXmlDocument doc;
102 
103 		AnimationPtr animation;
104 
105 		try {
106 			RawData* data = m_vfs->open(animationFilename);
107 
108 			if (data) {
109 				if (data->getDataLength() != 0) {
110 					doc.Parse(data->readString(data->getDataLength()).c_str());
111 
112 					if (doc.Error()) {
113 						return animation;
114 					}
115 
116 					// done with data delete resource
117 					delete data;
118 					data = 0;
119 				}
120 			}
121 		}
122 		catch (NotFound& e) {
123 			FL_ERR(_log, e.what());
124 
125 			// TODO - should we abort here
126 			//        or rethrow the exception
127 			//        or just keep going
128 
129 			return animation;
130 		}
131 
132 		// if we get here then everything loaded properly
133 		// so we can just parse out the contents
134 		TiXmlElement* root = doc.RootElement();
135 
136 		if (root && root->ValueStr() == "assets") {
137 			animation = loadAnimation(filename, root->FirstChildElement("animation"));
138 		}
139 
140 		return animation;
141 	}
142 
loadMultiple(const std::string & filename)143 	std::vector<AnimationPtr> AnimationLoader::loadMultiple(const std::string& filename) {
144 		bfs::path animPath(filename);
145 
146 		std::string animationFilename = animPath.string();
147 
148 		TiXmlDocument doc;
149 
150 		std::vector<AnimationPtr> animationVector;
151 
152 		try {
153 			RawData* data = m_vfs->open(animationFilename);
154 
155 			if (data) {
156 				if (data->getDataLength() != 0) {
157 					doc.Parse(data->readString(data->getDataLength()).c_str());
158 
159 					if (doc.Error()) {
160 						return animationVector;
161 					}
162 
163 					// done with data delete resource
164 					delete data;
165 					data = 0;
166 				}
167 			}
168 		}
169 		catch (NotFound& e) {
170 			FL_ERR(_log, e.what());
171 
172 			// TODO - should we abort here
173 			//        or rethrow the exception
174 			//        or just keep going
175 
176 			return animationVector;
177 		}
178 
179 		// if we get here then everything loaded properly
180 		// so we can just parse out the contents
181 		TiXmlElement* root = doc.RootElement();
182 
183 		if (root && root->ValueStr() == "assets") {
184 			for (TiXmlElement* animationElem = root->FirstChildElement("animation"); animationElem; animationElem = animationElem->NextSiblingElement("animation")) {
185 				AnimationPtr animation = loadAnimation(filename, animationElem);
186 				if (animation) {
187 					animationVector.push_back(animation);
188 				}
189 			}
190 		}
191 
192 		return animationVector;
193 	}
194 
loadAnimation(const std::string & filename,TiXmlElement * animationElem)195 	AnimationPtr AnimationLoader::loadAnimation(const std::string& filename, TiXmlElement* animationElem) {
196 		AnimationPtr animation;
197 		if (!animationElem) {
198 			return animation;
199 		}
200 
201 		bfs::path animPath(filename);
202 		std::string animationFilename = animPath.string();
203 
204 		bool alreadyLoaded = false;
205 		// first try to use the id, if no id exists it use the filename as fallback
206 		const std::string* animationId = animationElem->Attribute(std::string("id"));
207 		if (animationId) {
208 			if (!m_animationManager->exists(*animationId)) {
209 				animation = m_animationManager->create(*animationId);
210 			} else {
211 				animation = m_animationManager->getPtr(*animationId);
212 				alreadyLoaded = animation->getFrameCount() != 0;
213 			}
214 		} else {
215 			if (HasParentPath(animPath)) {
216 				animPath= GetParentPath(animPath) / animationFilename;
217 			} else {
218 				animPath = bfs::path(animationFilename);
219 			}
220 			if (!m_animationManager->exists(animPath.string())) {
221 				animation = m_animationManager->create(animPath.string());
222 			} else {
223 				animation = m_animationManager->getPtr(animPath.string());
224 				alreadyLoaded = animation->getFrameCount() != 0;
225 			}
226 		}
227 
228 		if (alreadyLoaded) {
229 			return animation;
230 		}
231 
232 		int direction = 0;
233 		int actionFrame = -1;
234 		int animDelay = 0;
235 		int animXoffset = 0;
236 		int animYoffset = 0;
237 
238 		int success = animationElem->QueryValueAttribute("direction", &direction);
239 		if (success == TIXML_SUCCESS) {
240 			animation->setDirection(direction);
241 		}
242 		success = animationElem->QueryValueAttribute("action_frame", &actionFrame);
243 		if (success == TIXML_SUCCESS) {
244 			animation->setActionFrame(actionFrame);
245 		}
246 		animationElem->QueryValueAttribute("delay", &animDelay);
247 		animationElem->QueryValueAttribute("x_offset", &animXoffset);
248 		animationElem->QueryValueAttribute("y_offset", &animYoffset);
249 
250 		for (TiXmlElement* frameElement = animationElem->FirstChildElement("frame"); frameElement; frameElement = frameElement->NextSiblingElement("frame")) {
251 			const std::string* sourceId = frameElement->Attribute(std::string("source"));
252 			if (sourceId) {
253 				bfs::path framePath(filename);
254 
255 				if (HasParentPath(framePath)) {
256 					framePath = GetParentPath(framePath) / *sourceId;
257 					if (!bfs::exists(framePath)) {
258 						framePath = bfs::path(*sourceId);
259 					}
260 				} else {
261 					framePath = bfs::path(*sourceId);
262 				}
263 
264 				ImagePtr imagePtr;
265 				if (!m_imageManager->exists(framePath.string())) {
266 					imagePtr = m_imageManager->create(framePath.string());
267 				} else {
268 					imagePtr = m_imageManager->getPtr(framePath.string());
269 				}
270 
271 				if (imagePtr) {
272 					int frameXoffset = 0;
273 					success = frameElement->QueryValueAttribute("x_offset", &frameXoffset);
274 					if (success == TIXML_SUCCESS) {
275 						imagePtr->setXShift(frameXoffset);
276 					} else {
277 						imagePtr->setXShift(animXoffset);
278 					}
279 
280 					int frameYoffset = 0;
281 					success = frameElement->QueryValueAttribute("y_offset", &frameYoffset);
282 					if (success == TIXML_SUCCESS) {
283 						imagePtr->setYShift(frameYoffset);
284 					} else {
285 						imagePtr->setYShift(animYoffset);
286 					}
287 
288 					int frameDelay = 0;
289 					success = frameElement->QueryValueAttribute("delay", &frameDelay);
290 					if (success == TIXML_SUCCESS) {
291 						animation->addFrame(imagePtr, frameDelay);
292 					} else {
293 						animation->addFrame(imagePtr, animDelay);
294 					}
295 				}
296 			}
297 		}
298 
299 		return animation;
300 	}
301 }
302