You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/apps/opencs/model/world/idcollection.hpp

204 lines
6.0 KiB
C++

#ifndef CSM_WOLRD_IDCOLLECTION_H
#define CSM_WOLRD_IDCOLLECTION_H
#include <filesystem>
#include <memory>
#include <string>
#include <apps/opencs/model/world/record.hpp>
#include <components/esm/esmcommon.hpp>
#include <components/esm3/loadland.hpp>
#include "collection.hpp"
#include "land.hpp"
#include "pathgrid.hpp"
namespace ESM
{
class ESMReader;
struct LandTexture;
}
namespace CSMWorld
{
struct Pathgrid;
/// \brief Single type collection of top level records
template <typename ESXRecordT>
class BaseIdCollection : public Collection<ESXRecordT>
{
virtual void loadRecord(ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted, bool base);
public:
/// \return Index of loaded record (-1 if no record was loaded)
int load(ESM::ESMReader& reader, bool base);
/// \param index Index at which the record can be found.
/// Special values: -2 index unknown, -1 record does not exist yet and therefore
/// does not have an index
///
/// \return index
int load(const ESXRecordT& record, bool base, int index = -2);
bool tryDelete(const ESM::RefId& id);
///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored.
///
/// \return Has the ID been deleted?
};
template <class ESXRecordT>
class IdCollection : public BaseIdCollection<ESXRecordT>
{
};
template <>
class IdCollection<ESM::LandTexture> : public BaseIdCollection<ESM::LandTexture>
{
std::map<std::pair<int, std::uint16_t>, ESM::RefId> mIndices;
void loadRecord(ESM::LandTexture& record, ESM::ESMReader& reader, bool& isDeleted, bool base) override;
std::uint16_t assignNewIndex(ESM::RefId id);
public:
const Record<ESM::LandTexture>* searchRecord(std::uint16_t index, int plugin) const;
const std::string* getLandTexture(std::uint16_t index, int plugin) const;
bool touchRecord(const ESM::RefId& id) override;
void cloneRecord(
const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type) override;
void appendBlankRecord(const ESM::RefId& id, UniversalId::Type type) override;
void removeRows(int index, int count) override;
void replace(int index, std::unique_ptr<RecordBase> record) override;
};
template <typename ESXRecordT>
void BaseIdCollection<ESXRecordT>::loadRecord(
ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted, bool base)
{
record.load(reader, isDeleted);
}
template <>
inline void BaseIdCollection<Land>::loadRecord(Land& record, ESM::ESMReader& reader, bool& isDeleted, bool base)
{
record.load(reader, isDeleted);
// Load all land data for now. A future optimisation may only load non-base data
// if a suitable mechanism for avoiding race conditions can be established.
int flags = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
record.loadData(flags);
// Prevent data from being reloaded.
record.mContext.filename.clear();
if (!base)
record.setPlugin(-1);
}
template <typename ESXRecordT>
int BaseIdCollection<ESXRecordT>::load(ESM::ESMReader& reader, bool base)
{
ESXRecordT record;
bool isDeleted = false;
loadRecord(record, reader, isDeleted, base);
ESM::RefId id = getRecordId(record);
int index = this->searchId(id);
if (isDeleted)
{
if (index == -1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
return -1;
}
if (base)
{
this->removeRows(index, 1);
return -1;
}
auto baseRecord = std::make_unique<Record<ESXRecordT>>(this->getRecord(index));
baseRecord->mState = RecordBase::State_Deleted;
this->setRecord(index, std::move(baseRecord));
return index;
}
return load(record, base, index);
}
template <typename ESXRecordT>
int BaseIdCollection<ESXRecordT>::load(const ESXRecordT& record, bool base, int index)
{
if (index == -2) // index unknown
index = this->searchId(getRecordId(record));
if (index == -1)
{
// new record
auto record2 = std::make_unique<Record<ESXRecordT>>();
record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2->mBase : record2->mModified) = record;
index = this->getSize();
this->appendRecord(std::move(record2));
}
else
{
// old record
auto record2 = std::make_unique<Record<ESXRecordT>>(Collection<ESXRecordT>::getRecord(index));
if (base)
record2->mBase = record;
else
record2->setModified(record);
this->setRecord(index, std::move(record2));
}
return index;
}
template <typename ESXRecordT>
bool BaseIdCollection<ESXRecordT>::tryDelete(const ESM::RefId& id)
{
int index = this->searchId(id);
if (index == -1)
return false;
const Record<ESXRecordT>& record = Collection<ESXRecordT>::getRecord(index);
if (record.isDeleted())
return false;
if (record.mState == RecordBase::State_ModifiedOnly)
{
Collection<ESXRecordT>::removeRows(index, 1);
}
else
{
auto record2 = std::make_unique<Record<ESXRecordT>>(Collection<ESXRecordT>::getRecord(index));
record2->mState = RecordBase::State_Deleted;
this->setRecord(index, std::move(record2));
}
return true;
}
template <>
int BaseIdCollection<Pathgrid>::load(ESM::ESMReader& reader, bool base);
}
#endif