Ability to disable resource cache.
[Tsunagari.git] / src / resourcer.cpp
blobf5d41d5ad766836777ee7a6fca65986caafecb50
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 images[name] = result;
76 return result;
79 void Resourcer::getBitmap(Gosu::Bitmap& bitmap, const std::string& name)
81 BufferPtr buffer(read(name));
82 if (!buffer)
83 return;
84 return Gosu::loadImageFile(bitmap, buffer->frontReader());
87 Gosu::Image* Resourcer::bitmapSection(const Gosu::Bitmap& src,
88 unsigned x, unsigned y, unsigned w, unsigned h, bool tileable)
90 return new Gosu::Image(window->graphics(), src, x, y, w, h, tileable);
93 XMLDocRef Resourcer::getXMLDoc(const std::string& name)
95 if (conf->cache_enabled) {
96 XMLMap::iterator entry = xmls.find(name);
97 if (entry != xmls.end())
98 return entry->second;
101 XMLDocRef result(getXMLDocFromDisk(name));
102 xmls[name] = result;
103 return result;
106 // use RAII to ensure doc is freed
107 // boost::shared_ptr<void> alwaysFreeTheDoc(doc, xmlFreeDoc);
108 xmlDoc* Resourcer::getXMLDocFromDisk(const std::string& name)
110 const std::string docStr = getString(name);
111 if (docStr.empty())
112 return NULL;
114 xmlParserCtxt* ctxt = xmlNewParserCtxt();
115 const std::string pathname = path(name);
116 ctxt->vctxt.userData = (void*)&pathname;
117 ctxt->vctxt.error = xmlErrorCb;
118 boost::shared_ptr<void> ctxtDeleter(ctxt, xmlFreeParserCtxt);
120 xmlDoc* doc = xmlCtxtReadMemory(ctxt, docStr.c_str(),
121 (int)docStr.size(), NULL, NULL,
122 XML_PARSE_NOBLANKS |
123 XML_PARSE_NONET |
124 XML_PARSE_DTDVALID);
125 if (!doc) {
126 Log::err(pathname, "Could not parse file");
127 return NULL;
129 else if (!ctxt->valid) {
130 Log::err(pathname, "XML document does not follow DTD");
131 return NULL;
134 return doc;
137 /* FIXME
138 * We use Gosu::Sample for music because Gosu::Song's SDL implementation
139 * doesn't support loading from a memory buffer at the moment.
141 SampleRef Resourcer::getSample(const std::string& name)
143 if (conf->cache_enabled) {
144 SampleRefMap::iterator entry = samples.find(name);
145 if (entry != samples.end())
146 return entry->second;
149 BufferPtr buffer(read(name));
150 if (!buffer)
151 return SampleRef();
152 SampleRef result(new Gosu::Sample(buffer->frontReader()));
153 samples[name] = result;
154 return result;
157 std::string Resourcer::getString(const std::string& name)
159 StringMap::iterator entry = strings.find(name);
160 if (entry != strings.end())
161 return entry->second;
163 std::string result = getStringFromDisk(name);
165 strings[name] = result;
166 return result;
169 std::string Resourcer::getStringFromDisk(const std::string& name)
171 struct zip_stat stat;
172 zip_file* zf;
173 int size;
174 char* buf;
175 std::string str;
177 if (zip_stat(z, name.c_str(), 0x0, &stat)) {
178 Log::err(path(name), "file missing");
179 return "";
182 size = (int)stat.size;
183 buf = new char[size + 1];
184 buf[size] = '\0';
186 zf = zip_fopen(z, name.c_str(), 0x0);
187 if (!zf) {
188 Log::err(path(name),
189 std::string("opening : ") + zip_strerror(z));
190 return "";
193 if (zip_fread(zf, buf, size) != size) {
194 Log::err(path(name), "reading didn't complete");
195 zip_fclose(zf);
196 return "";
199 str = buf;
200 delete[] buf;
202 zip_fclose(zf);
203 return str;
206 Gosu::Buffer* Resourcer::read(const std::string& name)
208 struct zip_stat stat;
209 zip_file* zf;
210 int size;
212 if (zip_stat(z, name.c_str(), 0x0, &stat)) {
213 Log::err(path(name), "file missing");
214 return NULL;
217 size = (int)stat.size;
219 if (!(zf = zip_fopen(z, name.c_str(), 0x0))) {
220 Log::err(path(name),
221 std::string("opening : ") + zip_strerror(z));
222 return NULL;
225 Gosu::Buffer* buffer = new Gosu::Buffer;
226 buffer->resize(size);
227 if (zip_fread(zf, buffer->data(), size) != size) {
228 Log::err(path(name), "reading didn't complete");
229 zip_fclose(zf);
230 delete buffer;
231 return NULL;
234 zip_fclose(zf);
235 return buffer;
238 std::string Resourcer::path(const std::string& entry_name) const
240 return conf->world + "/" + entry_name;