From 2284367591e82b836039a0f7803a03252f6291bb Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sat, 21 Oct 2017 22:46:46 +0100 Subject: [PATCH 01/19] travis: Add build with frozen versions for dependencies --- .travis.yml | 6 ++++ .travis/linux-frozen/build.sh | 4 +++ .travis/linux-frozen/docker.sh | 27 ++++++++++++++++++ .travis/linux-frozen/install_package.py | 38 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100755 .travis/linux-frozen/build.sh create mode 100755 .travis/linux-frozen/docker.sh create mode 100755 .travis/linux-frozen/install_package.py diff --git a/.travis.yml b/.travis.yml index 41a78d345..e87700b5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,12 @@ matrix: install: "./.travis/macos/deps.sh" script: "./.travis/macos/build.sh" after_success: "./.travis/macos/upload.sh" + - os: linux + env: NAME="linux build (frozen versions of dependencies)" + sudo: required + dist: trusty + services: docker + script: "./.travis/linux-frozen/build.sh" deploy: provider: releases diff --git a/.travis/linux-frozen/build.sh b/.travis/linux-frozen/build.sh new file mode 100755 index 000000000..25c655516 --- /dev/null +++ b/.travis/linux-frozen/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash -ex + +docker pull ubuntu:16.04 +docker run -v $(pwd):/citra ubuntu:16.04 /bin/bash -ex /citra/.travis/linux-frozen/docker.sh diff --git a/.travis/linux-frozen/docker.sh b/.travis/linux-frozen/docker.sh new file mode 100755 index 000000000..edca9a7a1 --- /dev/null +++ b/.travis/linux-frozen/docker.sh @@ -0,0 +1,27 @@ +#!/bin/bash -ex + +cd /citra + +apt-get update +apt-get install -y build-essential wget git python-launchpadlib libssl-dev + +# Install specific versions of packages with their dependencies +# The apt repositories remove older versions regularly, so we can't use +# apt-get and have to pull the packages directly from the archives. +/citra/.travis/linux-frozen/install_package.py \ + libsdl2-dev 2.0.4+dfsg1-2ubuntu2 xenial \ + qtbase5-dev 5.2.1+dfsg-1ubuntu14.3 trusty \ + libqt5opengl5-dev 5.2.1+dfsg-1ubuntu14.3 trusty \ + libcurl4-openssl-dev 7.47.0-1ubuntu2.3 xenial \ + libicu52 52.1-3ubuntu0.6 trusty + +# Get a recent version of CMake +wget https://cmake.org/files/v3.9/cmake-3.9.0-Linux-x86_64.sh +echo y | sh cmake-3.9.0-Linux-x86_64.sh --prefix=cmake +export PATH=/citra/cmake/cmake-3.9.0-Linux-x86_64/bin:$PATH + +mkdir build && cd build +cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_BUILD_TYPE=Release +make -j4 + +ctest -VV -C Release diff --git a/.travis/linux-frozen/install_package.py b/.travis/linux-frozen/install_package.py new file mode 100755 index 000000000..bdf333716 --- /dev/null +++ b/.travis/linux-frozen/install_package.py @@ -0,0 +1,38 @@ +#!/usr/bin/python + +import sys, re, subprocess +from launchpadlib.launchpad import Launchpad + +cachedir = '/.launchpadlib/cache/' +launchpad = Launchpad.login_anonymously('grab build info', 'production', cachedir, version='devel') + +processed_packages = [] +deb_file_list = [] + +def get_url(pkg, distro): + build_link = launchpad.archives.getByReference(reference='ubuntu').getPublishedBinaries(binary_name=pkg[0], distro_arch_series='https://api.launchpad.net/devel/ubuntu/'+distro+'/amd64', version=pkg[1], exact_match=True, order_by_date=True).entries[0]['build_link'] + deb_name = pkg[0] + '_' + pkg[1] + '_amd64.deb' + deb_link = build_link + '/+files/' + deb_name + return [deb_link, deb_name] + +def list_dependencies(deb_file): + t=subprocess.check_output(['bash', '-c', 'dpkg -I ' + deb_file + ' | grep -oP "^ Depends\: \K.*$"']) + deps=[i.strip() for i in t.split(',')] + equals_re = re.compile(r'^(.*) \(= (.*)\)$') + return [equals_re.sub(r'\1=\2', i).split('=') for i in filter(equals_re.match, deps)] + +def get_package(pkg, distro): + if pkg in processed_packages: + return + print 'Getting ' + pkg[0] + '...' + url = get_url(pkg, distro) + subprocess.check_call(['wget', '--quiet', url[0], '-O', url[1]]) + for dep in list_dependencies(url[1]): + get_package(dep, distro) + processed_packages.append(pkg) + deb_file_list.append('./' + url[1]) + +for i in xrange(1, len(sys.argv), 3): + get_package([sys.argv[i], sys.argv[i + 1]], sys.argv[i + 2]) + +subprocess.check_call(['apt-get', 'install', '-y'] + deb_file_list) From 95df4e674a2300b6a29d0e0d765211cd0e5c7294 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 16:22:14 -0500 Subject: [PATCH 02/19] Services/AC: Converted the ac:i and ac:u services to the new service framework. --- src/core/hle/service/ac/ac.cpp | 212 +++++++++++++------------- src/core/hle/service/ac/ac.h | 245 +++++++++++++++++-------------- src/core/hle/service/ac/ac_i.cpp | 51 ++++--- src/core/hle/service/ac/ac_i.h | 11 +- src/core/hle/service/ac/ac_u.cpp | 51 ++++--- src/core/hle/service/ac/ac_u.h | 11 +- src/core/hle/service/service.cpp | 3 +- 7 files changed, 300 insertions(+), 284 deletions(-) diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp index e3dd23949..bf9cad3d4 100644 --- a/src/core/hle/service/ac/ac.cpp +++ b/src/core/hle/service/ac/ac.cpp @@ -2,11 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include - #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/result.h" @@ -17,169 +16,170 @@ namespace Service { namespace AC { +void Module::Interface::CreateDefaultConfig(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1, 0, 0); -struct ACConfig { - std::array data; -}; + std::size_t desc_size; + VAddr ac_config_addr = rp.PeekStaticBuffer(0, &desc_size); -static ACConfig default_config{}; + ASSERT_MSG(desc_size >= sizeof(Module::ACConfig), + "Output buffer size can't fit ACConfig structure"); -static bool ac_connected = false; + Memory::WriteBlock(ac_config_addr, &ac->default_config, sizeof(ACConfig)); -static Kernel::SharedPtr close_event; -static Kernel::SharedPtr connect_event; -static Kernel::SharedPtr disconnect_event; - -void CreateDefaultConfig(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u32 ac_config_addr = cmd_buff[65]; - - ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), - "Output buffer size not equal ACConfig size"); - - Memory::WriteBlock(ac_config_addr, &default_config, sizeof(ACConfig)); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushStaticBuffer(ac_config_addr, sizeof(ACConfig), 0); LOG_WARNING(Service_AC, "(STUBBED) called"); } -void ConnectAsync(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::ConnectAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x4, 0, 6); - connect_event = Kernel::g_handle_table.Get(cmd_buff[4]); - if (connect_event) { - connect_event->name = "AC:connect_event"; - connect_event->Signal(); - ac_connected = true; - } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + rp.Skip(2, false); // ProcessId descriptor + ac->connect_event = rp.PopObject(); - LOG_WARNING(Service_AC, "(STUBBED) called"); -} - -void GetConnectResult(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - LOG_WARNING(Service_AC, "(STUBBED) called"); -} - -void CloseAsync(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - if (ac_connected && disconnect_event) { - disconnect_event->Signal(); + if (ac->connect_event) { + ac->connect_event->name = "AC:connect_event"; + ac->connect_event->Signal(); + ac->ac_connected = true; } - close_event = Kernel::g_handle_table.Get(cmd_buff[4]); - if (close_event) { - close_event->name = "AC:close_event"; - close_event->Signal(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +void Module::Interface::GetConnectResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x5, 0, 2); + rp.Skip(2, false); // ProcessId descriptor + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::Interface::CloseAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x8, 0, 4); + rp.Skip(2, false); // ProcessId descriptor + + ac->close_event = rp.PopObject(); + + if (ac->ac_connected && ac->disconnect_event) { + ac->disconnect_event->Signal(); } - ac_connected = false; + if (ac->close_event) { + ac->close_event->name = "AC:close_event"; + ac->close_event->Signal(); + } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_AC, "(STUBBED) called"); + ac->ac_connected = false; + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); } -void GetCloseResult(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::GetCloseResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x9, 0, 2); + rp.Skip(2, false); // ProcessId descriptor - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_AC, "(STUBBED) called"); } -void GetWifiStatus(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::GetWifiStatus(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xD, 0, 0); // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; // Connection type set to none + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); // Connection type set to none LOG_WARNING(Service_AC, "(STUBBED) called"); } -void GetInfraPriority(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::GetInfraPriority(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x27, 0, 2); + VAddr ac_config = rp.PopStaticBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; // Infra Priority, default 0 + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); // Infra Priority, default 0 LOG_WARNING(Service_AC, "(STUBBED) called"); } -void SetRequestEulaVersion(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::SetRequestEulaVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2D, 2, 2); - u32 major = cmd_buff[1] & 0xFF; - u32 minor = cmd_buff[2] & 0xFF; + u32 major = rp.Pop(); + u32 minor = rp.Pop(); - ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2), - "Input buffer size not equal ACConfig size"); - ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), - "Output buffer size not equal ACConfig size"); + VAddr ac_config = rp.PopStaticBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; // Infra Priority + // TODO(Subv): Copy over the input ACConfig to the stored ACConfig. + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushStaticBuffer(ac_config, sizeof(ACConfig), 0); LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor); } -void RegisterDisconnectEvent(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x30, 0, 4); + rp.Skip(2, false); // ProcessId descriptor - disconnect_event = Kernel::g_handle_table.Get(cmd_buff[4]); - if (disconnect_event) { - disconnect_event->name = "AC:disconnect_event"; + ac->disconnect_event = rp.PopObject(); + if (ac->disconnect_event) { + ac->disconnect_event->name = "AC:disconnect_event"; } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_AC, "(STUBBED) called"); } -void IsConnected(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x3E, 1, 2); + u32 unk = rp.Pop(); + u32 unk_descriptor = rp.Pop(); + u32 unk_param = rp.Pop(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = ac_connected; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(ac->ac_connected); - LOG_WARNING(Service_AC, "(STUBBED) called"); + LOG_WARNING(Service_AC, "(STUBBED) called unk=%08X descriptor=%08X param=%08X", unk, + unk_descriptor, unk_param); } -void SetClientVersion(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::SetClientVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x40, 1, 2); - const u32 version = cmd_buff[1]; - self->SetVersion(version); + u32 version = rp.Pop(); + rp.Skip(2, false); // ProcessId descriptor LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x%08X", version); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); } -void Init() { - AddService(new AC_I); - AddService(new AC_U); +Module::Interface::Interface(std::shared_ptr ac, const char* name, u32 max_session) + : ac(std::move(ac)), ServiceFramework(name, max_session) {} - ac_connected = false; - - close_event = nullptr; - connect_event = nullptr; - disconnect_event = nullptr; -} - -void Shutdown() { - ac_connected = false; - - close_event = nullptr; - connect_event = nullptr; - disconnect_event = nullptr; +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto ac = std::make_shared(); + std::make_shared(ac)->InstallAsService(service_manager); + std::make_shared(ac)->InstallAsService(service_manager); } } // namespace AC diff --git a/src/core/hle/service/ac/ac.h b/src/core/hle/service/ac/ac.h index 6185faf9b..1990a67ae 100644 --- a/src/core/hle/service/ac/ac.h +++ b/src/core/hle/service/ac/ac.h @@ -4,131 +4,156 @@ #pragma once +#include +#include +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class Event; +} + namespace Service { - -class Interface; - namespace AC { +class Module final { +public: + class Interface : public ServiceFramework { + public: + Interface(std::shared_ptr ac, const char* name, u32 max_session); -/** - * AC::CreateDefaultConfig service function - * Inputs: - * 64 : ACConfig size << 14 | 2 - * 65 : pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void CreateDefaultConfig(Interface* self); + /** + * AC::CreateDefaultConfig service function + * Inputs: + * 64 : ACConfig size << 14 | 2 + * 65 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void CreateDefaultConfig(Kernel::HLERequestContext& ctx); -/** - * AC::ConnectAsync service function - * Inputs: - * 1 : ProcessId Header - * 3 : Copy Handle Header - * 4 : Connection Event handle - * 5 : ACConfig size << 14 | 2 - * 6 : pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void ConnectAsync(Interface* self); + /** + * AC::ConnectAsync service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Connection Event handle + * 5 : ACConfig size << 14 | 2 + * 6 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void ConnectAsync(Kernel::HLERequestContext& ctx); -/** - * AC::GetConnectResult service function - * Inputs: - * 1 : ProcessId Header - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void GetConnectResult(Interface* self); + /** + * AC::GetConnectResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetConnectResult(Kernel::HLERequestContext& ctx); -/** - * AC::CloseAsync service function - * Inputs: - * 1 : ProcessId Header - * 3 : Copy Handle Header - * 4 : Event handle, should be signaled when AC connection is closed - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void CloseAsync(Interface* self); + /** + * AC::CloseAsync service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void CloseAsync(Kernel::HLERequestContext& ctx); -/** - * AC::GetCloseResult service function - * Inputs: - * 1 : ProcessId Header - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void GetCloseResult(Interface* self); + /** + * AC::GetCloseResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetCloseResult(Kernel::HLERequestContext& ctx); -/** - * AC::GetWifiStatus service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. - */ -void GetWifiStatus(Interface* self); + /** + * AC::GetWifiStatus service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. + */ + void GetWifiStatus(Kernel::HLERequestContext& ctx); -/** - * AC::GetInfraPriority service function - * Inputs: - * 1 : ACConfig size << 14 | 2 - * 2 : pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Infra Priority - */ -void GetInfraPriority(Interface* self); + /** + * AC::GetInfraPriority service function + * Inputs: + * 1 : ACConfig size << 14 | 2 + * 2 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ + void GetInfraPriority(Kernel::HLERequestContext& ctx); -/** - * AC::SetRequestEulaVersion service function - * Inputs: - * 1 : Eula Version major - * 2 : Eula Version minor - * 3 : ACConfig size << 14 | 2 - * 4 : Input pointer to ACConfig struct - * 64 : ACConfig size << 14 | 2 - * 65 : Output pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Infra Priority - */ -void SetRequestEulaVersion(Interface* self); + /** + * AC::SetRequestEulaVersion service function + * Inputs: + * 1 : Eula Version major + * 2 : Eula Version minor + * 3 : ACConfig size << 14 | 2 + * 4 : Input pointer to ACConfig struct + * 64 : ACConfig size << 14 | 2 + * 65 : Output pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ + void SetRequestEulaVersion(Kernel::HLERequestContext& ctx); -/** - * AC::RegisterDisconnectEvent service function - * Inputs: - * 1 : ProcessId Header - * 3 : Copy Handle Header - * 4 : Event handle, should be signaled when AC connection is closed - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void RegisterDisconnectEvent(Interface* self); + /** + * AC::RegisterDisconnectEvent service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx); -/** - * AC::IsConnected service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : bool, is connected - */ -void IsConnected(Interface* self); + /** + * AC::IsConnected service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : bool, is connected + */ + void IsConnected(Kernel::HLERequestContext& ctx); -/** - * AC::SetClientVersion service function - * Inputs: - * 1 : Used SDK Version - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void SetClientVersion(Interface* self); + /** + * AC::SetClientVersion service function + * Inputs: + * 1 : Used SDK Version + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void SetClientVersion(Kernel::HLERequestContext& ctx); -/// Initialize AC service -void Init(); + protected: + std::shared_ptr ac; + }; -/// Shutdown AC service -void Shutdown(); +protected: + struct ACConfig { + std::array data; + }; + + ACConfig default_config{}; + + bool ac_connected = false; + + Kernel::SharedPtr close_event; + Kernel::SharedPtr connect_event; + Kernel::SharedPtr disconnect_event; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); } // namespace AC } // namespace Service diff --git a/src/core/hle/service/ac/ac_i.cpp b/src/core/hle/service/ac/ac_i.cpp index b22fe3698..c715bcdd0 100644 --- a/src/core/hle/service/ac/ac_i.cpp +++ b/src/core/hle/service/ac/ac_i.cpp @@ -2,37 +2,36 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac_i.h" namespace Service { namespace AC { -const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, - {0x00040006, ConnectAsync, "ConnectAsync"}, - {0x00050002, GetConnectResult, "GetConnectResult"}, - {0x00070002, nullptr, "CancelConnectAsync"}, - {0x00080004, CloseAsync, "CloseAsync"}, - {0x00090002, GetCloseResult, "GetCloseResult"}, - {0x000A0000, nullptr, "GetLastErrorCode"}, - {0x000C0000, nullptr, "GetStatus"}, - {0x000D0000, GetWifiStatus, "GetWifiStatus"}, - {0x000E0042, nullptr, "GetCurrentAPInfo"}, - {0x00100042, nullptr, "GetCurrentNZoneInfo"}, - {0x00110042, nullptr, "GetNZoneApNumService"}, - {0x001D0042, nullptr, "ScanAPs"}, - {0x00240042, nullptr, "AddDenyApType"}, - {0x00270002, GetInfraPriority, "GetInfraPriority"}, - {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, - {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, - {0x003C0042, nullptr, "GetAPSSIDList"}, - {0x003E0042, IsConnected, "IsConnected"}, - {0x00400042, SetClientVersion, "SetClientVersion"}, -}; - -AC_I::AC_I() { - Register(FunctionTable); +// TODO(Subv): Find out the correct number of concurrent sessions allowed +AC_I::AC_I(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:i", 1) { + static const FunctionInfo functions[] = { + {0x00010000, &AC_I::CreateDefaultConfig, "CreateDefaultConfig"}, + {0x00040006, &AC_I::ConnectAsync, "ConnectAsync"}, + {0x00050002, &AC_I::GetConnectResult, "GetConnectResult"}, + {0x00070002, nullptr, "CancelConnectAsync"}, + {0x00080004, &AC_I::CloseAsync, "CloseAsync"}, + {0x00090002, &AC_I::GetCloseResult, "GetCloseResult"}, + {0x000A0000, nullptr, "GetLastErrorCode"}, + {0x000C0000, nullptr, "GetStatus"}, + {0x000D0000, &AC_I::GetWifiStatus, "GetWifiStatus"}, + {0x000E0042, nullptr, "GetCurrentAPInfo"}, + {0x00100042, nullptr, "GetCurrentNZoneInfo"}, + {0x00110042, nullptr, "GetNZoneApNumService"}, + {0x001D0042, nullptr, "ScanAPs"}, + {0x00240042, nullptr, "AddDenyApType"}, + {0x00270002, &AC_I::GetInfraPriority, "GetInfraPriority"}, + {0x002D0082, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"}, + {0x00300004, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, + {0x003C0042, nullptr, "GetAPSSIDList"}, + {0x003E0042, &AC_I::IsConnected, "IsConnected"}, + {0x00400042, &AC_I::SetClientVersion, "SetClientVersion"}, + }; + RegisterHandlers(functions); } } // namespace AC diff --git a/src/core/hle/service/ac/ac_i.h b/src/core/hle/service/ac/ac_i.h index 465bba59c..c6f9d6d49 100644 --- a/src/core/hle/service/ac/ac_i.h +++ b/src/core/hle/service/ac/ac_i.h @@ -4,18 +4,15 @@ #pragma once -#include "core/hle/service/service.h" +#include +#include "core/hle/service/ac/ac.h" namespace Service { namespace AC { -class AC_I final : public Interface { +class AC_I final : public Module::Interface { public: - AC_I(); - - std::string GetPortName() const override { - return "ac:i"; - } + explicit AC_I(std::shared_ptr ac); }; } // namespace AC diff --git a/src/core/hle/service/ac/ac_u.cpp b/src/core/hle/service/ac/ac_u.cpp index 346671b4a..988f27b9d 100644 --- a/src/core/hle/service/ac/ac_u.cpp +++ b/src/core/hle/service/ac/ac_u.cpp @@ -2,37 +2,36 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac_u.h" namespace Service { namespace AC { -const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, - {0x00040006, ConnectAsync, "ConnectAsync"}, - {0x00050002, GetConnectResult, "GetConnectResult"}, - {0x00070002, nullptr, "CancelConnectAsync"}, - {0x00080004, CloseAsync, "CloseAsync"}, - {0x00090002, GetCloseResult, "GetCloseResult"}, - {0x000A0000, nullptr, "GetLastErrorCode"}, - {0x000C0000, nullptr, "GetStatus"}, - {0x000D0000, GetWifiStatus, "GetWifiStatus"}, - {0x000E0042, nullptr, "GetCurrentAPInfo"}, - {0x00100042, nullptr, "GetCurrentNZoneInfo"}, - {0x00110042, nullptr, "GetNZoneApNumService"}, - {0x001D0042, nullptr, "ScanAPs"}, - {0x00240042, nullptr, "AddDenyApType"}, - {0x00270002, GetInfraPriority, "GetInfraPriority"}, - {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, - {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, - {0x003C0042, nullptr, "GetAPSSIDList"}, - {0x003E0042, IsConnected, "IsConnected"}, - {0x00400042, SetClientVersion, "SetClientVersion"}, -}; - -AC_U::AC_U() { - Register(FunctionTable); +// TODO(Subv): Find out the correct number of concurrent sessions allowed +AC_U::AC_U(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:u", 1) { + static const FunctionInfo functions[] = { + {0x00010000, &AC_U::CreateDefaultConfig, "CreateDefaultConfig"}, + {0x00040006, &AC_U::ConnectAsync, "ConnectAsync"}, + {0x00050002, &AC_U::GetConnectResult, "GetConnectResult"}, + {0x00070002, nullptr, "CancelConnectAsync"}, + {0x00080004, &AC_U::CloseAsync, "CloseAsync"}, + {0x00090002, &AC_U::GetCloseResult, "GetCloseResult"}, + {0x000A0000, nullptr, "GetLastErrorCode"}, + {0x000C0000, nullptr, "GetStatus"}, + {0x000D0000, &AC_U::GetWifiStatus, "GetWifiStatus"}, + {0x000E0042, nullptr, "GetCurrentAPInfo"}, + {0x00100042, nullptr, "GetCurrentNZoneInfo"}, + {0x00110042, nullptr, "GetNZoneApNumService"}, + {0x001D0042, nullptr, "ScanAPs"}, + {0x00240042, nullptr, "AddDenyApType"}, + {0x00270002, &AC_U::GetInfraPriority, "GetInfraPriority"}, + {0x002D0082, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"}, + {0x00300004, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, + {0x003C0042, nullptr, "GetAPSSIDList"}, + {0x003E0042, &AC_U::IsConnected, "IsConnected"}, + {0x00400042, &AC_U::SetClientVersion, "SetClientVersion"}, + }; + RegisterHandlers(functions); } } // namespace AC diff --git a/src/core/hle/service/ac/ac_u.h b/src/core/hle/service/ac/ac_u.h index f9d21e112..6643f3ce5 100644 --- a/src/core/hle/service/ac/ac_u.h +++ b/src/core/hle/service/ac/ac_u.h @@ -4,18 +4,15 @@ #pragma once -#include "core/hle/service/service.h" +#include +#include "core/hle/service/ac/ac.h" namespace Service { namespace AC { -class AC_U final : public Interface { +class AC_U final : public Module::Interface { public: - AC_U(); - - std::string GetPortName() const override { - return "ac:u"; - } + explicit AC_U(std::shared_ptr ac); }; } // namespace AC diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index f267aad74..8fc801b77 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -216,11 +216,11 @@ void Init() { SM::ServiceManager::InstallInterfaces(SM::g_service_manager); NS::InstallInterfaces(*SM::g_service_manager); + AC::InstallInterfaces(*SM::g_service_manager); AddNamedPort(new ERR::ERR_F); FS::ArchiveInit(); - AC::Init(); ACT::Init(); AM::Init(); APT::Init(); @@ -273,7 +273,6 @@ void Shutdown() { BOSS::Shutdown(); APT::Shutdown(); AM::Shutdown(); - AC::Shutdown(); FS::ArchiveShutdown(); SM::g_service_manager = nullptr; From 47c0c87c4735fafe943eaa0281d8229ca3c315da Mon Sep 17 00:00:00 2001 From: wwylele Date: Wed, 1 Nov 2017 11:47:39 +0200 Subject: [PATCH 03/19] video_core: clean format warnings --- src/video_core/regs_framebuffer.h | 6 +++--- .../renderer_opengl/gl_rasterizer.cpp | 3 ++- .../renderer_opengl/gl_shader_gen.cpp | 14 ++++++++------ src/video_core/renderer_opengl/pica_to_gl.h | 10 +++++----- .../shader/shader_jit_x64_compiler.cpp | 2 +- src/video_core/swrasterizer/framebuffer.cpp | 17 ++++++++++------- src/video_core/swrasterizer/lighting.cpp | 3 ++- src/video_core/swrasterizer/rasterizer.cpp | 2 +- 8 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/video_core/regs_framebuffer.h b/src/video_core/regs_framebuffer.h index 7b565f911..8020faee8 100644 --- a/src/video_core/regs_framebuffer.h +++ b/src/video_core/regs_framebuffer.h @@ -193,7 +193,7 @@ struct FramebufferRegs { case ColorFormat::RGBA4: return 2; default: - LOG_CRITICAL(HW_GPU, "Unknown color format %u", format); + LOG_CRITICAL(HW_GPU, "Unknown color format %u", static_cast(format)); UNIMPLEMENTED(); } } @@ -258,7 +258,7 @@ struct FramebufferRegs { return 4; } - ASSERT_MSG(false, "Unknown depth format %u", format); + ASSERT_MSG(false, "Unknown depth format %u", static_cast(format)); } // Returns the number of bits per depth component of the specified depth format @@ -271,7 +271,7 @@ struct FramebufferRegs { return 24; } - ASSERT_MSG(false, "Unknown depth format %u", format); + ASSERT_MSG(false, "Unknown depth format %u", static_cast(format)); } INSERT_PADDING_WORDS(0x20); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7e09e4712..16f0726ad 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1338,7 +1338,8 @@ void RasterizerOpenGL::SyncCullMode() { break; default: - LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", regs.rasterizer.cull_mode.Value()); + LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %u", + static_cast(regs.rasterizer.cull_mode.Value())); UNIMPLEMENTED(); break; } diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 9fe183944..531247d2a 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -265,7 +265,7 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config, break; default: out += "vec4(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); + LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", static_cast(source)); break; } } @@ -323,7 +323,7 @@ static void AppendColorModifier(std::string& out, const PicaShaderConfig& config break; default: out += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); + LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", static_cast(modifier)); break; } } @@ -372,7 +372,7 @@ static void AppendAlphaModifier(std::string& out, const PicaShaderConfig& config break; default: out += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); + LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", static_cast(modifier)); break; } } @@ -416,7 +416,8 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper break; default: out += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); + LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", + static_cast(operation)); break; } out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0 @@ -456,7 +457,8 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper break; default: out += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation); + LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", + static_cast(operation)); break; } out += ", 0.0, 1.0)"; @@ -486,7 +488,7 @@ static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareF default: out += "false"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); + LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", static_cast(func)); break; } } diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index c7fa1f873..640c6ea51 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -103,7 +103,7 @@ inline GLenum BlendEquation(Pica::FramebufferRegs::BlendEquation equation) { // Range check table for input if (static_cast(equation) >= ARRAY_SIZE(blend_equation_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %d", equation); + LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %u", static_cast(equation)); UNREACHABLE(); return GL_FUNC_ADD; @@ -133,7 +133,7 @@ inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) { // Range check table for input if (static_cast(factor) >= ARRAY_SIZE(blend_func_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor); + LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %u", static_cast(factor)); UNREACHABLE(); return GL_ONE; @@ -164,7 +164,7 @@ inline GLenum LogicOp(Pica::FramebufferRegs::LogicOp op) { // Range check table for input if (static_cast(op) >= ARRAY_SIZE(logic_op_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op); + LOG_CRITICAL(Render_OpenGL, "Unknown logic op %u", static_cast(op)); UNREACHABLE(); return GL_COPY; @@ -187,7 +187,7 @@ inline GLenum CompareFunc(Pica::FramebufferRegs::CompareFunc func) { // Range check table for input if (static_cast(func) >= ARRAY_SIZE(compare_func_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func); + LOG_CRITICAL(Render_OpenGL, "Unknown compare function %u", static_cast(func)); UNREACHABLE(); return GL_ALWAYS; @@ -210,7 +210,7 @@ inline GLenum StencilOp(Pica::FramebufferRegs::StencilAction action) { // Range check table for input if (static_cast(action) >= ARRAY_SIZE(stencil_op_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action); + LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %u", static_cast(action)); UNREACHABLE(); return GL_KEEP; diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp index 1b31623bd..5a856dcaa 100644 --- a/src/video_core/shader/shader_jit_x64_compiler.cpp +++ b/src/video_core/shader/shader_jit_x64_compiler.cpp @@ -853,7 +853,7 @@ void JitShader::Compile_NextInstr() { } else { // Unhandled instruction LOG_CRITICAL(HW_GPU, "Unhandled instruction: 0x%02x (0x%08x)", - instr.opcode.Value().EffectiveOpCode(), instr.hex); + static_cast(instr.opcode.Value().EffectiveOpCode()), instr.hex); } } diff --git a/src/video_core/swrasterizer/framebuffer.cpp b/src/video_core/swrasterizer/framebuffer.cpp index f34eab6cf..fb41ce4f5 100644 --- a/src/video_core/swrasterizer/framebuffer.cpp +++ b/src/video_core/swrasterizer/framebuffer.cpp @@ -58,7 +58,7 @@ void DrawPixel(int x, int y, const Math::Vec4& color) { default: LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", - framebuffer.color_format.Value()); + static_cast(framebuffer.color_format.Value())); UNIMPLEMENTED(); } } @@ -94,7 +94,7 @@ const Math::Vec4 GetPixel(int x, int y) { default: LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", - framebuffer.color_format.Value()); + static_cast(framebuffer.color_format.Value())); UNIMPLEMENTED(); } @@ -123,7 +123,8 @@ u32 GetDepth(int x, int y) { case FramebufferRegs::DepthFormat::D24S8: return Color::DecodeD24S8(src_pixel).x; default: - LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); + LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", + static_cast(framebuffer.depth_format.Value())); UNIMPLEMENTED(); return 0; } @@ -151,7 +152,7 @@ u8 GetStencil(int x, int y) { LOG_WARNING( HW_GPU, "GetStencil called for function which doesn't have a stencil component (format %u)", - framebuffer.depth_format); + static_cast(framebuffer.depth_format.Value())); return 0; } } @@ -184,7 +185,8 @@ void SetDepth(int x, int y, u32 value) { break; default: - LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); + LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", + static_cast(framebuffer.depth_format.Value())); UNIMPLEMENTED(); break; } @@ -215,7 +217,8 @@ void SetStencil(int x, int y, u8 value) { break; default: - LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); + LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", + static_cast(framebuffer.depth_format.Value())); UNIMPLEMENTED(); break; } @@ -294,7 +297,7 @@ Math::Vec4 EvaluateBlendEquation(const Math::Vec4& src, const Math::Vec4 break; default: - LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation); + LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation 0x%x", static_cast(equation)); UNIMPLEMENTED(); } diff --git a/src/video_core/swrasterizer/lighting.cpp b/src/video_core/swrasterizer/lighting.cpp index 5fa748611..04f81ef40 100644 --- a/src/video_core/swrasterizer/lighting.cpp +++ b/src/video_core/swrasterizer/lighting.cpp @@ -43,7 +43,8 @@ std::tuple, Math::Vec4> ComputeFragmentsColors( surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f); surface_tangent = perturbation; } else { - LOG_ERROR(HW_GPU, "Unknown bump mode %u", lighting.config0.bump_mode.Value()); + LOG_ERROR(HW_GPU, "Unknown bump mode %u", + static_cast(lighting.config0.bump_mode.Value())); } } else { surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f); diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp index 586587eb8..533ee6f01 100644 --- a/src/video_core/swrasterizer/rasterizer.cpp +++ b/src/video_core/swrasterizer/rasterizer.cpp @@ -801,7 +801,7 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve return std::min(combiner_output.a(), static_cast(255 - dest.a())); default: - LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor); + LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", static_cast(factor)); UNIMPLEMENTED(); break; } From 499508389c7701f11ab07dd7cdc19b2c7cbba53f Mon Sep 17 00:00:00 2001 From: wwylele Date: Wed, 1 Nov 2017 12:19:51 +0200 Subject: [PATCH 04/19] core: clear format warnings --- src/core/core.cpp | 5 ++-- src/core/gdbstub/gdbstub.cpp | 10 ++++---- src/core/hle/applets/applet.cpp | 4 ++-- src/core/hle/kernel/address_arbiter.cpp | 2 +- src/core/hle/service/apt/apt.cpp | 24 ++++++++++--------- src/core/hle/service/dsp_dsp.cpp | 10 ++++---- src/core/hle/service/fs/archive.cpp | 13 ++++++----- src/core/hle/service/fs/fs_user.cpp | 5 ++-- src/core/hle/service/ndm/ndm.cpp | 31 ++++++++++++++----------- src/core/hle/service/sm/srv.cpp | 5 ++-- src/core/hle/service/y2r_u.cpp | 25 +++++++++++--------- src/core/hw/aes/ccm.cpp | 4 ++-- src/core/hw/gpu.cpp | 4 ++-- 13 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 0c7a72987..41a972134 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -96,14 +96,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file ResultStatus init_result{Init(emu_window, system_mode.first.get())}; if (init_result != ResultStatus::Success) { - LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result); + LOG_CRITICAL(Core, "Failed to initialize system (Error %u)!", + static_cast(init_result)); System::Shutdown(); return init_result; } const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)}; if (Loader::ResultStatus::Success != load_result) { - LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); + LOG_CRITICAL(Core, "Failed to load ROM (Error %u)!", static_cast(load_result)); System::Shutdown(); switch (load_result) { diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index d6be16ef6..68704e72e 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -302,7 +302,7 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) { auto bp = p.find(addr); if (bp != p.end()) { LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", - bp->second.len, bp->second.addr, type); + bp->second.len, bp->second.addr, static_cast(type)); p.erase(addr); } } @@ -348,8 +348,8 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) { if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { LOG_DEBUG(Debug_GDBStub, - "Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type, - addr, bp->second.addr, bp->second.addr + len, len); + "Found breakpoint type %d @ %08x, range: %08x - %08x (%u bytes)\n", + static_cast(type), addr, bp->second.addr, bp->second.addr + len, len); return true; } } @@ -738,8 +738,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) { breakpoint.len = len; p.insert({addr, breakpoint}); - LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len, - breakpoint.addr); + LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", + static_cast(type), breakpoint.len, breakpoint.addr); return true; } diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 9c43ed2fd..879dfff0d 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -62,7 +62,7 @@ ResultCode Applet::Create(Service::APT::AppletId id) { applets[id] = std::make_shared(id); break; default: - LOG_ERROR(Service_APT, "Could not create applet %u", id); + LOG_ERROR(Service_APT, "Could not create applet %u", static_cast(id)); // TODO(Subv): Find the right error code return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported, ErrorLevel::Permanent); @@ -82,7 +82,7 @@ std::shared_ptr Applet::Get(Service::APT::AppletId id) { static void AppletUpdateEvent(u64 applet_id, int cycles_late) { Service::APT::AppletId id = static_cast(applet_id); std::shared_ptr applet = Applet::Get(id); - ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id); + ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", static_cast(id)); applet->Update(); diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 776d342f0..fc71adc25 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -74,7 +74,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, } default: - LOG_ERROR(Kernel, "unknown type=%d", type); + LOG_ERROR(Kernel, "unknown type=%d", static_cast(type)); return ERR_INVALID_ENUM_VALUE_FND; } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 59ea9823d..e82a32868 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -554,7 +554,7 @@ void ReceiveParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); - LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); if (!next_parameter) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -603,7 +603,7 @@ void GlanceParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); - LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); if (!next_parameter) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -767,7 +767,7 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040 AppletId applet_id = static_cast(rp.Pop()); - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast(applet_id)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -775,7 +775,8 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { - LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); + LOG_WARNING(Service_APT, "applet has already been started id=%08X", + static_cast(applet_id)); rb.Push(RESULT_SUCCESS); } else { rb.Push(HLE::Applets::Applet::Create(applet_id)); @@ -800,7 +801,7 @@ void PreloadLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 AppletId applet_id = static_cast(rp.Pop()); - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast(applet_id)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -808,7 +809,8 @@ void PreloadLibraryApplet(Service::Interface* self) { auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { - LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); + LOG_WARNING(Service_APT, "applet has already been started id=%08X", + static_cast(applet_id)); rb.Push(RESULT_SUCCESS); } else { rb.Push(HLE::Applets::Applet::Create(applet_id)); @@ -823,7 +825,7 @@ void StartLibraryApplet(Service::Interface* self) { Kernel::Handle handle = rp.PopHandle(); VAddr buffer_addr = rp.PopStaticBuffer(); - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast(applet_id)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -867,7 +869,7 @@ void SetScreenCapPostPermission(Service::Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", - screen_capture_post_permission); + static_cast(screen_capture_post_permission)); } void GetScreenCapPostPermission(Service::Interface* self) { @@ -877,7 +879,7 @@ void GetScreenCapPostPermission(Service::Interface* self) { rb.Push(RESULT_SUCCESS); // No error rb.Push(static_cast(screen_capture_post_permission)); LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", - screen_capture_post_permission); + static_cast(screen_capture_post_permission)); } void GetAppletInfo(Service::Interface* self) { @@ -899,7 +901,7 @@ void GetAppletInfo(Service::Interface* self) { rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, ErrorLevel::Status)); } - LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); + LOG_WARNING(Service_APT, "(stubbed) called appid=%u", static_cast(app_id)); } void GetStartupArgument(Service::Interface* self) { @@ -928,7 +930,7 @@ void GetStartupArgument(Service::Interface* self) { } LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x", - startup_argument_type, parameter_size); + static_cast(startup_argument_type), parameter_size); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 42f8950f9..5ff2262a0 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -301,7 +301,7 @@ static void WriteProcessPipe(Service::Interface* self) { } ASSERT_MSG(Memory::IsValidVirtualAddress(buffer), - "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); std::vector message(size); for (u32 i = 0; i < size; i++) { @@ -359,8 +359,8 @@ static void ReadPipeIfPossible(Service::Interface* self) { DSP::HLE::DspPipe pipe = static_cast(pipe_index); ASSERT_MSG(Memory::IsValidVirtualAddress(addr), - "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, - size, addr); + "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, + unknown, size, addr); cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -403,8 +403,8 @@ static void ReadPipe(Service::Interface* self) { DSP::HLE::DspPipe pipe = static_cast(pipe_index); ASSERT_MSG(Memory::IsValidVirtualAddress(addr), - "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, - size, addr); + "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, + unknown, size, addr); if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector response = DSP::HLE::PipeRead(pipe, size); diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 02911160b..ec1a65223 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -105,8 +106,8 @@ void File::HandleSyncRequest(Kernel::SharedPtr server_ses offset, length, address); if (offset + length > backend->GetSize()) { - LOG_ERROR(Service_FS, - "Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX", + LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%" PRIx64 + " length=0x%08X file_size=0x%" PRIx64, offset, length, backend->GetSize()); } @@ -191,7 +192,7 @@ void File::HandleSyncRequest(Kernel::SharedPtr server_ses // Unknown command... default: - LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); + LOG_ERROR(Service_FS, "Unknown command=0x%08X!", static_cast(cmd)); ResultCode error = UnimplementedFunction(ErrorModule::FS); cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. return; @@ -231,7 +232,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr serve // Unknown command... default: - LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); + LOG_ERROR(Service_FS, "Unknown command=0x%08X!", static_cast(cmd)); ResultCode error = UnimplementedFunction(ErrorModule::FS); cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. return; @@ -297,7 +298,7 @@ ResultCode RegisterArchiveType(std::unique_ptr&& factor auto& archive = result.first->second; LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), - id_code); + static_cast(id_code)); return RESULT_SUCCESS; } @@ -472,7 +473,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) { } else if (media_type == MediaType::SDMC) { media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX); } else { - LOG_ERROR(Service_FS, "Unsupported media type %u", media_type); + LOG_ERROR(Service_FS, "Unsupported media type %u", static_cast(media_type)); return ResultCode(-1); // TODO(Subv): Find the right error code } diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index b9eab7838..4bd43d15d 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/assert.h" #include "common/common_types.h" #include "common/file_util.h" @@ -311,8 +312,8 @@ static void CreateFile(Service::Interface* self) { FileSys::Path file_path(filename_type, filename_size, filename_ptr); - LOG_DEBUG(Service_FS, "type=%u size=%llu data=%s", static_cast(filename_type), file_size, - file_path.DebugStr().c_str()); + LOG_DEBUG(Service_FS, "type=%u size=%" PRIx64 " data=%s", static_cast(filename_type), + file_size, file_path.DebugStr().c_str()); cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; } diff --git a/src/core/hle/service/ndm/ndm.cpp b/src/core/hle/service/ndm/ndm.cpp index 096c0cdac..9f57b3a5a 100644 --- a/src/core/hle/service/ndm/ndm.cpp +++ b/src/core/hle/service/ndm/ndm.cpp @@ -34,7 +34,7 @@ void EnterExclusiveState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast(exclusive_state)); } void LeaveExclusiveState(Service::Interface* self) { @@ -43,7 +43,7 @@ void LeaveExclusiveState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast(exclusive_state)); } void QueryExclusiveMode(Service::Interface* self) { @@ -52,7 +52,7 @@ void QueryExclusiveMode(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast(exclusive_state); - LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast(exclusive_state)); } void LockState(Service::Interface* self) { @@ -61,7 +61,7 @@ void LockState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled); + LOG_WARNING(Service_NDM, "(STUBBED) called"); } void UnlockState(Service::Interface* self) { @@ -70,7 +70,7 @@ void UnlockState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled); + LOG_WARNING(Service_NDM, "(STUBBED) called"); } void SuspendDaemons(Service::Interface* self) { @@ -86,7 +86,7 @@ void SuspendDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x6, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X", static_cast(daemon_bit_mask)); } void ResumeDaemons(Service::Interface* self) { @@ -101,7 +101,7 @@ void ResumeDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X", static_cast(daemon_bit_mask)); } void SuspendScheduler(Service::Interface* self) { @@ -157,7 +157,7 @@ void SetScanInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval); + LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X", scan_interval); } void GetScanInterval(Service::Interface* self) { @@ -166,7 +166,7 @@ void GetScanInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x11, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = scan_interval; - LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval); + LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X", scan_interval); } void SetRetryInterval(Service::Interface* self) { @@ -175,7 +175,7 @@ void SetRetryInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval); + LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X", retry_interval); } void GetRetryInterval(Service::Interface* self) { @@ -184,7 +184,7 @@ void GetRetryInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x13, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = retry_interval; - LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval); + LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X", retry_interval); } void OverrideDefaultDaemons(Service::Interface* self) { @@ -200,7 +200,8 @@ void OverrideDefaultDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", + static_cast(default_daemon_bit_mask)); } void ResetDefaultDaemons(Service::Interface* self) { @@ -209,7 +210,8 @@ void ResetDefaultDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X", + static_cast(default_daemon_bit_mask)); } void GetDefaultDaemons(Service::Interface* self) { @@ -218,7 +220,8 @@ void GetDefaultDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast(default_daemon_bit_mask); - LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X", + static_cast(default_daemon_bit_mask)); } void ClearHalfAwakeMacFilter(Service::Interface* self) { diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 5c955cf54..4c3dd69ce 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -92,7 +92,7 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { if (name_len > Service::kMaxPortSize) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERR_INVALID_NAME_SIZE); - LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len); + LOG_ERROR(Service_SRV, "called name_len=0x%zX -> ERR_INVALID_NAME_SIZE", name_len); return; } std::string name(name_buf.data(), name_len); @@ -122,7 +122,8 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { rb.Push(ERR_MAX_CONNECTIONS_REACHED); rb.PushObjects(std::move(client_port).Unwrap()); } else { - LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); + LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), + session.Code().raw); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(session.Code()); } diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 57172ddd6..3f1a0c452 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -91,7 +91,7 @@ static void SetInputFormat(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast(conversion.input_format)); } static void GetInputFormat(Interface* self) { @@ -101,7 +101,7 @@ static void GetInputFormat(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.input_format); - LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast(conversion.input_format)); } static void SetOutputFormat(Interface* self) { @@ -112,7 +112,7 @@ static void SetOutputFormat(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast(conversion.output_format)); } static void GetOutputFormat(Interface* self) { @@ -122,7 +122,7 @@ static void GetOutputFormat(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.output_format); - LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast(conversion.output_format)); } static void SetRotation(Interface* self) { @@ -133,7 +133,7 @@ static void SetRotation(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast(conversion.rotation)); } static void GetRotation(Interface* self) { @@ -143,7 +143,7 @@ static void GetRotation(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.rotation); - LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast(conversion.rotation)); } static void SetBlockAlignment(Interface* self) { @@ -154,7 +154,8 @@ static void SetBlockAlignment(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", + static_cast(conversion.block_alignment)); } static void GetBlockAlignment(Interface* self) { @@ -164,7 +165,8 @@ static void GetBlockAlignment(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.block_alignment); - LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", + static_cast(conversion.block_alignment)); } /** @@ -664,9 +666,10 @@ cleanup: Service_Y2R, "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", - params->input_format, params->output_format, params->rotation, params->block_alignment, - params->input_line_width, params->input_lines, params->standard_coefficient, - params->padding, params->alpha); + static_cast(params->input_format), static_cast(params->output_format), + static_cast(params->rotation), static_cast(params->block_alignment), + params->input_line_width, params->input_lines, + static_cast(params->standard_coefficient), params->padding, params->alpha); } static void PingProcess(Interface* self) { diff --git a/src/core/hw/aes/ccm.cpp b/src/core/hw/aes/ccm.cpp index dc7035ab6..b28b579f3 100644 --- a/src/core/hw/aes/ccm.cpp +++ b/src/core/hw/aes/ccm.cpp @@ -46,7 +46,7 @@ public: std::vector EncryptSignCCM(const std::vector& pdata, const CCMNonce& nonce, size_t slot_id) { if (!IsNormalKeyAvailable(slot_id)) { - LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id); + LOG_ERROR(HW_AES, "Key slot %zu not available. Will use zero key.", slot_id); } const AESKey normal = GetNormalKey(slot_id); std::vector cipher(pdata.size() + CCM_MAC_SIZE); @@ -67,7 +67,7 @@ std::vector EncryptSignCCM(const std::vector& pdata, const CCMNonce& non std::vector DecryptVerifyCCM(const std::vector& cipher, const CCMNonce& nonce, size_t slot_id) { if (!IsNormalKeyAvailable(slot_id)) { - LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id); + LOG_ERROR(HW_AES, "Key slot %zu not available. Will use zero key.", slot_id); } const AESKey normal = GetNormalKey(slot_id); const std::size_t pdata_size = cipher.size() - CCM_MAC_SIZE; diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 83ad9d898..03751dcac 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -65,7 +65,7 @@ static Math::Vec4 DecodePixel(Regs::PixelFormat input_format, const u8* src_ return Color::DecodeRGBA4(src_pixel); default: - LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format); + LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", static_cast(input_format)); return {0, 0, 0, 0}; } } @@ -303,7 +303,7 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) { default: LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", - config.output_format.Value()); + static_cast(config.output_format.Value())); break; } } From 590b1e84534cb70c23e70a1f632edb80b3b19e16 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sat, 4 Nov 2017 10:49:17 +0000 Subject: [PATCH 05/19] xbyak: Update to v5.53 --- externals/xbyak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/xbyak b/externals/xbyak index fe4765d2f..c5da3778e 160000 --- a/externals/xbyak +++ b/externals/xbyak @@ -1 +1 @@ -Subproject commit fe4765d2fed4e990ea5e9661b6bc5fc9bf48ec16 +Subproject commit c5da3778e7f84013fe8c26fcf18a67881bd1e825 From 61b88aeaee23c35039bc0cf0b58033ffb03bce2c Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sat, 4 Nov 2017 10:49:37 +0000 Subject: [PATCH 06/19] dynarmic: Update to 8f15e3f70cb96e56705e5de6ba97b5d09423a56b --- externals/dynarmic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/dynarmic b/externals/dynarmic index 8f15e3f70..d3fb60328 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit 8f15e3f70cb96e56705e5de6ba97b5d09423a56b +Subproject commit d3fb603287dd4b0b06b921b639ef79dc9a850cdc From 5d123144ff2b8d1cec1a6f1a46096b6bde9f425c Mon Sep 17 00:00:00 2001 From: Hexagon12 Date: Sat, 4 Nov 2017 15:03:04 +0200 Subject: [PATCH 07/19] Removed all instances of WSACleanup();. --- src/core/hle/service/soc_u.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index acc36bedb..f2f91e7c2 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -794,10 +794,6 @@ static void ShutdownSockets(Interface* self) { // TODO(Subv): Implement CleanupSockets(); -#ifdef _WIN32 - WSACleanup(); -#endif - u32* cmd_buffer = Kernel::GetCommandBuffer(); cmd_buffer[1] = 0; } @@ -908,9 +904,6 @@ SOC_U::SOC_U() { SOC_U::~SOC_U() { CleanupSockets(); -#ifdef _WIN32 - WSACleanup(); -#endif } } // namespace SOC From 4ded13813901993e66e799c0c18eb7eb18fc5592 Mon Sep 17 00:00:00 2001 From: Hexagon12 Date: Sat, 4 Nov 2017 16:30:48 +0200 Subject: [PATCH 08/19] Update soc_u.cpp --- src/core/hle/service/soc_u.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index f2f91e7c2..4ef3002d8 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -780,10 +780,6 @@ static void Connect(Interface* self) { static void InitializeSockets(Interface* self) { // TODO(Subv): Implement -#ifdef _WIN32 - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); -#endif u32* cmd_buffer = Kernel::GetCommandBuffer(); cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); @@ -900,10 +896,18 @@ const Interface::FunctionInfo FunctionTable[] = { SOC_U::SOC_U() { Register(FunctionTable); + +#ifdef _WIN32 + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); +#endif } SOC_U::~SOC_U() { CleanupSockets(); +#ifdef _WIN32 + WSACleanup(); +#endif } } // namespace SOC From 230ea063a58d77d674b66d584e60a24fbfd99039 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Wed, 4 Oct 2017 08:07:32 +0200 Subject: [PATCH 09/19] Service/UDS: Implement SendTo, PullPacket, HandleSecureData, and Bind --- src/core/hle/service/nwm/nwm_uds.cpp | 223 +++++++++++++++++++++----- src/core/hle/service/nwm/nwm_uds.h | 1 - src/core/hle/service/nwm/uds_data.cpp | 9 ++ src/core/hle/service/nwm/uds_data.h | 9 ++ 4 files changed, 202 insertions(+), 40 deletions(-) diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 0aa63cc1e..d2baf249c 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -47,8 +47,17 @@ static NodeList node_info; // Node information about our own system. static NodeInfo current_node; -// Mapping of bind node ids to their respective events. -static std::unordered_map> bind_node_events; +struct BindNodeData { + u32 bind_node_id; ///< Id of the bind node associated with this data. + u8 channel; ///< Channel that this bind node was bound to. + u16 network_node_id; ///< Node id this bind node is associated with, only packets from this + /// network node will be received. + Kernel::SharedPtr event; ///< Receive event for this bind node. + std::deque> received_packets; ///< List of packets received on this channel. +}; + +// Mapping of data channels to their internal data. +static std::unordered_map channel_data; // The WiFi network channel that the network is currently on. // Since we're not actually interacting with physical radio waves, this is just a dummy value. @@ -75,6 +84,9 @@ constexpr size_t MaxBeaconFrames = 15; // List of the last beacons received from the network. static std::list received_beacons; +// Network node id used when a SecureData packet is addressed to every connected node. +constexpr u16 BroadcastNetworkNodeId = 0xFFFF; + /** * Returns a list of received 802.11 beacon frames from the specified sender since the last call. */ @@ -143,7 +155,7 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { "Could not join network"); { std::lock_guard lock(connection_status_mutex); - ASSERT(connection_status.status == static_cast(NetworkStatus::Connecting)); + ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); } // Send the EAPoL-Start packet to the server. @@ -159,6 +171,7 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { } static void HandleEAPoLPacket(const Network::WifiPacket& packet) { + std::lock_guard hle_lock(HLE::g_hle_lock); std::lock_guard lock(connection_status_mutex); if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { @@ -205,7 +218,6 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) { SendPacket(eapol_logoff); // TODO(B3N30): Broadcast updated node list // The 3ds does this presumably to support spectators. - std::lock_guard lock(HLE::g_hle_lock); connection_status_event->Signal(); } else { if (connection_status.status != static_cast(NetworkStatus::NotConnected)) { @@ -242,6 +254,58 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) { } } +static void HandleSecureDataPacket(const Network::WifiPacket& packet) { + auto secure_data = ParseSecureDataHeader(packet.data); + std::lock_guard hle_lock(HLE::g_hle_lock); + std::lock_guard lock(connection_status_mutex); + + if (secure_data.src_node_id == connection_status.network_node_id) { + // Ignore packets that came from ourselves. + return; + } + + if (secure_data.dest_node_id != connection_status.network_node_id && + secure_data.dest_node_id != BroadcastNetworkNodeId) { + // The packet wasn't addressed to us, we can only act as a router if we're the host. + // However, we might have received this packet due to a broadcast from the host, in that + // case just ignore it. + ASSERT_MSG(packet.destination_address == Network::BroadcastMac || + connection_status.status == static_cast(NetworkStatus::ConnectedAsHost), + "Can't be a router if we're not a host"); + + if (connection_status.status == static_cast(NetworkStatus::ConnectedAsHost) && + secure_data.dest_node_id != BroadcastNetworkNodeId) { + // Broadcast the packet so the right receiver can get it. + // TODO(B3N30): Is there a flag that makes this kind of routing be unicast instead of + // multicast? Perhaps this is a way to allow spectators to see some of the packets. + Network::WifiPacket out_packet = packet; + out_packet.destination_address = Network::BroadcastMac; + SendPacket(out_packet); + } + return; + } + + // The packet is addressed to us (or to everyone using the broadcast node id), handle it. + // TODO(B3N30): We don't currently send nor handle management frames. + ASSERT(!secure_data.is_management); + + // TODO(B3N30): Allow more than one bind node per channel. + auto channel_info = channel_data.find(secure_data.data_channel); + // Ignore packets from channels we're not interested in. + if (channel_info == channel_data.end()) + return; + + if (channel_info->second.network_node_id != BroadcastNetworkNodeId && + channel_info->second.network_node_id != secure_data.src_node_id) + return; + + // Add the received packet to the data queue. + channel_info->second.received_packets.emplace_back(packet.data); + + // Signal the data event. We can do this directly because we locked g_hle_lock + channel_info->second.event->Signal(); +} + /* * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11 * authentication frame with SEQ1. @@ -329,7 +393,7 @@ static void HandleDataFrame(const Network::WifiPacket& packet) { HandleEAPoLPacket(packet); break; case EtherType::SecureData: - // TODO(B3N30): Handle SecureData packets + HandleSecureDataPacket(packet); break; } } @@ -557,8 +621,6 @@ static void Bind(Interface* self) { u8 data_channel = rp.Pop(); u16 network_node_id = rp.Pop(); - // TODO(Subv): Store the data channel and verify it when receiving data frames. - LOG_DEBUG(Service_NWM, "called"); if (data_channel == 0) { @@ -569,13 +631,15 @@ static void Bind(Interface* self) { } // Create a new event for this bind node. - // TODO(Subv): Signal this event when new data is received on this data channel. auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::BindNodeEvent" + std::to_string(bind_node_id)); - bind_node_events[bind_node_id] = event; + std::lock_guard lock(connection_status_mutex); + + ASSERT(channel_data.find(data_channel) == channel_data.end()); + // TODO(B3N30): Support more than one bind node per channel. + channel_data[data_channel] = {bind_node_id, data_channel, network_node_id, event}; IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); } @@ -722,31 +786,25 @@ static void SendTo(Interface* self) { size_t desc_size; const VAddr input_address = rp.PopStaticBuffer(&desc_size, false); - ASSERT(desc_size == data_size); + ASSERT(desc_size >= data_size); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - u16 network_node_id; - - { - std::lock_guard lock(connection_status_mutex); - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && - connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { - rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, - ErrorSummary::InvalidState, ErrorLevel::Status)); - return; - } - - if (dest_node_id == connection_status.network_node_id) { - rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Status)); - return; - } - - network_node_id = connection_status.network_node_id; + std::lock_guard lock(connection_status_mutex); + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && + connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; } - // TODO(Subv): Do something with the flags. + if (dest_node_id == connection_status.network_node_id) { + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + return; + } + + // TODO(B3N30): Do something with the flags. constexpr size_t MaxSize = 0x5C6; if (data_size > MaxSize) { @@ -758,20 +816,107 @@ static void SendTo(Interface* self) { std::vector data(data_size); Memory::ReadBlock(input_address, data.data(), data.size()); - // TODO(Subv): Increment the sequence number after each sent packet. + // TODO(B3N30): Increment the sequence number after each sent packet. u16 sequence_number = 0; - std::vector data_payload = - GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number); + std::vector data_payload = GenerateDataPayload( + data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); - // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt + // TODO(B3N30): Retrieve the MAC address of the dest_node_id and our own to encrypt // and encapsulate the payload. - // TODO(Subv): Send the frame. + Network::WifiPacket packet; + // Data frames are sent to the host, who then decides where to route it to. If we're the host, + // just directly broadcast the frame. + if (connection_status.status == static_cast(NetworkStatus::ConnectedAsHost)) + packet.destination_address = Network::BroadcastMac; + else + packet.destination_address = network_info.host_mac_address; + packet.channel = network_channel; + packet.data = std::move(data_payload); + packet.type = Network::WifiPacket::PacketType::Data; + + SendPacket(packet); rb.Push(RESULT_SUCCESS); +} - LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u", - static_cast(dest_node_id), data_size, flags, static_cast(data_channel)); +/** + * NWM_UDS::PullPacket service function. + * Receives a data frame from the specified bind node id + * Inputs: + * 0 : Command header. + * 1 : Bind node id. + * 2 : Max out buff size >> 2. + * 3 : Max out buff size. + * 64 : Output buffer descriptor + * 65 : Output buffer address + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Received data size + * 3 : u16 Source network node id + * 4 : Buffer descriptor + * 5 : Buffer address + */ +static void PullPacket(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 3, 0); + + u32 bind_node_id = rp.Pop(); + u32 max_out_buff_size_aligned = rp.Pop(); + u32 max_out_buff_size = rp.Pop(); + + size_t desc_size; + const VAddr output_address = rp.PeekStaticBuffer(0, &desc_size); + ASSERT(desc_size == max_out_buff_size); + + std::lock_guard lock(connection_status_mutex); + + auto channel = + std::find_if(channel_data.begin(), channel_data.end(), [bind_node_id](const auto& data) { + return data.second.bind_node_id == bind_node_id; + }); + + if (channel == channel_data.end()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + // TODO(B3N30): Find the right error code + rb.Push(-1); + return; + } + + if (channel->second.received_packets.empty()) { + Memory::ZeroBlock(output_address, desc_size); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + rb.Push(0); + rb.PushStaticBuffer(output_address, desc_size, 0); + return; + } + + const auto& next_packet = channel->second.received_packets.front(); + + auto secure_data = ParseSecureDataHeader(next_packet); + auto data_size = secure_data.GetActualDataSize(); + + if (data_size > max_out_buff_size) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(0xE10113E9); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); + Memory::ZeroBlock(output_address, desc_size); + // Write the actual data. + Memory::WriteBlock(output_address, + next_packet.data() + sizeof(LLCHeader) + sizeof(SecureDataHeader), + data_size); + + rb.Push(RESULT_SUCCESS); + rb.Push(data_size); + rb.Push(secure_data.src_node_id); + rb.PushStaticBuffer(output_address, desc_size, 0); + + channel->second.received_packets.pop_front(); } /** @@ -993,7 +1138,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00110040, nullptr, "GetApplicationData"}, {0x00120100, Bind, "Bind"}, {0x00130040, nullptr, "Unbind"}, - {0x001400C0, nullptr, "PullPacket"}, + {0x001400C0, PullPacket, "PullPacket"}, {0x00150080, nullptr, "SetMaxSendDelay"}, {0x00170182, SendTo, "SendTo"}, {0x001A0000, GetChannel, "GetChannel"}, @@ -1018,7 +1163,7 @@ NWM_UDS::NWM_UDS() { NWM_UDS::~NWM_UDS() { network_info = {}; - bind_node_events.clear(); + channel_data.clear(); connection_status_event = nullptr; recv_buffer_memory = nullptr; diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index f1caaf974..5508959fc 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -42,7 +42,6 @@ using NodeList = std::vector; enum class NetworkStatus { NotConnected = 3, ConnectedAsHost = 6, - Connecting = 7, ConnectedAsClient = 9, ConnectedAsSpectator = 10, }; diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 4b389710f..8f0743819 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp @@ -275,6 +275,15 @@ std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 return buffer; } +SecureDataHeader ParseSecureDataHeader(const std::vector& data) { + SecureDataHeader header; + + // Skip the LLC header + std::memcpy(&header, data.data() + sizeof(LLCHeader), sizeof(header)); + + return header; +} + std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { EAPoLStartPacket eapol_start{}; eapol_start.association_id = association_id; diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index 76bccb1bf..4161025a9 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h @@ -51,6 +51,10 @@ struct SecureDataHeader { u16_be sequence_number; u16_be dest_node_id; u16_be src_node_id; + + u32 GetActualDataSize() { + return protocol_size - sizeof(SecureDataHeader); + } }; static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size"); @@ -118,6 +122,11 @@ static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wro std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number); +/* + * Returns the SecureDataHeader stored in an 802.11 data frame. + */ +SecureDataHeader ParseSecureDataHeader(const std::vector& data); + /* * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS * communication. From f6d16c3f87386de35317e0f641ee5fd44dcdcd26 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Wed, 4 Oct 2017 09:10:19 +0200 Subject: [PATCH 10/19] Service/UDS: Implement Unbind and GetNodeInformation --- src/core/hle/service/nwm/nwm_uds.cpp | 63 +++++++++++++++++++++++++++- src/core/hle/service/nwm/nwm_uds.h | 2 +- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index d2baf249c..67a43be00 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -599,6 +599,36 @@ static void GetConnectionStatus(Interface* self) { LOG_DEBUG(Service_NWM, "called"); } +/** + * NWM_UDS::GetNodeInformation service function. + * Returns the node inforamtion structure for the currently connected node. + * Inputs: + * 0 : Command header. + * 1 : Node ID. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2-11 : NodeInfo structure. + */ +static void GetNodeInformation(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 1, 0); + u16 network_node_id = rp.Pop(); + + IPC::RequestBuilder rb = rp.MakeBuilder(11, 0); + rb.Push(RESULT_SUCCESS); + + { + std::lock_guard lock(connection_status_mutex); + auto itr = std::find_if(node_info.begin(), node_info.end(), + [network_node_id](const NodeInfo& node) { + return node.network_node_id == network_node_id; + }); + ASSERT(itr != node_info.end()); + rb.PushRaw(*itr); + } + LOG_DEBUG(Service_NWM, "called"); +} + /** * NWM_UDS::Bind service function. * Binds a BindNodeId to a data channel and retrieves a data event. @@ -644,6 +674,35 @@ static void Bind(Interface* self) { rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); } +/** + * NWM_UDS::Unbind service function. + * Unbinds a BindNodeId from a data channel. + * Inputs: + * 1 : BindNodeId + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Unbind(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 1, 0); + + u32 bind_node_id = rp.Pop(); + + std::lock_guard lock(connection_status_mutex); + + auto itr = + std::find_if(channel_data.begin(), channel_data.end(), [bind_node_id](const auto& data) { + return data.second.bind_node_id == bind_node_id; + }); + + if (itr != channel_data.end()) { + channel_data.erase(itr); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + /** * NWM_UDS::BeginHostingNetwork service function. * Creates a network and starts broadcasting its presence. @@ -1131,13 +1190,13 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00090442, nullptr, "ConnectNetwork (deprecated)"}, {0x000A0000, nullptr, "DisconnectNetwork"}, {0x000B0000, GetConnectionStatus, "GetConnectionStatus"}, - {0x000D0040, nullptr, "GetNodeInformation"}, + {0x000D0040, GetNodeInformation, "GetNodeInformation"}, {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"}, {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, {0x00100042, SetApplicationData, "SetApplicationData"}, {0x00110040, nullptr, "GetApplicationData"}, {0x00120100, Bind, "Bind"}, - {0x00130040, nullptr, "Unbind"}, + {0x00130040, Unbind, "Unbind"}, {0x001400C0, PullPacket, "PullPacket"}, {0x00150080, nullptr, "SetMaxSendDelay"}, {0x00170182, SendTo, "SendTo"}, diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 5508959fc..f95ae25aa 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -32,7 +32,7 @@ struct NodeInfo { std::array username; INSERT_PADDING_BYTES(4); u16_le network_node_id; - INSERT_PADDING_BYTES(6); + std::array address; }; static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size."); From ed9db735a2ff4ffef34f47734b4e8d755ec6bb70 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Wed, 4 Oct 2017 09:18:21 +0200 Subject: [PATCH 11/19] Service/UDS: Updated BeginHostingNetwork --- src/core/hle/service/nwm/nwm_uds.cpp | 129 +++++++++++++++++++++------ src/core/hle/service/nwm/nwm_uds.h | 3 +- src/core/hle/service/nwm/uds_data.h | 2 +- 3 files changed, 104 insertions(+), 30 deletions(-) diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 67a43be00..74d6fdad2 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,12 @@ namespace Service { namespace NWM { +namespace ErrCodes { +enum { + NotInitialized = 2, +}; +} // namespace ErrCodes + // Event that is signaled every time the connection status changes. static Kernel::SharedPtr connection_status_event; @@ -37,6 +44,8 @@ static Kernel::SharedPtr recv_buffer_memory; // Connection status of this 3DS. static ConnectionStatus connection_status{}; +static std::atomic initialized(false); + /* Node information about the current network. * The amount of elements in this vector is always the maximum number * of nodes specified in the network configuration. @@ -155,7 +164,7 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { "Could not join network"); { std::lock_guard lock(connection_status_mutex); - ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); + ASSERT(connection_status.status == static_cast(NetworkStatus::Connecting)); } // Send the EAPoL-Start packet to the server. @@ -171,8 +180,9 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { } static void HandleEAPoLPacket(const Network::WifiPacket& packet) { - std::lock_guard hle_lock(HLE::g_hle_lock); - std::lock_guard lock(connection_status_mutex); + std::unique_lock hle_lock(HLE::g_hle_lock, std::defer_lock); + std::unique_lock lock(connection_status_mutex, std::defer_lock); + std::lock(hle_lock, lock); if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { @@ -220,7 +230,7 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) { // The 3ds does this presumably to support spectators. connection_status_event->Signal(); } else { - if (connection_status.status != static_cast(NetworkStatus::NotConnected)) { + if (connection_status.status != static_cast(NetworkStatus::Connecting)) { LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", connection_status.status); return; @@ -249,15 +259,15 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) { // Some games require ConnectToNetwork to block, for now it doesn't // If blocking is implemented this lock needs to be changed, // otherwise it might cause deadlocks - std::lock_guard lock(HLE::g_hle_lock); connection_status_event->Signal(); } } static void HandleSecureDataPacket(const Network::WifiPacket& packet) { auto secure_data = ParseSecureDataHeader(packet.data); - std::lock_guard hle_lock(HLE::g_hle_lock); - std::lock_guard lock(connection_status_mutex); + std::unique_lock hle_lock(HLE::g_hle_lock, std::defer_lock); + std::unique_lock lock(connection_status_mutex, std::defer_lock); + std::lock(hle_lock, lock); if (secure_data.src_node_id == connection_status.network_node_id) { // Ignore packets that came from ourselves. @@ -315,7 +325,7 @@ void StartConnectionSequence(const MacAddress& server) { WifiPacket auth_request; { std::lock_guard lock(connection_status_mutex); - ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); + connection_status.status = static_cast(NetworkStatus::Connecting); // TODO(Subv): Handle timeout. @@ -546,6 +556,8 @@ static void InitializeWithVersion(Interface* self) { recv_buffer_memory = Kernel::g_handle_table.Get(sharedmem_handle); + initialized = true; + ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); { @@ -614,8 +626,12 @@ static void GetNodeInformation(Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 1, 0); u16 network_node_id = rp.Pop(); - IPC::RequestBuilder rb = rp.MakeBuilder(11, 0); - rb.Push(RESULT_SUCCESS); + if (!initialized) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotInitialized, ErrorModule::UDS, + ErrorSummary::StatusChanged, ErrorLevel::Status)); + return; + } { std::lock_guard lock(connection_status_mutex); @@ -623,7 +639,15 @@ static void GetNodeInformation(Interface* self) { [network_node_id](const NodeInfo& node) { return node.network_node_id == network_node_id; }); - ASSERT(itr != node_info.end()); + if (itr == node_info.end()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(11, 0); + rb.Push(RESULT_SUCCESS); rb.PushRaw(*itr); } LOG_DEBUG(Service_NWM, "called"); @@ -653,13 +677,29 @@ static void Bind(Interface* self) { LOG_DEBUG(Service_NWM, "called"); - if (data_channel == 0) { + if (data_channel == 0 || bind_node_id == 0) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, ErrorSummary::WrongArgument, ErrorLevel::Usage)); return; } + constexpr size_t MaxBindNodes = 16; + if (channel_data.size() >= MaxBindNodes) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::OutOfMemory, ErrorModule::UDS, + ErrorSummary::OutOfResource, ErrorLevel::Status)); + return; + } + + constexpr u32 MinRecvBufferSize = 0x5F4; + if (recv_buffer_size < MinRecvBufferSize) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + // Create a new event for this bind node. auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::BindNodeEvent" + std::to_string(bind_node_id)); @@ -687,6 +727,12 @@ static void Unbind(Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 1, 0); u32 bind_node_id = rp.Pop(); + if (bind_node_id == 0) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } std::lock_guard lock(connection_status_mutex); @@ -699,8 +745,13 @@ static void Unbind(Interface* self) { channel_data.erase(itr); } - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); rb.Push(RESULT_SUCCESS); + rb.Push(bind_node_id); + // TODO(B3N30): Find out what the other return values are + rb.Push(0); + rb.Push(0); + rb.Push(0); } /** @@ -729,13 +780,14 @@ static void BeginHostingNetwork(Interface* self) { LOG_DEBUG(Service_NWM, "called"); - Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo)); - - // The real UDS module throws a fatal error if this assert fails. - ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); - { std::lock_guard lock(connection_status_mutex); + + Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo)); + + // The real UDS module throws a fatal error if this assert fails. + ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); + connection_status.status = static_cast(NetworkStatus::ConnectedAsHost); // Ensure the application data size is less than the maximum value. @@ -749,11 +801,13 @@ static void BeginHostingNetwork(Interface* self) { connection_status.max_nodes = network_info.max_nodes; // Resize the nodes list to hold max_nodes. + node_info.clear(); node_info.resize(network_info.max_nodes); // There's currently only one node in the network (the host). connection_status.total_nodes = 1; network_info.total_nodes = 1; + // The host is always the first node connection_status.network_node_id = 1; current_node.network_node_id = 1; @@ -762,12 +816,22 @@ static void BeginHostingNetwork(Interface* self) { connection_status.node_bitmask |= 1; // Notify the application that the first node was set. connection_status.changed_nodes |= 1; - node_info[0] = current_node; - } - // If the game has a preferred channel, use that instead. - if (network_info.channel != 0) - network_channel = network_info.channel; + if (auto room_member = Network::GetRoomMember().lock()) { + if (room_member->IsConnected()) { + network_info.host_mac_address = room_member->GetMacAddress(); + } else { + network_info.host_mac_address = {{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; + } + } + node_info[0] = current_node; + + // If the game has a preferred channel, use that instead. + if (network_info.channel != 0) + network_channel = network_info.channel; + else + network_info.channel = DefaultNetworkChannel; + } connection_status_event->Signal(); @@ -775,8 +839,7 @@ static void BeginHostingNetwork(Interface* self) { CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU), beacon_broadcast_event, 0); - LOG_WARNING(Service_NWM, - "An UDS network has been created, but broadcasting it is unimplemented."); + LOG_DEBUG(Service_NWM, "An UDS network has been created."); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -929,6 +992,14 @@ static void PullPacket(Interface* self) { ASSERT(desc_size == max_out_buff_size); std::lock_guard lock(connection_status_mutex); + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost) && + connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && + connection_status.status != static_cast(NetworkStatus::ConnectedAsSpectator)) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } auto channel = std::find_if(channel_data.begin(), channel_data.end(), [bind_node_id](const auto& data) { @@ -937,8 +1008,8 @@ static void PullPacket(Interface* self) { if (channel == channel_data.end()) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - // TODO(B3N30): Find the right error code - rb.Push(-1); + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); return; } @@ -959,7 +1030,8 @@ static void PullPacket(Interface* self) { if (data_size > max_out_buff_size) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(0xE10113E9); + rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); return; } @@ -1225,6 +1297,7 @@ NWM_UDS::~NWM_UDS() { channel_data.clear(); connection_status_event = nullptr; recv_buffer_memory = nullptr; + initialized = false; { std::lock_guard lock(connection_status_mutex); diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index f95ae25aa..f1caaf974 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -32,7 +32,7 @@ struct NodeInfo { std::array username; INSERT_PADDING_BYTES(4); u16_le network_node_id; - std::array address; + INSERT_PADDING_BYTES(6); }; static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size."); @@ -42,6 +42,7 @@ using NodeList = std::vector; enum class NetworkStatus { NotConnected = 3, ConnectedAsHost = 6, + Connecting = 7, ConnectedAsClient = 9, ConnectedAsSpectator = 10, }; diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index 4161025a9..59906f677 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h @@ -52,7 +52,7 @@ struct SecureDataHeader { u16_be dest_node_id; u16_be src_node_id; - u32 GetActualDataSize() { + u32 GetActualDataSize() const { return protocol_size - sizeof(SecureDataHeader); } }; From 68dba11805881b0c5e0a35c7698e3a9568fc4241 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 23 Oct 2017 15:52:23 -0500 Subject: [PATCH 12/19] Kernel/SVC: Don't let svcReleaseMutex release a mutex owned by another thread. This behavior was reverse engineered from the 3DS kernel. --- src/core/hle/kernel/errors.h | 1 + src/core/hle/kernel/mutex.cpp | 42 ++++++++++++++++++++++++----------- src/core/hle/kernel/mutex.h | 10 +++++++-- src/core/hle/svc.cpp | 4 +--- 4 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 64aa61460..004764c63 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -13,6 +13,7 @@ enum { OutOfHandles = 19, SessionClosedByRemote = 26, PortNameTooLong = 30, + WrongLockingThread = 31, NoPendingSessions = 35, WrongPermission = 46, InvalidBufferDescriptor = 48, diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 2cbca5e5b..d559c06be 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -7,6 +7,7 @@ #include #include "common/assert.h" #include "core/core.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/thread.h" @@ -58,19 +59,34 @@ void Mutex::Acquire(Thread* thread) { lock_count++; } -void Mutex::Release() { - // Only release if the mutex is held - if (lock_count > 0) { - lock_count--; - - // Yield to the next thread only if we've fully released the mutex - if (lock_count == 0) { - holding_thread->held_mutexes.erase(this); - holding_thread->UpdatePriority(); - holding_thread = nullptr; - WakeupAllWaitingThreads(); - Core::System::GetInstance().PrepareReschedule(); +ResultCode Mutex::Release(Thread* thread) { + // We can only release the mutex if it's held by the calling thread. + if (thread != holding_thread) { + if (holding_thread) { + LOG_ERROR( + Kernel, + "Tried to release a mutex (owned by thread id %u) from a different thread id %u", + holding_thread->thread_id, thread->thread_id); } + return ResultCode(ErrCodes::WrongLockingThread, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } + + // Note: It should not be possible for the situation where the mutex has a holding thread with a + // zero lock count to occur. The real kernel still checks for this, so we do too. + if (lock_count <= 0) + return ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::Kernel, + ErrorSummary::InvalidState, ErrorLevel::Permanent); + + lock_count--; + + // Yield to the next thread only if we've fully released the mutex + if (lock_count == 0) { + holding_thread->held_mutexes.erase(this); + holding_thread->UpdatePriority(); + holding_thread = nullptr; + WakeupAllWaitingThreads(); + Core::System::GetInstance().PrepareReschedule(); } } @@ -102,4 +118,4 @@ void Mutex::UpdatePriority() { } } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index bacacd690..3afb99c59 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace Kernel { @@ -52,7 +53,12 @@ public: void AddWaitingThread(SharedPtr thread) override; void RemoveWaitingThread(Thread* thread) override; - void Release(); + /** + * Attempts to release the mutex from the specified thread. + * @param thread Thread that wants to release the mutex. + * @returns The result code of the operation. + */ + ResultCode Release(Thread* thread); private: Mutex(); @@ -65,4 +71,4 @@ private: */ void ReleaseThreadMutexes(Thread* thread); -} // namespace +} // namespace Kernel diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index e8ca419d5..fe06af8f8 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -818,9 +818,7 @@ static ResultCode ReleaseMutex(Kernel::Handle handle) { if (mutex == nullptr) return ERR_INVALID_HANDLE; - mutex->Release(); - - return RESULT_SUCCESS; + return mutex->Release(Kernel::GetCurrentThread()); } /// Get the ID of the specified process From 81e36150d3a4b98a826ea0000acab235aa6505eb Mon Sep 17 00:00:00 2001 From: Hexagon12 Date: Sat, 4 Nov 2017 17:32:12 +0200 Subject: [PATCH 13/19] Clang pls --- src/core/hle/service/soc_u.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 4ef3002d8..10e6ba649 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -779,8 +779,8 @@ static void Connect(Interface* self) { } static void InitializeSockets(Interface* self) { -// TODO(Subv): Implement - + // TODO(Subv): Implement + u32* cmd_buffer = Kernel::GetCommandBuffer(); cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); cmd_buffer[1] = RESULT_SUCCESS.raw; From 695ff7cff3970f8ac5a0bdc395f0f176cd6078d6 Mon Sep 17 00:00:00 2001 From: Hexagon12 Date: Sat, 4 Nov 2017 17:41:20 +0200 Subject: [PATCH 14/19] Clang pls (again) --- src/core/hle/service/soc_u.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 10e6ba649..21136772a 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -780,7 +780,7 @@ static void Connect(Interface* self) { static void InitializeSockets(Interface* self) { // TODO(Subv): Implement - + u32* cmd_buffer = Kernel::GetCommandBuffer(); cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); cmd_buffer[1] = RESULT_SUCCESS.raw; From ee5aecee3f80f934cc3d2837fa4a908c7f4c8f2d Mon Sep 17 00:00:00 2001 From: BreadFish64 Date: Sat, 4 Nov 2017 12:59:27 -0500 Subject: [PATCH 15/19] Implement About Button Functionality (#3005) --- dist/icons/citra.png | Bin 0 -> 21077 bytes dist/icons/icons.qrc | 1 + src/citra_qt/CMakeLists.txt | 3 + src/citra_qt/aboutdialog.cpp | 19 +++++ src/citra_qt/aboutdialog.h | 25 ++++++ src/citra_qt/aboutdialog.ui | 156 +++++++++++++++++++++++++++++++++++ src/citra_qt/main.cpp | 11 +++ src/citra_qt/main.h | 3 + src/citra_qt/main.ui | 6 ++ 9 files changed, 224 insertions(+) create mode 100644 dist/icons/citra.png create mode 100644 src/citra_qt/aboutdialog.cpp create mode 100644 src/citra_qt/aboutdialog.h create mode 100644 src/citra_qt/aboutdialog.ui diff --git a/dist/icons/citra.png b/dist/icons/citra.png new file mode 100644 index 0000000000000000000000000000000000000000..83fa499dd35c238dd5e302e1cfe05b59b65f28c2 GIT binary patch literal 21077 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 literal 0 HcmV?d00001 diff --git a/dist/icons/icons.qrc b/dist/icons/icons.qrc index f0c44862f..a25804907 100644 --- a/dist/icons/icons.qrc +++ b/dist/icons/icons.qrc @@ -2,5 +2,6 @@ checked.png failed.png + citra.png diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index add7566c2..8b6c09c2b 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -23,6 +23,7 @@ set(SRCS debugger/profiler.cpp debugger/registers.cpp debugger/wait_tree.cpp + aboutdialog.cpp util/spinbox.cpp util/util.cpp bootmanager.cpp @@ -57,6 +58,7 @@ set(HEADERS debugger/wait_tree.h util/spinbox.h util/util.h + aboutdialog.h bootmanager.h game_list.h game_list_p.h @@ -75,6 +77,7 @@ set(UIS configuration/configure_system.ui configuration/configure_web.ui debugger/registers.ui + aboutdialog.ui hotkeys.ui main.ui ) diff --git a/src/citra_qt/aboutdialog.cpp b/src/citra_qt/aboutdialog.cpp new file mode 100644 index 000000000..9c11168ba --- /dev/null +++ b/src/citra_qt/aboutdialog.cpp @@ -0,0 +1,19 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "aboutdialog.h" +#include "common/scm_rev.h" +#include "ui_aboutdialog.h" + +AboutDialog::AboutDialog(QWidget* parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), + ui(new Ui::AboutDialog) { + ui->setupUi(this); + ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( + Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); +} + +AboutDialog::~AboutDialog() { + delete ui; +} diff --git a/src/citra_qt/aboutdialog.h b/src/citra_qt/aboutdialog.h new file mode 100644 index 000000000..606789774 --- /dev/null +++ b/src/citra_qt/aboutdialog.h @@ -0,0 +1,25 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog { + Q_OBJECT + +public: + explicit AboutDialog(QWidget* parent = 0); + ~AboutDialog(); + +private: + Ui::AboutDialog* ui; +}; + +#endif // ABOUTDIALOG_H diff --git a/src/citra_qt/aboutdialog.ui b/src/citra_qt/aboutdialog.ui new file mode 100644 index 000000000..32f0a0356 --- /dev/null +++ b/src/citra_qt/aboutdialog.ui @@ -0,0 +1,156 @@ + + + AboutDialog + + + + 0 + 0 + 752 + 300 + + + + About Citra + + + + + 630 + 250 + 101 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + 20 + 20 + 241 + 251 + + + + <html><head/><body><p><img src=":/icons/citra.png"/></p></body></html> + + + + + + 270 + 10 + 100 + 50 + + + + <html><head/><body><p><span style=" font-size:28pt;">Citra</span></p></body></html> + + + + + + 270 + 70 + 451 + 21 + + + + <html><head/><body><p>%1 | %2-%3</p></body></html> + + + + + + 270 + 90 + 461 + 131 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">Citra is a free and open source 3DS emulator </span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">licensed under GPLv2.0 or any later version.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">This software should not be used to play games </span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">you have not legally obtained.</span></p></body></html> + + + + + + 270 + 230 + 361 + 16 + + + + <html><head/><body><p><a href="https://citra-emu.org/"><span style=" text-decoration: underline; color:#0000ff;">Website</span></a> | <a href="https://community.citra-emu.org/"><span style=" text-decoration: underline; color:#0000ff;">Forum</span></a> | <a href="https://github.com/citra-emu"><span style=" text-decoration: underline; color:#0000ff;">Source Code</span></a> | <a href="//github.com/citra-emu/citra/graphs/contributors"><span style=" text-decoration: underline; color:#0000ff;">Contributors</span></a> | <a href="https://github.com/citra-emu/citra/blob/master/license.txt"><span style=" text-decoration: underline; color:#0000ff;">License</span></a></p></body></html> + + + + + + 150 + 270 + 450 + 16 + + + + <html><head/><body><p><span style=" font-size:7pt;">&quot;3DS&quot; is a trademark of Nintendo. Citra is not affiliated with Nintendo in any way.</span></p></body></html> + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index e0de66e02..00230d4bd 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include "citra_qt/aboutdialog.h" #include "citra_qt/bootmanager.h" #include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_dialog.h" @@ -340,6 +341,11 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key()); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); + + // Help + connect(ui.action_FAQ, &QAction::triggered, + []() { QDesktopServices::openUrl(QUrl("https://citra-emu.org/wiki/faq/")); }); + connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnMenuAboutCitra); } void GMainWindow::OnDisplayTitleBars(bool show) { @@ -801,6 +807,11 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det } } +void GMainWindow::OnMenuAboutCitra() { + AboutDialog about{this}; + about.exec(); +} + bool GMainWindow::ConfirmClose() { if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) return true; diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 4efab9a4a..18354bdf7 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -25,6 +25,7 @@ class MicroProfileDialog; class ProfilerWidget; class RegistersWidget; class WaitTreeWidget; +class AboutDialog; class GMainWindow : public QMainWindow { Q_OBJECT @@ -131,6 +132,8 @@ private slots: void ToggleWindowMode(); void OnCreateGraphicsSurfaceViewer(); void OnCoreError(Core::System::ResultStatus, std::string); + /// Called whenever a user selects Help->About Citra + void OnMenuAboutCitra(); private: void UpdateStatusBar(); diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 617343e51..442904f01 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -96,6 +96,7 @@ &Help + @@ -142,6 +143,11 @@ &Stop + + + FAQ + + About Citra From 2e6c80d1aafe26c7299cb0bccf3c7f265d845552 Mon Sep 17 00:00:00 2001 From: James Rowe Date: Fri, 18 Aug 2017 23:05:49 -0600 Subject: [PATCH 16/19] Qt updater integration, based on QtAutoUpdater --- src/citra_qt/CMakeLists.txt | 5 +- src/citra_qt/configuration/config.cpp | 11 + .../configuration/configure_general.cpp | 8 + .../configuration/configure_general.ui | 72 +++-- src/citra_qt/main.cpp | 61 ++++ src/citra_qt/main.h | 12 +- src/citra_qt/main.ui | 16 + src/citra_qt/ui_settings.h | 4 + src/citra_qt/updater/updater.cpp | 304 ++++++++++++++++++ src/citra_qt/updater/updater.h | 120 +++++++ src/citra_qt/updater/updater_p.h | 66 ++++ 11 files changed, 651 insertions(+), 28 deletions(-) create mode 100644 src/citra_qt/updater/updater.cpp create mode 100644 src/citra_qt/updater/updater.h create mode 100644 src/citra_qt/updater/updater_p.h diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 8b6c09c2b..e4929a0b4 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -23,9 +23,10 @@ set(SRCS debugger/profiler.cpp debugger/registers.cpp debugger/wait_tree.cpp - aboutdialog.cpp + updater/updater.cpp util/spinbox.cpp util/util.cpp + aboutdialog.cpp bootmanager.cpp game_list.cpp hotkeys.cpp @@ -56,6 +57,8 @@ set(HEADERS debugger/profiler.h debugger/registers.h debugger/wait_tree.h + updater/updater.h + updater/updater_p.h util/spinbox.h util/util.h aboutdialog.h diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 601c8b0e3..97993e426 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -157,6 +157,12 @@ void Config::ReadValues() { qt_config->beginGroup("UI"); UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); + qt_config->beginGroup("Updater"); + UISettings::values.check_for_update_on_start = + qt_config->value("check_for_update_on_start", true).toBool(); + UISettings::values.update_on_close = qt_config->value("update_on_close", false).toBool(); + qt_config->endGroup(); + qt_config->beginGroup("UILayout"); UISettings::values.geometry = qt_config->value("geometry").toByteArray(); UISettings::values.state = qt_config->value("state").toByteArray(); @@ -307,6 +313,11 @@ void Config::SaveValues() { qt_config->beginGroup("UI"); qt_config->setValue("theme", UISettings::values.theme); + qt_config->beginGroup("Updater"); + qt_config->setValue("check_for_update_on_start", UISettings::values.check_for_update_on_start); + qt_config->setValue("update_on_close", UISettings::values.update_on_close); + qt_config->endGroup(); + qt_config->beginGroup("UILayout"); qt_config->setValue("geometry", UISettings::values.geometry); qt_config->setValue("state", UISettings::values.state); diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index 939379717..f8f6c305d 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -20,6 +20,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) this->setConfiguration(); ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->updateBox->setVisible(UISettings::values.updater_found); } ConfigureGeneral::~ConfigureGeneral() {} @@ -29,6 +30,9 @@ void ConfigureGeneral::setConfiguration() { ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit); + ui->toggle_update_check->setChecked(UISettings::values.check_for_update_on_start); + ui->toggle_auto_update->setChecked(UISettings::values.update_on_close); + // The first item is "auto-select" with actual value -1, so plus one here will do the trick ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1); @@ -40,6 +44,10 @@ void ConfigureGeneral::applyConfiguration() { UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.theme = ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); + + UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked(); + UISettings::values.update_on_close = ui->toggle_auto_update->isChecked(); + Settings::values.region_value = ui->region_combobox->currentIndex() - 1; Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::Apply(); diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui index eedf2cbb0..00e5b49f5 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -25,16 +25,16 @@ - + - Search sub-directories for games + Confirm exit while emulation is running - + - Confirm exit while emulation is running + Search sub-directories for games @@ -44,24 +44,51 @@ - - - Performance - - - - - - - - Enable CPU JIT - - - - - + + + Updates + + + + + + + + Check for updates on start + + + + + + + Silently auto update after closing + + + - + + + + + + + + Performance + + + + + + + + Enable CPU JIT + + + + + + + @@ -149,8 +176,7 @@ - - + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 00230d4bd..5976e092b 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -30,6 +30,7 @@ #include "citra_qt/hotkeys.h" #include "citra_qt/main.h" #include "citra_qt/ui_settings.h" +#include "citra_qt/updater/updater.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -100,6 +101,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { InitializeDebugWidgets(); InitializeRecentFileMenuActions(); InitializeHotkeys(); + ShowUpdaterWidgets(); SetDefaultUIGeometry(); RestoreUIState(); @@ -118,6 +120,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { // Show one-time "callout" messages to the user ShowCallouts(); + if (UISettings::values.check_for_update_on_start) { + CheckForUpdates(); + } + QStringList args = QApplication::arguments(); if (args.length() >= 2) { BootGame(args[1]); @@ -139,6 +145,10 @@ void GMainWindow::InitializeWidgets() { game_list = new GameList(this); ui.horizontalLayout->addWidget(game_list); + // Setup updater + updater = new Updater(this); + UISettings::values.updater_found = updater->HasUpdater(); + // Create status bar message_label = new QLabel(); // Configured separately for left alignment @@ -268,6 +278,13 @@ void GMainWindow::InitializeHotkeys() { }); } +void GMainWindow::ShowUpdaterWidgets() { + ui.action_Check_For_Updates->setVisible(UISettings::values.updater_found); + ui.action_Open_Maintenance_Tool->setVisible(UISettings::values.updater_found); + + connect(updater, &Updater::CheckUpdatesDone, this, &GMainWindow::OnUpdateFound); +} + void GMainWindow::SetDefaultUIGeometry() { // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half const QRect screenRect = QApplication::desktop()->screenGeometry(this); @@ -346,6 +363,10 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_FAQ, &QAction::triggered, []() { QDesktopServices::openUrl(QUrl("https://citra-emu.org/wiki/faq/")); }); connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnMenuAboutCitra); + connect(ui.action_Check_For_Updates, &QAction::triggered, this, + &GMainWindow::OnCheckForUpdates); + connect(ui.action_Open_Maintenance_Tool, &QAction::triggered, this, + &GMainWindow::OnOpenUpdater); } void GMainWindow::OnDisplayTitleBars(bool show) { @@ -368,6 +389,46 @@ void GMainWindow::OnDisplayTitleBars(bool show) { } } +void GMainWindow::OnCheckForUpdates() { + CheckForUpdates(); +} + +void GMainWindow::CheckForUpdates() { + if (updater->CheckForUpdates()) { + LOG_INFO(Frontend, "Update check started"); + } else { + LOG_WARNING(Frontend, "Unable to start check for updates"); + } +} + +void GMainWindow::OnUpdateFound(bool found, bool error) { + if (error) { + LOG_WARNING(Frontend, "Update check failed"); + return; + } + + if (!found) { + LOG_INFO(Frontend, "No updates found"); + return; + } + + LOG_INFO(Frontend, "Update found!"); + auto result = QMessageBox::question( + this, tr("Update available!"), + tr("An update for Citra is available. Do you wish to install it now?

