only save to cache if cache enabled
[Tsunagari.git] / src / resourcer.cpp
blob0ec7d795de11c2c33903a569617a4f65ac99ded3
1 /******************************
2 ** Tsunagari Tile Engine **
3 ** resourcer.cpp **
4 ** Copyright 2011 OmegaSDG **
5 ******************************/
7 #include <errno.h>
8 #include <stdlib.h>
10 #include <boost/scoped_ptr.hpp>
11 #include <boost/shared_ptr.hpp>
12 #include <Gosu/Audio.hpp>
13 #include <Gosu/Bitmap.hpp>
14 #include <Gosu/Image.hpp>
15 #include <Gosu/IO.hpp>
16 #include <zip.h>
18 #include "common.h"
19 #include "config.h"
20 #include "log.h"
21 #include "resourcer.h"
22 #include "window.h"
24 static void xmlErrorCb(void* pstrFilename, const char* msg, ...)
26 const std::string* filename = (const std::string*)pstrFilename;
27 char buf[512];
28 va_list ap;
30 va_start(ap, msg);
31 snprintf(buf, sizeof(buf)-1, msg, va_arg(ap, char*));
32 Log::err(*filename, buf);
33 va_end(ap);
36 Resourcer::Resourcer(GameWindow* window, ClientValues* conf)
37 : window(window), z(NULL), conf(conf)
41 Resourcer::~Resourcer()
43 if (z && zip_close(z))
44 Log::err(conf->world,
45 std::string("closing : ") + zip_strerror(z));
48 bool Resourcer::init()
50 int err;
51 z = zip_open(conf->world.c_str(), 0x0, &err);
52 if (!z) {
53 char buf[512];
54 zip_error_to_str(buf, sizeof(buf), err, errno);
55 Log::err(conf->world, buf);
58 return z;
61 ImageRef Resourcer::getImage(const std::string& name)
63 if (conf->cache_enabled) {
64 ImageRefMap::iterator entry = images.find(name);
65 if (entry != images.end())
66 return entry->second;
69 BufferPtr buffer(read(name));
70 if (!buffer)
71 return ImageRef();
72 Gosu::Bitmap bitmap;
73 Gosu::loadImageFile(bitmap, buffer->frontReader());
74 ImageRef result(new Gosu::Image(window->graphics(), bitmap, false));
75 if (conf->cache_enabled)
76 images[name] = result;
77 return result;
80 void Resourcer::getBitmap(Gosu::Bitmap& bitmap, const std::string& name)
82 BufferPtr buffer(read(name));
83 if (!buffer)
84 return;
85 return Gosu::loadImageFile(bitmap, buffer->frontReader());
88 Gosu::Image* Resourcer::bitmapSection(const Gosu::Bitmap& src,
89 unsigned x, unsigned y, unsigned w, unsigned h, bool tileable)
91 return new Gosu::Image(window->graphics(), src, x, y, w, h, tileable);
94 /* FIXME
95 * We use Gosu::Sample for music because Gosu::Song's SDL implementation
96 * doesn't support loading from a memory buffer at the moment.
98 SampleRef Resourcer::getSample(const std::string& name)
100 if (conf->cache_enabled) {
101 SampleRefMap::iterator entry = samples.find(name);
102 if (entry != samples.end())
103 return entry->second;
106 BufferPtr buffer(read(name));
107 if (!buffer)
108 return SampleRef();
109 SampleRef result(new Gosu::Sample(buffer->frontReader()));
110 if (conf->cache_enabled)
111 samples[name] = result;
112 return result;
115 XMLDocRef Resourcer::getXMLDoc(const std::string& name, const std::string& dtdPath)
117 if (conf->cache_enabled) {
118 XMLMap::iterator entry = xmls.find(name);
119 if (entry != xmls.end())
120 return entry->second;
123 XMLDocRef result(readXMLDocFromDisk(name, dtdPath));
124 if (conf->cache_enabled)
125 xmls[name] = result;
126 return result;
129 // use RAII to ensure doc is freed
130 // boost::shared_ptr<void> alwaysFreeTheDoc(doc, xmlFreeDoc);
131 xmlDoc* Resourcer::readXMLDocFromDisk(const std::string& name, const std::string& dtdPath)
133 const std::string docStr = readStringFromDisk(name);
134 if (docStr.empty())
135 return NULL;
137 xmlParserCtxt* pc = xmlNewParserCtxt();
138 const std::string pathname = path(name);
139 pc->vctxt.userData = (void*)&pathname;
140 pc->vctxt.error = xmlErrorCb;
142 // Parse the XML. Hand over our error callback fn.
143 xmlDoc* doc = xmlCtxtReadMemory(pc, docStr.c_str(),
144 (int)docStr.size(), NULL, NULL,
145 XML_PARSE_NOBLANKS |
146 XML_PARSE_NONET);
147 xmlFreeParserCtxt(pc);
148 if (!doc) {
149 Log::err(pathname, "Could not parse file");
150 return NULL;
153 // Load up a Document Type Definition for validating the document.
154 xmlDtd* dtd = xmlParseDTD(NULL, (const xmlChar*)dtdPath.c_str());
155 if (!dtd) {
156 Log::err(dtdPath, "file not found");
157 return NULL;
160 // Assert the document is sane here and now so we don't have to have a
161 // billion if-else statements while traversing the document tree.
162 xmlValidCtxt* vc = xmlNewValidCtxt();
163 int valid = xmlValidateDtd(vc, doc, dtd);
164 xmlFreeValidCtxt(vc);
165 xmlFreeDtd(dtd);
167 if (!valid) {
168 Log::err(pathname, "XML document does not follow DTD");
169 return NULL;
172 return doc;
175 std::string Resourcer::readStringFromDisk(const std::string& name)
177 struct zip_stat stat;
178 zip_file* zf;
179 int size;
180 char* buf;
181 std::string str;
183 if (zip_stat(z, name.c_str(), 0x0, &stat)) {
184 Log::err(path(name), "file missing");
185 return "";
188 size = (int)stat.size;
189 buf = new char[size + 1];
190 buf[size] = '\0';
192 zf = zip_fopen(z, name.c_str(), 0x0);
193 if (!zf) {
194 Log::err(path(name),
195 std::string("opening : ") + zip_strerror(z));
196 return "";
199 if (zip_fread(zf, buf, size) != size) {
200 Log::err(path(name), "reading didn't complete");
201 zip_fclose(zf);
202 return "";
205 str = buf;
206 delete[] buf;
208 zip_fclose(zf);
209 return str;
212 Gosu::Buffer* Resourcer::read(const std::string& name)
214 struct zip_stat stat;
215 zip_file* zf;
216 int size;
218 if (zip_stat(z, name.c_str(), 0x0, &stat)) {
219 Log::err(path(name), "file missing");
220 return NULL;
223 size = (int)stat.size;
225 if (!(zf = zip_fopen(z, name.c_str(), 0x0))) {
226 Log::err(path(name),
227 std::string("opening : ") + zip_strerror(z));
228 return NULL;
231 Gosu::Buffer* buffer = new Gosu::Buffer;
232 buffer->resize(size);
233 if (zip_fread(zf, buffer->data(), size) != size) {
234 Log::err(path(name), "reading didn't complete");
235 zip_fclose(zf);
236 delete buffer;
237 return NULL;
240 zip_fclose(zf);
241 return buffer;
244 std::string Resourcer::path(const std::string& entry_name) const
246 return conf->world + "/" + entry_name;