mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 16:15:43 +00:00
Load SoundCurve files for IW4
This commit is contained in:
parent
2a6ed376d2
commit
77b6b7c87a
212
src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.cpp
Normal file
212
src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
#include "SndCurveReader.h"
|
||||
|
||||
#include "Parsing/Impl/AbstractParser.h"
|
||||
#include "Parsing/Impl/ParserSingleInputStream.h"
|
||||
#include "Parsing/Simple/SimpleLexer.h"
|
||||
#include "Parsing/Simple/SimpleParserValue.h"
|
||||
#include "Parsing/Simple/Matcher/SimpleMatcherFactory.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<SimpleParserValue, SndCurveParserState>;
|
||||
|
||||
class SndCurveMagicSequence final : public snd_sequence_t
|
||||
{
|
||||
public:
|
||||
SndCurveMagicSequence()
|
||||
{
|
||||
const SimpleMatcherFactory create(this);
|
||||
AddMatchers({
|
||||
create.Keyword("SNDCURVE")
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
void ProcessMatch(SndCurveParserState* state, SequenceResult<SimpleParserValue>& 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<SimpleParserValue>& 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<size_t>(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<double>(token.IntegerValue());
|
||||
|
||||
return token.FloatingPointValue();
|
||||
}
|
||||
|
||||
protected:
|
||||
void ProcessMatch(SndCurveParserState* state, SequenceResult<SimpleParserValue>& 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<SimpleParserValue, SndCurveParserState>
|
||||
{
|
||||
public:
|
||||
explicit SndCurveParser(ILexer<SimpleParserValue>* lexer)
|
||||
: AbstractParser<SimpleParserValue, SndCurveParserState>(lexer, std::make_unique<SndCurveParserState>())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
const std::vector<sequence_t*>& GetTestsForState() override
|
||||
{
|
||||
switch (m_state->m_status)
|
||||
{
|
||||
case SndCurveParserStatus::EXPECT_MAGIC:
|
||||
{
|
||||
static std::vector<sequence_t*> expectMagicSequences
|
||||
{
|
||||
new SndCurveMagicSequence()
|
||||
};
|
||||
return expectMagicSequences;
|
||||
}
|
||||
|
||||
case SndCurveParserStatus::EXPECT_KNOT_COUNT:
|
||||
{
|
||||
static std::vector<sequence_t*> expectKnotCountSequences
|
||||
{
|
||||
new SndCurveKnotCountSequence()
|
||||
};
|
||||
return expectKnotCountSequences;
|
||||
}
|
||||
|
||||
case SndCurveParserStatus::KNOTS:
|
||||
{
|
||||
static std::vector<sequence_t*> 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::Result> 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_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<Result>(std::move(parser.GetResult()));
|
||||
}
|
32
src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.h
Normal file
32
src/ObjLoading/AssetLoading/SndCurve/SndCurveReader.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <istream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
|
||||
class SndCurveReader
|
||||
{
|
||||
public:
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
struct Knot
|
||||
{
|
||||
double m_x;
|
||||
double m_y;
|
||||
};
|
||||
|
||||
std::vector<Knot> 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<Result> Read() const;
|
||||
};
|
@ -1,13 +1,25 @@
|
||||
#include "AssetLoaderSndCurve.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "ObjLoading.h"
|
||||
#include "AssetLoading/SndCurve/SndCurveReader.h"
|
||||
#include "Game/IW4/IW4.h"
|
||||
#include "Pool/GlobalAssetPool.h"
|
||||
|
||||
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<SndCurve>();
|
||||
@ -15,3 +27,52 @@ void* AssetLoaderSndCurve::CreateEmptyAsset(const std::string& assetName, Memory
|
||||
sndCurve->filename = memory->Dup(assetName.c_str());
|
||||
return sndCurve;
|
||||
}
|
||||
|
||||
bool AssetLoaderSndCurve::CanLoadFromRaw() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (!sndCurveData)
|
||||
return false;
|
||||
|
||||
if(sndCurveData->m_knots.size() > std::extent_v<decltype(SndCurve::knots)>)
|
||||
{
|
||||
std::cerr << "Failed to load SndCurve \"" << assetName << "\": Too many knots (" << sndCurveData->m_knots.size() << ")\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* sndCurve = memory->Create<SndCurve>();
|
||||
sndCurve->filename = memory->Dup(assetName.c_str());
|
||||
sndCurve->knotCount = static_cast<uint16_t>(sndCurveData->m_knots.size());
|
||||
|
||||
for(auto i = 0u; i < std::extent_v<decltype(SndCurve::knots)>; i++)
|
||||
{
|
||||
if(i < sndCurveData->m_knots.size())
|
||||
{
|
||||
const auto& [x, y] = sndCurveData->m_knots[i];
|
||||
sndCurve->knots[i][0] = static_cast<float>(x);
|
||||
sndCurve->knots[i][1] = static_cast<float>(y);
|
||||
}
|
||||
else
|
||||
{
|
||||
sndCurve->knots[i][0] = 0;
|
||||
sndCurve->knots[i][1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
manager->AddAsset(ASSET_TYPE_SOUND_CURVE, assetName, sndCurve);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -8,7 +8,11 @@ namespace IW4
|
||||
{
|
||||
class AssetLoaderSndCurve final : public BasicAssetLoader<ASSET_TYPE_SOUND_CURVE, SndCurve>
|
||||
{
|
||||
static std::string GetAssetFilename(const std::string& assetName);
|
||||
|
||||
public:
|
||||
_NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override;
|
||||
_NODISCARD bool CanLoadFromRaw() const override;
|
||||
bool LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override;
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user