Compare commits
37 Commits
1019d8a0f3
...
4072842507
Author | SHA1 | Date |
---|---|---|
goeiecool9999 | 4072842507 | |
goeiecool9999 | c796527bb1 | |
goeiecool9999 | da3144a801 | |
splatoon1enjoyer | 13b90874f9 | |
Exzap | cf41c3b136 | |
Xphalnos | 97d8cf4ba3 | |
GaryOderNichts | b2a6cccc89 | |
GaryOderNichts | 10d553e1c9 | |
Exzap | 3f8722f0a6 | |
Exzap | 065fb7eb58 | |
goeiecool9999 | 09f32d0936 | |
goeiecool9999 | 925e853980 | |
goeiecool9999 | f633113864 | |
goeiecool9999 | f928eee5f3 | |
goeiecool9999 | f1d4c399a2 | |
goeiecool9999 | e0f5a9f98e | |
goeiecool9999 | a143556d89 | |
goeiecool9999 | a5aba28673 | |
goeiecool9999 | 2cd6dbd9b7 | |
goeiecool9999 | 2223860829 | |
goeiecool9999 | da9c3c84e8 | |
goeiecool9999 | 4468e7a7f2 | |
goeiecool9999 | adc988eb1b | |
goeiecool9999 | 91c5a01dbc | |
goeiecool9999 | 4ac7cb9f62 | |
goeiecool9999 | 9c2b8a0621 | |
goeiecool9999 | 49b28c16a8 | |
goeiecool9999 | c8b4e48120 | |
goeiecool9999 | 57ee2413e7 | |
goeiecool9999 | 5033318daa | |
goeiecool9999 | cab8a1f3c5 | |
goeiecool9999 | dc127e5e61 | |
goeiecool9999 | 363fe5901a | |
goeiecool9999 | 04ed1c4ed9 | |
goeiecool9999 | 312ecbccde | |
goeiecool9999 | 9354db6a16 | |
goeiecool9999 | a7a116a6f1 |
|
@ -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' }}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 53bef8994c541b6561884a8395ea35715ece75db
|
||||
Subproject commit cbf4a6641528cee6f172328984576f51698de726
|
|
@ -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 = {};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ public:
|
|||
static std::vector<DeviceDescriptionPtr> GetDevices();
|
||||
|
||||
private:
|
||||
uint32 GetQueuedBuffers() const;
|
||||
|
||||
struct XAudioDeleter
|
||||
{
|
||||
void operator()(IXAudio2* ptr) const;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -5,9 +5,11 @@
|
|||
|
||||
enum class NetworkService
|
||||
{
|
||||
Offline,
|
||||
Nintendo,
|
||||
Pretendo,
|
||||
Custom
|
||||
Custom,
|
||||
COUNT = Custom
|
||||
};
|
||||
|
||||
struct NetworkConfig
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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{};
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "cemu",
|
||||
"version-string": "1.0",
|
||||
"builtin-baseline": "53bef8994c541b6561884a8395ea35715ece75db",
|
||||
"builtin-baseline": "cbf4a6641528cee6f172328984576f51698de726",
|
||||
"dependencies": [
|
||||
"pugixml",
|
||||
"zlib",
|
||||
|
|
Loading…
Reference in New Issue