diff --git a/src/ObjCommon/Parsing/GenericGraph2D.h b/src/ObjCommon/Parsing/GenericGraph2D.h new file mode 100644 index 00000000..854ee620 --- /dev/null +++ b/src/ObjCommon/Parsing/GenericGraph2D.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +class GenericGraph2DKnot +{ +public: + double x; + double y; + + GenericGraph2DKnot() = default; + + GenericGraph2DKnot(const double x, const double y) + : x(x), + y(y) + { + } +}; + +class GenericGraph2D +{ +public: + std::string name; + std::vector knots; +}; diff --git a/src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.cpp b/src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.cpp deleted file mode 100644 index 36d52b56..00000000 --- a/src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "SndCurveReader.h" - -#include "Parsing/Impl/AbstractParser.h" -#include "Parsing/Impl/ParserSingleInputStream.h" -#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" -#include "Parsing/Simple/SimpleLexer.h" -#include "Parsing/Simple/SimpleParserValue.h" - -enum class SndCurveParserStatus -{ - EXPECT_MAGIC, - EXPECT_KNOT_COUNT, - KNOTS -}; - -class SndCurveParserState -{ -public: - SndCurveParserStatus m_status; - size_t m_knot_count; - SndCurveReader::Result m_result; - - SndCurveParserState() - : m_status(SndCurveParserStatus::EXPECT_MAGIC), - m_knot_count(0u) - { - } -}; - -using snd_sequence_t = AbstractSequence; - -class SndCurveMagicSequence final : public snd_sequence_t -{ -public: - SndCurveMagicSequence() - { - const SimpleMatcherFactory create(this); - AddMatchers({ - create.Keyword("SNDCURVE"), - }); - } - -protected: - void ProcessMatch(SndCurveParserState* state, SequenceResult& result) const override - { - state->m_status = SndCurveParserStatus::EXPECT_KNOT_COUNT; - } -}; - -class SndCurveKnotCountSequence final : public snd_sequence_t -{ - static constexpr auto CAPTURE_KNOT_COUNT = 1; - -public: - SndCurveKnotCountSequence() - { - const SimpleMatcherFactory create(this); - AddMatchers({ - create.Integer().Capture(CAPTURE_KNOT_COUNT), - }); - } - -protected: - void ProcessMatch(SndCurveParserState* state, SequenceResult& result) const override - { - state->m_status = SndCurveParserStatus::KNOTS; - - const auto& knotCountToken = result.NextCapture(CAPTURE_KNOT_COUNT); - if (knotCountToken.IntegerValue() < 0) - throw ParsingException(knotCountToken.GetPos(), "Negative knot count is invalid"); - - state->m_knot_count = static_cast(knotCountToken.IntegerValue()); - state->m_result.m_knots.reserve(state->m_knot_count); - } -}; - -class SndCurveKnotSequence final : public snd_sequence_t -{ - static constexpr auto CAPTURE_X = 1; - static constexpr auto CAPTURE_Y = 2; - -public: - SndCurveKnotSequence() - { - const SimpleMatcherFactory create(this); - AddMatchers({ - create - .Or({ - create.FloatingPoint(), - create.Integer(), - }) - .Capture(CAPTURE_X), - - create - .Or({ - create.FloatingPoint(), - create.Integer(), - }) - .Capture(CAPTURE_Y), - }); - } - -private: - static double GetValue(const SimpleParserValue& token) - { - if (token.m_type == SimpleParserValueType::INTEGER) - return static_cast(token.IntegerValue()); - - return token.FloatingPointValue(); - } - -protected: - void ProcessMatch(SndCurveParserState* state, SequenceResult& result) const override - { - const auto& xToken = result.NextCapture(CAPTURE_X); - const auto& yToken = result.NextCapture(CAPTURE_Y); - - if (state->m_result.m_knots.size() >= state->m_knot_count) - throw ParsingException(xToken.GetPos(), "Too many knots"); - - const auto xValue = GetValue(xToken); - const auto yValue = GetValue(yToken); - - state->m_result.m_knots.push_back(SndCurveReader::Result::Knot{xValue, yValue}); - } -}; - -class SndCurveParser final : public AbstractParser -{ -public: - explicit SndCurveParser(ILexer* lexer) - : AbstractParser(lexer, std::make_unique()) - { - } - -protected: - const std::vector& GetTestsForState() override - { - switch (m_state->m_status) - { - case SndCurveParserStatus::EXPECT_MAGIC: - { - static std::vector expectMagicSequences{ - new SndCurveMagicSequence(), - }; - return expectMagicSequences; - } - - case SndCurveParserStatus::EXPECT_KNOT_COUNT: - { - static std::vector expectKnotCountSequences{ - new SndCurveKnotCountSequence(), - }; - return expectKnotCountSequences; - } - - case SndCurveParserStatus::KNOTS: - { - static std::vector knotsSequences{ - new SndCurveKnotSequence(), - }; - return knotsSequences; - } - } - - assert(false); - throw std::runtime_error("Invalid parsing status"); - } - -public: - _NODISCARD SndCurveReader::Result& GetResult() const - { - return m_state->m_result; - } - - _NODISCARD bool HasExpectedKnotCount() const - { - return m_state->m_knot_count == m_state->m_result.m_knots.size(); - } -}; - -SndCurveReader::SndCurveReader(std::istream& stream, const std::string& filename) - : m_stream(stream), - m_filename(filename) -{ -} - -std::unique_ptr SndCurveReader::Read() const -{ - ParserSingleInputStream parserStream(m_stream, m_filename); - - SimpleLexer::Config lexerConfig; - lexerConfig.m_emit_new_line_tokens = false; - lexerConfig.m_read_strings = false; - lexerConfig.m_read_integer_numbers = true; - lexerConfig.m_read_floating_point_numbers = true; - - SimpleLexer lexer(&parserStream, std::move(lexerConfig)); - SndCurveParser parser(&lexer); - - if (!parser.Parse()) - { - std::cerr << "Failed to parse SndCurve \"" << m_filename << "\"\n"; - return nullptr; - } - - if (!parser.HasExpectedKnotCount()) - { - std::cerr << "Failed to load SndCurve: Actual knot count differs from expected: \"" << m_filename << "\"\n"; - return nullptr; - } - - return std::make_unique(std::move(parser.GetResult())); -} diff --git a/src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.h b/src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.h deleted file mode 100644 index c8067927..00000000 --- a/src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Utils/ClassUtils.h" - -#include -#include -#include - -class SndCurveReader -{ -public: - class Result - { - public: - struct Knot - { - double m_x; - double m_y; - }; - - std::vector m_knots; - }; - -private: - std::istream& m_stream; - const std::string& m_filename; - -public: - SndCurveReader(std::istream& stream, const std::string& filename); - - _NODISCARD std::unique_ptr Read() const; -}; diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.cpp index 53822d4a..e21d5190 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.cpp @@ -1,9 +1,9 @@ #include "AssetLoaderSndCurve.h" -#include "AssetLoading/SndCurve/SndCurveReader.h" #include "Game/IW4/IW4.h" #include "ObjLoading.h" #include "Pool/GlobalAssetPool.h" +#include "Sound/SoundCurveLoader.h" #include #include @@ -11,15 +11,6 @@ using namespace IW4; -std::string AssetLoaderSndCurve::GetAssetFilename(const std::string& assetName) -{ - std::ostringstream ss; - - ss << "soundaliases/" << assetName << ".vfcurve"; - - return ss.str(); -} - void* AssetLoaderSndCurve::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { auto* sndCurve = memory->Create(); @@ -36,33 +27,26 @@ bool AssetLoaderSndCurve::CanLoadFromRaw() const bool AssetLoaderSndCurve::LoadFromRaw( const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const { - const auto filename = GetAssetFilename(assetName); - const auto file = searchPath->Open(filename); - if (!file.IsOpen()) - return false; - - const SndCurveReader reader(*file.m_stream, filename); - - const auto sndCurveData = reader.Read(); + const auto sndCurveData = sound_curve::LoadSoundCurve(manager, assetName); if (!sndCurveData) return false; - if (sndCurveData->m_knots.size() > std::extent_v) + if (sndCurveData->knots.size() > std::extent_v) { - std::cerr << "Failed to load SndCurve \"" << assetName << "\": Too many knots (" << sndCurveData->m_knots.size() << ")\n"; + std::cerr << "Failed to load SndCurve \"" << assetName << "\": Too many knots (" << sndCurveData->knots.size() << ")\n"; return false; } auto* sndCurve = memory->Create(); sndCurve->filename = memory->Dup(assetName.c_str()); - sndCurve->knotCount = static_cast(sndCurveData->m_knots.size()); + sndCurve->knotCount = static_cast(sndCurveData->knots.size()); for (auto i = 0u; i < std::extent_v; i++) { - if (i < sndCurveData->m_knots.size()) + if (i < sndCurveData->knots.size()) { - const auto& [x, y] = sndCurveData->m_knots[i]; + const auto& [x, y] = sndCurveData->knots[i]; sndCurve->knots[i][0] = static_cast(x); sndCurve->knots[i][1] = static_cast(y); } diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.h b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.h index e261327c..5dea47ab 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.h +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderSndCurve.h @@ -8,8 +8,6 @@ namespace IW4 { class AssetLoaderSndCurve final : public BasicAssetLoader { - static std::string GetAssetFilename(const std::string& assetName); - public: _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; _NODISCARD bool CanLoadFromRaw() const override; diff --git a/src/ObjLoading/Parsing/Graph2D/Graph2DReader.cpp b/src/ObjLoading/Parsing/Graph2D/Graph2DReader.cpp new file mode 100644 index 00000000..f6803f80 --- /dev/null +++ b/src/ObjLoading/Parsing/Graph2D/Graph2DReader.cpp @@ -0,0 +1,229 @@ +#include "Graph2DReader.h" + +#include "Parsing/Impl/AbstractParser.h" +#include "Parsing/Impl/ParserSingleInputStream.h" +#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" +#include "Parsing/Simple/SimpleLexer.h" +#include "Parsing/Simple/SimpleParserValue.h" + +#include + +namespace graph2d +{ + enum class SndCurveParserStatus + { + EXPECT_MAGIC, + EXPECT_KNOT_COUNT, + KNOTS + }; + + class SndCurveParserState + { + public: + SndCurveParserStatus m_status; + size_t m_expected_knot_count; + GenericGraph2D m_result; + + SndCurveParserState() + : m_status(SndCurveParserStatus::EXPECT_MAGIC), + m_expected_knot_count(0u) + { + } + }; + + using snd_sequence_t = AbstractSequence; + + class SndCurveMagicSequence final : public snd_sequence_t + { + public: + explicit SndCurveMagicSequence(std::string magicWord) + { + const SimpleMatcherFactory create(this); + AddMatchers({ + create.Keyword(std::move(magicWord)), + }); + } + + protected: + void ProcessMatch(SndCurveParserState* state, SequenceResult& result) const override + { + state->m_status = SndCurveParserStatus::EXPECT_KNOT_COUNT; + } + }; + + class SndCurveKnotCountSequence final : public snd_sequence_t + { + static constexpr auto CAPTURE_KNOT_COUNT = 1; + + public: + SndCurveKnotCountSequence() + { + const SimpleMatcherFactory create(this); + AddMatchers({ + create.Integer().Capture(CAPTURE_KNOT_COUNT), + }); + } + + protected: + void ProcessMatch(SndCurveParserState* state, SequenceResult& result) const override + { + state->m_status = SndCurveParserStatus::KNOTS; + + const auto& knotCountToken = result.NextCapture(CAPTURE_KNOT_COUNT); + if (knotCountToken.IntegerValue() < 0) + throw ParsingException(knotCountToken.GetPos(), "Negative knot count is invalid"); + + state->m_expected_knot_count = static_cast(knotCountToken.IntegerValue()); + state->m_result.knots.reserve(state->m_expected_knot_count); + } + }; + + class SndCurveKnotSequence final : public snd_sequence_t + { + static constexpr auto CAPTURE_X = 1; + static constexpr auto CAPTURE_Y = 2; + + public: + SndCurveKnotSequence() + { + const SimpleMatcherFactory create(this); + AddMatchers({ + create + .Or({ + create.FloatingPoint(), + create.Integer(), + }) + .Capture(CAPTURE_X), + + create + .Or({ + create.FloatingPoint(), + create.Integer(), + }) + .Capture(CAPTURE_Y), + }); + } + + private: + static double GetValue(const SimpleParserValue& token) + { + if (token.m_type == SimpleParserValueType::INTEGER) + return token.IntegerValue(); + + return token.FloatingPointValue(); + } + + protected: + void ProcessMatch(SndCurveParserState* state, SequenceResult& result) const override + { + const auto& xToken = result.NextCapture(CAPTURE_X); + const auto& yToken = result.NextCapture(CAPTURE_Y); + + if (state->m_result.knots.size() >= state->m_expected_knot_count) + throw ParsingException(xToken.GetPos(), std::format("More knots than expected ({})", state->m_expected_knot_count)); + + const auto xValue = GetValue(xToken); + const auto yValue = GetValue(yToken); + + state->m_result.knots.emplace_back(xValue, yValue); + } + }; + + class SndCurveParser final : public AbstractParser + { + public: + explicit SndCurveParser(ILexer* lexer, std::string magicWord) + : AbstractParser(lexer, std::make_unique()), + m_magic_sequence(std::move(magicWord)), + m_magic_sequences({&m_magic_sequence}) + { + } + + protected: + const std::vector& GetTestsForState() override + { + switch (m_state->m_status) + { + case SndCurveParserStatus::EXPECT_MAGIC: + return m_magic_sequences; + + case SndCurveParserStatus::EXPECT_KNOT_COUNT: + { + static std::vector expectKnotCountSequences{ + new SndCurveKnotCountSequence(), + }; + return expectKnotCountSequences; + } + + case SndCurveParserStatus::KNOTS: + { + static std::vector knotsSequences{ + new SndCurveKnotSequence(), + }; + return knotsSequences; + } + } + + assert(false); + throw std::runtime_error("Invalid parsing status"); + } + + public: + _NODISCARD GenericGraph2D& GetResult() const + { + return m_state->m_result; + } + + _NODISCARD bool HasExpectedKnotCount() const + { + return GetExpectedKnotCount() == GetActualKnotCount(); + } + + [[nodiscard]] size_t GetExpectedKnotCount() const + { + return m_state->m_expected_knot_count; + } + + [[nodiscard]] size_t GetActualKnotCount() const + { + return m_state->m_result.knots.size(); + } + + private: + SndCurveMagicSequence m_magic_sequence; + std::vector m_magic_sequences; + }; + + std::unique_ptr + Read(const std::string& graphTypeName, std::string graphMagicWord, std::istream& stream, const std::string& fileName, std::string graphName) + { + ParserSingleInputStream parserStream(stream, fileName); + + SimpleLexer::Config lexerConfig; + lexerConfig.m_emit_new_line_tokens = false; + lexerConfig.m_read_strings = false; + lexerConfig.m_read_integer_numbers = true; + lexerConfig.m_read_floating_point_numbers = true; + + SimpleLexer lexer(&parserStream, std::move(lexerConfig)); + SndCurveParser parser(&lexer, std::move(graphMagicWord)); + + if (!parser.Parse()) + { + std::cerr << std::format("Failed to parse {} \"{}\"\n", graphTypeName, graphName); + return nullptr; + } + + if (!parser.HasExpectedKnotCount()) + { + std::cerr << std::format("Failed to load {} \"{}\": Actual knot count ({}) differs from expected ({})\n", + graphTypeName, + graphName, + parser.GetActualKnotCount(), + parser.GetExpectedKnotCount()); + return nullptr; + } + + return std::make_unique(std::move(parser.GetResult())); + } +} // namespace graph2d diff --git a/src/ObjLoading/Parsing/Graph2D/Graph2DReader.h b/src/ObjLoading/Parsing/Graph2D/Graph2DReader.h new file mode 100644 index 00000000..43e80472 --- /dev/null +++ b/src/ObjLoading/Parsing/Graph2D/Graph2DReader.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Parsing/GenericGraph2D.h" + +#include +#include + +namespace graph2d +{ + [[nodiscard]] std::unique_ptr + Read(const std::string& graphTypeName, std::string graphMagicWord, std::istream& stream, const std::string& fileName, std::string graphName); +} diff --git a/src/ObjLoading/Sound/SoundCurveLoader.cpp b/src/ObjLoading/Sound/SoundCurveLoader.cpp new file mode 100644 index 00000000..17b2efdc --- /dev/null +++ b/src/ObjLoading/Sound/SoundCurveLoader.cpp @@ -0,0 +1,23 @@ +#include "SoundCurveLoader.h" + +#include "Parsing/Graph2D/Graph2DReader.h" + +#include +#include + +namespace sound_curve +{ + std::unique_ptr LoadSoundCurve(const IAssetLoadingManager* manager, const std::string& soundCurveName) + { + auto* searchPath = manager->GetAssetLoadingContext()->m_raw_search_path; + const auto fileName = std::format("soundaliases/{}.vfcurve", soundCurveName); + const auto file = searchPath->Open(fileName); + if (!file.IsOpen()) + { + std::cerr << std::format("Failed to open file for sound curve \"{}\"\n", soundCurveName); + return nullptr; + } + + return graph2d::Read("sound curve", "SNDCURVE", *file.m_stream, fileName, soundCurveName); + } +} // namespace sound_curve diff --git a/src/ObjLoading/Sound/SoundCurveLoader.h b/src/ObjLoading/Sound/SoundCurveLoader.h new file mode 100644 index 00000000..7982e780 --- /dev/null +++ b/src/ObjLoading/Sound/SoundCurveLoader.h @@ -0,0 +1,11 @@ +#pragma once + +#include "AssetLoading/IAssetLoadingManager.h" +#include "Parsing/GenericGraph2D.h" + +#include + +namespace sound_curve +{ + std::unique_ptr LoadSoundCurve(const IAssetLoadingManager* manager, const std::string& soundCurveName); +}