Commit d5d96c58 by Maarten L. Hekkelman

resource loading

parent 9b61a06e
Version 1.0.1
- Changed the way resources are looked up, local dir first,
then /var/cache and finally compiled in resources (with mrc).
Version 1.0.0
- First public release
\ No newline at end of file
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include <list> #include <list>
#include <iostream>
#include <filesystem>
#include <unistd.h> #include <unistd.h>
...@@ -190,6 +192,11 @@ class Progress ...@@ -190,6 +192,11 @@ class Progress
struct ProgressImpl* mImpl; struct ProgressImpl* mImpl;
}; };
// --------------------------------------------------------------------
// Resources
std::unique_ptr<std::istream> loadResource(std::filesystem::path name);
} }
...@@ -3093,39 +3093,16 @@ void File::loadDictionary() ...@@ -3093,39 +3093,16 @@ void File::loadDictionary()
void File::loadDictionary(const char* dict) void File::loadDictionary(const char* dict)
{ {
for (;;) fs::path dict_name(dict);
{
fs::path name(dict);
try auto data = loadResource(dict);
{ if (not data and dict_name.extension().string() != ".dic")
if (fs::exists(name)) data = loadResource(dict_name.parent_path() / (dict_name.filename().string() + ".dic"));
{
std::ifstream is(name);
loadDictionary(is);
break;
}
}
catch (...) {}
fs::path datadir = CACHE_DIR; if (not data)
throw std::runtime_error("Dictionary not found or defined (" + dict_name.string() + ")");
if (not fs::is_directory(datadir)) loadDictionary(*data);
throw std::runtime_error("Could not find dictionary " + name.string() + " and " CACHE_DIR " also does not exist");
name = datadir / name;
if (not fs::exists(name) and name.extension().string() != ".dic")
name = datadir / (name.string() + ".dic");
if (fs::exists(name))
{
std::ifstream is(name);
loadDictionary(is);
break;
}
throw std::runtime_error("Dictionary not found or defined (" + name.string() + ")");
}
} }
void File::loadDictionary(std::istream& is) void File::loadDictionary(std::istream& is)
......
...@@ -748,3 +748,443 @@ void Progress::message(const std::string& inMessage) ...@@ -748,3 +748,443 @@ void Progress::message(const std::string& inMessage)
} }
} }
// --------------------------------------------------------------------
//
// Try to find a named resource. Can be either a local file with this name,
// a file located in our cache directory or a resource linked in with mrc.
//
// We have a special, private version of mrsrc here. To be able to create
// shared libraries and still be able to link when there's no mrc used.
namespace mrsrc
{
/// \brief Internal data structure as generated by mrc
struct rsrc_imp
{
unsigned int m_next;
unsigned int m_child;
unsigned int m_name;
unsigned int m_size;
unsigned int m_data;
};
}
extern const __attribute__((weak)) mrsrc::rsrc_imp gResourceIndex[];
extern const __attribute__((weak)) char gResourceData[];
extern const __attribute__((weak)) char gResourceName[];
namespace mrsrc
{
class rsrc_data
{
public:
static rsrc_data& instance()
{
static rsrc_data s_instance;
return s_instance;
}
const rsrc_imp* index() const { return m_index; }
const char* data(unsigned int offset) const
{
return m_data + offset;
}
const char* name(unsigned int offset) const
{
return m_name + offset;
}
private:
rsrc_data()
{
if (gResourceIndex and gResourceIndex and gResourceName)
{
m_index = gResourceIndex;
m_data = gResourceData;
m_name = gResourceName;
}
}
rsrc_imp m_dummy = {};
const rsrc_imp* m_index = &m_dummy;
const char* m_data = "";
const char* m_name = "";
};
/// \brief Class mrsrc::rsrc contains a pointer to the data in the
/// resource, as well as offering an iterator interface to its
/// children.
class rsrc
{
public:
rsrc() : m_impl(rsrc_data::instance().index()) {}
rsrc(const rsrc& other)
: m_impl(other.m_impl) {}
rsrc& operator=(const rsrc& other)
{
m_impl = other.m_impl;
return *this;
}
rsrc(std::filesystem::path path);
std::string name() const { return rsrc_data::instance().name(m_impl->m_name); }
const char* data() const { return rsrc_data::instance().data(m_impl->m_data); }
unsigned long size() const { return m_impl->m_size; }
explicit operator bool() const { return m_impl != NULL and m_impl->m_size > 0; }
template<typename RSRC>
class iterator_t
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = RSRC;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
iterator_t(const rsrc_imp* cur)
: m_cur(cur) {}
iterator_t(const iterator_t& i)
: m_cur(i.m_cur)
{
}
iterator_t& operator=(const iterator_t& i)
{
m_cur = i.m_cur;
return *this;
}
reference operator*() { return m_cur; }
pointer operator->() { return& m_cur; }
iterator_t& operator++()
{
if (m_cur.m_impl->m_next)
m_cur.m_impl = rsrc_data::instance().index() + m_cur.m_impl->m_next;
else
m_cur.m_impl = nullptr;
return *this;
}
iterator_t operator++(int)
{
auto tmp(*this);
this->operator++();
return tmp;
}
bool operator==(const iterator_t& rhs) const { return m_cur.m_impl == rhs.m_cur.m_impl; }
bool operator!=(const iterator_t& rhs) const { return m_cur.m_impl != rhs.m_cur.m_impl; }
private:
value_type m_cur;
};
using iterator = iterator_t<rsrc>;
iterator begin() const
{
const rsrc_imp* impl = nullptr;
if (m_impl and m_impl->m_child)
impl = rsrc_data::instance().index() + m_impl->m_child;
return iterator(impl);
}
iterator end() const
{
return iterator(nullptr);
}
private:
rsrc(const rsrc_imp* imp)
: m_impl(imp) {}
const rsrc_imp *m_impl;
};
inline rsrc::rsrc(std::filesystem::path p)
{
m_impl = rsrc_data::instance().index();
// using std::filesytem::path would have been natural here of course...
auto pb = p.begin();
auto pe = p.end();
while (m_impl != nullptr and pb != pe)
{
auto name = *pb++;
const rsrc_imp* impl = nullptr;
for (rsrc child: *this)
{
if (child.name() == name)
{
impl = child.m_impl;
break;
}
}
m_impl = impl;
}
if (pb != pe) // not found
m_impl = nullptr;
}
// --------------------------------------------------------------------
template<typename CharT, typename Traits>
class basic_streambuf : public std::basic_streambuf<CharT, Traits>
{
public:
typedef CharT char_type;
typedef Traits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
/// \brief constructor taking a \a path to the resource in memory
basic_streambuf(const std::string& path)
: m_rsrc(path)
{
init();
}
/// \brief constructor taking a \a rsrc
basic_streambuf(const rsrc& rsrc)
: m_rsrc(rsrc)
{
init();
}
basic_streambuf(const basic_streambuf&) = delete;
basic_streambuf(basic_streambuf&& rhs)
: basic_streambuf(rhs.m_rsrc)
{
}
basic_streambuf& operator=(const basic_streambuf&) = delete;
basic_streambuf& operator=(basic_streambuf&& rhs)
{
swap(rhs);
return *this;
}
void swap(basic_streambuf& rhs)
{
std::swap(m_begin, rhs.m_begin);
std::swap(m_end, rhs.m_end);
std::swap(m_current, rhs.m_current);
}
private:
void init()
{
m_begin = reinterpret_cast<const char_type*>(m_rsrc.data());
m_end = reinterpret_cast<const char_type*>(m_rsrc.data() + m_rsrc.size());
m_current = m_begin;
}
int_type underflow()
{
if (m_current == m_end)
return traits_type::eof();
return traits_type::to_int_type(*m_current);
}
int_type uflow()
{
if (m_current == m_end)
return traits_type::eof();
return traits_type::to_int_type(*m_current++);
}
int_type pbackfail(int_type ch)
{
if (m_current == m_begin or (ch != traits_type::eof() and ch != m_current[-1]))
return traits_type::eof();
return traits_type::to_int_type(*--m_current);
}
std::streamsize showmanyc()
{
assert(std::less_equal<const char*>()(m_current, m_end));
return m_end - m_current;
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which)
{
switch (dir)
{
case std::ios_base::beg:
m_current = m_begin + off;
break;
case std::ios_base::end:
m_current = m_end + off;
break;
case std::ios_base::cur:
m_current += off;
break;
default:
break;
}
if (m_current < m_begin)
m_current = m_begin;
if (m_current > m_end)
m_current = m_end;
return m_current - m_begin;
}
pos_type seekpos(pos_type pos, std::ios_base::openmode which)
{
m_current = m_begin + pos;
if (m_current < m_begin)
m_current = m_begin;
if (m_current > m_end)
m_current = m_end;
return m_current - m_begin;
}
private:
rsrc m_rsrc;
const char_type* m_begin;
const char_type* m_end;
const char_type* m_current;
};
using streambuf = basic_streambuf<char, std::char_traits<char>>;
// --------------------------------------------------------------------
// class mrsrc::istream
template<typename CharT, typename Traits>
class basic_istream : public std::basic_istream<CharT, Traits>
{
public:
typedef CharT char_type;
typedef Traits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
private:
using __streambuf_type = basic_streambuf<CharT, Traits>;
using __istream_type = std::basic_istream<CharT, Traits>;
__streambuf_type m_buffer;
public:
basic_istream(const std::string& path)
: __istream_type(&m_buffer)
, m_buffer(path)
{
this->init(&m_buffer);
}
basic_istream(rsrc& resource)
: __istream_type(&m_buffer)
, m_buffer(resource)\
{
this->init(&m_buffer);
}
basic_istream(const basic_istream&) = delete;
basic_istream(basic_istream&& rhs)
: __istream_type(std::move(rhs))
, m_buffer(std::move(rhs.m_buffer))
{
__istream_type::set_rdbuf(&m_buffer);
}
basic_istream& operator=(const basic_istream& ) = delete;
basic_istream& operator=(basic_istream&& rhs)
{
__istream_type::operator=(std::move(rhs));
m_buffer = std::move(rhs.m_buffer);
return *this;
}
void swap(basic_istream& rhs)
{
__istream_type::swap(rhs);
m_buffer.swap(rhs.m_buffer);
}
__streambuf_type* rdbuf() const
{
return const_cast<__streambuf_type*>(&m_buffer);
}
};
using istream = basic_istream<char, std::char_traits<char>>;
}
// --------------------------------------------------------------------
namespace cif
{
// --------------------------------------------------------------------
std::unique_ptr<std::istream> loadResource(std::filesystem::path name)
{
std::unique_ptr<std::istream> result;
fs::path p = name;
if (not fs::exists(p))
p = fs::path(CACHE_DIR) / p;
if (fs::exists(p))
{
std::unique_ptr<std::ifstream> file(new std::ifstream(p));
if (file->is_open())
result.reset(file.release());
}
if (not result)
{
mrsrc::rsrc rsrc(name);
if (rsrc)
result.reset(new mrsrc::istream(rsrc));
}
return result;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment