diff --git a/apps/esmtool/tes4.cpp b/apps/esmtool/tes4.cpp index 2959b8145a..d635545f6e 100644 --- a/apps/esmtool/tes4.cpp +++ b/apps/esmtool/tes4.cpp @@ -80,6 +80,45 @@ namespace EsmTool template constexpr bool hasModel = HasModel::value; + template > + struct HasNif : std::false_type {}; + + template + struct HasNif> : std::true_type {}; + + template + constexpr bool hasNif = HasNif::value; + + template > + struct HasKf : std::false_type {}; + + template + struct HasKf> : std::true_type {}; + + template + constexpr bool hasKf = HasKf::value; + + template + struct WriteArray + { + std::string_view mPrefix; + const T& mValue; + + explicit WriteArray(std::string_view prefix, const T& value) + : mPrefix(prefix) + , mValue(value) + { + } + }; + + template + std::ostream& operator<<(std::ostream& stream, const WriteArray& write) + { + for (const auto& value : write.mValue) + stream << write.mPrefix << value; + return stream; + } + template void readTypedRecord(const Params& params, ESM4::Reader& reader) { @@ -100,6 +139,10 @@ namespace EsmTool std::cout << "\n EditorId: " << value.mEditorId; if constexpr (hasModel) std::cout << "\n Model: " << value.mModel; + if constexpr (hasNif) + std::cout << "\n Nif:" << WriteArray("\n - ", value.mNif); + if constexpr (hasKf) + std::cout << "\n Kf:" << WriteArray("\n - ", value.mKf); std::cout << '\n'; } diff --git a/components/esm4/loadcrea.cpp b/components/esm4/loadcrea.cpp index 3da8a0c114..a18f3b28a8 100644 --- a/components/esm4/loadcrea.cpp +++ b/components/esm4/loadcrea.cpp @@ -121,15 +121,8 @@ void ESM4::Creature::load(ESM4::Reader& reader) case ESM4::SUB_NAM1: reader.getZString(mBloodDecal); break; case ESM4::SUB_NIFZ: { - std::string str; - if (!reader.getZString(str)) + if (!reader.getZeroTerminatedStringArray(mNif)) 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; } case ESM4::SUB_NIFT: @@ -149,15 +142,8 @@ void ESM4::Creature::load(ESM4::Reader& reader) } case ESM4::SUB_KFFZ: { - std::string str; - if (!reader.getZString(str)) + if (!reader.getZeroTerminatedStringArray(mKf)) 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; } case ESM4::SUB_TPLT: reader.get(mBaseTemplate); break; // FO3 diff --git a/components/esm4/loadnpc.cpp b/components/esm4/loadnpc.cpp index 7c3597b029..78a65d2d62 100644 --- a/components/esm4/loadnpc.cpp +++ b/components/esm4/loadnpc.cpp @@ -144,19 +144,12 @@ void ESM4::Npc::load(ESM4::Reader& reader) case ESM4::SUB_MODB: reader.get(mBoundRadius); break; 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: // Forward_SheogorathWithCane.kf // TurnLeft_SheogorathWithCane.kf // TurnRight_SheogorathWithCane.kf - std::stringstream ss(str); - std::string file; - while (std::getline(ss, file, '\0')) // split the strings - mKf.push_back(file); - + if (!reader.getZeroTerminatedStringArray(mKf)) + throw std::runtime_error ("NPC_ KFFZ data read error"); break; } case ESM4::SUB_LNAM: reader.get(mHairLength); break; diff --git a/components/esm4/reader.cpp b/components/esm4/reader.cpp index cc13f5db97..0e661007d5 100644 --- a/components/esm4/reader.cpp +++ b/components/esm4/reader.cpp @@ -693,4 +693,30 @@ bool Reader::getStringImpl(std::string& str, std::size_t size, return false; // FIXME: throw instead? } +bool Reader::getZeroTerminatedStringArray(std::vector& values) +{ + const std::size_t size = mCtx.subRecordHeader.dataSize; + std::string input(size, '\0'); + mStream->read(input.data(), size); + + if (mStream->gcount() != static_cast(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; +} + } diff --git a/components/esm4/reader.hpp b/components/esm4/reader.hpp index a3f6efea39..69a45d6ef2 100644 --- a/components/esm4/reader.hpp +++ b/components/esm4/reader.hpp @@ -293,6 +293,8 @@ namespace ESM4 { return getStringImpl(str, mCtx.subRecordHeader.dataSize, *mStream, mEncoder); } + bool getZeroTerminatedStringArray(std::vector& values); + void enterGroup(); void exitGroupCheck();