Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example_community_patch_settings_da.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_de.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_en-GB-x-cockney.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_en-x-minionese.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_es.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_fr.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_nl.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_ru.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
2 changes: 1 addition & 1 deletion example_community_patch_settings_tlh.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# <[=======================================]>

[audio]
# Comma-separated audio event names to suppress; leave blank to allow all audio events
# Comma-separated audio event names to suppress; use "All" as a wildcard for named events handled by this filter
disabled_events = ""

# Write audio event names to the trace log for troubleshooting
Expand Down
22 changes: 19 additions & 3 deletions mods/src/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ namespace DCS = DefaultConfig::Sync;
namespace DCSC = DefaultConfig::SystemConfig;
namespace DCSH = DefaultConfig::Shortcuts;

namespace
{
constexpr bool is_all_audio_wildcard(std::string_view value)
{
return value.size() == 3 && (value[0] == 'a' || value[0] == 'A') && (value[1] == 'l' || value[1] == 'L')
&& (value[2] == 'l' || value[2] == 'L');
}

static_assert(is_all_audio_wildcard("aLl"));
static_assert(!is_all_audio_wildcard(std::string_view{"All\0suffix", 10}));
static_assert(!is_all_audio_wildcard(std::string_view{"\xffll", 3}));
} // namespace

static const eastl::tuple<const char*, int> bannerTypes[] = {
{"All", ToastState::All},
{"Standard", ToastState::Standard},
Expand Down Expand Up @@ -1043,12 +1056,15 @@ void Config::Load()
get_config_or_default(config, parsed, "ui", "disable_toast_banners", DCU::disable_toast_banners, write_config);
this->trace_audio_events =
get_config_or_default(config, parsed, "audio", "trace_events", DCA::trace_events, write_config);
auto disabled_audio_events = get_config_or_default<std::string>(config, parsed, "audio", "disabled_events",
DCA::disabled_events, write_config);
auto disabled_audio_events = get_config_or_default<std::string>(
config, parsed, "audio", "disabled_events", DCA::disabled_events, write_config);
this->disable_all_audio_events = false;
this->disabled_audio_events.clear();
for (const auto& event : StrSplit(disabled_audio_events, ',')) {
auto stripped = StripAsciiWhitespace(event);
if (!stripped.empty()) {
if (is_all_audio_wildcard(stripped)) {
this->disable_all_audio_events = true;
} else if (!stripped.empty()) {
this->disabled_audio_events.emplace_back(stripped);
}
}
Expand Down
1 change: 1 addition & 0 deletions mods/src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ class Config final
bool disable_first_popup;
bool disable_toast_banners;
bool trace_audio_events;
bool disable_all_audio_events;
std::vector<std::string> disabled_audio_events;
bool auto_open_bulk_claim_flyout;
bool auto_confirm_ft_upgrade;
Expand Down
90 changes: 72 additions & 18 deletions mods/src/patches/parts/audio_events.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "str_utils.h"

#include <il2cpp/il2cpp_helper.h>
#include <il2cpp/method_contract.h>

#include <spud/detour.h>
#include <spdlog/spdlog.h>
Expand All @@ -13,30 +14,67 @@
struct FabricEventManager {
};

namespace
{
enum class FabricEventAction : int {
PlaySound = 0,
StopSound = 1,
UnpauseSound = 3,
AdvanceSequence = 16,
StopAll = 21,
UnloadAudio = 23,
PlayScheduled = 33,
};

constexpr bool can_start_audio(int event_action)
{
switch (static_cast<FabricEventAction>(event_action)) {
case FabricEventAction::PlaySound:
case FabricEventAction::UnpauseSound:
case FabricEventAction::AdvanceSequence:
case FabricEventAction::PlayScheduled:
return true;
default:
return false;
}
}

constexpr bool should_suppress_audio_event(bool disable_all, bool event_is_disabled, int event_action)
{ return disable_all ? can_start_audio(event_action) : event_is_disabled; }

static_assert(should_suppress_audio_event(true, false, static_cast<int>(FabricEventAction::PlaySound)));
static_assert(!should_suppress_audio_event(true, false, static_cast<int>(FabricEventAction::StopSound)));
static_assert(should_suppress_audio_event(false, true, static_cast<int>(FabricEventAction::StopAll)));
static_assert(!should_suppress_audio_event(true, true, static_cast<int>(FabricEventAction::UnloadAudio)));
} // namespace

// Fabric.EventManager.PostEvent(string, EventAction, object, GameObject,
// InitialiseParameters, bool, OnEventNotify)
bool FabricEventManager_PostEvent_Hook(auto original, FabricEventManager* _this, Il2CppString* event_name,
int event_action, Il2CppObject* parameter, Il2CppObject* parent_game_object,
Il2CppObject* initialise_parameters, bool add_to_queue,
Il2CppObject* on_event_notify)
{
if (event_name == nullptr) {
auto& config = Config::Get();
if (!event_name || (!config.trace_audio_events && !config.disable_all_audio_events
&& config.disabled_audio_events.empty())) {
return original(_this, event_name, event_action, parameter, parent_game_object, initialise_parameters,
add_to_queue, on_event_notify);
}

const auto event = to_string(event_name);
auto& config = Config::Get();

if (config.trace_audio_events) {
spdlog::info("Audio event: {}", event);
}

if (std::ranges::find(config.disabled_audio_events, event) != config.disabled_audio_events.end()) {
spdlog::debug("Suppressed audio event: {}", event);
return false;
try {
const auto event = to_string(event_name);
if (config.trace_audio_events)
spdlog::info("Audio event: {}", event);
const bool event_is_disabled =
std::ranges::find(config.disabled_audio_events, event) != config.disabled_audio_events.end();
if (should_suppress_audio_event(config.disable_all_audio_events, event_is_disabled, event_action)) {
spdlog::debug("Suppressed audio event: {}", event);
return false;
}
} catch (...) {
// Filter processing failure preserves the native event path.
}

return original(_this, event_name, event_action, parameter, parent_game_object, initialise_parameters,
add_to_queue, on_event_notify);
}
Expand All @@ -49,12 +87,28 @@ void InstallAudioEventHooks()
return;
}

// The shorter PostEvent overloads funnel into this seven-argument overload.
const auto method = helper.GetMethodInfo("PostEvent", 7);
if (method == nullptr || method->methodPointer == nullptr) {
ErrorMsg::MissingMethod("EventManager", "PostEvent");
// String overloads funnel here. Require the complete instance ABI to avoid
// the integer-ID overload and incompatible future signatures.
const auto* method = method_contract::Resolve(helper.get_cls(), "PostEvent", false, "System.Boolean",
{"System.String", "Fabric.EventAction", "System.Object", "UnityEngine.GameObject",
"Fabric.InitialiseParameters", "System.Boolean", "Fabric.OnEventNotify"});
if (!method || method->has_full_generic_sharing_signature) {
ErrorMsg::MissingMethod("EventManager", "PostEvent(string, EventAction, object, GameObject, InitialiseParameters, bool, OnEventNotify)");
return;
}

SPUD_STATIC_DETOUR(method->methodPointer, FabricEventManager_PostEvent_Hook);
for (const auto index : {0, 2, 3, 4, 6}) {
auto* cls = il2cpp_class_from_type(method->parameters[index]);
if (!cls || il2cpp_class_is_valuetype(cls)) {
spdlog::error("Fabric PostEvent reference argument is incompatible");
return;
}
}
auto* action = il2cpp_class_from_type(method->parameters[1]);
const auto* action_type = action && il2cpp_class_is_enum(action) ? il2cpp_class_enum_basetype(action) : nullptr;
if (!action_type || action_type->byref || action_type->type != IL2CPP_TYPE_I4) {
spdlog::error("Fabric PostEvent action enum is incompatible");
return;
}
if (!SPUD_STATIC_DETOUR(method->methodPointer, FabricEventManager_PostEvent_Hook))
spdlog::error("Fabric named audio event hook was not installed");
}
Loading