Skip to content
Open
Show file tree
Hide file tree
Changes from all 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