" + "This will terminate emulation, if it is running."), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (result == QMessageBox::Yes) { + updater->LaunchUIOnExit(); + close(); + } +} + +void GMainWindow::OnOpenUpdater() { + updater->LaunchUI(); +} + bool GMainWindow::LoadROM(const QString& filename) { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 18354bdf7..fe7ff6c4a 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -2,8 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#ifndef _CITRA_QT_MAIN_HXX_ -#define _CITRA_QT_MAIN_HXX_ +#pragma once #include #include @@ -24,6 +23,7 @@ class GRenderWindow; class MicroProfileDialog; class ProfilerWidget; class RegistersWidget; +class Updater; class WaitTreeWidget; class AboutDialog; @@ -82,6 +82,8 @@ private: void ShutdownGame(); void ShowCallouts(); + void ShowUpdaterWidgets(); + void CheckForUpdates(); /** * Stores the filename in the recently loaded files list. @@ -134,6 +136,9 @@ private slots: void OnCoreError(Core::System::ResultStatus, std::string); /// Called whenever a user selects Help->About Citra void OnMenuAboutCitra(); + void OnUpdateFound(bool found, bool error); + void OnCheckForUpdates(); + void OnOpenUpdater(); private: void UpdateStatusBar(); @@ -166,6 +171,7 @@ private: GraphicsVertexShaderWidget* graphicsVertexShaderWidget; GraphicsTracingWidget* graphicsTracingWidget; WaitTreeWidget* waitTreeWidget; + Updater* updater; QAction* actions_recent_files[max_recent_files_item]; @@ -174,5 +180,3 @@ protected: void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; }; - -#endif // _CITRA_QT_MAIN_HXX_ diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 442904f01..417a8a6a6 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -96,6 +96,9 @@ &Help + + + @@ -211,6 +214,19 @@ Fullscreen
+ + + Modify Citra Install + + + Opens the maintenance tool to modify your Citra installation + + + + + Check for Updates + + diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h index ffbe14bc3..dba4c5d3c 100644 --- a/src/citra_qt/ui_settings.h +++ b/src/citra_qt/ui_settings.h @@ -39,6 +39,10 @@ struct Values { bool confirm_before_closing; bool first_start; + bool updater_found; + bool update_on_close; + bool check_for_update_on_start; + QString roms_path; QString symbols_path; QString gamedir; diff --git a/src/citra_qt/updater/updater.cpp b/src/citra_qt/updater/updater.cpp new file mode 100644 index 000000000..399300266 --- /dev/null +++ b/src/citra_qt/updater/updater.cpp @@ -0,0 +1,304 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. +// +// Based on the original work by Felix Barx +// Copyright (c) 2015, Felix Barz +// All rights reserved. + +#include +#include +#include +#include +#include +#include "citra_qt/ui_settings.h" +#include "citra_qt/updater/updater.h" +#include "citra_qt/updater/updater_p.h" +#include "common/logging/log.h" + +#ifdef Q_OS_OSX +#define DEFAULT_TOOL_PATH QStringLiteral("../../../../maintenancetool") +#else +#define DEFAULT_TOOL_PATH QStringLiteral("../maintenancetool") +#endif + +Updater::Updater(QObject* parent) : Updater(DEFAULT_TOOL_PATH, parent) {} + +Updater::Updater(const QString& maintenance_tool_path, QObject* parent) + : QObject(parent), backend(std::make_unique(this)) { + backend->tool_path = UpdaterPrivate::ToSystemExe(maintenance_tool_path); +} + +Updater::~Updater() = default; + +bool Updater::ExitedNormally() const { + return backend->normal_exit; +} + +int Updater::ErrorCode() const { + return backend->last_error_code; +} + +QByteArray Updater::ErrorLog() const { + return backend->last_error_log; +} + +bool Updater::IsRunning() const { + return backend->running; +} + +QList Updater::LatestUpdateInfo() const { + return backend->update_info; +} + +bool Updater::HasUpdater() const { + return backend->HasUpdater(); +} + +bool Updater::CheckForUpdates() { + return backend->StartUpdateCheck(); +} + +void Updater::AbortUpdateCheck(int max_delay, bool async) { + backend->StopUpdateCheck(max_delay, async); +} + +void Updater::LaunchUI() { + backend->LaunchUI(); +} + +void Updater::SilentlyUpdate() { + backend->SilentlyUpdate(); +} + +void Updater::LaunchUIOnExit() { + backend->LaunchUIOnExit(); +} + +Updater::UpdateInfo::UpdateInfo() = default; + +Updater::UpdateInfo::UpdateInfo(const Updater::UpdateInfo&) = default; + +Updater::UpdateInfo::UpdateInfo(QString name, QString version, quint64 size) + : name(std::move(name)), version(std::move(version)), size(size) {} + +UpdaterPrivate::UpdaterPrivate(Updater* parent_ptr) : QObject(nullptr), parent(parent_ptr) { + connect(qApp, &QCoreApplication::aboutToQuit, this, &UpdaterPrivate::AboutToExit, + Qt::DirectConnection); + qRegisterMetaType("QProcess::ExitStatus"); +} + +UpdaterPrivate::~UpdaterPrivate() { + if (main_process && main_process->state() != QProcess::NotRunning) { + main_process->kill(); + main_process->waitForFinished(1000); + } +} + +QString UpdaterPrivate::ToSystemExe(QString base_path) { +#if defined(Q_OS_WIN32) + if (!base_path.endsWith(QStringLiteral(".exe"))) + return base_path + QStringLiteral(".exe"); + else + return base_path; +#elif defined(Q_OS_OSX) + if (base_path.endsWith(QStringLiteral(".app"))) + base_path.truncate(base_path.lastIndexOf(QStringLiteral("."))); + return base_path + QStringLiteral(".app/Contents/MacOS/") + QFileInfo(base_path).fileName(); +#elif defined(Q_OS_UNIX) + return base_path; +#endif +} + +bool UpdaterPrivate::HasUpdater() const { + QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); + return tool_info.exists(); +} + +bool UpdaterPrivate::StartUpdateCheck() { + if (running || !HasUpdater()) { + return false; + } + + update_info.clear(); + normal_exit = true; + last_error_code = EXIT_SUCCESS; + last_error_log.clear(); + + QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); + main_process = new QProcess(this); + main_process->setProgram(tool_info.absoluteFilePath()); + main_process->setArguments({QStringLiteral("--checkupdates"), QStringLiteral("-v")}); + + connect(main_process, + static_cast(&QProcess::finished), this, + &UpdaterPrivate::UpdaterReady, Qt::QueuedConnection); + connect(main_process, static_cast(&QProcess::error), + this, &UpdaterPrivate::UpdaterError, Qt::QueuedConnection); + + main_process->start(QIODevice::ReadOnly); + running = true; + + emit parent->UpdateInfoChanged(update_info); + emit parent->RunningChanged(true); + return true; +} + +void UpdaterPrivate::StopUpdateCheck(int delay, bool async) { + if (main_process == nullptr || main_process->state() == QProcess::NotRunning) { + return; + } + + if (delay > 0) { + main_process->terminate(); + if (async) { + QTimer *timer = new QTimer(this); + timer->setSingleShot(true); + + connect(timer, &QTimer::timeout, [=]() { + StopUpdateCheck(0, false); + timer->deleteLater(); + }); + + timer->start(delay); + } else { + if (!main_process->waitForFinished(delay)) { + main_process->kill(); + main_process->waitForFinished(100); + } + } + } else { + main_process->kill(); + main_process->waitForFinished(100); + } +} + +XMLParseResult UpdaterPrivate::ParseResult(const QByteArray& output, + QList& out) { + const auto out_string = QString::fromUtf8(output); + const auto xml_begin = out_string.indexOf(QStringLiteral("")); + if (xml_begin < 0) + return XMLParseResult::NoUpdate; + const auto xml_end = out_string.indexOf(QStringLiteral(""), xml_begin); + if (xml_end < 0) + return XMLParseResult::NoUpdate; + + QList updates; + QXmlStreamReader reader(out_string.mid(xml_begin, (xml_end + 10) - xml_begin)); + + reader.readNextStartElement(); + // should always work because it was search for + if (reader.name() != QStringLiteral("updates")) { + return XMLParseResult::InvalidXML; + } + + while (reader.readNextStartElement()) { + if (reader.name() != QStringLiteral("update")) + return XMLParseResult::InvalidXML; + + auto ok = false; + Updater::UpdateInfo info( + reader.attributes().value(QStringLiteral("name")).toString(), + reader.attributes().value(QStringLiteral("version")).toString(), + reader.attributes().value(QStringLiteral("size")).toULongLong(&ok)); + + if (info.name.isEmpty() || info.version.isNull() || !ok) + return XMLParseResult::InvalidXML; + if (reader.readNextStartElement()) + return XMLParseResult::InvalidXML; + + updates.append(info); + } + + if (reader.hasError()) { + LOG_ERROR(Frontend, "Cannot read xml for update: %s", + reader.errorString().toStdString().c_str()); + return XMLParseResult::InvalidXML; + } + + out = updates; + return XMLParseResult::Success; +} + +void UpdaterPrivate::UpdaterReady(int exit_code, QProcess::ExitStatus exit_status) { + if (main_process == nullptr) { + return; + } + + if (exit_status != QProcess::NormalExit) { + UpdaterError(QProcess::Crashed); + return; + } + + normal_exit = true; + last_error_code = exit_code; + last_error_log = main_process->readAllStandardError(); + const auto update_out = main_process->readAllStandardOutput(); + main_process->deleteLater(); + main_process = nullptr; + + running = false; + emit parent->RunningChanged(false); + + QList update_info; + auto err = ParseResult(update_out, update_info); + bool has_error = false; + + if (err == XMLParseResult::Success) { + if (!update_info.isEmpty()) + emit parent->UpdateInfoChanged(update_info); + } else if (err == XMLParseResult::InvalidXML) { + has_error = true; + } + + emit parent->CheckUpdatesDone(!update_info.isEmpty(), has_error); +} + +void UpdaterPrivate::UpdaterError(QProcess::ProcessError error) { + if (main_process) { + normal_exit = false; + last_error_code = error; + last_error_log = main_process->errorString().toUtf8(); + main_process->deleteLater(); + main_process = nullptr; + + running = false; + emit parent->RunningChanged(false); + emit parent->CheckUpdatesDone(false, true); + } +} + +void UpdaterPrivate::LaunchWithArguments(const QStringList& args) { + if (!HasUpdater()) { + return; + } + + QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); + + if (!QProcess::startDetached(tool_info.absoluteFilePath(), args, tool_info.absolutePath())) { + LOG_WARNING(Frontend, "Unable to start program %s", + tool_info.absoluteFilePath().toStdString().c_str()); + } +} + +void UpdaterPrivate::LaunchUI() { + LOG_INFO(Frontend, "Launching update UI..."); + LaunchWithArguments(run_arguments); +} + +void UpdaterPrivate::SilentlyUpdate() { + LOG_INFO(Frontend, "Launching silent update..."); + LaunchWithArguments(silent_arguments); +} + +void UpdaterPrivate::AboutToExit() { + if (launch_ui_on_exit) { + LaunchUI(); + } else if (UISettings::values.update_on_close) { + SilentlyUpdate(); + } +} + +void UpdaterPrivate::LaunchUIOnExit() { + launch_ui_on_exit = true; +} diff --git a/src/citra_qt/updater/updater.h b/src/citra_qt/updater/updater.h new file mode 100644 index 000000000..a7220dd91 --- /dev/null +++ b/src/citra_qt/updater/updater.h @@ -0,0 +1,120 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. +// +// Based on the original work by Felix Barx +// Copyright (c) 2015, Felix Barz +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of QtAutoUpdater nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +class UpdaterPrivate; + +/// The main updater. Can check for updates and run the maintenancetool as updater +class Updater : public QObject { + Q_OBJECT; + + /// Specifies whether the updater is currently checking for updates or not + Q_PROPERTY(bool running READ IsRunning NOTIFY RunningChanged); + /// Holds extended information about the last update check + Q_PROPERTY(QList update_info READ LatestUpdateInfo NOTIFY UpdateInfoChanged); + +public: + /// Provides information about updates for components + struct UpdateInfo { + /// The name of the component that has an update + QString name; + /// The new version for that compontent + QString version; + /// The update download size (in Bytes) + quint64 size = 0; + + /// Default Constructor + UpdateInfo(); + /// Copy Constructor + UpdateInfo(const UpdateInfo& other); + /// Constructor that takes name, version and size + UpdateInfo(QString name, QString version, quint64 size); + }; + + /// Default constructor + explicit Updater(QObject* parent = nullptr); + /// Constructor with an explicitly set path + explicit Updater(const QString& maintenance_tool_path, QObject* parent = nullptr); + /// Destroys the updater and kills the update check (if running) + ~Updater(); + + /// Returns `true`, if the updater exited normally + bool ExitedNormally() const; + /// Returns the mainetancetools error code of the last update + int ErrorCode() const; + /// returns the error output (stderr) of the last update + QByteArray ErrorLog() const; + + /// readAcFn{Updater::running} + bool IsRunning() const; + /// readAcFn{Updater::updateInfo} + QList LatestUpdateInfo() const; + + /// Launches the updater UI formally + void LaunchUI(); + + /// Silently updates the application in the background + void SilentlyUpdate(); + + /// Checks to see if a updater application is available + bool HasUpdater() const; + + /// Instead of silently updating, explictly open the UI on shutdown + void LaunchUIOnExit(); + +public slots: + /// Starts checking for updates + bool CheckForUpdates(); + /// Aborts checking for updates + void AbortUpdateCheck(int max_delay = 5000, bool async = false); + +signals: + /// Will be emitted as soon as the updater finished checking for updates + void CheckUpdatesDone(bool has_updates, bool has_error); + + /// notifyAcFn{Updater::running} + void RunningChanged(bool running); + /// notifyAcFn{Updater::updateInfo} + void UpdateInfoChanged(QList update_info); + +private: + std::unique_ptr backend; +}; diff --git a/src/citra_qt/updater/updater_p.h b/src/citra_qt/updater/updater_p.h new file mode 100644 index 000000000..210400535 --- /dev/null +++ b/src/citra_qt/updater/updater_p.h @@ -0,0 +1,66 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. +// +// Based on the original work by Felix Barx +// Copyright (c) 2015, Felix Barz +// All rights reserved. + +#pragma once + +#include +#include "citra_qt/updater/updater.h" + +enum class XMLParseResult { + Success, + NoUpdate, + InvalidXML, +}; + +class UpdaterPrivate : public QObject { + Q_OBJECT; + +public: + explicit UpdaterPrivate(Updater* parent_ptr); + ~UpdaterPrivate(); + + static QString ToSystemExe(QString base_path); + + bool HasUpdater() const; + + bool StartUpdateCheck(); + + void LaunchWithArguments(const QStringList& args); + void LaunchUI(); + void SilentlyUpdate(); + + void LaunchUIOnExit(); + +public slots: + void StopUpdateCheck(int delay, bool async); + void UpdaterReady(int exit_code, QProcess::ExitStatus exit_status); + void UpdaterError(QProcess::ProcessError error); + + void AboutToExit(); + +private: + XMLParseResult ParseResult(const QByteArray& output, QList& out); + + Updater* parent; + + QString tool_path{}; + QList update_info{}; + bool normal_exit = true; + int last_error_code = 0; + QByteArray last_error_log = EXIT_SUCCESS; + + bool running = false; + QProcess* main_process = nullptr; + + bool launch_ui_on_exit = false; + + QStringList run_arguments{"--updater"}; + QStringList silent_arguments{"--silentUpdate"}; + + friend class Updater; +}; From 7361ee97500d2ac893f4386af0ec5904df48b041 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 14 Oct 2017 18:35:45 +1100 Subject: [PATCH 17/19] Defer update prompt if emulating, and show no update found on explicit click --- src/citra_qt/main.cpp | 30 ++++++++++++++++++++++++++++++ src/citra_qt/main.h | 5 +++++ 2 files changed, 35 insertions(+) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 5976e092b..3149ab877 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -390,6 +390,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) { } void GMainWindow::OnCheckForUpdates() { + explicit_update_check = true; CheckForUpdates(); } @@ -409,10 +410,29 @@ void GMainWindow::OnUpdateFound(bool found, bool error) { if (!found) { LOG_INFO(Frontend, "No updates found"); + + // If the user explicitly clicked the "Check for Updates" button, we are + // going to want to show them a prompt anyway. + if (explicit_update_check) { + explicit_update_check = false; + ShowNoUpdatePrompt(); + } + return; + } + + if (emulation_running && !explicit_update_check) { + LOG_INFO(Frontend, "Update found, deferring as game is running"); + defer_update_prompt = true; return; } LOG_INFO(Frontend, "Update found!"); + explicit_update_check = false; + + ShowUpdatePrompt(); +} + +void GMainWindow::ShowUpdatePrompt() { auto result = QMessageBox::question( this, tr("Update available!"), tr("An update for Citra is available. Do you wish to install it now?

" @@ -425,6 +445,11 @@ void GMainWindow::OnUpdateFound(bool found, bool error) { } } +void GMainWindow::ShowNoUpdatePrompt() { + QMessageBox::information(this, tr("No update found"), tr("No update has been found for Citra."), + QMessageBox::Ok, QMessageBox::Ok); +} + void GMainWindow::OnOpenUpdater() { updater->LaunchUI(); } @@ -584,6 +609,11 @@ void GMainWindow::ShutdownGame() { emu_frametime_label->setVisible(false); emulation_running = false; + + if (defer_update_prompt) { + defer_update_prompt = false; + ShowUpdatePrompt(); + } } void GMainWindow::StoreRecentFile(const QString& filename) { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index fe7ff6c4a..878b2becf 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -83,6 +83,8 @@ private: void ShowCallouts(); void ShowUpdaterWidgets(); + void ShowUpdatePrompt(); + void ShowNoUpdatePrompt(); void CheckForUpdates(); /** @@ -173,6 +175,9 @@ private: WaitTreeWidget* waitTreeWidget; Updater* updater; + bool explicit_update_check = false; + bool defer_update_prompt = false; + QAction* actions_recent_files[max_recent_files_item]; protected: From 53a88a0e13e95cc5da737ab64222fa6d7a1ead95 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 14 Oct 2017 22:10:05 +1100 Subject: [PATCH 18/19] Fix edge case for checking for updates when already deferred --- src/citra_qt/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 3149ab877..0e9e8f1c5 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -433,6 +433,8 @@ void GMainWindow::OnUpdateFound(bool found, bool error) { } void GMainWindow::ShowUpdatePrompt() { + defer_update_prompt = false; + auto result = QMessageBox::question( this, tr("Update available!"), tr("An update for Citra is available. Do you wish to install it now?

" @@ -611,7 +613,6 @@ void GMainWindow::ShutdownGame() { emulation_running = false; if (defer_update_prompt) { - defer_update_prompt = false; ShowUpdatePrompt(); } } From b579bf0cc20ece7602b2ff77b6941140a85a1f65 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 31 Oct 2017 15:41:18 +1100 Subject: [PATCH 19/19] Fixed up method docs for updater --- src/citra_qt/updater/updater.cpp | 14 ++--- src/citra_qt/updater/updater.h | 89 +++++++++++++++++++++++++------- src/citra_qt/updater/updater_p.h | 2 +- 3 files changed, 77 insertions(+), 28 deletions(-) diff --git a/src/citra_qt/updater/updater.cpp b/src/citra_qt/updater/updater.cpp index 399300266..b5a448ca3 100644 --- a/src/citra_qt/updater/updater.cpp +++ b/src/citra_qt/updater/updater.cpp @@ -152,15 +152,15 @@ void UpdaterPrivate::StopUpdateCheck(int delay, bool async) { if (delay > 0) { main_process->terminate(); if (async) { - QTimer *timer = new QTimer(this); - timer->setSingleShot(true); + QTimer* timer = new QTimer(this); + timer->setSingleShot(true); - connect(timer, &QTimer::timeout, [=]() { - StopUpdateCheck(0, false); - timer->deleteLater(); - }); + connect(timer, &QTimer::timeout, [=]() { + StopUpdateCheck(0, false); + timer->deleteLater(); + }); - timer->start(delay); + timer->start(delay); } else { if (!main_process->waitForFinished(delay)) { main_process->kill(); diff --git a/src/citra_qt/updater/updater.h b/src/citra_qt/updater/updater.h index a7220dd91..e54590534 100644 --- a/src/citra_qt/updater/updater.h +++ b/src/citra_qt/updater/updater.h @@ -61,58 +61,107 @@ public: /// The update download size (in Bytes) quint64 size = 0; - /// Default Constructor + /** + * Default Constructor + */ UpdateInfo(); - /// Copy Constructor + + /** + * Copy Constructor + */ UpdateInfo(const UpdateInfo& other); - /// Constructor that takes name, version and size + + /** + * Constructor that takes name, version and size. + */ UpdateInfo(QString name, QString version, quint64 size); }; - /// Default constructor + /** + * Default constructor + **/ explicit Updater(QObject* parent = nullptr); - /// Constructor with an explicitly set path + + /** + * Constructor with an explicitly set path + **/ explicit Updater(const QString& maintenance_tool_path, QObject* parent = nullptr); - /// Destroys the updater and kills the update check (if running) + + /** + * Destroys the updater and kills the update check (if running) + **/ ~Updater(); - /// Returns `true`, if the updater exited normally + /** + * Returns `true`, if the updater exited normally + **/ bool ExitedNormally() const; - /// Returns the mainetancetools error code of the last update + + /** + * Returns the mainetancetools error code from the last update check, if any. + **/ int ErrorCode() const; - /// returns the error output (stderr) of the last update + + /** + * Returns the error output (stderr) of the last update + **/ QByteArray ErrorLog() const; - /// readAcFn{Updater::running} + /** + * Returns if a update check is running. + **/ bool IsRunning() const; - /// readAcFn{Updater::updateInfo} + + /** + * Returns the latest update information available, if any. + **/ QList LatestUpdateInfo() const; - /// Launches the updater UI formally + /** + * Launches the updater UI formally + **/ void LaunchUI(); - /// Silently updates the application in the background + /** + * Silently updates the application in the background + **/ void SilentlyUpdate(); - /// Checks to see if a updater application is available + /** + * Checks to see if a updater application is available + **/ bool HasUpdater() const; - /// Instead of silently updating, explictly open the UI on shutdown + /** + * Instead of silently updating, explictly open the UI on shutdown + **/ void LaunchUIOnExit(); public slots: - /// Starts checking for updates + /** + * Starts checking for updates + **/ bool CheckForUpdates(); - /// Aborts checking for updates + + /** + * Aborts checking for updates + **/ void AbortUpdateCheck(int max_delay = 5000, bool async = false); signals: - /// Will be emitted as soon as the updater finished checking for updates + /** + * Will be emitted as soon as the updater finished checking for updates + **/ void CheckUpdatesDone(bool has_updates, bool has_error); - /// notifyAcFn{Updater::running} + /** + * Emitted when a update check operation changes stage + **/ void RunningChanged(bool running); - /// notifyAcFn{Updater::updateInfo} + + /** + * Emitted when new update information has been found + **/ void UpdateInfoChanged(QList update_info); private: diff --git a/src/citra_qt/updater/updater_p.h b/src/citra_qt/updater/updater_p.h index 210400535..175a95623 100644 --- a/src/citra_qt/updater/updater_p.h +++ b/src/citra_qt/updater/updater_p.h @@ -29,6 +29,7 @@ public: bool HasUpdater() const; bool StartUpdateCheck(); + void StopUpdateCheck(int delay, bool async); void LaunchWithArguments(const QStringList& args); void LaunchUI(); @@ -37,7 +38,6 @@ public: void LaunchUIOnExit(); public slots: - void StopUpdateCheck(int delay, bool async); void UpdaterReady(int exit_code, QProcess::ExitStatus exit_status); void UpdaterError(QProcess::ProcessError error);