Compare commits

...

37 Commits

Author SHA1 Message Date
goeiecool9999 4072842507
Merge c796527bb1 into 13b90874f9 2024-05-14 14:42:21 +02:00
goeiecool9999 c796527bb1 don't print error when file doesn't exist 2024-05-14 14:42:15 +02:00
goeiecool9999 da3144a801 Merge branch 'refs/heads/main' into loadaudio
# Conflicts:
#	src/config/CemuConfig.h
2024-05-14 14:22:48 +02:00
splatoon1enjoyer 13b90874f9
Fix commas edge case in strings when parsing an assembly line (#1201) 2024-05-13 16:52:25 +02:00
Exzap cf41c3b136
CI: Use submodule commit of vcpkg 2024-05-10 09:33:32 +02:00
Xphalnos 97d8cf4ba3
vcpkg: Update libraries (#1198) 2024-05-10 09:32:06 +02:00
GaryOderNichts b2a6cccc89
nn_act: Implement GetTransferableId (#1197) 2024-05-09 12:12:34 +02:00
GaryOderNichts 10d553e1c9
zlib125: Implement `deflateInit_` (#1194) 2024-05-07 11:56:28 +02:00
Exzap 3f8722f0a6 Track online-enable and network-service settings per-account instead of globally 2024-05-06 18:18:42 +02:00
Exzap 065fb7eb58 coreinit: Add reschedule special case to avoid a deadlock
Fixes Just Dance 2019 locking up on boot
2024-05-06 09:15:36 +02:00
goeiecool9999 09f32d0936 Merge branch 'main' into loadaudio 2024-04-03 18:32:25 +02:00
goeiecool9999 925e853980 Merge branch 'main' into loadaudio 2024-03-28 01:48:00 +01:00
goeiecool9999 f633113864 Merge branch 'main' into loadaudio 2024-03-18 01:46:06 +01:00
goeiecool9999 f928eee5f3 add log message to indicate file open failed 2024-02-12 12:30:04 +01:00
goeiecool9999 f1d4c399a2 more explicit and sensible error handling 2024-02-12 12:24:32 +01:00
goeiecool9999 e0f5a9f98e Merge branch 'main' into loadaudio 2024-01-22 12:02:24 +01:00
goeiecool9999 a143556d89 name the magic values 2024-01-15 12:04:51 +01:00
goeiecool9999 a5aba28673 disable by default 2024-01-15 11:11:04 +01:00
goeiecool9999 2cd6dbd9b7 Merge branch 'main' into loadaudio 2024-01-15 11:10:46 +01:00
goeiecool9999 2223860829 fix encoding error present since the repo was opened 2023-12-23 23:22:36 +01:00
goeiecool9999 da9c3c84e8 Fix no audio on zero latency bug. make Xaudio consistent with other APIs 2023-12-23 23:19:46 +01:00
goeiecool9999 4468e7a7f2 substitute jthread for thread 2023-12-17 19:38:34 +01:00
goeiecool9999 adc988eb1b fix macOS build (?) 2023-12-17 18:18:41 +01:00
goeiecool9999 91c5a01dbc stuttering observed, move to jthread 2023-12-17 18:03:17 +01:00
goeiecool9999 4ac7cb9f62 make globals static 2023-12-17 17:23:46 +01:00
goeiecool9999 9c2b8a0621 fix sm3dw loop point 2023-12-17 13:44:21 +01:00
goeiecool9999 49b28c16a8 adjust assert 2023-12-17 12:36:51 +01:00
goeiecool9999 c8b4e48120 fix loop 2023-12-17 12:28:27 +01:00
goeiecool9999 57ee2413e7 fix indentation mistake 2023-12-17 11:53:54 +01:00
goeiecool9999 5033318daa move audio playback code to a more reasonable place 2023-12-17 11:49:27 +01:00
goeiecool9999 cab8a1f3c5 add config option to enable/disable boot sound, don't play during OSScreen section 2023-12-17 11:42:33 +01:00
goeiecool9999 dc127e5e61 fix mistake while refactoring Settings code 2023-12-17 11:17:38 +01:00
goeiecool9999 363fe5901a add some more error handling, whitespace in cmake file 2023-12-17 04:45:54 +01:00
goeiecool9999 04ed1c4ed9 simplify deinit 2023-12-17 04:33:01 +01:00
goeiecool9999 312ecbccde attempt to implement looping 2023-12-17 04:24:11 +01:00
goeiecool9999 9354db6a16 fix bugs and clean up 2023-12-17 03:38:37 +01:00
goeiecool9999 a7a116a6f1 proof of concept 2023-12-17 02:50:00 +01:00
28 changed files with 506 additions and 236 deletions

View File

@ -28,7 +28,6 @@ jobs:
run: |
cd dependencies/vcpkg
git fetch --unshallow
git checkout 431eb6bda0950874c8d4ed929cc66e15d8aae46f
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
@ -138,7 +137,6 @@ jobs:
run: |
cd dependencies/vcpkg
git fetch --unshallow
git checkout 431eb6bda0950874c8d4ed929cc66e15d8aae46f
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
@ -218,7 +216,6 @@ jobs:
run: |
cd dependencies/vcpkg
git fetch --unshallow
git pull --all
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}

2
dependencies/vcpkg vendored

@ -1 +1 @@
Subproject commit 53bef8994c541b6561884a8395ea35715ece75db
Subproject commit cbf4a6641528cee6f172328984576f51698de726

View File

@ -25,6 +25,9 @@
#include "util/helpers/Serializer.h"
#include <wx/msgdlg.h>
#include <audio/IAudioAPI.h>
#include <util/bootSound/BootSoundReader.h>
#include <thread>
#if BOOST_OS_WINDOWS
#include <psapi.h>
@ -66,6 +69,9 @@ void LatteShaderCache_LoadVulkanPipelineCache(uint64 cacheTitleId);
bool LatteShaderCache_updatePipelineLoadingProgress();
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines);
void LatteShaderCache_InitBootSound();
void LatteShaderCache_ShutdownBootSound();
void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path pathGenericPre1_25_0, fs::path pathGenericPre1_16_0);
struct
@ -299,6 +305,10 @@ void LatteShaderCache_Load()
loadBackgroundTexture(true, g_shaderCacheLoaderState.textureTVId);
loadBackgroundTexture(false, g_shaderCacheLoaderState.textureDRCId);
// initialise resources for playing bootup sound
if(GetConfig().play_boot_sound)
LatteShaderCache_InitBootSound();
sint32 numLoadedShaders = 0;
uint32 loadIndex = 0;
@ -365,6 +375,9 @@ void LatteShaderCache_Load()
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureTVId);
if (g_shaderCacheLoaderState.textureDRCId)
g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId);
// free resources for playing boot sound
LatteShaderCache_ShutdownBootSound();
}
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines)
@ -806,3 +819,86 @@ void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path
}
}
}
static std::atomic<bool> audiothread_keeprunning = true;
void LatteShaderCache_StreamBootSound()
{
constexpr sint32 sampleRate = 48'000;
constexpr sint32 bitsPerSample = 16;
constexpr sint32 samplesPerBlock = sampleRate / 10; // block is 1/10th of a second
constexpr sint32 nChannels = 2;
static_assert(bitsPerSample % 8 == 0, "bits per sample is not a multiple of 8");
AudioAPIPtr bootSndAudioDev;
std::unique_ptr<BootSoundReader> bootSndFileReader;
FSCVirtualFile* bootSndFileHandle = 0;
try
{
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
if(!bootSndAudioDev)
return;
}
catch (const std::runtime_error& ex)
{
cemuLog_log(LogType::Force, "Failed to initialise audio device for bootup sound");
return;
}
bootSndAudioDev->SetAudioDelayOverride(4);
bootSndAudioDev->Play();
std::string sndPath = fmt::format("{}/meta/{}", CafeSystem::GetMlcStoragePath(CafeSystem::GetForegroundTitleId()), "bootSound.btsnd");
sint32 fscStatus = FSC_STATUS_UNDEFINED;
if(!fsc_doesFileExist(sndPath.c_str()))
return;
bootSndFileHandle = fsc_open(sndPath.c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &fscStatus);
if(!bootSndFileHandle)
{
cemuLog_log(LogType::Force, "failed to open bootSound.btsnd");
return;
}
constexpr sint32 audioBlockSize = samplesPerBlock * (bitsPerSample/8) * nChannels;
bootSndFileReader = std::make_unique<BootSoundReader>(bootSndFileHandle, audioBlockSize);
if(bootSndAudioDev && bootSndFileHandle && bootSndFileReader)
{
while(audiothread_keeprunning)
{
while (bootSndAudioDev->NeedAdditionalBlocks())
{
sint16* data = bootSndFileReader->getSamples();
if(data == nullptr)
{
audiothread_keeprunning = false;
break;
}
bootSndAudioDev->FeedBlock(data);
}
// sleep for the duration of a single block
std::this_thread::sleep_for(std::chrono::milliseconds(samplesPerBlock / (sampleRate/ 1'000)));
}
}
if(bootSndFileHandle)
fsc_close(bootSndFileHandle);
}
static std::thread g_bootSndPlayThread;
void LatteShaderCache_InitBootSound()
{
audiothread_keeprunning = true;
if(!g_bootSndPlayThread.joinable())
g_bootSndPlayThread = std::thread{LatteShaderCache_StreamBootSound};
}
void LatteShaderCache_ShutdownBootSound()
{
audiothread_keeprunning = false;
if(g_bootSndPlayThread.joinable())
g_bootSndPlayThread.join();
g_bootSndPlayThread = {};
}

View File

@ -763,6 +763,11 @@ namespace coreinit
uint32 coreIndex = OSGetCoreId();
if (!newThread->context.hasCoreAffinitySet(coreIndex))
return false;
// special case: if current and new thread are running only on the same core then reschedule even if priority is equal
// this resolves a deadlock in Just Dance 2019 where one thread would always reacquire the same mutex within it's timeslice, blocking another thread on the same core from acquiring it
if ((1<<coreIndex) == newThread->context.affinity && currentThread->context.affinity == newThread->context.affinity && currentThread->effectivePriority == newThread->effectivePriority)
return true;
// otherwise reschedule if new thread has higher priority
return newThread->effectivePriority < currentThread->effectivePriority;
}

View File

@ -308,6 +308,22 @@ void nnActExport_GetPrincipalIdEx(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, 0); // ResultSuccess
}
void nnActExport_GetTransferableId(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(unique, 0);
cemuLog_logDebug(LogType::Force, "nn_act.GetTransferableId(0x{:08x})", hCPU->gpr[3]);
uint64 transferableId;
uint32 r = nn::act::GetTransferableIdEx(&transferableId, unique, iosu::act::ACT_SLOT_CURRENT);
if (NN_RESULT_IS_FAILURE(r))
{
transferableId = 0;
}
osLib_returnFromFunction64(hCPU, _swapEndianU64(transferableId));
}
void nnActExport_GetTransferableIdEx(PPCInterpreter_t* hCPU)
{
ppcDefineParamStructPtr(transferableId, uint64, 0);
@ -691,6 +707,7 @@ void nnAct_load()
osLib_addFunction("nn_act", "GetPrincipalId__Q2_2nn3actFv", nnActExport_GetPrincipalId);
osLib_addFunction("nn_act", "GetPrincipalIdEx__Q2_2nn3actFPUiUc", nnActExport_GetPrincipalIdEx);
// transferable id
osLib_addFunction("nn_act", "GetTransferableId__Q2_2nn3actFUi", nnActExport_GetTransferableId);
osLib_addFunction("nn_act", "GetTransferableIdEx__Q2_2nn3actFPULUiUc", nnActExport_GetTransferableIdEx);
// persistent id
osLib_addFunction("nn_act", "GetPersistentId__Q2_2nn3actFv", nnActExport_GetPersistentId);

View File

@ -396,90 +396,35 @@ namespace snd_core
void AXOut_init()
{
auto& config = GetConfig();
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
numQueuedFramesSndGeneric = 0;
std::unique_lock lock(g_audioMutex);
if (!g_tvAudio)
{
sint32 channels;
switch (config.tv_channels)
try
{
case 0:
channels = 1; // will mix mono sound on both output channels
break;
case 2:
channels = 6;
break;
default: // stereo
channels = 2;
break;
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(true, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
}
IAudioAPI::DeviceDescriptionPtr device_description;
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
catch (std::runtime_error& ex)
{
auto devices = IAudioAPI::GetDevices(audio_api);
const auto it = std::find_if(devices.begin(), devices.end(), [&config](const auto& d) {return d->GetIdentifier() == config.tv_device; });
if (it != devices.end())
device_description = *it;
}
if (device_description)
{
try
{
g_tvAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, device_description, 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
g_tvAudio->SetVolume(config.tv_volume);
}
catch (std::runtime_error& ex)
{
cemuLog_log(LogType::Force, "can't initialize tv audio: {}", ex.what());
exit(0);
}
cemuLog_log(LogType::Force, "can't initialize tv audio: {}", ex.what());
exit(0);
}
}
if (!g_padAudio)
{
sint32 channels;
switch (config.pad_channels)
try
{
case 0:
channels = 1; // will mix mono sound on both output channels
break;
case 2:
channels = 6;
break;
default: // stereo
channels = 2;
break;
g_padAudio = IAudioAPI::CreateDeviceFromConfig(false, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
if(g_padAudio)
g_padVolume = g_padAudio->GetVolume();
}
IAudioAPI::DeviceDescriptionPtr device_description;
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
catch (std::runtime_error& ex)
{
auto devices = IAudioAPI::GetDevices(audio_api);
const auto it = std::find_if(devices.begin(), devices.end(), [&config](const auto& d) {return d->GetIdentifier() == config.pad_device; });
if (it != devices.end())
device_description = *it;
}
if (device_description)
{
try
{
g_padAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, device_description, 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
g_padAudio->SetVolume(config.pad_volume);
g_padVolume = config.pad_volume;
}
catch (std::runtime_error& ex)
{
cemuLog_log(LogType::Force, "can't initialize pad audio: {}", ex.what());
exit(0);
}
cemuLog_log(LogType::Force, "can't initialize pad audio: {}", ex.what());
exit(0);
}
}
}

View File

@ -213,6 +213,32 @@ void zlib125Export_inflateReset2(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, r);
}
void zlib125Export_deflateInit_(PPCInterpreter_t* hCPU)
{
ppcDefineParamStructPtr(zstream, z_stream_ppc2, 0);
ppcDefineParamS32(level, 1);
ppcDefineParamStr(version, 2);
ppcDefineParamS32(streamsize, 3);
z_stream hzs;
zlib125_setupHostZStream(zstream, &hzs, false);
// setup internal memory allocator if requested
if (zstream->zalloc == nullptr)
zstream->zalloc = PPCInterpreter_makeCallableExportDepr(zlib125_zcalloc);
if (zstream->zfree == nullptr)
zstream->zfree = PPCInterpreter_makeCallableExportDepr(zlib125_zcfree);
if (streamsize != sizeof(z_stream_ppc2))
assert_dbg();
sint32 r = deflateInit_(&hzs, level, version, sizeof(z_stream));
zlib125_setupUpdateZStream(&hzs, zstream);
osLib_returnFromFunction(hCPU, r);
}
void zlib125Export_deflateInit2_(PPCInterpreter_t* hCPU)
{
ppcDefineParamStructPtr(zstream, z_stream_ppc2, 0);
@ -345,6 +371,7 @@ namespace zlib
osLib_addFunction("zlib125", "inflateReset", zlib125Export_inflateReset);
osLib_addFunction("zlib125", "inflateReset2", zlib125Export_inflateReset2);
osLib_addFunction("zlib125", "deflateInit_", zlib125Export_deflateInit_);
osLib_addFunction("zlib125", "deflateInit2_", zlib125Export_deflateInit2_);
osLib_addFunction("zlib125", "deflateBound", zlib125Export_deflateBound);
osLib_addFunction("zlib125", "deflate", zlib125Export_deflate);

View File

@ -2418,6 +2418,9 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut*
_ppcAssembler_translateAlias(instructionName);
// parse operands
internalInfo.listOperandStr.clear();
bool isInString = false;
while (currentPtr < endPtr)
{
currentPtr++;
@ -2425,7 +2428,10 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut*
// find end of operand
while (currentPtr < endPtr)
{
if (*currentPtr == ',')
if (*currentPtr == '"')
isInString=!isInString;
if (*currentPtr == ',' && !isInString)
break;
currentPtr++;
}

View File

@ -114,7 +114,7 @@ CubebAPI::~CubebAPI()
bool CubebAPI::NeedAdditionalBlocks() const
{
std::shared_lock lock(m_mutex);
return m_buffer.size() < s_audioDelay * m_bytesPerBlock;
return m_buffer.size() < GetAudioDelay() * m_bytesPerBlock;
}
bool CubebAPI::FeedBlock(sint16* data)

View File

@ -210,7 +210,7 @@ void DirectSoundAPI::SetVolume(sint32 volume)
bool DirectSoundAPI::NeedAdditionalBlocks() const
{
std::shared_lock lock(m_mutex);
return m_buffer.size() < s_audioDelay;
return m_buffer.size() < GetAudioDelay();
}
std::vector<DirectSoundAPI::DeviceDescriptionPtr> DirectSoundAPI::GetDevices()

View File

@ -97,7 +97,40 @@ bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
return false;
}
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
{
auto& config = GetConfig();
sint32 channels = CemuConfig::AudioChannelsToNChannels(TV ? config.tv_channels : config.pad_channels);
return CreateDeviceFromConfig(TV, rate, channels, samples_per_block, bits_per_sample);
}
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
{
AudioAPIPtr audioAPIDev;
auto& config = GetConfig();
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
auto& selectedDevice = TV ? config.tv_device : config.pad_device;
if(selectedDevice.empty())
return {};
IAudioAPI::DeviceDescriptionPtr device_description;
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
{
auto devices = IAudioAPI::GetDevices(audio_api);
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) {return d->GetIdentifier() == selectedDevice; });
if (it != devices.end())
device_description = *it;
}
if (!device_description)
throw std::runtime_error("failed to find selected device while trying to create audio device");
audioAPIDev = CreateDevice(audio_api, device_description, rate, channels, samples_per_block, bits_per_sample);
audioAPIDev->SetVolume(TV ? config.tv_volume : config.pad_volume);
return audioAPIDev;
}
AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
{
@ -167,3 +200,12 @@ std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
}
}
void IAudioAPI::SetAudioDelayOverride(uint32 delay)
{
m_audioDelayOverride = delay;
}
uint32 IAudioAPI::GetAudioDelay() const
{
return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay;
}

View File

@ -55,11 +55,15 @@ public:
virtual bool FeedBlock(sint16* data) = 0;
virtual bool Play() = 0;
virtual bool Stop() = 0;
void SetAudioDelayOverride(uint32 delay);
uint32 GetAudioDelay() const;
static void PrintLogging();
static void InitializeStatic();
static bool IsAudioAPIAvailable(AudioAPI api);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api);
@ -75,9 +79,10 @@ protected:
bool m_playing = false;
static std::array<bool, AudioAPIEnd> s_availableApis;
static uint32 s_audioDelay;
uint32 m_audioDelayOverride = 0;
private:
static uint32 s_audioDelay;
void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample);
};

