mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-19 13:11:32 +00:00
Merge branch 'esm4_zero_terminated_strings' into 'master'
Fix reading array of zero terminated strings (ESM4 NIFZ and KFFZ sub records) See merge request OpenMW/openmw!2403
This commit is contained in:
commit
6f95154ca8
5 changed files with 75 additions and 25 deletions
|
@ -80,6 +80,45 @@ namespace EsmTool
|
||||||
template <class T>
|
template <class T>
|
||||||
constexpr bool hasModel = HasModel<T>::value;
|
constexpr bool hasModel = HasModel<T>::value;
|
||||||
|
|
||||||
|
template <class T, class = std::void_t<>>
|
||||||
|
struct HasNif : std::false_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct HasNif<T, std::void_t<decltype(T::mNif)>> : std::true_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
constexpr bool hasNif = HasNif<T>::value;
|
||||||
|
|
||||||
|
template <class T, class = std::void_t<>>
|
||||||
|
struct HasKf : std::false_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct HasKf<T, std::void_t<decltype(T::mKf)>> : std::true_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
constexpr bool hasKf = HasKf<T>::value;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct WriteArray
|
||||||
|
{
|
||||||
|
std::string_view mPrefix;
|
||||||
|
const T& mValue;
|
||||||
|
|
||||||
|
explicit WriteArray(std::string_view prefix, const T& value)
|
||||||
|
: mPrefix(prefix)
|
||||||
|
, mValue(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::ostream& operator<<(std::ostream& stream, const WriteArray<T>& write)
|
||||||
|
{
|
||||||
|
for (const auto& value : write.mValue)
|
||||||
|
stream << write.mPrefix << value;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void readTypedRecord(const Params& params, ESM4::Reader& reader)
|
void readTypedRecord(const Params& params, ESM4::Reader& reader)
|
||||||
{
|
{
|
||||||
|
@ -100,6 +139,10 @@ namespace EsmTool
|
||||||
std::cout << "\n EditorId: " << value.mEditorId;
|
std::cout << "\n EditorId: " << value.mEditorId;
|
||||||
if constexpr (hasModel<T>)
|
if constexpr (hasModel<T>)
|
||||||
std::cout << "\n Model: " << value.mModel;
|
std::cout << "\n Model: " << value.mModel;
|
||||||
|
if constexpr (hasNif<T>)
|
||||||
|
std::cout << "\n Nif:" << WriteArray("\n - ", value.mNif);
|
||||||
|
if constexpr (hasKf<T>)
|
||||||
|
std::cout << "\n Kf:" << WriteArray("\n - ", value.mKf);
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,15 +121,8 @@ void ESM4::Creature::load(ESM4::Reader& reader)
|
||||||
case ESM4::SUB_NAM1: reader.getZString(mBloodDecal); break;
|
case ESM4::SUB_NAM1: reader.getZString(mBloodDecal); break;
|
||||||
case ESM4::SUB_NIFZ:
|
case ESM4::SUB_NIFZ:
|
||||||
{
|
{
|
||||||
std::string str;
|
if (!reader.getZeroTerminatedStringArray(mNif))
|
||||||
if (!reader.getZString(str))
|
|
||||||
throw std::runtime_error ("CREA NIFZ data read error");
|
throw std::runtime_error ("CREA NIFZ data read error");
|
||||||
|
|
||||||
std::stringstream ss(str);
|
|
||||||
std::string file;
|
|
||||||
while (std::getline(ss, file, '\0')) // split the strings
|
|
||||||
mNif.push_back(file);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM4::SUB_NIFT:
|
case ESM4::SUB_NIFT:
|
||||||
|
@ -149,15 +142,8 @@ void ESM4::Creature::load(ESM4::Reader& reader)
|
||||||
}
|
}
|
||||||
case ESM4::SUB_KFFZ:
|
case ESM4::SUB_KFFZ:
|
||||||
{
|
{
|
||||||
std::string str;
|
if (!reader.getZeroTerminatedStringArray(mKf))
|
||||||
if (!reader.getZString(str))
|
|
||||||
throw std::runtime_error ("CREA KFFZ data read error");
|
throw std::runtime_error ("CREA KFFZ data read error");
|
||||||
|
|
||||||
std::stringstream ss(str);
|
|
||||||
std::string file;
|
|
||||||
while (std::getline(ss, file, '\0')) // split the strings
|
|
||||||
mKf.push_back(file);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM4::SUB_TPLT: reader.get(mBaseTemplate); break; // FO3
|
case ESM4::SUB_TPLT: reader.get(mBaseTemplate); break; // FO3
|
||||||
|
|
|
@ -144,19 +144,12 @@ void ESM4::Npc::load(ESM4::Reader& reader)
|
||||||
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
|
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
|
||||||
case ESM4::SUB_KFFZ:
|
case ESM4::SUB_KFFZ:
|
||||||
{
|
{
|
||||||
std::string str;
|
|
||||||
if (!reader.getZString(str))
|
|
||||||
throw std::runtime_error ("NPC_ KFFZ data read error");
|
|
||||||
|
|
||||||
// Seems to be only below 3, and only happens 3 times while loading TES4:
|
// Seems to be only below 3, and only happens 3 times while loading TES4:
|
||||||
// Forward_SheogorathWithCane.kf
|
// Forward_SheogorathWithCane.kf
|
||||||
// TurnLeft_SheogorathWithCane.kf
|
// TurnLeft_SheogorathWithCane.kf
|
||||||
// TurnRight_SheogorathWithCane.kf
|
// TurnRight_SheogorathWithCane.kf
|
||||||
std::stringstream ss(str);
|
if (!reader.getZeroTerminatedStringArray(mKf))
|
||||||
std::string file;
|
throw std::runtime_error ("NPC_ KFFZ data read error");
|
||||||
while (std::getline(ss, file, '\0')) // split the strings
|
|
||||||
mKf.push_back(file);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM4::SUB_LNAM: reader.get(mHairLength); break;
|
case ESM4::SUB_LNAM: reader.get(mHairLength); break;
|
||||||
|
|
|
@ -693,4 +693,30 @@ bool Reader::getStringImpl(std::string& str, std::size_t size,
|
||||||
return false; // FIXME: throw instead?
|
return false; // FIXME: throw instead?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Reader::getZeroTerminatedStringArray(std::vector<std::string>& values)
|
||||||
|
{
|
||||||
|
const std::size_t size = mCtx.subRecordHeader.dataSize;
|
||||||
|
std::string input(size, '\0');
|
||||||
|
mStream->read(input.data(), size);
|
||||||
|
|
||||||
|
if (mStream->gcount() != static_cast<std::streamsize>(size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string_view inputView(input.data(), input.size());
|
||||||
|
std::string buffer;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
std::string_view value(inputView.data());
|
||||||
|
const std::size_t next = inputView.find_first_not_of('\0', value.size());
|
||||||
|
if (mEncoder != nullptr)
|
||||||
|
value = mEncoder->getUtf8(value, ToUTF8::BufferAllocationPolicy::UseGrowFactor, buffer);
|
||||||
|
values.emplace_back(value);
|
||||||
|
if (next == std::string_view::npos)
|
||||||
|
break;
|
||||||
|
inputView = inputView.substr(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,6 +293,8 @@ namespace ESM4 {
|
||||||
return getStringImpl(str, mCtx.subRecordHeader.dataSize, *mStream, mEncoder);
|
return getStringImpl(str, mCtx.subRecordHeader.dataSize, *mStream, mEncoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getZeroTerminatedStringArray(std::vector<std::string>& values);
|
||||||
|
|
||||||
void enterGroup();
|
void enterGroup();
|
||||||
void exitGroupCheck();
|
void exitGroupCheck();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue