232 lines
7.8 KiB
C
232 lines
7.8 KiB
C
|
/*
|
||
|
https://github.com/DFHack/dfhack
|
||
|
Copyright (c) 2009-2018 DFHack Team
|
||
|
|
||
|
This software is provided 'as-is', without any express or implied
|
||
|
warranty. In no event will the authors be held liable for any
|
||
|
damages arising from the use of this software.
|
||
|
|
||
|
Permission is granted to anyone to use this software for any
|
||
|
purpose, including commercial applications, and to alter it and
|
||
|
redistribute it freely, subject to the following restrictions:
|
||
|
|
||
|
1. The origin of this software must not be misrepresented; you must
|
||
|
not claim that you wrote the original software. If you use this
|
||
|
software in a product, an acknowledgment in the product documentation
|
||
|
would be appreciated but is not required.
|
||
|
|
||
|
2. Altered source versions must be plainly marked as such, and
|
||
|
must not be misrepresented as being the original software.
|
||
|
|
||
|
3. This notice may not be removed or altered from any source
|
||
|
distribution.
|
||
|
*/
|
||
|
|
||
|
#pragma once
|
||
|
#ifndef CL_MOD_PERSISTENCE
|
||
|
#define CL_MOD_PERSISTENCE
|
||
|
|
||
|
/**
|
||
|
* \defgroup grp_persistence Persistence: code related to saving and loading data
|
||
|
* @ingroup grp_modules
|
||
|
*/
|
||
|
|
||
|
#include <fstream>
|
||
|
#include <memory>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
|
||
|
#include "Export.h"
|
||
|
#include "Error.h"
|
||
|
|
||
|
namespace DFHack
|
||
|
{
|
||
|
class Core;
|
||
|
|
||
|
namespace Persistence
|
||
|
{
|
||
|
struct LegacyData;
|
||
|
class Internal;
|
||
|
}
|
||
|
|
||
|
class DFHACK_EXPORT PersistentDataItem {
|
||
|
size_t index;
|
||
|
std::shared_ptr<Persistence::LegacyData> data;
|
||
|
public:
|
||
|
static const int NumInts = 7;
|
||
|
|
||
|
bool isValid() const;
|
||
|
size_t get_index() const
|
||
|
{
|
||
|
CHECK_INVALID_ARGUMENT(isValid());
|
||
|
return index;
|
||
|
}
|
||
|
int entry_id() const { return isValid() ? int(index) + 100 : 0; }
|
||
|
|
||
|
int raw_id() const { return isValid() ? -int(index) - 100 : 0; }
|
||
|
|
||
|
const std::string &key() const;
|
||
|
|
||
|
std::string &val();
|
||
|
const std::string &val() const;
|
||
|
int &ival(int i);
|
||
|
int ival(int i) const;
|
||
|
|
||
|
// Pack binary data into string field.
|
||
|
// Since DF serialization chokes on NUL bytes,
|
||
|
// use bit magic to ensure none of the bytes is 0.
|
||
|
// Choose the lowest bit for padding so that
|
||
|
// sign-extend can be used normally.
|
||
|
|
||
|
size_t data_size() const
|
||
|
{
|
||
|
return val().size();
|
||
|
}
|
||
|
|
||
|
bool check_data(size_t off, size_t sz = 1) const
|
||
|
{
|
||
|
return (data_size() >= off + sz);
|
||
|
}
|
||
|
void ensure_data(size_t off, size_t sz = 0)
|
||
|
{
|
||
|
if (data_size() < off + sz)
|
||
|
{
|
||
|
val().resize(off + sz, '\x01');
|
||
|
}
|
||
|
}
|
||
|
template<size_t N>
|
||
|
uint8_t (&pdata(size_t off))[N]
|
||
|
{
|
||
|
ensure_data(off, N);
|
||
|
typedef uint8_t array[N];
|
||
|
return *(array *)(val().data() + off);
|
||
|
}
|
||
|
template<size_t N>
|
||
|
const uint8_t (&pdata(size_t off) const)[N]
|
||
|
{
|
||
|
CHECK_INVALID_ARGUMENT(check_data(off, N));
|
||
|
typedef const uint8_t array[N];
|
||
|
return *(array *)(val().data() + off);
|
||
|
}
|
||
|
template<size_t N>
|
||
|
const uint8_t (&cpdata(size_t off) const)[N]
|
||
|
{
|
||
|
return pdata<N>(off);
|
||
|
}
|
||
|
|
||
|
static const size_t int7_size = 1;
|
||
|
uint8_t get_uint7(size_t off) const
|
||
|
{
|
||
|
auto p = pdata<int7_size>(off);
|
||
|
return p[0] >> 1;
|
||
|
}
|
||
|
int8_t get_int7(size_t off) const
|
||
|
{
|
||
|
return int8_t(get_uint7(off));
|
||
|
}
|
||
|
void set_uint7(size_t off, uint8_t val)
|
||
|
{
|
||
|
auto p = pdata<int7_size>(off);
|
||
|
p[0] = uint8_t((val << 1) | 1);
|
||
|
}
|
||
|
void set_int7(size_t off, int8_t val)
|
||
|
{
|
||
|
set_uint7(off, uint8_t(val));
|
||
|
}
|
||
|
|
||
|
static const size_t int28_size = 4;
|
||
|
uint32_t get_uint28(size_t off) const
|
||
|
{
|
||
|
auto p = pdata<int28_size>(off);
|
||
|
return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((p[3]&~1U)<<20);
|
||
|
}
|
||
|
int32_t get_int28(size_t off) const
|
||
|
{
|
||
|
return int32_t(get_uint28(off));
|
||
|
}
|
||
|
void set_uint28(size_t off, uint32_t val)
|
||
|
{
|
||
|
auto p = pdata<int28_size>(off);
|
||
|
p[0] = uint8_t((val<<1) | 1);
|
||
|
p[1] = uint8_t((val>>6) | 1);
|
||
|
p[2] = uint8_t((val>>13) | 1);
|
||
|
p[3] = uint8_t((val>>20) | 1);
|
||
|
}
|
||
|
void set_int28(size_t off, int32_t val)
|
||
|
{
|
||
|
set_uint28(off, val);
|
||
|
}
|
||
|
|
||
|
PersistentDataItem() : index(0), data(nullptr) {}
|
||
|
PersistentDataItem(size_t index, const std::shared_ptr<Persistence::LegacyData> &data)
|
||
|
: index(index), data(data) {}
|
||
|
};
|
||
|
namespace Persistence
|
||
|
{
|
||
|
class Internal
|
||
|
{
|
||
|
static void clear();
|
||
|
static void save();
|
||
|
static void load();
|
||
|
friend class ::DFHack::Core;
|
||
|
};
|
||
|
|
||
|
// Returns a new PersistentDataItem with the specified key.
|
||
|
// If there is no world loaded or the key is empty, returns an invalid item.
|
||
|
DFHACK_EXPORT PersistentDataItem addItem(const std::string &key);
|
||
|
// Returns an existing PersistentDataItem with the specified key.
|
||
|
// If "added" is not null and there is no such item, a new item is returned and
|
||
|
// the bool value is set to true. If "added" is not null and an item is found or
|
||
|
// no new item can be created, the bool value is set to false.
|
||
|
DFHACK_EXPORT PersistentDataItem getByKey(const std::string &key, bool *added = nullptr);
|
||
|
// Returns an existing PersistentDataItem with the specified index.
|
||
|
// If there is no world loaded or the index is empty, returns an invalid item.
|
||
|
DFHACK_EXPORT PersistentDataItem getByIndex(size_t index);
|
||
|
// If the item is invalid, returns false. Otherwise, deletes the item and returns
|
||
|
// true. All references to the item are invalid as soon as this function returns.
|
||
|
DFHACK_EXPORT bool deleteItem(const PersistentDataItem &item);
|
||
|
// Fills the vector with references to each persistent item.
|
||
|
DFHACK_EXPORT void getAll(std::vector<PersistentDataItem> &vec);
|
||
|
// Fills the vector with references to each persistent item with a key that is
|
||
|
// greater than or equal to "min" and less than "max".
|
||
|
DFHACK_EXPORT void getAllByKeyRange(std::vector<PersistentDataItem> &vec, const std::string &min, const std::string &max);
|
||
|
// Fills the vector with references to each persistent item with a key that is
|
||
|
// equal to the given key.
|
||
|
DFHACK_EXPORT void getAllByKey(std::vector<PersistentDataItem> &vec, const std::string &key);
|
||
|
|
||
|
#if defined(__GNUC__) && __GNUC__ < 5
|
||
|
// file stream move constructors are missing in libstdc++ before version 5.
|
||
|
template<typename T>
|
||
|
class DFHACK_EXPORT gcc_4_fstream_shim : public T
|
||
|
{
|
||
|
const std::string file;
|
||
|
public:
|
||
|
explicit gcc_4_fstream_shim() : T(), file() {}
|
||
|
explicit gcc_4_fstream_shim(const std::string &file) : T(file), file() {}
|
||
|
gcc_4_fstream_shim(gcc_4_fstream_shim<T> && s) : T(), file(s.file)
|
||
|
{
|
||
|
if (!file.empty())
|
||
|
{
|
||
|
T::open(file.c_str());
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
#define FSTREAM(x) gcc_4_fstream_shim<x>
|
||
|
#else
|
||
|
#define FSTREAM(x) x
|
||
|
#endif
|
||
|
|
||
|
// Returns an input stream that data can be read from. If no world is currently loaded,
|
||
|
// or no data has been saved with the specified key, the stream is invalid and acts
|
||
|
// as if the file is empty.
|
||
|
DFHACK_EXPORT FSTREAM(std::ifstream) readSaveData(const std::string &key);
|
||
|
// Returns an output stream that data can be saved to. If no world is currently loaded,
|
||
|
// an invalid stream is returned, and writing to it has no effect.
|
||
|
DFHACK_EXPORT FSTREAM(std::ofstream) writeSaveData(const std::string &key);
|
||
|
|
||
|
#undef FSTREAM
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|