Makefile: can make any world
[Tsunagari.git] / src / resourcer.cpp
blob8437187bb3ca6189cf44fb4c85a4bce00d9dfd95
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 #ifndef LIBXML_TREE_ENABLED
25 #error Tree must be enabled in libxml2
26 #endif
28 static void xmlErrorCb(void* pstrFilename, const char* msg, ...)
30 const std::string* filename = (const std::string*)pstrFilename;
31 char buf[512];
32 va_list ap;
34 va_start(ap, msg);
35 snprintf(buf, sizeof(buf)-1, msg, va_arg(ap, char*));
36 Log::err(*filename, buf);
37 va_end(ap);
40 Resourcer::Resourcer(GameWindow* window, ClientValues* conf)
41 : window(window), z(NULL), conf(conf)
45 Resourcer::~Resourcer()
47 if (z && zip_close(z))
48 Log::err(conf->world,
49 std::string("closing : ") + zip_strerror(z));
52 bool Resourcer::init()
54 int err;
55 z = zip_open(conf->world.c_str(), 0x0, &err);
56 if (!z) {
57 char buf[512];
58 zip_error_to_str(buf, sizeof(buf), err, errno);
59 Log::err(conf->world, buf);
62 return z;
65 ImageRef Resourcer::getImage(const std::string& name)
67 if (conf->cache_enabled) {
68 ImageRefMap::iterator entry = images.find(name);
69 if (entry != images.end())
70 return entry->second;
73 BufferPtr buffer(read(name));
74 if (!buffer)
75 return ImageRef();
76 Gosu::Bitmap bitmap;
77 Gosu::loadImageFile(bitmap, buffer->frontReader());
78 ImageRef result(new Gosu::Image(window->graphics(), bitmap, false));
79 if (conf->cache_enabled)
80 images[name] = result;
81 return result;
84 void Resourcer::getBitmap(Gosu::Bitmap& bitmap, const std::string& name)
86 BufferPtr buffer(read(name));
87 if (!buffer)
88 return;
89 return Gosu::loadImageFile(bitmap, buffer->frontReader());
92 Gosu::Image* Resourcer::bitmapSection(const Gosu::Bitmap& src,
93 unsigned x, unsigned y, unsigned w, unsigned h, bool tileable)
95 return new Gosu::Image(window->graphics(), src, x, y, w, h, tileable);
98 /* FIXME
99 * We use Gosu::Sample for music because Gosu::Song's SDL implementation
100 * doesn't support loading from a memory buffer at the moment.
102 SampleRef Resourcer::getSample(const std::string& name)
104 if (conf->cache_enabled) {
105 SampleRefMap::iterator entry = samples.find(name);
106 if (entry != samples.end())
107 return entry->second;
110 BufferPtr buffer(read(name));
111 if (!buffer)
112 return SampleRef();
113 SampleRef result(new Gosu::Sample(buffer->frontReader()));
114 if (conf->cache_enabled)
115 samples[name] = result;
116 return result;
119 XMLDocRef Resourcer::getXMLDoc(const std::string& name, const std::string& dtdPath)
121 if (conf->cache_enabled) {
122 XMLMap::iterator entry = xmls.find(name);
123 if (entry != xmls.end())
124 return entry->second;
127 XMLDocRef result(readXMLDocFromDisk(name, dtdPath));
128 if (conf->cache_enabled)
129 xmls[name] = result;
130 return result;
133 // use RAII to ensure doc is freed
134 // boost::shared_ptr<void> alwaysFreeTheDoc(doc, xmlFreeDoc);
135 xmlDoc* Resourcer::readXMLDocFromDisk(const std::string& name, const std::string& dtdPath)
137 const std::string docStr = readStringFromDisk(name);
138 if (docStr.empty())
139 return NULL;
141 xmlParserCtxt* pc = xmlNewParserCtxt();
142 const std::string pathname = path(name);
143 pc->vctxt.userData = (void*)&pathname;
144 pc->vctxt.error = xmlErrorCb;
146 // Parse the XML. Hand over our error callback fn.
147 xmlDoc* doc = xmlCtxtReadMemory(pc, docStr.c_str(),
148 (int)docStr.size(), NULL, NULL,
149 XML_PARSE_NOBLANKS |
150 XML_PARSE_NONET);
151 xmlFreeParserCtxt(pc);
152 if (!doc) {
153 Log::err(pathname, "Could not parse file");
154 return NULL;
157 // Load up a Document Type Definition for validating the document.
158 xmlDtd* dtd = xmlParseDTD(NULL, (const xmlChar*)dtdPath.c_str());
159 if (!dtd) {
160 Log::err(dtdPath, "file not found");
161 return NULL;
164 // Assert the document is sane here and now so we don't have to have a
165 // billion if-else statements while traversing the document tree.
166 xmlValidCtxt* vc = xmlNewValidCtxt();
167 int valid = xmlValidateDtd(vc, doc, dtd);
168 xmlFreeValidCtxt(vc);
169 xmlFreeDtd(dtd);
171 if (!valid) {
172 Log::err(pathname, "XML document does not follow DTD");
173 return NULL;
176 return doc;
179 std::string Resourcer::readStringFromDisk(const std::string& name)
181 struct zip_stat stat;
182 zip_file* zf;
183 int size;
184 char* buf;
185 std::string str;
187 if (zip_stat(z, name.c_str(), 0x0, &stat)) {
188 Log::err(path(name), "file missing");
189 return "";
192 size = (int)stat.size;
193 buf = new char[size + 1];
194 buf[size] = '\0';
196 zf = zip_fopen(z, name.c_str(), 0x0);
197 if (!zf) {
198 Log::err(path(name),
199 std::string("opening : ") + zip_strerror(z));
200 return "";
203 if (zip_fread(zf, buf, size) != size) {
204 Log::err(path(name), "reading didn't complete");
205 zip_fclose(zf);
206 return "";
209 str = buf;
210 delete[] buf;
212 zip_fclose(zf);
213 return str;
216 Gosu::Buffer* Resourcer::read(const std::string& name)
218 struct zip_stat stat;
219 zip_file* zf;
220 int size;
222 if (zip_stat(z, name.c_str(), 0x0, &stat)) {
223 Log::err(path(name), "file missing");
224 return NULL;
227 size = (int)stat.size;
229 if (!(zf = zip_fopen(z, name.c_str(), 0x0))) {
230 Log::err(path(name),
231 std::string("opening : ") + zip_strerror(z));
232 return NULL;
235 Gosu::Buffer* buffer = new Gosu::Buffer;
236 buffer->resize(size);
237 if (zip_fread(zf, buffer->data(), size) != size) {
238 Log::err(path(name), "reading didn't complete");
239 zip_fclose(zf);
240 delete buffer;
241 return NULL;
244 zip_fclose(zf);
245 return buffer;
248 std::string Resourcer::path(const std::string& entry_name) const
250 return conf->world + "/" + entry_name;