Extract a RAII RenderEndpoint that owns the enumerator/endpoint/client/ render-event/buffer for both AudioMirror::run_hooked and run_loopback, replacing two hand-rolled setup+teardown blocks and their two identical fail lambdas with one set_error helper; every COM object now frees on each exit path automatically. Route audio_format_verifier's mono decode through the shared correlate_detail::decode_layout instead of a second copy, and read the debug env var via GetEnvironmentVariableA (drops the getenv C4996). Fold the near-identical read_frame/read_pixel staging-copy setup in SharedTextureSource into one map_staging_copy, and the three separate case-insensitive filter helpers (log/injection panels) into ui/text_match.hpp. Comments: drop game-name anecdotes and "the old code"/version phrasing; genericize the override-file example; fix a stale heartbeat-interval note. Behavior unchanged (host tests + mock_game_test pass).
69 lines
2.1 KiB
C++
69 lines
2.1 KiB
C++
// Per-game audio-format overrides, persisted next to the exe so a game whose hooked
|
|
// format was wrong/unrecoverable is auto-corrected on its next launch. Keyed by the
|
|
// game's image name (case-insensitive). Two writers: the operator's manual Override, and
|
|
// auto-learning a format the hook caught exactly at IAudioClient::Initialize (ground
|
|
// truth). A human-readable one-line-per-game text file:
|
|
//
|
|
// # CoopAllTheThings per-game audio overrides
|
|
// <image.exe> = 44100 2 32 float
|
|
// <image.exe> = 48000 2 16 pcm
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
namespace coop
|
|
{
|
|
|
|
struct AudioFormatOverride
|
|
{
|
|
std::uint32_t rate = 0;
|
|
std::uint32_t channels = 0;
|
|
std::uint32_t bits = 0;
|
|
std::uint32_t format_tag = 0; // WAVE_FORMAT_PCM (1) / WAVE_FORMAT_IEEE_FLOAT (3)
|
|
|
|
[[nodiscard]] bool valid() const
|
|
{
|
|
return rate != 0 && channels != 0 && bits != 0;
|
|
}
|
|
bool operator==(const AudioFormatOverride& o) const
|
|
{
|
|
return rate == o.rate && channels == o.channels && bits == o.bits && format_tag == o.format_tag;
|
|
}
|
|
bool operator!=(const AudioFormatOverride& o) const
|
|
{
|
|
return !(*this == o);
|
|
}
|
|
};
|
|
|
|
class AudioOverrideStore
|
|
{
|
|
public:
|
|
// `path` empty -> default (exe_dir/coop_audio_overrides.ini). Does not load yet.
|
|
explicit AudioOverrideStore(std::wstring path = {});
|
|
|
|
void load(); // (re)read the file; missing file is fine (empty store)
|
|
|
|
// Look up an override by image name (case-insensitive, basename). False if none.
|
|
[[nodiscard]] bool find(const std::wstring& image_name, AudioFormatOverride& out) const;
|
|
|
|
// Store image_name -> fmt and persist immediately. Sets *differed = true when an
|
|
// existing entry for that game differed from `fmt` (caller warns the operator).
|
|
void set(const std::wstring& image_name, const AudioFormatOverride& fmt, bool* differed = nullptr);
|
|
|
|
[[nodiscard]] const std::wstring& path() const
|
|
{
|
|
return path_;
|
|
}
|
|
|
|
private:
|
|
static std::wstring key_of(const std::wstring& image_name); // lowercased basename
|
|
void save() const;
|
|
|
|
std::wstring path_;
|
|
std::map<std::wstring, AudioFormatOverride> map_;
|
|
};
|
|
|
|
} // namespace coop
|