LTEX importing

new-script-api
Kyle Cooley 7 years ago
parent 5c3e90da88
commit 97d0fd756a

@ -2,6 +2,7 @@
#include <cmath>
#include <sstream>
#include <unordered_set>
#include <components/misc/stringops.hpp>
@ -40,6 +41,90 @@ void CSMWorld::TouchCommand::undo()
}
}
CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable, const std::string& id, QUndoCommand* parent)
: QUndoCommand(parent)
, mLands(landTable)
, mLtexs(ltexTable)
, mId(id)
, mOld(nullptr)
, mChanged(false)
{
setText(("Touch " + mId).c_str());
mOld.reset(mLands.getRecord(mId).clone());
}
void CSMWorld::TouchLandCommand::redo()
{
int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex);
int oldPlugin = mLands.data(mLands.getModelIndex(mId, pluginColumn)).toInt();
mChanged = mLands.touchRecord(mId);
if (mChanged)
{
// Original data
int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);
QByteArray textureByteArray = mLands.data(mLands.getModelIndex(mId, textureColumn)).toByteArray();
const uint16_t* textureData = reinterpret_cast<uint16_t*>(textureByteArray.data());
// Need to make a copy so the old values can be looked up
QByteArray newTextureByteArray(textureByteArray.data(), textureByteArray.size());
uint16_t* newTextureData = reinterpret_cast<uint16_t*>(newTextureByteArray.data());
// Find all indices used
std::unordered_set<int> texIndices;
for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i)
{
// All indices are offset by 1 for a default texture
if (textureData[i] > 0)
texIndices.insert(textureData[i] - 1);
}
std::vector<std::string> oldTextures;
for (int index : texIndices)
{
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));
}
// Import the textures, replace old values
LandTextureIdTable::ImportResults results = dynamic_cast<LandTextureIdTable&>(mLtexs).importTextures(oldTextures);
mCreatedTextures = std::move(results.createdRecords);
for (const auto& it : results.recordMapping)
{
int plugin = 0, newIndex = 0, oldIndex = 0;
LandTexture::parseUniqueRecordId(it.first, plugin, oldIndex);
LandTexture::parseUniqueRecordId(it.second, plugin, newIndex);
if (newIndex != oldIndex)
{
for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i)
{
// All indices are offset by 1 for a default texture
if (textureData[i] == oldIndex)
newTextureData[i] = newIndex + 1;
}
}
}
mLands.setData(mLands.getModelIndex(mId, textureColumn), newTextureByteArray);
}
}
void CSMWorld::TouchLandCommand::undo()
{
if (mChanged)
{
mLands.setRecord(mId, *mOld);
mChanged = false;
for (const std::string& id : mCreatedTextures)
{
int row = mLtexs.getModelIndex(id, 0).row();
mLtexs.removeRows(row, 1);
}
mCreatedTextures.clear();
}
}
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)

