From 8aee84c46ea68ebb2a66d959decab54be53818a5 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 4 Aug 2021 09:06:04 +1000 Subject: [PATCH] Disallow entry of strings longer than the lengths allowed by the file format. It is possible to allow longer strings but that will require an extension in the omwaddon format as well as changes to the reader to handle that extension. Such changes should be a separate MR. (applied the patch in https://gitlab.com/OpenMW/openmw/-/issues/3066) --- CHANGELOG.md | 1 + apps/opencs/model/world/columnbase.cpp | 3 ++- apps/opencs/model/world/columnbase.hpp | 1 + apps/opencs/model/world/columnimp.hpp | 3 ++- apps/opencs/model/world/data.cpp | 4 +-- apps/opencs/model/world/refidcollection.cpp | 9 ++++--- apps/opencs/view/world/cellcreator.cpp | 7 +++++ apps/opencs/view/world/genericcreator.cpp | 5 ++++ apps/opencs/view/world/genericcreator.hpp | 2 ++ .../view/world/idcompletiondelegate.cpp | 17 ++++++++++++ .../view/world/referenceablecreator.cpp | 26 +++++++++++++++++++ .../view/world/referenceablecreator.hpp | 3 +++ apps/opencs/view/world/util.cpp | 8 ++++++ 13 files changed, 82 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90fda783a2..42a50a4ce7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1942,6 +1942,7 @@ Bug #2025: Missing mouse-over text for non affordable items Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall" Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding + Bug #3066: Editor doesn't check if IDs and other strings are longer than their hardcoded field length Feature #471: Editor: Special case implementation for top-level window with single sub-window Feature #472: Editor: Sub-Window re-use settings Feature #704: Font colors import from fallback settings diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index cf333c1b1a..6f7bb12b3c 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -104,7 +104,8 @@ bool CSMWorld::ColumnBase::isId (Display display) bool CSMWorld::ColumnBase::isText (Display display) { return display==Display_String || display==Display_LongString || - display==Display_String32 || display==Display_LongString256; + display==Display_String32 || display==Display_String64 || + display==Display_LongString256; } bool CSMWorld::ColumnBase::isScript (Display display) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 6dc58bd634..b7c0d8a248 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -135,6 +135,7 @@ namespace CSMWorld Display_InfoCondVar, Display_InfoCondComp, Display_String32, + Display_String64, Display_LongString256, Display_BookType, Display_BloodType, diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 17518937c2..e0148d6cc6 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -334,7 +334,8 @@ namespace CSMWorld template struct NameColumn : public Column { - NameColumn() : Column (Columns::ColumnId_Name, ColumnBase::Display_String) {} + NameColumn(ColumnBase::Display display = ColumnBase::Display_String) + : Column (Columns::ColumnId_Name, display) {} QVariant get (const Record& record) const override { diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 192602290d..01bd858c62 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -130,7 +130,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mFactions.addColumn (new StringIdColumn); mFactions.addColumn (new RecordStateColumn); mFactions.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Faction)); - mFactions.addColumn (new NameColumn); + mFactions.addColumn (new NameColumn(ColumnBase::Display_String32)); mFactions.addColumn (new AttributesColumn (0)); mFactions.addColumn (new AttributesColumn (1)); mFactions.addColumn (new HiddenColumn); @@ -339,7 +339,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); - mCells.addColumn (new NameColumn); + mCells.addColumn (new NameColumn(ColumnBase::Display_String64)); mCells.addColumn (new FlagColumn (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 4ee99a2702..177844a31d 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -59,7 +59,9 @@ CSMWorld::RefIdCollection::RefIdCollection() NameColumns nameColumns (modelColumns); - mColumns.emplace_back(Columns::ColumnId_Name, ColumnBase::Display_String); + // Only items that can be placed in a container have the 32 character limit, but enforce + // that for all referenceable types for now. + mColumns.emplace_back(Columns::ColumnId_Name, ColumnBase::Display_String32); nameColumns.mName = &mColumns.back(); mColumns.emplace_back(Columns::ColumnId_Script, ColumnBase::Display_Script); nameColumns.mScript = &mColumns.back(); @@ -231,9 +233,9 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_Boolean)); mColumns.back().addColumn( - new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String)); + new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String32)); mColumns.back().addColumn( - new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String)); + new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String32)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiTargetCell, CSMWorld::ColumnBase::Display_String)); mColumns.back().addColumn( @@ -479,6 +481,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.emplace_back(Columns::ColumnId_Class, ColumnBase::Display_Class); npcColumns.mClass = &mColumns.back(); + // NAME32 enforced in IdCompletionDelegate::createEditor() mColumns.emplace_back(Columns::ColumnId_Faction, ColumnBase::Display_Faction); npcColumns.mFaction = &mColumns.back(); diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 5b428a4b37..22c27c3d74 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -84,6 +84,13 @@ void CSVWorld::CellCreator::setType (int index) mYLabel->setVisible (index==1); mY->setVisible (index==1); + // The cell name is limited to 64 characters. (ESM::Header::GMDT::mCurrentCell) + std::string text = mType->currentText().toStdString(); + if (text == "Interior Cell") + GenericCreator::setEditorMaxLength (64); + else + GenericCreator::setEditorMaxLength (32767); + update(); } diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 23813f8066..8ae8f8764d 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -184,6 +184,11 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo connect (&mData, SIGNAL (idListChanged()), this, SLOT (dataIdListChanged())); } +void CSVWorld::GenericCreator::setEditorMaxLength (int length) +{ + mId->setMaxLength (length); +} + void CSVWorld::GenericCreator::setEditLock (bool locked) { mLocked = locked; diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 3e2a43c918..90c5946ae5 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -84,6 +84,8 @@ namespace CSVWorld std::string getNamespace() const; + void setEditorMaxLength(int length); + private: void updateNamespace(); diff --git a/apps/opencs/view/world/idcompletiondelegate.cpp b/apps/opencs/view/world/idcompletiondelegate.cpp index 447bcc25d9..9ef04ec3ab 100644 --- a/apps/opencs/view/world/idcompletiondelegate.cpp +++ b/apps/opencs/view/world/idcompletiondelegate.cpp @@ -81,6 +81,23 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent, CSMWorld::IdCompletionManager &completionManager = getDocument().getIdCompletionManager(); CSVWidget::DropLineEdit *editor = new CSVWidget::DropLineEdit(display, parent); editor->setCompleter(completionManager.getCompleter(display).get()); + + // The savegame format limits the player faction string to 32 characters. + // The region sound name is limited to 32 characters. (ESM::Region::SoundRef::mSound) + // The script name is limited to 32 characters. (ESM::Script::SCHD::mName) + // The cell name is limited to 64 characters. (ESM::Header::GMDT::mCurrentCell) + if (display == CSMWorld::ColumnBase::Display_Faction || + display == CSMWorld::ColumnBase::Display_Sound || + display == CSMWorld::ColumnBase::Display_Script || + display == CSMWorld::ColumnBase::Display_Referenceable) + { + editor->setMaxLength (32); + } + else if (display == CSMWorld::ColumnBase::Display_Cell) + { + editor->setMaxLength (64); + } + return editor; } diff --git a/apps/opencs/view/world/referenceablecreator.cpp b/apps/opencs/view/world/referenceablecreator.cpp index 836e8ac7dc..1a2f2bbaa3 100644 --- a/apps/opencs/view/world/referenceablecreator.cpp +++ b/apps/opencs/view/world/referenceablecreator.cpp @@ -33,6 +33,8 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd } insertBeforeButtons (mType, false); + + connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int))); } void CSVWorld::ReferenceableCreator::reset() @@ -41,6 +43,30 @@ void CSVWorld::ReferenceableCreator::reset() GenericCreator::reset(); } +void CSVWorld::ReferenceableCreator::setType (int index) +{ + // container items have name limit of 32 characters + std::string text = mType->currentText().toStdString(); + if (text == "Potion" || + text == "Apparatus" || + text == "Armor" || + text == "Book" || + text == "Clothing" || + text == "Ingredient" || + text == "ItemLevelledList" || + text == "Light" || + text == "Lockpick" || + text == "Miscellaneous" || + text == "Probe" || + text == "Repair" || + text == "Weapon") + { + GenericCreator::setEditorMaxLength (32); + } + else + GenericCreator::setEditorMaxLength (32767); +} + void CSVWorld::ReferenceableCreator::cloneMode (const std::string& originId, const CSMWorld::UniversalId::Type type) { diff --git a/apps/opencs/view/world/referenceablecreator.hpp b/apps/opencs/view/world/referenceablecreator.hpp index d4657bcf7f..354347cc88 100644 --- a/apps/opencs/view/world/referenceablecreator.hpp +++ b/apps/opencs/view/world/referenceablecreator.hpp @@ -29,6 +29,9 @@ namespace CSVWorld void toggleWidgets(bool active = true) override; + private slots: + + void setType (int index); }; } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 58d3d49e44..80bd4580c8 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -291,6 +291,14 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO return widget; } + case CSMWorld::ColumnBase::Display_String64: + { + // For other Display types (that represent record IDs) with drop support IdCompletionDelegate is used + CSVWidget::DropLineEdit *widget = new CSVWidget::DropLineEdit(display, parent); + widget->setMaxLength (64); + return widget; + } + default: return QStyledItemDelegate::createEditor (parent, option, index);