diff --git a/files.cmake b/files.cmake index 90a0dcd4f8..0f915e5abe 100644 --- a/files.cmake +++ b/files.cmake @@ -1498,6 +1498,8 @@ set(DUSK_FILES src/dusk/mods/svc/gfx.cpp src/dusk/mods/svc/hook.cpp src/dusk/mods/svc/host.cpp + src/dusk/mods/svc/id_allocator.cpp + src/dusk/mods/svc/id_allocator.hpp src/dusk/mods/svc/log.cpp src/dusk/mods/svc/overlay.cpp src/dusk/mods/svc/registry.cpp diff --git a/sdk/include/mods/svc/audio_res.h b/sdk/include/mods/svc/audio_res.h index 0d126c1912..b5af109959 100644 --- a/sdk/include/mods/svc/audio_res.h +++ b/sdk/include/mods/svc/audio_res.h @@ -52,6 +52,14 @@ typedef struct AudioResService { AudioWaveInfo const* wave_info, AudioWaveHandle* out_handle); + ModResult (*add_wave)( + ModContext* ctx, + AudioWaveBank bank, + char const* file_name, + AudioWaveInfo const* wave_info, + AudioWaveHandle* out_handle, + uint16_t* out_wave_id); + ModResult (*remove_wave)(ModContext* ctx, AudioWaveHandle handle); } AudioResService; diff --git a/src/dusk/mods/svc/audio_res/audio_res.cpp b/src/dusk/mods/svc/audio_res/audio_res.cpp index 28a18453ca..a63eb4e3ce 100644 --- a/src/dusk/mods/svc/audio_res/audio_res.cpp +++ b/src/dusk/mods/svc/audio_res/audio_res.cpp @@ -16,6 +16,7 @@ using namespace dusk::helpers::cast; constexpr AudioResService s_audioResService{ .header = SERVICE_HEADER(AudioResService, AUDIO_RES_SERVICE_MAJOR, AUDIO_RES_SERVICE_MINOR), .replace_wave = &wsys::insert_replace_wave, + .add_wave = &wsys::insert_add_wave, .remove_wave = &wsys::remove_wave, }; diff --git a/src/dusk/mods/svc/audio_res/wsys.cpp b/src/dusk/mods/svc/audio_res/wsys.cpp index 5abe9cea71..266c0ca55e 100644 --- a/src/dusk/mods/svc/audio_res/wsys.cpp +++ b/src/dusk/mods/svc/audio_res/wsys.cpp @@ -1,10 +1,9 @@ #include "wsys.hpp" -#include "../registry.hpp" #include "../slot_map.hpp" +#include "../id_allocator.hpp" #include "aurora/lib/logging.hpp" #include "dusk/audio/DuskAudioSystem.h" #include "dusk/mods/loader/loader.hpp" -#include "helpers/alignment.hpp" #include "helpers/cast.hpp" namespace dusk::mods::svc::audio_res::wsys { @@ -26,6 +25,12 @@ constexpr ContainerLoadFunction s_containerLoadFunctions[] = { #endif }; +PlainIdAllocator sound_effect_id_allocator(5'000); +PlainIdAllocator music_sample_id_allocator(1'000); + +PlainIdAllocator& id_allocator_for_bank(AudioWaveBank const bank) { + return bank == SoundEffects ? sound_effect_id_allocator : music_sample_id_allocator; +} bool validate_raw_size(LoadedMod const& mod, std::string const& path, uintptr_t actual_size, AudioRawWave const& raw, u32& sample_count) { u32 samples_per_block; @@ -122,32 +127,25 @@ JASWaveInfo wave_info_from_slot(RuntimeWaveReplacementSlot const& slot) { } bool wave_remove(LoadedMod const& mod, AudioWaveHandle const handle) { - auto const result = s_waveReplacements.erase_owned(handle, mod); - wave_replacements_dirty |= result; - return result; -} - -} - -absl::flat_hash_map s_replacements; -std::mutex s_replacements_mutex; - -ModResult remove_wave(ModContext* ctx, AudioWaveHandle handle) { - auto* mod = mod_from_context(ctx); - if (mod == nullptr || handle == 0) { - return MOD_INVALID_ARGUMENT; + auto const found = s_waveReplacements.find_owned(handle, mod); + if (found == nullptr) { + return false; } - if (!wave_remove(*mod, handle)) { - Log.error("[{}] remove wave failed: unknown handle {}", mod->metadata.id, handle); - return MOD_INVALID_ARGUMENT; + + if (found->value.mod_defined) { + auto& allocator = id_allocator_for_bank(found->value.bank); + allocator.free(found->value.wave_id); } - return MOD_OK; + + s_waveReplacements.erase_owned(handle, mod); + return true; } -ModResult insert_replace_wave( +ModResult insert_replace_wave_core( ModContext* ctx, AudioWaveBank bank, u16 wave_id, + bool mod_defined, char const* file_name, AudioWaveInfo const* wave_info, AudioWaveHandle* out_handle) { @@ -164,6 +162,7 @@ ModResult insert_replace_wave( slot.bundle_path = file_name; slot.bank = bank; slot.wave_id = wave_id; + slot.mod_defined = mod_defined; ModResult result; if (wave_info && wave_info->raw_wave) { @@ -197,6 +196,65 @@ ModResult insert_replace_wave( return MOD_OK; } + +} + +absl::flat_hash_map s_replacements; +std::mutex s_replacements_mutex; + +ModResult remove_wave(ModContext* ctx, AudioWaveHandle handle) { + auto* mod = mod_from_context(ctx); + if (mod == nullptr || handle == 0) { + return MOD_INVALID_ARGUMENT; + } + if (!wave_remove(*mod, handle)) { + Log.error("[{}] remove wave failed: unknown handle {}", mod->metadata.id, handle); + return MOD_INVALID_ARGUMENT; + } + return MOD_OK; +} + +ModResult insert_replace_wave( + ModContext* ctx, + AudioWaveBank bank, + u16 wave_id, + char const* file_name, + AudioWaveInfo const* wave_info, + AudioWaveHandle* out_handle) { + + return insert_replace_wave_core(ctx, bank, wave_id, false, file_name, wave_info, out_handle); +} + +ModResult insert_add_wave( + ModContext* ctx, + AudioWaveBank bank, + char const* file_name, + AudioWaveInfo const* wave_info, + AudioWaveHandle* out_handle, + u16* out_wave_id) { + + if (out_wave_id == nullptr) { + return MOD_INVALID_ARGUMENT; + } + + *out_wave_id = 0; + + if (bank != SoundEffects && bank != MusicSamples) { + return MOD_INVALID_ARGUMENT; + } + + auto& allocator = id_allocator_for_bank(bank); + auto const new_wave_id = allocator.alloc(); + + auto const result = insert_replace_wave_core(ctx, bank, new_wave_id, true, file_name, wave_info, out_handle); + if (result != MOD_OK) { + allocator.free(new_wave_id); + } + + *out_wave_id = new_wave_id; + return result; +} + void remove_mod(LoadedMod& mod) { s_waveReplacements.erase_all(mod); diff --git a/src/dusk/mods/svc/audio_res/wsys.hpp b/src/dusk/mods/svc/audio_res/wsys.hpp index 76b966f65a..16d144e4b3 100644 --- a/src/dusk/mods/svc/audio_res/wsys.hpp +++ b/src/dusk/mods/svc/audio_res/wsys.hpp @@ -82,6 +82,7 @@ extern std::mutex s_replacements_mutex; struct RuntimeWaveReplacementSlot { std::string bundle_path; AudioWaveBank bank; + bool mod_defined; u16 wave_id; u8 base_key; @@ -115,6 +116,14 @@ ModResult insert_replace_wave( AudioWaveInfo const* wave_info, AudioWaveHandle* out_handle); +ModResult insert_add_wave( + ModContext* ctx, + AudioWaveBank bank, + char const* file_name, + AudioWaveInfo const* wave_info, + AudioWaveHandle* out_handle, + u16* out_wave_id); + ModResult remove_wave(ModContext* ctx, AudioWaveHandle handle); } \ No newline at end of file diff --git a/src/dusk/mods/svc/id_allocator.cpp b/src/dusk/mods/svc/id_allocator.cpp new file mode 100644 index 0000000000..3778bc7b81 --- /dev/null +++ b/src/dusk/mods/svc/id_allocator.cpp @@ -0,0 +1,9 @@ +#include "id_allocator.hpp" + +namespace dusk::mods::svc { + +void id_allocator_exhausted() { + CRASH("ID allocator exhausted!"); +} + +} // namespace dusk::mods::svc \ No newline at end of file diff --git a/src/dusk/mods/svc/id_allocator.hpp b/src/dusk/mods/svc/id_allocator.hpp new file mode 100644 index 0000000000..b336e0fbfa --- /dev/null +++ b/src/dusk/mods/svc/id_allocator.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +namespace dusk::mods::svc { + +[[noreturn]] void id_allocator_exhausted(); + +template requires std::is_integral_v +class PlainIdAllocator { + std::vector reusable; + T alloc_next; + T alloc_max; + +public: + explicit PlainIdAllocator(T first, T max=std::numeric_limits::max()) + : alloc_next(first), alloc_max(max) { + + } + + T alloc() { + if (reusable.empty()) { + if (alloc_next == alloc_max) { + id_allocator_exhausted(); + } + + return alloc_next++; + } + + auto val = reusable.back(); + reusable.pop_back(); + return val; + } + + void free(T value) { + assert(value < alloc_max && value < alloc_next); + + reusable.push_back(value); + } +}; + +} \ No newline at end of file