@ -31,8 +31,8 @@ namespace CSMWorld
TouchCommand(IdTable& model, const std::string& id, QUndoCommand* parent=nullptr);
virtual void redo();
virtual void undo();
void redo() override;
void undo() override;
private:
@ -43,6 +43,27 @@ namespace CSMWorld
bool mChanged;
};
class TouchLandCommand : public QUndoCommand
{
public:
TouchLandCommand(IdTable& landTable, IdTable& ltexTable, const std::string& id,
QUndoCommand* parent = nullptr);
void redo() override;
void undo() override;
private:
IdTable& mLands;
IdTable& mLtexs;
std::string mId;
std::unique_ptr<RecordBase> mOld;
std::vector<std::string> mCreatedTextures;
bool mChanged;
};
class ModifyCommand : public QUndoCommand
{
QAbstractItemModel *mModel;

@ -1,11 +1,13 @@
#include "idtable.hpp"
#include <limits>
#include <stdexcept>
#include <components/esm/cellid.hpp>
#include "collectionbase.hpp"
#include "columnbase.hpp"
#include "landtexture.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
: IdTableBase (features), mIdCollection (idCollection)
@ -332,3 +334,58 @@ Qt::ItemFlags CSMWorld::LandTextureIdTable::flags(const QModelIndex& index) cons
return flags;
}
CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector<std::string>& ids)
{
ImportResults results;
for (const std::string& id : ids)
{
int plugin, index;
LandTexture::parseUniqueRecordId(id, plugin, index);
int oldRow = idCollection()->searchId(id);
// If it does not exist or it is in the current plugin, it can be skipped.
if (oldRow <= 0 || plugin == 0)
{
results.recordMapping.push_back(std::make_pair(id, id));
continue;
}
// Try a direct mapping to the current plugin first. Otherwise iterate until one is found.
// Iteration is deterministic to avoid duplicates.
do {
std::string newId = LandTexture::createUniqueRecordId(0, index);
int newRow = idCollection()->searchId(newId);
if (newRow < 0)
{
// Id not taken, clone it
cloneRecord(id, newId, UniversalId::Type_LandTexture);
results.createdRecords.push_back(newId);
results.recordMapping.push_back(std::make_pair(id, newId));
break;
}
// Id is taken, check if same handle and texture. Note that mId is the handle.
const LandTexture& oldLtex = dynamic_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow)).get();
const LandTexture& newLtex = dynamic_cast<const Record<LandTexture>&>(idCollection()->getRecord(newRow)).get();
if (oldLtex.mId == newLtex.mId && oldLtex.mTexture == newLtex.mTexture)
{
// It's a match
results.recordMapping.push_back(std::make_pair(id, newId));
break;
}
// Determine next index. Spread out the indices to reduce conflicts.
size_t MaxIndex = std::numeric_limits<uint16_t>::max();
size_t Prime = (1 << 19) - 1; // A mersenne prime
size_t K = 2154;
index = ((K * index) % Prime) % MaxIndex;
} while (true);
}
return results;
}

@ -100,11 +100,22 @@ namespace CSMWorld
};
/// An IdTable customized to handle the more unique needs of LandTextureId's which behave
/// differently from other records.
/// differently from other records. The major difference is that base records cannot be
/// modified.
class LandTextureIdTable : public IdTable
{
public:
struct ImportResults
{
using StringPair = std::pair<std::string,std::string>;
/// The newly added records
std::vector<std::string> createdRecords;
/// The 1st string is the original id, the 2nd is the mapped id
std::vector<StringPair> recordMapping;
};
LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0);
QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override;
@ -112,6 +123,9 @@ namespace CSMWorld
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
Qt::ItemFlags flags (const QModelIndex & index) const override;
/// Finds and maps/recreates the specified ids.
ImportResults importTextures(const std::vector<std::string>& ids);
};
}

@ -1,7 +1,7 @@
#include "land.hpp"
#include <sstream>
#include <exception>
#include <stdexcept>
namespace CSMWorld
{

@ -1,7 +1,7 @@
#include "landtexture.hpp"
#include <string>
#include <exception>
#include <stdexcept>
#include <components/esm/esmreader.hpp>

@ -2,6 +2,8 @@
#include <limits>
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/land.hpp"
namespace CSVWorld
@ -47,6 +49,24 @@ namespace CSVWorld
mY->setValue(y);
}
void LandCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)
{
// Combine multiple touch commands into one "macro" command
std::unique_ptr<QUndoCommand> macro(new QUndoCommand());
macro->setText("Touch records");
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
for (const CSMWorld::UniversalId& uid : ids)
{
// This is not leaked, touchCmd is a child of macro and managed by Qt
CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId(), macro.get());
}
// Execute
getUndoStack().push(macro.release());
}
void LandCreator::focus()
{
mX->setFocus();

@ -23,6 +23,8 @@ namespace CSVWorld
void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;
void touch(const std::vector<CSMWorld::UniversalId>& ids) override;
void focus() override;
void reset() override;

Loading…
Cancel
Save