blob: 000912d4eb47b6a228d199b12c4a7a0328b4fbe7 [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>
Ethan Yonker3fdcda42016-11-30 12:29:37 -060013#include <fcntl.h>
Dees_Troy51a0e822012-09-05 15:24:24 -040014
thatb2e8f672015-03-05 20:25:39 +010015#include "../minzip/Zip.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040016extern "C" {
Dees_Troy2673cec2013-04-02 20:22:16 +000017#include "../twcommon.h"
Ethan Yonker63e414f2015-02-06 15:44:39 -060018#include "gui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040019}
Ethan Yonkerfbb43532015-12-28 21:54:50 +010020#include "../minuitwrp/minui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040021
22#include "rapidxml.hpp"
23#include "objects.hpp"
24
25#define TMP_RESOURCE_NAME "/tmp/extract.bin"
26
Ethan Yonkerd0514ba2015-10-22 14:17:47 -050027Resource::Resource(xml_node<>* node, ZipArchive* pZip __unused)
Dees_Troy51a0e822012-09-05 15:24:24 -040028{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020029 if (node && node->first_attribute("name"))
30 mName = node->first_attribute("name")->value();
Dees_Troy51a0e822012-09-05 15:24:24 -040031}
32
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020033int Resource::ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
Dees_Troy51a0e822012-09-05 15:24:24 -040034{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020035 if (!pZip)
36 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040037
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020038 std::string src = folderName + "/" + fileName + fileExtn;
Dees_Troy51a0e822012-09-05 15:24:24 -040039
40 const ZipEntry* binary = mzFindZipEntry(pZip, src.c_str());
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020041 if (binary == NULL) {
42 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040043 }
44
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020045 unlink(destFile.c_str());
46 int fd = creat(destFile.c_str(), 0666);
47 if (fd < 0)
48 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040049
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020050 int ret = 0;
51 if (!mzExtractZipEntryToFile(pZip, binary, fd))
52 ret = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040053
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020054 close(fd);
55 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -040056}
57
thatb240f4a2016-03-14 01:21:38 +010058void Resource::LoadImage(ZipArchive* pZip, std::string file, gr_surface* surface)
Ethan Yonker63e414f2015-02-06 15:44:39 -060059{
thatb240f4a2016-03-14 01:21:38 +010060 int rc = 0;
Ethan Yonker63e414f2015-02-06 15:44:39 -060061 if (ExtractResource(pZip, "images", file, ".png", TMP_RESOURCE_NAME) == 0)
62 {
thatb240f4a2016-03-14 01:21:38 +010063 rc = res_create_surface(TMP_RESOURCE_NAME, surface);
Ethan Yonker63e414f2015-02-06 15:44:39 -060064 unlink(TMP_RESOURCE_NAME);
65 }
66 else if (ExtractResource(pZip, "images", file, "", TMP_RESOURCE_NAME) == 0)
67 {
68 // JPG includes the .jpg extension in the filename so extension should be blank
thatb240f4a2016-03-14 01:21:38 +010069 rc = res_create_surface(TMP_RESOURCE_NAME, surface);
Ethan Yonker63e414f2015-02-06 15:44:39 -060070 unlink(TMP_RESOURCE_NAME);
71 }
72 else if (!pZip)
73 {
74 // File name in xml may have included .png so try without adding .png
thatb240f4a2016-03-14 01:21:38 +010075 rc = res_create_surface(file.c_str(), surface);
Ethan Yonker63e414f2015-02-06 15:44:39 -060076 }
thatb240f4a2016-03-14 01:21:38 +010077 if (rc != 0)
78 LOGINFO("Failed to load image from %s%s, error %d\n", file.c_str(), pZip ? " (zip)" : "", rc);
Ethan Yonker63e414f2015-02-06 15:44:39 -060079}
80
81void Resource::CheckAndScaleImage(gr_surface source, gr_surface* destination, int retain_aspect)
82{
83 if (!source) {
84 *destination = NULL;
85 return;
86 }
87 if (get_scale_w() != 0 && get_scale_h() != 0) {
88 float scale_w = get_scale_w(), scale_h = get_scale_h();
89 if (retain_aspect) {
90 if (scale_w < scale_h)
91 scale_h = scale_w;
92 else
93 scale_w = scale_h;
94 }
95 if (res_scale_surface(source, destination, scale_w, scale_h)) {
96 LOGINFO("Error scaling image, using regular size.\n");
97 *destination = source;
98 }
99 } else {
100 *destination = source;
101 }
102}
103
Dees_Troy51a0e822012-09-05 15:24:24 -0400104FontResource::FontResource(xml_node<>* node, ZipArchive* pZip)
105 : Resource(node, pZip)
106{
Ethan Yonker74db1572015-10-28 12:44:49 -0500107 origFontSize = 0;
108 origFont = NULL;
109 LoadFont(node, pZip);
110}
111
112void FontResource::LoadFont(xml_node<>* node, ZipArchive* pZip)
113{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200114 std::string file;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200115 xml_attribute<>* attr;
Dees_Troy51a0e822012-09-05 15:24:24 -0400116
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200117 mFont = NULL;
118 if (!node)
119 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400120
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200121 attr = node->first_attribute("filename");
122 if (!attr)
123 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400124
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200125 file = attr->value();
126
Matt Mowera8a89d12016-12-30 18:10:37 -0600127 if (file.size() >= 4 && file.compare(file.size()-4, 4, ".ttf") == 0)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200128 {
Ethan Yonker74db1572015-10-28 12:44:49 -0500129 int font_size = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200130
Ethan Yonker74db1572015-10-28 12:44:49 -0500131 if (origFontSize != 0) {
132 attr = node->first_attribute("scale");
133 if (attr == NULL)
134 return;
135 font_size = origFontSize * atoi(attr->value()) / 100;
136 } else {
137 attr = node->first_attribute("size");
138 if (attr == NULL)
139 return;
140 font_size = scale_theme_min(atoi(attr->value()));
141 origFontSize = font_size;
142 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200143
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200144 int dpi = 300;
145
146 attr = node->first_attribute("dpi");
Matt Mowera8a89d12016-12-30 18:10:37 -0600147 if (attr)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200148 dpi = atoi(attr->value());
149
thatb240f4a2016-03-14 01:21:38 +0100150 // we can't use TMP_RESOURCE_NAME here because the ttf subsystem is caching the name and scaling needs to reload the font
151 std::string tmpname = "/tmp/" + file;
152 if (ExtractResource(pZip, "fonts", file, "", tmpname) == 0)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200153 {
thatb240f4a2016-03-14 01:21:38 +0100154 mFont = gr_ttf_loadFont(tmpname.c_str(), font_size, dpi);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200155 }
156 else
157 {
Dees Troy3454ade2015-01-20 19:21:04 +0000158 file = std::string(TWRES "fonts/") + file;
Ethan Yonker74db1572015-10-28 12:44:49 -0500159 mFont = gr_ttf_loadFont(file.c_str(), font_size, dpi);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200160 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200161 }
162 else
163 {
Ethan Yonker88037f42015-10-04 22:09:08 -0500164 LOGERR("Non-TTF fonts are no longer supported.\n");
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200165 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400166}
167
Ethan Yonker74db1572015-10-28 12:44:49 -0500168void FontResource::DeleteFont() {
Matt Mowera8a89d12016-12-30 18:10:37 -0600169 if (mFont)
Ethan Yonker74db1572015-10-28 12:44:49 -0500170 gr_ttf_freeFont(mFont);
171 mFont = NULL;
Matt Mowera8a89d12016-12-30 18:10:37 -0600172 if (origFont)
Ethan Yonker74db1572015-10-28 12:44:49 -0500173 gr_ttf_freeFont(origFont);
174 origFont = NULL;
175}
176
177void FontResource::Override(xml_node<>* node, ZipArchive* pZip) {
178 if (!origFont) {
179 origFont = mFont;
180 } else if (mFont) {
181 gr_ttf_freeFont(mFont);
182 mFont = NULL;
183 }
184 LoadFont(node, pZip);
185}
186
Dees_Troy51a0e822012-09-05 15:24:24 -0400187FontResource::~FontResource()
188{
Ethan Yonker74db1572015-10-28 12:44:49 -0500189 DeleteFont();
Dees_Troy51a0e822012-09-05 15:24:24 -0400190}
191
that5267a212015-05-06 23:45:57 +0200192ImageResource::ImageResource(xml_node<>* node, ZipArchive* pZip)
Dees_Troy51a0e822012-09-05 15:24:24 -0400193 : Resource(node, pZip)
194{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200195 std::string file;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600196 gr_surface temp_surface = NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -0400197
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200198 mSurface = NULL;
Ethan Yonker619a7212014-12-03 16:47:37 -0600199 if (!node) {
200 LOGERR("ImageResource node is NULL\n");
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200201 return;
Ethan Yonker619a7212014-12-03 16:47:37 -0600202 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400203
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200204 if (node->first_attribute("filename"))
205 file = node->first_attribute("filename")->value();
Ethan Yonker63e414f2015-02-06 15:44:39 -0600206 else {
207 LOGERR("No filename specified for image resource.\n");
208 return;
209 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400210
that5267a212015-05-06 23:45:57 +0200211 bool retain_aspect = (node->first_attribute("retainaspect") != NULL);
212 // the value does not matter, if retainaspect is present, we assume that we want to retain it
Ethan Yonker63e414f2015-02-06 15:44:39 -0600213 LoadImage(pZip, file, &temp_surface);
214 CheckAndScaleImage(temp_surface, &mSurface, retain_aspect);
Dees_Troy51a0e822012-09-05 15:24:24 -0400215}
216
217ImageResource::~ImageResource()
218{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200219 if (mSurface)
220 res_free_surface(mSurface);
Dees_Troy51a0e822012-09-05 15:24:24 -0400221}
222
that5267a212015-05-06 23:45:57 +0200223AnimationResource::AnimationResource(xml_node<>* node, ZipArchive* pZip)
Dees_Troy51a0e822012-09-05 15:24:24 -0400224 : Resource(node, pZip)
225{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200226 std::string file;
227 int fileNum = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400228
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200229 if (!node)
230 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400231
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200232 if (node->first_attribute("filename"))
233 file = node->first_attribute("filename")->value();
Ethan Yonker63e414f2015-02-06 15:44:39 -0600234 else {
235 LOGERR("No filename specified for image resource.\n");
236 return;
237 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400238
that5267a212015-05-06 23:45:57 +0200239 bool retain_aspect = (node->first_attribute("retainaspect") != NULL);
240 // the value does not matter, if retainaspect is present, we assume that we want to retain it
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200241 for (;;)
242 {
243 std::ostringstream fileName;
244 fileName << file << std::setfill ('0') << std::setw (3) << fileNum;
Dees_Troy51a0e822012-09-05 15:24:24 -0400245
Ethan Yonker63e414f2015-02-06 15:44:39 -0600246 gr_surface surface, temp_surface = NULL;
247 LoadImage(pZip, fileName.str(), &temp_surface);
248 CheckAndScaleImage(temp_surface, &surface, retain_aspect);
249 if (surface) {
250 mSurfaces.push_back(surface);
251 fileNum++;
252 } else
253 break; // Done loading animation images
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200254 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400255}
256
257AnimationResource::~AnimationResource()
258{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200259 std::vector<gr_surface>::iterator it;
Dees_Troy51a0e822012-09-05 15:24:24 -0400260
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200261 for (it = mSurfaces.begin(); it != mSurfaces.end(); ++it)
262 res_free_surface(*it);
263
264 mSurfaces.clear();
Dees_Troy51a0e822012-09-05 15:24:24 -0400265}
266
that74ac6062015-03-04 22:39:34 +0100267FontResource* ResourceManager::FindFont(const std::string& name) const
Dees_Troy51a0e822012-09-05 15:24:24 -0400268{
that74ac6062015-03-04 22:39:34 +0100269 for (std::vector<FontResource*>::const_iterator it = mFonts.begin(); it != mFonts.end(); ++it)
270 if (name == (*it)->GetName())
271 return *it;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200272 return NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -0400273}
274
that74ac6062015-03-04 22:39:34 +0100275ImageResource* ResourceManager::FindImage(const std::string& name) const
Dees_Troy51a0e822012-09-05 15:24:24 -0400276{
that74ac6062015-03-04 22:39:34 +0100277 for (std::vector<ImageResource*>::const_iterator it = mImages.begin(); it != mImages.end(); ++it)
278 if (name == (*it)->GetName())
279 return *it;
280 return NULL;
281}
282
283AnimationResource* ResourceManager::FindAnimation(const std::string& name) const
284{
285 for (std::vector<AnimationResource*>::const_iterator it = mAnimations.begin(); it != mAnimations.end(); ++it)
286 if (name == (*it)->GetName())
287 return *it;
288 return NULL;
289}
290
thatb2e8f672015-03-05 20:25:39 +0100291std::string ResourceManager::FindString(const std::string& name) const
292{
Ethan Yonker74db1572015-10-28 12:44:49 -0500293 if (this != NULL) {
294 std::map<std::string, string_resource_struct>::const_iterator it = mStrings.find(name);
295 if (it != mStrings.end())
296 return it->second.value;
297 LOGERR("String resource '%s' not found. No default value.\n", name.c_str());
298 PageManager::AddStringResource("NO DEFAULT", name, "[" + name + ("]"));
299 } else {
300 LOGINFO("String resources not loaded when looking for '%s'. No default value.\n", name.c_str());
301 }
thatb2e8f672015-03-05 20:25:39 +0100302 return "[" + name + ("]");
303}
304
Ethan Yonker74db1572015-10-28 12:44:49 -0500305std::string ResourceManager::FindString(const std::string& name, const std::string& default_string) const
306{
307 if (this != NULL) {
308 std::map<std::string, string_resource_struct>::const_iterator it = mStrings.find(name);
309 if (it != mStrings.end())
310 return it->second.value;
311 LOGERR("String resource '%s' not found. Using default value.\n", name.c_str());
312 PageManager::AddStringResource("DEFAULT", name, default_string);
313 } else {
314 LOGINFO("String resources not loaded when looking for '%s'. Using default value.\n", name.c_str());
315 }
316 return default_string;
317}
318
319void ResourceManager::DumpStrings() const
320{
321 if (this == NULL) {
322 gui_print("No string resources\n");
323 return;
324 }
325 std::map<std::string, string_resource_struct>::const_iterator it;
326 gui_print("Dumping all strings:\n");
327 for (it = mStrings.begin(); it != mStrings.end(); it++)
328 gui_print("source: %s: '%s' = '%s'\n", it->second.source.c_str(), it->first.c_str(), it->second.value.c_str());
329 gui_print("Done dumping strings\n");
330}
331
that74ac6062015-03-04 22:39:34 +0100332ResourceManager::ResourceManager()
333{
Ethan Yonker780cd392014-07-21 15:24:39 -0500334}
335
Ethan Yonker74db1572015-10-28 12:44:49 -0500336void ResourceManager::AddStringResource(std::string resource_source, std::string resource_name, std::string value)
337{
338 string_resource_struct res;
339 res.source = resource_source;
340 res.value = value;
341 mStrings[resource_name] = res;
342}
343
344void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source)
Ethan Yonker780cd392014-07-21 15:24:39 -0500345{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200346 if (!resList)
347 return;
that0f425062015-03-04 23:05:00 +0100348
349 for (xml_node<>* child = resList->first_node(); child; child = child->next_sibling())
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200350 {
that0f425062015-03-04 23:05:00 +0100351 std::string type = child->name();
352 if (type == "resource") {
353 // legacy format : <resource type="...">
354 xml_attribute<>* attr = child->first_attribute("type");
355 type = attr ? attr->value() : "*unspecified*";
356 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400357
that74ac6062015-03-04 22:39:34 +0100358 bool error = false;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200359 if (type == "font")
360 {
that74ac6062015-03-04 22:39:34 +0100361 FontResource* res = new FontResource(child, pZip);
362 if (res->GetResource())
363 mFonts.push_back(res);
364 else {
365 error = true;
366 delete res;
367 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200368 }
Ethan Yonker74db1572015-10-28 12:44:49 -0500369 else if (type == "fontoverride")
370 {
371 if (mFonts.size() != 0 && child && child->first_attribute("name")) {
372 string FontName = child->first_attribute("name")->value();
373 size_t font_count = mFonts.size(), i;
374 bool found = false;
375
376 for (i = 0; i < font_count; i++) {
377 if (mFonts[i]->GetName() == FontName) {
378 mFonts[i]->Override(child, pZip);
379 found = true;
380 break;
381 }
382 }
383 if (!found) {
384 LOGERR("Unable to locate font '%s' for override.\n", FontName.c_str());
385 }
386 } else if (mFonts.size() != 0)
387 LOGERR("Unable to locate font name for type fontoverride.\n");
388 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200389 else if (type == "image")
390 {
that5267a212015-05-06 23:45:57 +0200391 ImageResource* res = new ImageResource(child, pZip);
that74ac6062015-03-04 22:39:34 +0100392 if (res->GetResource())
393 mImages.push_back(res);
394 else {
395 error = true;
396 delete res;
397 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200398 }
399 else if (type == "animation")
400 {
that5267a212015-05-06 23:45:57 +0200401 AnimationResource* res = new AnimationResource(child, pZip);
that74ac6062015-03-04 22:39:34 +0100402 if (res->GetResourceCount())
403 mAnimations.push_back(res);
404 else {
405 error = true;
406 delete res;
407 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200408 }
thatb2e8f672015-03-05 20:25:39 +0100409 else if (type == "string")
410 {
Ethan Yonker74db1572015-10-28 12:44:49 -0500411 if (xml_attribute<>* attr = child->first_attribute("name")) {
412 string_resource_struct res;
413 res.source = resource_source;
414 res.value = child->value();
415 mStrings[attr->value()] = res;
416 } else
thatb2e8f672015-03-05 20:25:39 +0100417 error = true;
418 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200419 else
420 {
421 LOGERR("Resource type (%s) not supported.\n", type.c_str());
that74ac6062015-03-04 22:39:34 +0100422 error = true;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200423 }
thatf6ed8fc2015-02-14 20:23:16 +0100424
that74ac6062015-03-04 22:39:34 +0100425 if (error)
thatf74ac872015-01-18 12:00:02 +0100426 {
427 std::string res_name;
428 if (child->first_attribute("name"))
429 res_name = child->first_attribute("name")->value();
430 if (res_name.empty() && child->first_attribute("filename"))
431 res_name = child->first_attribute("filename")->value();
432
433 if (!res_name.empty()) {
434 LOGERR("Resource (%s)-(%s) failed to load\n", type.c_str(), res_name.c_str());
435 } else
436 LOGERR("Resource type (%s) failed to load\n", type.c_str());
thatf74ac872015-01-18 12:00:02 +0100437 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200438 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400439}
440
441ResourceManager::~ResourceManager()
442{
that74ac6062015-03-04 22:39:34 +0100443 for (std::vector<FontResource*>::iterator it = mFonts.begin(); it != mFonts.end(); ++it)
444 delete *it;
Dees_Troy51a0e822012-09-05 15:24:24 -0400445
that74ac6062015-03-04 22:39:34 +0100446 for (std::vector<ImageResource*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
447 delete *it;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200448
that74ac6062015-03-04 22:39:34 +0100449 for (std::vector<AnimationResource*>::iterator it = mAnimations.begin(); it != mAnimations.end(); ++it)
450 delete *it;
Dees_Troy51a0e822012-09-05 15:24:24 -0400451}