blob: a8f41991bb0ce8ac61710f0dd57258b903419a10 [file] [log] [blame]
Dees_Troy51a0e822012-09-05 15:24:24 -04001// resource.cpp - Source to manage GUI resources
2
3#include <stdarg.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
Dees_Troy51a0e822012-09-05 15:24:24 -04007#include <unistd.h>
Dees_Troy51a0e822012-09-05 15:24:24 -04008
9#include <string>
10#include <sstream>
11#include <iostream>
12#include <iomanip>
13
thatb2e8f672015-03-05 20:25:39 +010014#include "../minzip/Zip.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040015extern "C" {
Dees_Troy2673cec2013-04-02 20:22:16 +000016#include "../twcommon.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040017#include "../minuitwrp/minui.h"
Ethan Yonker63e414f2015-02-06 15:44:39 -060018#include "gui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040019}
20
21#include "rapidxml.hpp"
22#include "objects.hpp"
23
24#define TMP_RESOURCE_NAME "/tmp/extract.bin"
25
Ethan Yonkerd0514ba2015-10-22 14:17:47 -050026Resource::Resource(xml_node<>* node, ZipArchive* pZip __unused)
Dees_Troy51a0e822012-09-05 15:24:24 -040027{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020028 if (node && node->first_attribute("name"))
29 mName = node->first_attribute("name")->value();
Dees_Troy51a0e822012-09-05 15:24:24 -040030}
31
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020032int Resource::ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
Dees_Troy51a0e822012-09-05 15:24:24 -040033{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020034 if (!pZip)
35 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040036
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020037 std::string src = folderName + "/" + fileName + fileExtn;
Dees_Troy51a0e822012-09-05 15:24:24 -040038
39 const ZipEntry* binary = mzFindZipEntry(pZip, src.c_str());
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020040 if (binary == NULL) {
41 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040042 }
43
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020044 unlink(destFile.c_str());
45 int fd = creat(destFile.c_str(), 0666);
46 if (fd < 0)
47 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040048
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020049 int ret = 0;
50 if (!mzExtractZipEntryToFile(pZip, binary, fd))
51 ret = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040052
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020053 close(fd);
54 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -040055}
56
Ethan Yonker63e414f2015-02-06 15:44:39 -060057void Resource::LoadImage(ZipArchive* pZip, std::string file, gr_surface* source)
58{
59 if (ExtractResource(pZip, "images", file, ".png", TMP_RESOURCE_NAME) == 0)
60 {
61 res_create_surface(TMP_RESOURCE_NAME, source);
62 unlink(TMP_RESOURCE_NAME);
63 }
64 else if (ExtractResource(pZip, "images", file, "", TMP_RESOURCE_NAME) == 0)
65 {
66 // JPG includes the .jpg extension in the filename so extension should be blank
67 res_create_surface(TMP_RESOURCE_NAME, source);
68 unlink(TMP_RESOURCE_NAME);
69 }
70 else if (!pZip)
71 {
72 // File name in xml may have included .png so try without adding .png
73 res_create_surface(file.c_str(), source);
74 }
75}
76
77void Resource::CheckAndScaleImage(gr_surface source, gr_surface* destination, int retain_aspect)
78{
79 if (!source) {
80 *destination = NULL;
81 return;
82 }
83 if (get_scale_w() != 0 && get_scale_h() != 0) {
84 float scale_w = get_scale_w(), scale_h = get_scale_h();
85 if (retain_aspect) {
86 if (scale_w < scale_h)
87 scale_h = scale_w;
88 else
89 scale_w = scale_h;
90 }
91 if (res_scale_surface(source, destination, scale_w, scale_h)) {
92 LOGINFO("Error scaling image, using regular size.\n");
93 *destination = source;
94 }
95 } else {
96 *destination = source;
97 }
98}
99
Dees_Troy51a0e822012-09-05 15:24:24 -0400100FontResource::FontResource(xml_node<>* node, ZipArchive* pZip)
101 : Resource(node, pZip)
102{
Ethan Yonker74db1572015-10-28 12:44:49 -0500103 origFontSize = 0;
104 origFont = NULL;
105 LoadFont(node, pZip);
106}
107
108void FontResource::LoadFont(xml_node<>* node, ZipArchive* pZip)
109{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200110 std::string file;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200111 xml_attribute<>* attr;
Dees_Troy51a0e822012-09-05 15:24:24 -0400112
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200113 mFont = NULL;
114 if (!node)
115 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400116
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200117 attr = node->first_attribute("filename");
118 if (!attr)
119 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400120
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200121 file = attr->value();
122
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200123 if(file.size() >= 4 && file.compare(file.size()-4, 4, ".ttf") == 0)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200124 {
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200125 m_type = TYPE_TTF;
Ethan Yonker74db1572015-10-28 12:44:49 -0500126 int font_size = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200127
Ethan Yonker74db1572015-10-28 12:44:49 -0500128 if (origFontSize != 0) {
129 attr = node->first_attribute("scale");
130 if (attr == NULL)
131 return;
132 font_size = origFontSize * atoi(attr->value()) / 100;
133 } else {
134 attr = node->first_attribute("size");
135 if (attr == NULL)
136 return;
137 font_size = scale_theme_min(atoi(attr->value()));
138 origFontSize = font_size;
139 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200140
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200141 int dpi = 300;
142
143 attr = node->first_attribute("dpi");
144 if(attr)
145 dpi = atoi(attr->value());
146
147 if (ExtractResource(pZip, "fonts", file, "", TMP_RESOURCE_NAME) == 0)
148 {
Ethan Yonker74db1572015-10-28 12:44:49 -0500149 mFont = gr_ttf_loadFont(TMP_RESOURCE_NAME, font_size, dpi);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200150 unlink(TMP_RESOURCE_NAME);
151 }
152 else
153 {
Dees Troy3454ade2015-01-20 19:21:04 +0000154 file = std::string(TWRES "fonts/") + file;
Ethan Yonker74db1572015-10-28 12:44:49 -0500155 mFont = gr_ttf_loadFont(file.c_str(), font_size, dpi);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200156 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200157 }
158 else
159 {
Ethan Yonker88037f42015-10-04 22:09:08 -0500160 LOGERR("Non-TTF fonts are no longer supported.\n");
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200161 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400162}
163
Ethan Yonker74db1572015-10-28 12:44:49 -0500164void FontResource::DeleteFont() {
165 if(mFont)
166 gr_ttf_freeFont(mFont);
167 mFont = NULL;
168 if(origFont)
169 gr_ttf_freeFont(origFont);
170 origFont = NULL;
171}
172
173void FontResource::Override(xml_node<>* node, ZipArchive* pZip) {
174 if (!origFont) {
175 origFont = mFont;
176 } else if (mFont) {
177 gr_ttf_freeFont(mFont);
178 mFont = NULL;
179 }
180 LoadFont(node, pZip);
181}
182
Dees_Troy51a0e822012-09-05 15:24:24 -0400183FontResource::~FontResource()
184{
Ethan Yonker74db1572015-10-28 12:44:49 -0500185 DeleteFont();
Dees_Troy51a0e822012-09-05 15:24:24 -0400186}
187
that5267a212015-05-06 23:45:57 +0200188ImageResource::ImageResource(xml_node<>* node, ZipArchive* pZip)
Dees_Troy51a0e822012-09-05 15:24:24 -0400189 : Resource(node, pZip)
190{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200191 std::string file;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600192 gr_surface temp_surface = NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -0400193
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200194 mSurface = NULL;
Ethan Yonker619a7212014-12-03 16:47:37 -0600195 if (!node) {
196 LOGERR("ImageResource node is NULL\n");
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200197 return;
Ethan Yonker619a7212014-12-03 16:47:37 -0600198 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400199
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200200 if (node->first_attribute("filename"))
201 file = node->first_attribute("filename")->value();
Ethan Yonker63e414f2015-02-06 15:44:39 -0600202 else {
203 LOGERR("No filename specified for image resource.\n");
204 return;
205 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400206
that5267a212015-05-06 23:45:57 +0200207 bool retain_aspect = (node->first_attribute("retainaspect") != NULL);
208 // the value does not matter, if retainaspect is present, we assume that we want to retain it
Ethan Yonker63e414f2015-02-06 15:44:39 -0600209 LoadImage(pZip, file, &temp_surface);
210 CheckAndScaleImage(temp_surface, &mSurface, retain_aspect);
Dees_Troy51a0e822012-09-05 15:24:24 -0400211}
212
213ImageResource::~ImageResource()
214{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200215 if (mSurface)
216 res_free_surface(mSurface);
Dees_Troy51a0e822012-09-05 15:24:24 -0400217}
218
that5267a212015-05-06 23:45:57 +0200219AnimationResource::AnimationResource(xml_node<>* node, ZipArchive* pZip)
Dees_Troy51a0e822012-09-05 15:24:24 -0400220 : Resource(node, pZip)
221{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200222 std::string file;
223 int fileNum = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400224
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200225 if (!node)
226 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400227
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200228 if (node->first_attribute("filename"))
229 file = node->first_attribute("filename")->value();
Ethan Yonker63e414f2015-02-06 15:44:39 -0600230 else {
231 LOGERR("No filename specified for image resource.\n");
232 return;
233 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400234
that5267a212015-05-06 23:45:57 +0200235 bool retain_aspect = (node->first_attribute("retainaspect") != NULL);
236 // the value does not matter, if retainaspect is present, we assume that we want to retain it
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200237 for (;;)
238 {
239 std::ostringstream fileName;
240 fileName << file << std::setfill ('0') << std::setw (3) << fileNum;
Dees_Troy51a0e822012-09-05 15:24:24 -0400241
Ethan Yonker63e414f2015-02-06 15:44:39 -0600242 gr_surface surface, temp_surface = NULL;
243 LoadImage(pZip, fileName.str(), &temp_surface);
244 CheckAndScaleImage(temp_surface, &surface, retain_aspect);
245 if (surface) {
246 mSurfaces.push_back(surface);
247 fileNum++;
248 } else
249 break; // Done loading animation images
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200250 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400251}
252
253AnimationResource::~AnimationResource()
254{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200255 std::vector<gr_surface>::iterator it;
Dees_Troy51a0e822012-09-05 15:24:24 -0400256
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200257 for (it = mSurfaces.begin(); it != mSurfaces.end(); ++it)
258 res_free_surface(*it);
259
260 mSurfaces.clear();
Dees_Troy51a0e822012-09-05 15:24:24 -0400261}
262
that74ac6062015-03-04 22:39:34 +0100263FontResource* ResourceManager::FindFont(const std::string& name) const
Dees_Troy51a0e822012-09-05 15:24:24 -0400264{
that74ac6062015-03-04 22:39:34 +0100265 for (std::vector<FontResource*>::const_iterator it = mFonts.begin(); it != mFonts.end(); ++it)
266 if (name == (*it)->GetName())
267 return *it;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200268 return NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -0400269}
270
that74ac6062015-03-04 22:39:34 +0100271ImageResource* ResourceManager::FindImage(const std::string& name) const
Dees_Troy51a0e822012-09-05 15:24:24 -0400272{
that74ac6062015-03-04 22:39:34 +0100273 for (std::vector<ImageResource*>::const_iterator it = mImages.begin(); it != mImages.end(); ++it)
274 if (name == (*it)->GetName())
275 return *it;
276 return NULL;
277}
278
279AnimationResource* ResourceManager::FindAnimation(const std::string& name) const
280{
281 for (std::vector<AnimationResource*>::const_iterator it = mAnimations.begin(); it != mAnimations.end(); ++it)
282 if (name == (*it)->GetName())
283 return *it;
284 return NULL;
285}
286
thatb2e8f672015-03-05 20:25:39 +0100287std::string ResourceManager::FindString(const std::string& name) const
288{
Ethan Yonker74db1572015-10-28 12:44:49 -0500289 if (this != NULL) {
290 std::map<std::string, string_resource_struct>::const_iterator it = mStrings.find(name);
291 if (it != mStrings.end())
292 return it->second.value;
293 LOGERR("String resource '%s' not found. No default value.\n", name.c_str());
294 PageManager::AddStringResource("NO DEFAULT", name, "[" + name + ("]"));
295 } else {
296 LOGINFO("String resources not loaded when looking for '%s'. No default value.\n", name.c_str());
297 }
thatb2e8f672015-03-05 20:25:39 +0100298 return "[" + name + ("]");
299}
300
Ethan Yonker74db1572015-10-28 12:44:49 -0500301std::string ResourceManager::FindString(const std::string& name, const std::string& default_string) const
302{
303 if (this != NULL) {
304 std::map<std::string, string_resource_struct>::const_iterator it = mStrings.find(name);
305 if (it != mStrings.end())
306 return it->second.value;
307 LOGERR("String resource '%s' not found. Using default value.\n", name.c_str());
308 PageManager::AddStringResource("DEFAULT", name, default_string);
309 } else {
310 LOGINFO("String resources not loaded when looking for '%s'. Using default value.\n", name.c_str());
311 }
312 return default_string;
313}
314
315void ResourceManager::DumpStrings() const
316{
317 if (this == NULL) {
318 gui_print("No string resources\n");
319 return;
320 }
321 std::map<std::string, string_resource_struct>::const_iterator it;
322 gui_print("Dumping all strings:\n");
323 for (it = mStrings.begin(); it != mStrings.end(); it++)
324 gui_print("source: %s: '%s' = '%s'\n", it->second.source.c_str(), it->first.c_str(), it->second.value.c_str());
325 gui_print("Done dumping strings\n");
326}
327
that74ac6062015-03-04 22:39:34 +0100328ResourceManager::ResourceManager()
329{
Ethan Yonker780cd392014-07-21 15:24:39 -0500330}
331
Ethan Yonker74db1572015-10-28 12:44:49 -0500332void ResourceManager::AddStringResource(std::string resource_source, std::string resource_name, std::string value)
333{
334 string_resource_struct res;
335 res.source = resource_source;
336 res.value = value;
337 mStrings[resource_name] = res;
338}
339
340void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source)
Ethan Yonker780cd392014-07-21 15:24:39 -0500341{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200342 if (!resList)
343 return;
that0f425062015-03-04 23:05:00 +0100344
345 for (xml_node<>* child = resList->first_node(); child; child = child->next_sibling())
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200346 {
that0f425062015-03-04 23:05:00 +0100347 std::string type = child->name();
348 if (type == "resource") {
349 // legacy format : <resource type="...">
350 xml_attribute<>* attr = child->first_attribute("type");
351 type = attr ? attr->value() : "*unspecified*";
352 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400353
that74ac6062015-03-04 22:39:34 +0100354 bool error = false;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200355 if (type == "font")
356 {
that74ac6062015-03-04 22:39:34 +0100357 FontResource* res = new FontResource(child, pZip);
358 if (res->GetResource())
359 mFonts.push_back(res);
360 else {
361 error = true;
362 delete res;
363 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200364 }
Ethan Yonker74db1572015-10-28 12:44:49 -0500365 else if (type == "fontoverride")
366 {
367 if (mFonts.size() != 0 && child && child->first_attribute("name")) {
368 string FontName = child->first_attribute("name")->value();
369 size_t font_count = mFonts.size(), i;
370 bool found = false;
371
372 for (i = 0; i < font_count; i++) {
373 if (mFonts[i]->GetName() == FontName) {
374 mFonts[i]->Override(child, pZip);
375 found = true;
376 break;
377 }
378 }
379 if (!found) {
380 LOGERR("Unable to locate font '%s' for override.\n", FontName.c_str());
381 }
382 } else if (mFonts.size() != 0)
383 LOGERR("Unable to locate font name for type fontoverride.\n");
384 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200385 else if (type == "image")
386 {
that5267a212015-05-06 23:45:57 +0200387 ImageResource* res = new ImageResource(child, pZip);
that74ac6062015-03-04 22:39:34 +0100388 if (res->GetResource())
389 mImages.push_back(res);
390 else {
391 error = true;
392 delete res;
393 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200394 }
395 else if (type == "animation")
396 {
that5267a212015-05-06 23:45:57 +0200397 AnimationResource* res = new AnimationResource(child, pZip);
that74ac6062015-03-04 22:39:34 +0100398 if (res->GetResourceCount())
399 mAnimations.push_back(res);
400 else {
401 error = true;
402 delete res;
403 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200404 }
thatb2e8f672015-03-05 20:25:39 +0100405 else if (type == "string")
406 {
Ethan Yonker74db1572015-10-28 12:44:49 -0500407 if (xml_attribute<>* attr = child->first_attribute("name")) {
408 string_resource_struct res;
409 res.source = resource_source;
410 res.value = child->value();
411 mStrings[attr->value()] = res;
412 } else
thatb2e8f672015-03-05 20:25:39 +0100413 error = true;
414 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200415 else
416 {
417 LOGERR("Resource type (%s) not supported.\n", type.c_str());
that74ac6062015-03-04 22:39:34 +0100418 error = true;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200419 }
thatf6ed8fc2015-02-14 20:23:16 +0100420
that74ac6062015-03-04 22:39:34 +0100421 if (error)
thatf74ac872015-01-18 12:00:02 +0100422 {
423 std::string res_name;
424 if (child->first_attribute("name"))
425 res_name = child->first_attribute("name")->value();
426 if (res_name.empty() && child->first_attribute("filename"))
427 res_name = child->first_attribute("filename")->value();
428
429 if (!res_name.empty()) {
430 LOGERR("Resource (%s)-(%s) failed to load\n", type.c_str(), res_name.c_str());
431 } else
432 LOGERR("Resource type (%s) failed to load\n", type.c_str());
thatf74ac872015-01-18 12:00:02 +0100433 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200434 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400435}
436
437ResourceManager::~ResourceManager()
438{
that74ac6062015-03-04 22:39:34 +0100439 for (std::vector<FontResource*>::iterator it = mFonts.begin(); it != mFonts.end(); ++it)
440 delete *it;
Dees_Troy51a0e822012-09-05 15:24:24 -0400441
that74ac6062015-03-04 22:39:34 +0100442 for (std::vector<ImageResource*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
443 delete *it;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200444
that74ac6062015-03-04 22:39:34 +0100445 for (std::vector<AnimationResource*>::iterator it = mAnimations.begin(); it != mAnimations.end(); ++it)
446 delete *it;
Dees_Troy51a0e822012-09-05 15:24:24 -0400447}