View File

@ -33,8 +33,8 @@ XAudio27API::XAudio27API(uint32 device_id, uint32 samplerate, uint32 channels, u
m_wfx.Format.nChannels = channels;
m_wfx.Format.nSamplesPerSec = samplerate;
m_wfx.Format.wBitsPerSample = bits_per_sample;
m_wfx.Format.nBlockAlign = (m_wfx.Format.nChannels * m_wfx.Format.wBitsPerSample) / 8; // must equal (nChannels × wBitsPerSample) / 8
m_wfx.Format.nAvgBytesPerSec = m_wfx.Format.nSamplesPerSec * m_wfx.Format.nBlockAlign; // must equal nSamplesPerSec × nBlockAlign.
m_wfx.Format.nBlockAlign = (m_wfx.Format.nChannels * m_wfx.Format.wBitsPerSample) / 8; // must equal (nChannels × wBitsPerSample) / 8
m_wfx.Format.nAvgBytesPerSec = m_wfx.Format.nSamplesPerSec * m_wfx.Format.nBlockAlign; // must equal nSamplesPerSec × nBlockAlign.
m_wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
m_wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
@ -199,9 +199,7 @@ bool XAudio27API::FeedBlock(sint16* data)
// check if we queued too many blocks
if(m_blocks_queued >= kBlockCount)
{
XAUDIO2_VOICE_STATE state{};
m_source_voice->GetState(&state);
m_blocks_queued = state.BuffersQueued;
m_blocks_queued = GetQueuedBuffers();
if (m_blocks_queued >= kBlockCount)
{
@ -222,7 +220,14 @@ bool XAudio27API::FeedBlock(sint16* data)
return true;
}
uint32 XAudio27API::GetQueuedBuffers() const
{
XAUDIO2_VOICE_STATE state{};
m_source_voice->GetState(&state);
return state.BuffersQueued;
}
bool XAudio27API::NeedAdditionalBlocks() const
{
return m_blocks_queued < s_audioDelay;
return GetQueuedBuffers() < GetAudioDelay();
}

View File

@ -47,6 +47,8 @@ public:
static std::vector<DeviceDescriptionPtr> GetDevices();
private:
uint32 GetQueuedBuffers() const;
struct XAudioDeleter
{
void operator()(IXAudio2* ptr) const;

View File

@ -270,9 +270,7 @@ bool XAudio2API::FeedBlock(sint16* data)
// check if we queued too many blocks
if (m_blocks_queued >= kBlockCount)
{
XAUDIO2_VOICE_STATE state{};
m_source_voice->GetState(&state);
m_blocks_queued = state.BuffersQueued;
m_blocks_queued = GetQueuedBuffers();
if (m_blocks_queued >= kBlockCount)
{
@ -293,7 +291,14 @@ bool XAudio2API::FeedBlock(sint16* data)
return true;
}
uint32 XAudio2API::GetQueuedBuffers() const
{
XAUDIO2_VOICE_STATE state{};
m_source_voice->GetState(&state);
return state.BuffersQueued;
}
bool XAudio2API::NeedAdditionalBlocks() const
{
return m_blocks_queued < s_audioDelay;
return GetQueuedBuffers() < GetAudioDelay();
}

View File

@ -46,6 +46,8 @@ public:
static const std::vector<DeviceDescriptionPtr>& GetDevices() { return s_devices; }
private:
uint32 GetQueuedBuffers() const;
static const std::vector<DeviceDescriptionPtr>& RefreshDevices();
struct XAudioDeleter

View File

@ -131,7 +131,12 @@ uint32 ActiveSettings::GetPersistentId()
bool ActiveSettings::IsOnlineEnabled()
{
return GetConfig().account.online_enabled && Account::GetAccount(GetPersistentId()).IsValidOnlineAccount() && HasRequiredOnlineFiles();
if(!Account::GetAccount(GetPersistentId()).IsValidOnlineAccount())
return false;
if(!HasRequiredOnlineFiles())
return false;
NetworkService networkService = static_cast<NetworkService>(GetConfig().GetAccountNetworkService(GetPersistentId()));
return networkService == NetworkService::Nintendo || networkService == NetworkService::Pretendo || networkService == NetworkService::Custom;
}
bool ActiveSettings::HasRequiredOnlineFiles()
@ -139,8 +144,9 @@ bool ActiveSettings::HasRequiredOnlineFiles()
return s_has_required_online_files;
}
NetworkService ActiveSettings::GetNetworkService() {
return static_cast<NetworkService>(GetConfig().account.active_service.GetValue());
NetworkService ActiveSettings::GetNetworkService()
{
return GetConfig().GetAccountNetworkService(GetPersistentId());
}
bool ActiveSettings::DumpShadersEnabled()

View File

@ -63,6 +63,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
fullscreen = parser.get("fullscreen", fullscreen);
proxy_server = parser.get("proxy_server", "");
disable_screensaver = parser.get("disable_screensaver", disable_screensaver);
play_boot_sound = parser.get("play_boot_sound", play_boot_sound);
console_language = parser.get("console_language", console_language.GetInitValue());
window_position.x = parser.get("window_position").get("x", -1);
@ -328,8 +329,22 @@ void CemuConfig::Load(XMLConfigParser& parser)
// account
auto acc = parser.get("Account");
account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id);
account.online_enabled = acc.get("OnlineEnabled", account.online_enabled);
account.active_service = acc.get("ActiveService",account.active_service);
// legacy online settings, we only parse these for upgrading purposes
account.legacy_online_enabled = acc.get("OnlineEnabled", account.legacy_online_enabled);
account.legacy_active_service = acc.get("ActiveService",account.legacy_active_service);
// per-account online setting
auto accService = parser.get("AccountService");
account.service_select.clear();
for (auto element = accService.get("SelectedService"); element.valid(); element = accService.get("SelectedService", element))
{
uint32 persistentId = element.get_attribute<uint32>("PersistentId", 0);
sint32 serviceIndex = element.get_attribute<sint32>("Service", 0);
NetworkService networkService = static_cast<NetworkService>(serviceIndex);
if (persistentId < Account::kMinPersistendId)
continue;
if(networkService == NetworkService::Offline || networkService == NetworkService::Nintendo || networkService == NetworkService::Pretendo || networkService == NetworkService::Custom)
account.service_select.emplace(persistentId, networkService);
}
// debug
auto debug = parser.get("Debug");
#if BOOST_OS_WINDOWS
@ -366,6 +381,7 @@ void CemuConfig::Save(XMLConfigParser& parser)
config.set<bool>("fullscreen", fullscreen);
config.set("proxy_server", proxy_server.GetValue().c_str());
config.set<bool>("disable_screensaver", disable_screensaver);
config.set<bool>("play_boot_sound", play_boot_sound);
// config.set("cpu_mode", cpu_mode.GetValue());
//config.set("console_region", console_region.GetValue());
@ -512,8 +528,17 @@ void CemuConfig::Save(XMLConfigParser& parser)
// account
auto acc = config.set("Account");
acc.set("PersistentId", account.m_persistent_id.GetValue());
acc.set("OnlineEnabled", account.online_enabled.GetValue());
acc.set("ActiveService",account.active_service.GetValue());
// legacy online mode setting
acc.set("OnlineEnabled", account.legacy_online_enabled.GetValue());
acc.set("ActiveService",account.legacy_active_service.GetValue());
// per-account online setting
auto accService = config.set("AccountService");
for(auto& it : account.service_select)
{
auto entry = accService.set("SelectedService");
entry.set_attribute("PersistentId", it.first);
entry.set_attribute("Service", static_cast<sint32>(it.second));
}
// debug
auto debug = config.set("Debug");
#if BOOST_OS_WINDOWS
@ -609,3 +634,30 @@ void CemuConfig::AddRecentNfcFile(std::string_view file)
while (recent_nfc_files.size() > kMaxRecentEntries)
recent_nfc_files.pop_back();
}
NetworkService CemuConfig::GetAccountNetworkService(uint32 persistentId)
{
auto it = account.service_select.find(persistentId);
if (it != account.service_select.end())
{
NetworkService serviceIndex = it->second;
// make sure the returned service is valid
if (serviceIndex != NetworkService::Offline &&
serviceIndex != NetworkService::Nintendo &&
serviceIndex != NetworkService::Pretendo &&
serviceIndex != NetworkService::Custom)
return NetworkService::Offline;
if( static_cast<NetworkService>(serviceIndex) == NetworkService::Custom && !NetworkConfig::XMLExists() )
return NetworkService::Offline; // custom is selected but no custom config exists
return serviceIndex;
}
// if not found, return the legacy value
if(!account.legacy_online_enabled)
return NetworkService::Offline;
return static_cast<NetworkService>(account.legacy_active_service.GetValue() + 1); // +1 because "Offline" now takes index 0
}
void CemuConfig::SetAccountSelectedService(uint32 persistentId, NetworkService serviceIndex)
{
account.service_select[persistentId] = serviceIndex;
}

View File

@ -8,6 +8,8 @@
#include <wx/language.h>
#include <wx/intl.h>
enum class NetworkService;
struct GameEntry
{
GameEntry() = default;
@ -378,6 +380,7 @@ struct CemuConfig
#endif
ConfigValue<bool> disable_screensaver{DISABLE_SCREENSAVER_DEFAULT};
#undef DISABLE_SCREENSAVER_DEFAULT
ConfigValue<bool> play_boot_sound{false};
std::vector<std::string> game_paths;
std::mutex game_cache_entries_mutex;
@ -483,8 +486,9 @@ struct CemuConfig
struct
{
ConfigValueBounds<uint32> m_persistent_id{ Account::kMinPersistendId, Account::kMinPersistendId, 0xFFFFFFFF };
ConfigValue<bool> online_enabled{false};
ConfigValue<int> active_service{0};
ConfigValue<bool> legacy_online_enabled{false};
ConfigValue<int> legacy_active_service{0};
std::unordered_map<uint32, NetworkService> service_select; // per-account service index. Key is persistentId
}account{};
// input
@ -509,7 +513,23 @@ struct CemuConfig
bool GetGameListCustomName(uint64 titleId, std::string& customName);
void SetGameListCustomName(uint64 titleId, std::string customName);
private:
NetworkService GetAccountNetworkService(uint32 persistentId);
void SetAccountSelectedService(uint32 persistentId, NetworkService serviceIndex);
static int AudioChannelsToNChannels(AudioChannels kStereo)
{
switch (kStereo)
{
case 0:
return 1; // will mix mono sound on both output channels
case 2:
return 6;
default: // stereo
return 2;
}
}
private:
GameEntry* GetGameEntryByTitleId(uint64 titleId);
GameEntry* CreateGameEntry(uint64 titleId);
};

View File

@ -34,14 +34,15 @@ void NetworkConfig::Load(XMLConfigParser& parser)
bool NetworkConfig::XMLExists()
{
static std::optional<bool> s_exists; // caches result of fs::exists
if(s_exists.has_value())
return *s_exists;
std::error_code ec;
if (!fs::exists(ActiveSettings::GetConfigPath("network_services.xml"), ec))
{
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
{
GetConfig().account.active_service = 0;
}
s_exists = false;
return false;
}
s_exists = true;
return true;
}

View File

@ -5,9 +5,11 @@
enum class NetworkService
{
Offline,
Nintendo,
Pretendo,
Custom
Custom,
COUNT = Custom
};
struct NetworkConfig

View File

@ -184,11 +184,16 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook)
m_disable_screensaver->SetToolTip(_("Prevents the system from activating the screen saver or going to sleep while running a game."));
second_row->Add(m_disable_screensaver, 0, botflag, 5);
// Enable/disable feral interactive gamemode
m_play_boot_sound = new wxCheckBox(box, wxID_ANY, _("Enable intro sound"));
m_play_boot_sound->SetToolTip(_("Play bootSound file while compiling shaders/pipelines."));
second_row->Add(m_play_boot_sound, 0, botflag, 5);
// Enable/disable feral interactive gamemode
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
m_feral_gamemode = new wxCheckBox(box, wxID_ANY, _("Enable Feral GameMode"));
m_feral_gamemode->SetToolTip(_("Use FeralInteractive GameMode if installed."));
second_row->Add(m_feral_gamemode, 0, botflag, 5);
second_row->AddSpacer(10);
m_feral_gamemode = new wxCheckBox(box, wxID_ANY, _("Enable Feral GameMode"));
m_feral_gamemode->SetToolTip(_("Use FeralInteractive GameMode if installed."));
second_row->Add(m_feral_gamemode, 0, botflag, 5);
#endif
// temporary workaround because feature crashes on macOS
@ -683,18 +688,6 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
content->Add(m_delete_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5);
m_delete_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountDelete, this);
wxString choices[] = { _("Nintendo"), _("Pretendo"), _("Custom") };
m_active_service = new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 3, wxRA_SPECIFY_COLS);
if (!NetworkConfig::XMLExists())
m_active_service->Enable(2, false);
m_active_service->SetItemToolTip(0, _("Connect to the official Nintendo Network Service"));
m_active_service->SetItemToolTip(1, _("Connect to the Pretendo Network Service"));
m_active_service->SetItemToolTip(2, _("Connect to a custom Network Service (configured via network_services.xml)"));
m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this);
content->Add(m_active_service, 0, wxEXPAND | wxALL, 5);
box_sizer->Add(content, 1, wxEXPAND, 5);
online_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
@ -704,17 +697,33 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
m_active_account->Enable(false);
m_create_account->Enable(false);
m_delete_account->Enable(false);
}
}
{
wxString choices[] = { _("Offline"), _("Nintendo"), _("Pretendo"), _("Custom") };
m_active_service = new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 4, wxRA_SPECIFY_COLS);
if (!NetworkConfig::XMLExists())
m_active_service->Enable(3, false);
m_active_service->SetItemToolTip(0, _("Online functionality disabled for this account"));
m_active_service->SetItemToolTip(1, _("Connect to the official Nintendo Network Service"));
m_active_service->SetItemToolTip(2, _("Connect to the Pretendo Network Service"));
m_active_service->SetItemToolTip(3, _("Connect to a custom Network Service (configured via network_services.xml)"));
m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this);
online_panel_sizer->Add(m_active_service, 0, wxEXPAND | wxALL, 5);
if (CafeSystem::IsTitleRunning())
{
m_active_service->Enable(false);
}
}
{
auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Online settings"));
auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Online play requirements"));
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
m_online_enabled = new wxCheckBox(box, wxID_ANY, _("Enable online mode"));
m_online_enabled->Bind(wxEVT_CHECKBOX, &GeneralSettings2::OnOnlineEnable, this);
box_sizer->Add(m_online_enabled, 0, wxEXPAND | wxALL, 5);
auto* row = new wxFlexGridSizer(0, 2, 0, 0);
row->SetFlexibleDirection(wxBOTH);
@ -873,6 +882,14 @@ GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched)
DisableSettings(game_launched);
}
uint32 GeneralSettings2::GetSelectedAccountPersistentId()
{
const auto active_account = m_active_account->GetSelection();
if (active_account == wxNOT_FOUND)
return GetConfig().account.m_persistent_id.GetInitValue();
return dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId();
}
void GeneralSettings2::StoreConfig()
{
auto* app = (CemuApp*)wxTheApp;
@ -916,6 +933,8 @@ void GeneralSettings2::StoreConfig()
ScreenSaver::SetInhibit(config.disable_screensaver);
}
config.play_boot_sound = m_play_boot_sound->IsChecked();
if (!LaunchSettings::GetMLCPath().has_value())
config.SetMLCPath(wxHelper::MakeFSPath(m_mlc_path->GetValue()), false);
@ -1038,14 +1057,7 @@ void GeneralSettings2::StoreConfig()
config.notification.friends = m_friends_data->GetValue();
// account
const auto active_account = m_active_account->GetSelection();
if (active_account == wxNOT_FOUND)
config.account.m_persistent_id = config.account.m_persistent_id.GetInitValue();
else
config.account.m_persistent_id = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId();
config.account.online_enabled = m_online_enabled->GetValue();
config.account.active_service = m_active_service->GetSelection();
config.account.m_persistent_id = GetSelectedAccountPersistentId();
// debug
config.crash_dump = (CrashDump)m_crash_dump->GetSelection();
@ -1371,14 +1383,13 @@ void GeneralSettings2::UpdateAccountInformation()
{
m_account_grid->SetSplitterPosition(100);
m_online_status->SetLabel(_("At least one issue has been found"));
const auto selection = m_active_account->GetSelection();
if(selection == wxNOT_FOUND)
{
m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage().Scale(16, 16));
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE);
ResetAccountInformation();
m_online_status->SetLabel(_("No account selected"));
return;
}
@ -1404,11 +1415,26 @@ void GeneralSettings2::UpdateAccountInformation()
index = 0;
country_property->SetChoiceSelection(index);
const bool online_valid = account.IsValidOnlineAccount() && ActiveSettings::HasRequiredOnlineFiles();
if (online_valid)
const bool online_fully_valid = account.IsValidOnlineAccount() && ActiveSettings::HasRequiredOnlineFiles();
if (ActiveSettings::HasRequiredOnlineFiles())
{
if(account.IsValidOnlineAccount())
m_online_status->SetLabel(_("Selected account is a valid online account"));
else
m_online_status->SetLabel(_("Selected account is not linked to a NNID or PNID"));
}
else
{
if(NCrypto::OTP_IsPresent() != NCrypto::SEEPROM_IsPresent())
m_online_status->SetLabel(_("OTP.bin or SEEPROM.bin is missing"));
else if(NCrypto::OTP_IsPresent() && NCrypto::SEEPROM_IsPresent())
m_online_status->SetLabel(_("OTP and SEEPROM present but no certificate files were found"));
else
m_online_status->SetLabel(_("Online play is not set up. Follow the guide below to get started"));
}
if(online_fully_valid)
{
m_online_status->SetLabel(_("Your account is a valid online account"));
m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_CHECK_YES).ConvertToImage().Scale(16, 16));
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() | wxBORDER_NONE);
}
@ -1417,7 +1443,28 @@ void GeneralSettings2::UpdateAccountInformation()
m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage().Scale(16, 16));
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE);
}
// enable/disable network service field depending on online requirements
m_active_service->Enable(online_fully_valid && !CafeSystem::IsTitleRunning());
if(online_fully_valid)
{
NetworkService service = GetConfig().GetAccountNetworkService(account.GetPersistentId());
m_active_service->SetSelection(static_cast<int>(service));
// set the config option here for the selected service
// this will guarantee that it's actually written to settings.xml
// allowing us to eventually get rid of the legacy option in the (far) future
GetConfig().SetAccountSelectedService(account.GetPersistentId(), service);
}
else
{
m_active_service->SetSelection(0); // force offline
}
wxString tmp = _("Network service");
tmp.append(" (");
tmp.append(wxString::FromUTF8(boost::nowide::narrow(account.GetMiiName())));
tmp.append(")");
m_active_service->SetLabel(tmp);
// refresh pane size
m_account_grid->InvalidateBestSize();
//m_account_grid->GetParent()->FitInside();
@ -1522,6 +1569,7 @@ void GeneralSettings2::ApplyConfig()
m_permanent_storage->SetValue(config.permanent_storage);
m_disable_screensaver->SetValue(config.disable_screensaver);
m_play_boot_sound->SetValue(config.play_boot_sound);
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
m_feral_gamemode->SetValue(config.feral_gamemode);
#endif
@ -1663,9 +1711,8 @@ void GeneralSettings2::ApplyConfig()
break;
}
}
m_online_enabled->SetValue(config.account.online_enabled);
m_active_service->SetSelection(config.account.active_service);
m_active_service->SetSelection((int)config.GetAccountNetworkService(ActiveSettings::GetPersistentId()));
UpdateAccountInformation();
// debug
@ -1673,20 +1720,6 @@ void GeneralSettings2::ApplyConfig()
m_gdb_port->SetValue(config.gdb_port.GetValue());
}
void GeneralSettings2::OnOnlineEnable(wxCommandEvent& event)
{
event.Skip();
if (!m_online_enabled->GetValue())
return;
// show warning if player enables online mode
const auto result = wxMessageBox(_("Please be aware that online mode lets you connect to OFFICIAL servers and therefore there is a risk of getting banned.\nOnly proceed if you are willing to risk losing online access with your Wii U and/or NNID."),
_("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this);
if (result == wxNO)
m_online_enabled->SetValue(false);
}
void GeneralSettings2::OnAudioAPISelected(wxCommandEvent& event)
{
IAudioAPI::AudioAPI api;
@ -1738,20 +1771,7 @@ void GeneralSettings2::UpdateAudioDevice()
if (m_game_launched && g_tvAudio)
channels = g_tvAudio->GetChannels();
else
{
switch (config.tv_channels)
{
case 0:
channels = 1;
break;
case 2:
channels = 6;
break;
default: // stereo
channels = 2;
break;
}
}
channels = CemuConfig::AudioChannelsToNChannels(config.tv_channels);
try
{
@ -1786,20 +1806,7 @@ void GeneralSettings2::UpdateAudioDevice()
if (m_game_launched && g_padAudio)
channels = g_padAudio->GetChannels();
else
{
switch (config.pad_channels)
{
case 0:
channels = 1;
break;
case 2:
channels = 6;
break;
default: // stereo
channels = 2;
break;
}
}
channels = CemuConfig::AudioChannelsToNChannels(config.pad_channels);
try
{
@ -1835,20 +1842,7 @@ void GeneralSettings2::UpdateAudioDevice()
if (m_game_launched && g_inputAudio)
channels = g_inputAudio->GetChannels();
else
{
switch (config.input_channels)
{
case 0:
channels = 1;
break;
case 2:
channels = 6;
break;
default: // stereo
channels = 2;
break;
}
}
channels = CemuConfig::AudioChannelsToNChannels(config.input_channels);
try
{
@ -1952,6 +1946,9 @@ void GeneralSettings2::OnActiveAccountChanged(wxCommandEvent& event)
void GeneralSettings2::OnAccountServiceChanged(wxCommandEvent& event)
{
auto& config = GetConfig();
uint32 peristentId = GetSelectedAccountPersistentId();
config.SetAccountSelectedService(peristentId, static_cast<NetworkService>(m_active_service->GetSelection()));
UpdateAccountInformation();
}
@ -2005,12 +2002,12 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event)
err << _("The following error(s) have been found:") << '\n';
if (validator.otp == OnlineValidator::FileState::Missing)
err << _("otp.bin missing in Cemu root directory") << '\n';
err << _("otp.bin missing in Cemu directory") << '\n';
else if(validator.otp == OnlineValidator::FileState::Corrupted)
err << _("otp.bin is invalid") << '\n';
if (validator.seeprom == OnlineValidator::FileState::Missing)
err << _("seeprom.bin missing in Cemu root directory") << '\n';
err << _("seeprom.bin missing in Cemu directory") << '\n';
else if(validator.seeprom == OnlineValidator::FileState::Corrupted)
err << _("seeprom.bin is invalid") << '\n';
@ -2045,9 +2042,10 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event)
wxString GeneralSettings2::GetOnlineAccountErrorMessage(OnlineAccountError error)
{
switch (error) {
switch (error)
{
case OnlineAccountError::kNoAccountId:
return _("AccountId missing (The account is not connected to a NNID)");
return _("AccountId missing (The account is not connected to a NNID/PNID)");
case OnlineAccountError::kNoPasswordCached:
return _("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)");
case OnlineAccountError::kPasswordCacheEmpty:

View File

@ -44,6 +44,7 @@ private:
wxCheckBox* m_auto_update, *m_save_screenshot;
wxCheckBox* m_permanent_storage;
wxCheckBox* m_disable_screensaver;
wxCheckBox* m_play_boot_sound;
#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE)
wxCheckBox* m_feral_gamemode;
#endif
@ -71,7 +72,6 @@ private:
wxButton* m_create_account, * m_delete_account;
wxChoice* m_active_account;
wxRadioBox* m_active_service;
wxCheckBox* m_online_enabled;
wxCollapsiblePane* m_account_information;
wxPropertyGrid* m_account_grid;
wxBitmapButton* m_validate_online;
@ -99,10 +99,11 @@ private:
void OnMLCPathSelect(wxCommandEvent& event);
void OnMLCPathChar(wxKeyEvent& event);
void OnShowOnlineValidator(wxCommandEvent& event);
void OnOnlineEnable(wxCommandEvent& event);
void OnAccountServiceChanged(wxCommandEvent& event);
static wxString GetOnlineAccountErrorMessage(OnlineAccountError error);
uint32 GetSelectedAccountPersistentId();
// updates cemu audio devices
void UpdateAudioDevice();
// refreshes audio device list for dropdown

View File

@ -948,38 +948,6 @@ void MainWindow::OnAccountSelect(wxCommandEvent& event)
g_config.Save();
}
//void MainWindow::OnConsoleRegion(wxCommandEvent& event)
//{
// switch (event.GetId())
// {
// case MAINFRAME_MENU_ID_OPTIONS_REGION_AUTO:
// GetConfig().console_region = ConsoleRegion::Auto;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_JPN:
// GetConfig().console_region = ConsoleRegion::JPN;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_USA:
// GetConfig().console_region = ConsoleRegion::USA;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_EUR:
// GetConfig().console_region = ConsoleRegion::EUR;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_CHN:
// GetConfig().console_region = ConsoleRegion::CHN;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_KOR:
// GetConfig().console_region = ConsoleRegion::KOR;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_TWN:
// GetConfig().console_region = ConsoleRegion::TWN;
// break;
// default:
// cemu_assert_debug(false);
// }
//
// g_config.Save();
//}
void MainWindow::OnConsoleLanguage(wxCommandEvent& event)
{
switch (event.GetId())

View File

@ -1,5 +1,7 @@
add_library(CemuUtil
boost/bluetooth.h
bootSound/BootSoundReader.cpp
bootSound/BootSoundReader.h
ChunkedHeap/ChunkedHeap.h
containers/flat_hash_map.hpp
containers/IntervalBucketContainer.h

View File

@ -0,0 +1,46 @@
#include "BootSoundReader.h"
#include "Cafe/CafeSystem.h"
BootSoundReader::BootSoundReader(FSCVirtualFile* bootsndFile, sint32 blockSize) : bootsndFile(bootsndFile), blockSize(blockSize)
{
// crash if this constructor is invoked with a blockSize that has a different number of samples per channel
cemu_assert(blockSize % (sizeof(sint16be) * 2) == 0);
fsc_setFileSeek(bootsndFile, 0);
fsc_readFile(bootsndFile, &muteBits, 4);
fsc_readFile(bootsndFile, &loopPoint, 4);
buffer.resize(blockSize / sizeof(sint16));
bufferBE.resize(blockSize / sizeof(sint16be));
// workaround: SM3DW has incorrect loop point
const auto titleId = CafeSystem::GetForegroundTitleId();
if(titleId == 0x0005000010145D00 || titleId == 0x0005000010145C00 || titleId == 0x0005000010106100)
loopPoint = 113074;
}
sint16* BootSoundReader::getSamples()
{
size_t totalRead = 0;
while(totalRead < blockSize)
{
auto read = fsc_readFile(bootsndFile, bufferBE.data(), blockSize - totalRead);
if (read == 0)
{
cemuLog_log(LogType::Force, "failed to read PCM samples from bootSound.btsnd");
return nullptr;
}
if (read % (sizeof(sint16be) * 2) != 0)
{
cemuLog_log(LogType::Force, "failed to play bootSound.btsnd: reading PCM data stopped at an odd number of samples (is the file corrupt?)");
return nullptr;
}
std::copy_n(bufferBE.begin(), read / sizeof(sint16be), buffer.begin() + (totalRead / sizeof(sint16)));
totalRead += read;
if (totalRead < blockSize)
fsc_setFileSeek(bootsndFile, 8 + loopPoint * 4);
}
return buffer.data();
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "Cafe/Filesystem/fsc.h"
class BootSoundReader
{
public:
BootSoundReader() = delete;
BootSoundReader(FSCVirtualFile* bootsndFile, sint32 blockSize);
sint16* getSamples();
private:
FSCVirtualFile* bootsndFile{};
sint32 blockSize{};
uint32be muteBits{};
uint32be loopPoint{};
std::vector<sint16> buffer{};
std::vector<sint16be> bufferBE{};
};

View File

@ -1,7 +1,7 @@
{
"name": "cemu",
"version-string": "1.0",
"builtin-baseline": "53bef8994c541b6561884a8395ea35715ece75db",
"builtin-baseline": "cbf4a6641528cee6f172328984576f51698de726",
"dependencies": [
"pugixml",
"zlib",