1 /*********************************
2 ** Tsunagari Tile Engine **
4 ** Copyright 2011-2012 OmegaSDG **
5 *********************************/
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to
10 // deal in the Software without restriction, including without limitation the
11 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30 #include <boost/foreach.hpp>
31 #include <boost/format.hpp>
32 #include <boost/python.hpp>
33 #include <boost/scoped_array.hpp>
34 #include <boost/scoped_ptr.hpp>
35 #include <boost/shared_ptr.hpp>
36 #include <Gosu/Bitmap.hpp>
37 #include <Gosu/Image.hpp>
38 #include <Gosu/IO.hpp>
41 #include "client-conf.h"
44 #include "resourcer.h"
48 #define ASSERT(x) if (!(x)) { return false; }
50 typedef boost::scoped_ptr
<Gosu::Buffer
> BufferPtr
;
53 static Resourcer
* globalResourcer
= NULL
;
55 Resourcer
* Resourcer::instance()
57 return globalResourcer
;
60 Resourcer::Resourcer()
62 globalResourcer
= this;
63 pythonSetGlobal("Resourcer", this);
66 Resourcer::~Resourcer()
71 static bool callInitpy(Resourcer
* rc
, const std::string
& archivePath
)
73 ASSERT(rc
->prependPath(archivePath
));
74 bool exists
= rc
->resourceExists("init.py");
75 Log::info(archivePath
,
76 std::string("init.py: ") +
77 (exists
? "found" : "not found"));
79 ASSERT(rc
->runPythonScript("init.py"));
80 ASSERT(rc
->rmPath(archivePath
));
84 bool Resourcer::init(char* argv0
)
86 ASSERT(PHYSFS_init(argv0
) != 0);
88 // If any of our archives contain a file called "init.py", call it.
89 BOOST_FOREACH(std::string archivePath
, conf
.dataPath
)
90 ASSERT(callInitpy(this, archivePath
));
91 ASSERT(callInitpy(this, BASE_ZIP_PATH
));
93 ASSERT(prependPath(BASE_ZIP_PATH
));
95 // DTDs must be loaded from BASE_ZIP. They cannot be allowed to be
96 // loaded from the world.
97 ASSERT(preloadDTDs());
99 ASSERT(prependPath(conf
.worldFilename
));
100 BOOST_FOREACH(std::string pathname
, conf
.dataPath
)
101 ASSERT(prependPath(pathname
));
106 bool Resourcer::prependPath(const std::string
& path
)
108 using namespace boost
;
110 int err
= PHYSFS_mount(path
.c_str(), NULL
, 0);
112 Log::fatal("Resourcer", str(
113 format("%s: could not open archive: %s")
114 % path
% PHYSFS_getLastError()));
121 bool Resourcer::appendPath(const std::string
& path
)
123 using namespace boost
;
125 int err
= PHYSFS_mount(path
.c_str(), NULL
, 1);
127 Log::fatal("Resourcer", str(
128 format("%s: could not open archive: %s")
129 % path
% PHYSFS_getLastError()));
136 bool Resourcer::rmPath(const std::string
& path
)
138 using namespace boost
;
140 int err
= PHYSFS_removeFromSearchPath(path
.c_str());
142 Log::err("Resourcer", str(format("libphysfs: %s: %s")
143 % path
% PHYSFS_getLastError()));
150 bool Resourcer::resourceExists(const std::string
& name
) const
152 return PHYSFS_exists(name
.c_str());
155 ImageRef
Resourcer::getImage(const std::string
& name
)
157 ImageRef existing
= images
.lifetimeRequest(name
);
161 BufferPtr
buffer(readBuffer(name
));
165 ImageRef
result(Image::create(buffer
->data(), buffer
->size()));
169 images
.lifetimePut(name
, result
);
173 TiledImageRef
Resourcer::getTiledImage(const std::string
& name
,
176 TiledImageRef existing
= tiles
.momentaryRequest(name
);
180 BufferPtr
buffer(readBuffer(name
));
182 return TiledImageRef();
184 TiledImageRef
result(
185 TiledImage::create(buffer
->data(), buffer
->size(), w
, h
)
188 return TiledImageRef();
190 tiles
.momentaryPut(name
, result
);
194 SampleRef
Resourcer::getSample(const std::string
& name
)
196 if (!conf
.audioEnabled
)
199 SampleRef existing
= sounds
.lifetimeRequest(name
);
203 BufferPtr
buffer(readBuffer(name
));
206 SampleRef
result(new Sound(new Gosu::Sample(buffer
->frontReader())));
208 sounds
.lifetimePut(name
, result
);
212 SongRef
Resourcer::getSong(const std::string
& name
)
214 if (!conf
.audioEnabled
)
217 SongRef existing
= songs
.lifetimeRequest(name
);
221 BufferPtr
buffer(readBuffer(name
));
224 SongRef
result(new Gosu::Song(buffer
->frontReader()));
226 songs
.lifetimePut(name
, result
);
230 XMLRef
Resourcer::getXMLDoc(const std::string
& name
,
231 const std::string
& dtdFile
)
233 XMLRef existing
= xmls
.momentaryRequest(name
);
237 XMLRef
result(readXMLDoc(name
, dtdFile
));
239 xmls
.momentaryPut(name
, result
);
243 bool Resourcer::runPythonScript(const std::string
& name
)
245 PyCodeObject
* existing
= codes
.momentaryRequest(name
);
247 return pythonExec(existing
);
249 std::string code
= readString(name
);
250 PyCodeObject
* result
= code
.size() ?
251 pythonCompile(name
.c_str(), code
.c_str()) : NULL
;
253 codes
.momentaryPut(name
, result
);
254 return pythonExec(result
);
257 std::string
Resourcer::getText(const std::string
& name
)
259 StringRef existing
= texts
.momentaryRequest(name
);
261 return *existing
.get();
263 StringRef
result(new std::string(readString(name
)));
265 texts
.momentaryPut(name
, result
);
266 return *result
.get();
269 void Resourcer::garbageCollect()
271 images
.garbageCollect();
272 tiles
.garbageCollect();
273 sounds
.garbageCollect();
274 songs
.garbageCollect();
275 xmls
.garbageCollect();
276 texts
.garbageCollect();
279 // FIXME: Should be moved to xml.cpp!!!!!!
280 DTDRef
Resourcer::parseDTD(const std::string
& path
)
282 xmlCharEncoding enc
= XML_CHAR_ENCODING_NONE
;
284 std::string bytes
= readString(path
);
288 xmlParserInputBuffer
* input
= xmlParserInputBufferCreateMem(
289 bytes
.c_str(), (int)bytes
.size(), enc
);
293 xmlDtd
* dtd
= xmlIOParseDTD(NULL
, input
, enc
);
297 return DTDRef(dtd
, xmlFreeDtd
);
300 // FIXME: Should be moved to xml.cpp!!!!!!
301 bool Resourcer::preloadDTDs()
303 ASSERT(dtds
["dtd/area.dtd"] = parseDTD("dtd/area.dtd"));
304 ASSERT(dtds
["dtd/entity.dtd"] = parseDTD("dtd/entity.dtd"));
305 ASSERT(dtds
["dtd/world.dtd"] = parseDTD("dtd/world.dtd"));
309 XMLDoc
* Resourcer::readXMLDoc(const std::string
& name
,
310 const std::string
& dtdPath
)
312 std::string p
= path(name
);
313 std::string data
= readString(name
);
314 xmlDtd
* dtd
= getDTD(dtdPath
);
316 if (!dtd
|| data
.empty())
318 XMLDoc
* doc
= new XMLDoc
;
319 if (!doc
->init(p
, data
, dtd
)) {
326 xmlDtd
* Resourcer::getDTD(const std::string
& name
)
328 TextMap::iterator it
= dtds
.find(name
);
329 return it
== dtds
.end() ? NULL
: it
->second
.get();
332 Gosu::Buffer
* Resourcer::readBuffer(const std::string
& name
)
334 Gosu::Buffer
* buf
= new Gosu::Buffer();
336 if (readFromDisk(name
, *buf
)) {
345 std::string
Resourcer::readString(const std::string
& name
)
348 return readFromDisk(name
, str
) ? str
: "";
352 bool Resourcer::readFromDisk(const std::string
& name
, T
& buf
)
354 using namespace boost
;
359 if (!PHYSFS_exists(name
.c_str())) {
360 Log::err("Resourcer", str(format("%s: file missing")
365 zf
= PHYSFS_openRead(name
.c_str());
367 Log::err("Resourcer", str(format("%s: error opening file: %s")
368 % path(name
) % PHYSFS_getLastError()));
372 size
= PHYSFS_fileLength(zf
);
374 Log::err("Resourcer", str(
375 format("%s: could not determine file size: %s")
376 % path(name
) % PHYSFS_getLastError()));
380 if (size
> std::numeric_limits
<uint32_t>::max()) {
381 // FIXME: Technically, we just need to issue multiple calls to
382 // PHYSFS_read. Fix when needed.
383 Log::err("Resourcer", str(format("%s: file too long (>4GB)")
395 if (PHYSFS_read(zf
, (char*)(buf
.data()),
396 (PHYSFS_uint32
)size
, 1) != 1) {
397 Log::err("Resourcer", str(format("%s: error reading file: %s")
398 % path(name
) % PHYSFS_getLastError()));
407 std::string
Resourcer::path(const std::string
& entryName
) const
409 // XXX: archive might not be world
410 return conf
.worldFilename
+ "/" + entryName
;
415 void exportResourcer()
417 using namespace boost::python
;
419 class_
<Resourcer
>("Resourcer", no_init
)
420 .def("resource_exists", &Resourcer::resourceExists
)
421 .def("run_python_script", &Resourcer::runPythonScript
)
422 .def("get_text", &Resourcer::getText
);