// 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 // brotato.exe = 44100 2 32 float // snb.exe = 48000 2 16 pcm #pragma once #include #include #include 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 map_; }; } // namespace coop