diff options
| -rw-r--r-- | .travis-build-docker.sh | 20 | ||||
| -rwxr-xr-x | .travis-build.sh | 10 | ||||
| -rwxr-xr-x | .travis-deps.sh | 30 | ||||
| -rwxr-xr-x | .travis-upload.sh | 11 | ||||
| -rw-r--r-- | .travis.yml | 16 | ||||
| -rw-r--r-- | appveyor.yml | 20 | ||||
| -rw-r--r-- | src/citra/default_ini.h | 6 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/core.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/service/apt/apt.cpp | 229 | ||||
| -rw-r--r-- | src/core/hle/service/apt/apt.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/dsp_dsp.cpp | 7 | ||||
| -rw-r--r-- | src/core/loader/ncch.cpp | 8 | ||||
| -rw-r--r-- | src/input_common/main.h | 2 | ||||
| -rw-r--r-- | src/input_common/sdl/sdl.cpp | 2 | ||||
| -rw-r--r-- | src/network/packet.cpp | 38 | ||||
| -rw-r--r-- | src/network/packet.h | 4 | ||||
| -rw-r--r-- | src/network/room.cpp | 84 | ||||
| -rw-r--r-- | src/network/room.h | 19 | ||||
| -rw-r--r-- | src/network/room_member.cpp | 128 | ||||
| -rw-r--r-- | src/network/room_member.h | 59 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 9 | 
22 files changed, 573 insertions, 142 deletions
diff --git a/.travis-build-docker.sh b/.travis-build-docker.sh new file mode 100644 index 000000000..ca6fae42b --- /dev/null +++ b/.travis-build-docker.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -e +set -x + +cd /citra + +apt-get update +apt-get install -y build-essential libsdl2-dev qtbase5-dev libqt5opengl5-dev libcurl4-openssl-dev libssl-dev wget git + +# 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 .. -DCMAKE_BUILD_TYPE=Release +make -j4 + +ctest -VV -C Release diff --git a/.travis-build.sh b/.travis-build.sh index df6e236b6..64f5aed94 100755 --- a/.travis-build.sh +++ b/.travis-build.sh @@ -44,15 +44,7 @@ fi  #if OS is linux or is not set  if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then -    export CC=gcc-6 -    export CXX=g++-6 -    export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH - -    mkdir build && cd build -    cmake .. -    make -j4 - -    ctest -VV -C Release +    docker run -v $(pwd):/citra ubuntu:16.04 /bin/bash /citra/.travis-build-docker.sh  elif [ "$TRAVIS_OS_NAME" = "osx" ]; then      set -o pipefail diff --git a/.travis-deps.sh b/.travis-deps.sh index 25a287c7f..0cee68041 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh @@ -5,35 +5,7 @@ set -x  #if OS is linux or is not set  if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then -    export CC=gcc-6 -    export CXX=g++-6 -    mkdir -p $HOME/.local - -    if [ ! -e $HOME/.local/bin/cmake ]; then -        echo "CMake not found in the cache, get and extract it..." -        curl -L http://www.cmake.org/files/v3.6/cmake-3.6.3-Linux-x86_64.tar.gz \ -            | tar -xz -C $HOME/.local --strip-components=1 -    else -        echo "Using cached CMake" -    fi - -    if [ ! -e $HOME/.local/lib/libSDL2.la ]; then -        echo "SDL2 not found in cache, get and build it..." -        wget http://libsdl.org/release/SDL2-2.0.5.tar.gz -O - | tar xz -        cd SDL2-2.0.5 -        ./configure --prefix=$HOME/.local -        make -j4 && make install -    else -        echo "Using cached SDL2" -    fi - -    export DEBIAN_FRONTEND=noninteractive -    # Amazing placebo security -    curl http://apt.llvm.org/llvm-snapshot.gpg.key | sudo -E apt-key add - -    sudo -E add-apt-repository "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main" -    sudo -E apt-get -yq update -    sudo -E apt-get -yq install clang-format-3.9 - +    docker pull ubuntu:16.04  elif [ "$TRAVIS_OS_NAME" = "osx" ]; then      brew update      brew install qt5 sdl2 dylibbundler p7zip diff --git a/.travis-upload.sh b/.travis-upload.sh index 17959b0e1..8c1fa21c5 100755 --- a/.travis-upload.sh +++ b/.travis-upload.sh @@ -123,9 +123,16 @@ cp README.md "$REV_NAME"  tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$REV_NAME" -mv "$REV_NAME" nightly +# Find out what release we are building +if [ -z $TRAVIS_TAG ]; then +    RELEASE_NAME=head +else +    RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1) +fi + +mv "$REV_NAME" $RELEASE_NAME -7z a "$REV_NAME.7z" nightly +7z a "$REV_NAME.7z" $RELEASE_NAME  # move the compiled archive into the artifacts directory to be uploaded by travis releases  mv "$ARCHIVE_NAME" artifacts/ diff --git a/.travis.yml b/.travis.yml index 3da22eb5d..b92d7f236 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,23 +8,15 @@ matrix:        sudo: false        osx_image: xcode7.3 +services: +  - docker +  addons:    apt: -    sources: -      - ubuntu-toolchain-r-test      packages: -      - gcc-6 -      - g++-6 -      - qt5-default -      - libqt5opengl5-dev -      - xorg-dev -      - lib32stdc++6 # For CMake +      - clang-format-3.9        - p7zip-full -cache: -  directories: -    - "$HOME/.local" -  install: "./.travis-deps.sh"  script: "./.travis-build.sh"  after_success: "./.travis-upload.sh" diff --git a/appveyor.yml b/appveyor.yml index eb4e7df87..94e9969f5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -46,13 +46,21 @@ after_build:          7z a -tzip $MSVC_BUILD_PDB .\build\bin\release\*.pdb          rm .\build\bin\release\*.pdb -        mkdir nightly -        Copy-Item .\build\bin\release\* -Destination nightly -Recurse -        Copy-Item .\license.txt -Destination nightly -        Copy-Item .\README.md -Destination nightly +        # Find out which kind of release we are producing by tag name +        if ($env:APPVEYOR_REPO_TAG_NAME) { +            $RELEASE_DIST, $RELEASE_VERSION = $env:APPVEYOR_REPO_TAG_NAME.split('-') +        } else { +            # There is no repo tag - make assumptions +            $RELEASE_DIST = "head" +        } -        7z a -tzip $MSVC_BUILD_NAME nightly\* -        7z a $MSVC_SEVENZIP nightly +        mkdir $RELEASE_DIST +        Copy-Item .\build\bin\release\* -Destination $RELEASE_DIST -Recurse +        Copy-Item .\license.txt -Destination $RELEASE_DIST +        Copy-Item .\README.md -Destination $RELEASE_DIST + +        7z a -tzip $MSVC_BUILD_NAME $RELEASE_DIST\* +        7z a $MSVC_SEVENZIP $RELEASE_DIST  test_script:    - cd build && ctest -VV -C Release && cd .. diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index a12498e0f..b0a0ebd3b 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -12,7 +12,7 @@ const char* sdl2_config_file = R"(  # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."  # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values -# for button input, the following devices are avaible: +# for button input, the following devices are available:  #  - "keyboard" (default) for keyboard input. Required parameters:  #      - "code": the code of the key to bind  #  - "sdl" for joystick input using SDL. Required parameters: @@ -21,7 +21,7 @@ const char* sdl2_config_file = R"(  #      - "hat"(optional): the index of the hat to bind as direction buttons  #      - "axis"(optional): the index of the axis to bind  #      - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" -#      - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is +#      - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is  #          triggered if the axis value crosses  #      - "direction"(only used for axis): "+" means the button is triggered when the axis value  #          is greater than the threshold; "-" means the button is triggered when the axis value @@ -42,7 +42,7 @@ button_zl=  button_zr=  button_home= -# for analog input, the following devices are avaible: +# for analog input, the following devices are available:  #  - "analog_from_button" (default) for emulating analog input from direction buttons.  Required parameters:  #      - "up", "down", "left", "right": sub-devices for each direction.  #          Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 360f407f3..0a6f97e4b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -388,7 +388,7 @@ set(HEADERS  create_directory_groups(${SRCS} ${HEADERS})  add_library(core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)  target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)  if (ENABLE_WEB_SERVICE)      target_link_libraries(core PUBLIC json-headers web_service) diff --git a/src/core/core.cpp b/src/core/core.cpp index d08f18623..5332318cf 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -19,6 +19,7 @@  #include "core/loader/loader.h"  #include "core/memory_setup.h"  #include "core/settings.h" +#include "network/network.h"  #include "video_core/video_core.h"  namespace Core { @@ -188,6 +189,10 @@ void System::Shutdown() {      cpu_core = nullptr;      app_loader = nullptr;      telemetry_session = nullptr; +    if (auto room_member = Network::GetRoomMember().lock()) { +        Network::GameInfo game_info{}; +        room_member->SendGameInfo(game_info); +    }      LOG_DEBUG(Core, "Shutdown OK");  } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 0109fa2b2..58d94768c 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -34,8 +34,6 @@ static bool shared_font_loaded = false;  static bool shared_font_relocated = false;  static Kernel::SharedPtr<Kernel::Mutex> lock; -static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event -static Kernel::SharedPtr<Kernel::Event> parameter_event;    ///< APT parameter event  static u32 cpu_percent; ///< CPU time available to the running application @@ -44,32 +42,160 @@ static u8 unknown_ns_state_field;  static ScreencapPostPermission screen_capture_post_permission; -/// Parameter data to be returned in the next call to Glance/ReceiveParameter +/// Parameter data to be returned in the next call to Glance/ReceiveParameter. +/// TODO(Subv): Use std::optional once we migrate to C++17.  static boost::optional<MessageParameter> next_parameter; +enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 }; + +static constexpr size_t NumAppletSlot = 4; + +enum class AppletSlot : u8 { +    Application, +    SystemApplet, +    HomeMenu, +    LibraryApplet, + +    // An invalid tag +    Error, +}; + +union AppletAttributes { +    u32 raw; + +    BitField<0, 3, u32> applet_pos; + +    AppletAttributes() : raw(0) {} +    AppletAttributes(u32 attributes) : raw(attributes) {} +}; + +struct AppletSlotData { +    AppletId applet_id; +    AppletSlot slot; +    bool registered; +    AppletAttributes attributes; +    Kernel::SharedPtr<Kernel::Event> notification_event; +    Kernel::SharedPtr<Kernel::Event> parameter_event; +}; + +// Holds data about the concurrently running applets in the system. +static std::array<AppletSlotData, NumAppletSlot> applet_slots = {}; + +// This overload returns nullptr if no applet with the specified id has been started. +static AppletSlotData* GetAppletSlotData(AppletId id) { +    auto GetSlot = [](AppletSlot slot) -> AppletSlotData* { +        return &applet_slots[static_cast<size_t>(slot)]; +    }; + +    if (id == AppletId::Application) { +        auto* slot = GetSlot(AppletSlot::Application); +        if (slot->applet_id != AppletId::None) +            return slot; + +        return nullptr; +    } + +    if (id == AppletId::AnySystemApplet) { +        auto* system_slot = GetSlot(AppletSlot::SystemApplet); +        if (system_slot->applet_id != AppletId::None) +            return system_slot; + +        // The Home Menu is also a system applet, but it lives in its own slot to be able to run +        // concurrently with other system applets. +        auto* home_slot = GetSlot(AppletSlot::HomeMenu); +        if (home_slot->applet_id != AppletId::None) +            return home_slot; + +        return nullptr; +    } + +    if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) { +        auto* slot = GetSlot(AppletSlot::LibraryApplet); +        if (slot->applet_id == AppletId::None) +            return nullptr; + +        u32 applet_pos = slot->attributes.applet_pos; + +        if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library)) +            return slot; + +        if (id == AppletId::AnySysLibraryApplet && +            applet_pos == static_cast<u32>(AppletPos::SysLibrary)) +            return slot; + +        return nullptr; +    } + +    if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) { +        auto* slot = GetSlot(AppletSlot::HomeMenu); +        if (slot->applet_id != AppletId::None) +            return slot; + +        return nullptr; +    } + +    for (auto& slot : applet_slots) { +        if (slot.applet_id == id) +            return &slot; +    } + +    return nullptr; +} + +static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) { +    // Mapping from AppletPos to AppletSlot +    static constexpr std::array<AppletSlot, 6> applet_position_slots = { +        AppletSlot::Application,   AppletSlot::LibraryApplet, AppletSlot::SystemApplet, +        AppletSlot::LibraryApplet, AppletSlot::Error,         AppletSlot::LibraryApplet}; + +    u32 applet_pos = attributes.applet_pos; +    if (applet_pos >= applet_position_slots.size()) +        return nullptr; + +    AppletSlot slot = applet_position_slots[applet_pos]; + +    if (slot == AppletSlot::Error) +        return nullptr; + +    return &applet_slots[static_cast<size_t>(slot)]; +} +  void SendParameter(const MessageParameter& parameter) {      next_parameter = parameter; -    // Signal the event to let the application know that a new parameter is ready to be read -    parameter_event->Signal(); +    // Signal the event to let the receiver know that a new parameter is ready to be read +    auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id)); +    ASSERT(slot_data); + +    slot_data->parameter_event->Signal();  }  void Initialize(Service::Interface* self) {      IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080      u32 app_id = rp.Pop<u32>(); -    u32 flags = rp.Pop<u32>(); -    IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); -    rb.Push(RESULT_SUCCESS); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), -                       Kernel::g_handle_table.Create(parameter_event).Unwrap()); +    u32 attributes = rp.Pop<u32>(); -    // TODO(bunnei): Check if these events are cleared every time Initialize is called. -    notification_event->Clear(); -    parameter_event->Clear(); +    LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes); + +    auto* const slot_data = GetAppletSlotData(attributes); + +    // Note: The real NS service does not check if the attributes value is valid before accessing +    // the data in the array +    ASSERT_MSG(slot_data, "Invalid application attributes"); + +    if (slot_data->registered) { +        IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); +        rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, +                           ErrorSummary::InvalidState, ErrorLevel::Status)); +        return; +    } -    ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); -    lock->Release(); +    slot_data->applet_id = static_cast<AppletId>(app_id); +    slot_data->attributes.raw = attributes; -    LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); +    IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); +    rb.Push(RESULT_SUCCESS); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), +                       Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());  }  void GetSharedFont(Service::Interface* self) { @@ -120,7 +246,12 @@ void GetLockHandle(Service::Interface* self) {      // this will cause the app to wait until parameter_event is signaled.      u32 applet_attributes = rp.Pop<u32>();      IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); -    rb.Push(RESULT_SUCCESS);    // No error +    rb.Push(RESULT_SUCCESS); // No error + +    // TODO(Subv): The output attributes should have an AppletPos of either Library or System | +    // Library (depending on the type of the last launched applet) if the input attributes' +    // AppletPos has the Library bit set. +      rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.      rb.Push<u32>(0);            // Least significant bit = power button state      Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); @@ -133,10 +264,22 @@ void GetLockHandle(Service::Interface* self) {  void Enable(Service::Interface* self) {      IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040      u32 attributes = rp.Pop<u32>(); + +    LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes); +      IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); -    rb.Push(RESULT_SUCCESS);   // No error -    parameter_event->Signal(); // Let the application know that it has been started -    LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); + +    auto* const slot_data = GetAppletSlotData(attributes); + +    if (!slot_data) { +        rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet, +                           ErrorSummary::InvalidState, ErrorLevel::Status)); +        return; +    } + +    slot_data->registered = true; + +    rb.Push(RESULT_SUCCESS);  }  void GetAppletManInfo(Service::Interface* self) { @@ -154,22 +297,27 @@ void GetAppletManInfo(Service::Interface* self) {  void IsRegistered(Service::Interface* self) {      IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 -    u32 app_id = rp.Pop<u32>(); +    AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());      IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);      rb.Push(RESULT_SUCCESS); // No error -    // TODO(Subv): An application is considered "registered" if it has already called APT::Enable -    // handle this properly once we implement multiprocess support. -    bool is_registered = false; // Set to not registered by default +    auto* const slot_data = GetAppletSlotData(app_id); + +    // Check if an LLE applet was registered first, then fallback to HLE applets +    bool is_registered = slot_data && slot_data->registered; -    if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { -        is_registered = HLE::Applets::IsLibraryAppletRunning(); -    } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { -        is_registered = true; // Set to registered +    if (!is_registered) { +        if (app_id == AppletId::AnyLibraryApplet) { +            is_registered = HLE::Applets::IsLibraryAppletRunning(); +        } else if (auto applet = HLE::Applets::Applet::Get(app_id)) { +            // The applet exists, set it as registered. +            is_registered = true; +        }      } +      rb.Push(is_registered); -    LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); +    LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));  }  void InquireNotification(Service::Interface* self) { @@ -864,14 +1012,23 @@ void Init() {      screen_capture_post_permission =          ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value -    // TODO(bunnei): Check if these are created in Initialize or on APT process startup. -    notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); -    parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); +    for (size_t slot = 0; slot < applet_slots.size(); ++slot) { +        auto& slot_data = applet_slots[slot]; +        slot_data.slot = static_cast<AppletSlot>(slot); +        slot_data.applet_id = AppletId::None; +        slot_data.attributes.raw = 0; +        slot_data.registered = false; +        slot_data.notification_event = +            Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification"); +        slot_data.parameter_event = +            Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); +    }      // Initialize the parameter to wake up the application.      next_parameter.emplace();      next_parameter->signal = static_cast<u32>(SignalType::Wakeup);      next_parameter->destination_id = static_cast<u32>(AppletId::Application); +    applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal();  }  void Shutdown() { @@ -879,8 +1036,12 @@ void Shutdown() {      shared_font_loaded = false;      shared_font_relocated = false;      lock = nullptr; -    notification_event = nullptr; -    parameter_event = nullptr; + +    for (auto& slot : applet_slots) { +        slot.registered = false; +        slot.notification_event = nullptr; +        slot.parameter_event = nullptr; +    }      next_parameter = boost::none; diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 106754853..96b28b438 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -72,6 +72,8 @@ enum class SignalType : u32 {  /// App Id's used by APT functions  enum class AppletId : u32 { +    None = 0, +    AnySystemApplet = 0x100,      HomeMenu = 0x101,      AlternateMenu = 0x103,      Camera = 0x110, @@ -83,6 +85,7 @@ enum class AppletId : u32 {      Miiverse = 0x117,      MiiversePost = 0x118,      AmiiboSettings = 0x119, +    AnySysLibraryApplet = 0x200,      SoftwareKeyboard1 = 0x201,      Ed1 = 0x202,      PnoteApp = 0x204, @@ -119,8 +122,9 @@ enum class ScreencapPostPermission : u32 {  namespace ErrCodes {  enum {      ParameterPresent = 2, +    InvalidAppletSlot = 4,  }; -} +} // namespace ErrCodes  /// Send a parameter to the currently-running application, which will read it via ReceiveParameter  void SendParameter(const MessageParameter& parameter); diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 7d746054f..42f8950f9 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -147,9 +147,10 @@ static void LoadComponent(Service::Interface* self) {      LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64,               Common::ComputeHash64(component_data.data(), component_data.size()));      // Some versions of the firmware have the location of DSP structures listed here. -    ASSERT(size > 0x37C); -    LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, -             Common::ComputeHash64(component_data.data() + 0x340, 60)); +    if (size > 0x37C) { +        LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, +                 Common::ComputeHash64(component_data.data() + 0x340, 60)); +    }      LOG_WARNING(Service_DSP,                  "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index c007069a9..7aff7f29b 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -20,6 +20,7 @@  #include "core/loader/ncch.h"  #include "core/loader/smdh.h"  #include "core/memory.h" +#include "network/network.h"  ////////////////////////////////////////////////////////////////////////////////////////////////////  // Loader namespace @@ -350,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() {      Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); +    if (auto room_member = Network::GetRoomMember().lock()) { +        Network::GameInfo game_info; +        ReadTitle(game_info.name); +        game_info.id = ncch_header.program_id; +        room_member->SendGameInfo(game_info); +    } +      is_loaded = true; // Set state to loaded      result = LoadExec(); // Load the executable into memory for booting diff --git a/src/input_common/main.h b/src/input_common/main.h index 140bbd014..e7fb3890e 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -11,7 +11,7 @@ namespace InputCommon {  /// Initializes and registers all built-in input device factories.  void Init(); -/// Unresisters all build-in input device factories and shut them down. +/// Deregisters all built-in input device factories and shuts them down.  void Shutdown();  class Keyboard; diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp index 756ee58b7..d404afa89 100644 --- a/src/input_common/sdl/sdl.cpp +++ b/src/input_common/sdl/sdl.cpp @@ -159,7 +159,7 @@ public:       *     - "axis"(optional): the index of the axis to bind       *     - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",       *         "down", "left" or "right" -     *     - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is +     *     - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is       *         triggered if the axis value crosses       *     - "direction"(only used for axis): "+" means the button is triggered when the axis value       *         is greater than the threshold; "-" means the button is triggered when the axis value diff --git a/src/network/packet.cpp b/src/network/packet.cpp index 660e92c0d..cc60f2fbc 100644 --- a/src/network/packet.cpp +++ b/src/network/packet.cpp @@ -13,6 +13,18 @@  namespace Network { +#ifndef htonll +u64 htonll(u64 x) { +    return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32)); +} +#endif + +#ifndef ntohll +u64 ntohll(u64 x) { +    return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32)); +} +#endif +  void Packet::Append(const void* in_data, std::size_t size_in_bytes) {      if (in_data && (size_in_bytes > 0)) {          std::size_t start = data.size(); @@ -100,6 +112,20 @@ Packet& Packet::operator>>(u32& out_data) {      return *this;  } +Packet& Packet::operator>>(s64& out_data) { +    s64 value; +    Read(&value, sizeof(value)); +    out_data = ntohll(value); +    return *this; +} + +Packet& Packet::operator>>(u64& out_data) { +    u64 value; +    Read(&value, sizeof(value)); +    out_data = ntohll(value); +    return *this; +} +  Packet& Packet::operator>>(float& out_data) {      Read(&out_data, sizeof(out_data));      return *this; @@ -183,6 +209,18 @@ Packet& Packet::operator<<(u32 in_data) {      return *this;  } +Packet& Packet::operator<<(s64 in_data) { +    s64 toWrite = htonll(in_data); +    Append(&toWrite, sizeof(toWrite)); +    return *this; +} + +Packet& Packet::operator<<(u64 in_data) { +    u64 toWrite = htonll(in_data); +    Append(&toWrite, sizeof(toWrite)); +    return *this; +} +  Packet& Packet::operator<<(float in_data) {      Append(&in_data, sizeof(in_data));      return *this; diff --git a/src/network/packet.h b/src/network/packet.h index 94b351ab1..5a2e58dc2 100644 --- a/src/network/packet.h +++ b/src/network/packet.h @@ -72,6 +72,8 @@ public:      Packet& operator>>(u16& out_data);      Packet& operator>>(s32& out_data);      Packet& operator>>(u32& out_data); +    Packet& operator>>(s64& out_data); +    Packet& operator>>(u64& out_data);      Packet& operator>>(float& out_data);      Packet& operator>>(double& out_data);      Packet& operator>>(char* out_data); @@ -89,6 +91,8 @@ public:      Packet& operator<<(u16 in_data);      Packet& operator<<(s32 in_data);      Packet& operator<<(u32 in_data); +    Packet& operator<<(s64 in_data); +    Packet& operator<<(u64 in_data);      Packet& operator<<(float in_data);      Packet& operator<<(double in_data);      Packet& operator<<(const char* in_data); diff --git a/src/network/room.cpp b/src/network/room.cpp index fbbaf8b93..261049ab0 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -4,9 +4,9 @@  #include <algorithm>  #include <atomic> +#include <mutex>  #include <random>  #include <thread> -#include <vector>  #include "enet/enet.h"  #include "network/packet.h"  #include "network/room.h" @@ -29,12 +29,14 @@ public:      struct Member {          std::string nickname;   ///< The nickname of the member. -        std::string game_name;  ///< The current game of the member +        GameInfo game_info;     ///< The current game of the member          MacAddress mac_address; ///< The assigned mac address of the member.          ENetPeer* peer;         ///< The remote peer.      };      using MemberList = std::vector<Member>; -    MemberList members; ///< Information about the members of this room. +    MemberList members;              ///< Information about the members of this room +    mutable std::mutex member_mutex; ///< Mutex for locking the members list +    /// This should be a std::shared_mutex as soon as C++17 is supported      RoomImpl()          : random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {} @@ -147,7 +149,7 @@ void Room::RoomImpl::ServerLoop() {                  case IdJoinRequest:                      HandleJoinRequest(&event);                      break; -                case IdSetGameName: +                case IdSetGameInfo:                      HandleGameNamePacket(&event);                      break;                  case IdWifiPacket: @@ -213,7 +215,10 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {      member.nickname = nickname;      member.peer = event->peer; -    members.push_back(std::move(member)); +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        members.push_back(std::move(member)); +    }      // Notify everyone that the room information has changed.      BroadcastRoomInformation(); @@ -223,12 +228,14 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {  bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {      // A nickname is valid if it is not already taken by anybody else in the room.      // TODO(B3N30): Check for empty names, spaces, etc. +    std::lock_guard<std::mutex> lock(member_mutex);      return std::all_of(members.begin(), members.end(),                         [&nickname](const auto& member) { return member.nickname != nickname; });  }  bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const {      // A MAC address is valid if it is not already taken by anybody else in the room. +    std::lock_guard<std::mutex> lock(member_mutex);      return std::all_of(members.begin(), members.end(),                         [&address](const auto& member) { return member.mac_address != address; });  } @@ -279,6 +286,7 @@ void Room::RoomImpl::SendCloseMessage() {      packet << static_cast<u8>(IdCloseRoom);      ENetPacket* enet_packet =          enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); +    std::lock_guard<std::mutex> lock(member_mutex);      for (auto& member : members) {          enet_peer_send(member.peer, 0, enet_packet);      } @@ -295,10 +303,14 @@ void Room::RoomImpl::BroadcastRoomInformation() {      packet << room_information.member_slots;      packet << static_cast<u32>(members.size()); -    for (const auto& member : members) { -        packet << member.nickname; -        packet << member.mac_address; -        packet << member.game_name; +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        for (const auto& member : members) { +            packet << member.nickname; +            packet << member.mac_address; +            packet << member.game_info.name; +            packet << member.game_info.id; +        }      }      ENetPacket* enet_packet = @@ -335,11 +347,13 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {                                                   ENET_PACKET_FLAG_RELIABLE);      if (destination_address == BroadcastMac) { // Send the data to everyone except the sender +        std::lock_guard<std::mutex> lock(member_mutex);          for (const auto& member : members) {              if (member.peer != event->peer)                  enet_peer_send(member.peer, 0, enet_packet);          }      } else { // Send the data only to the destination client +        std::lock_guard<std::mutex> lock(member_mutex);          auto member = std::find_if(members.begin(), members.end(),                                     [destination_address](const Member& member) -> bool {                                         return member.mac_address == destination_address; @@ -361,6 +375,8 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {      auto CompareNetworkAddress = [event](const Member member) -> bool {          return member.peer == event->peer;      }; + +    std::lock_guard<std::mutex> lock(member_mutex);      const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress);      if (sending_member == members.end()) {          return; // Received a chat message from a unknown sender @@ -385,22 +401,32 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {      in_packet.Append(event->packet->data, event->packet->dataLength);      in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type -    std::string game_name; -    in_packet >> game_name; -    auto member = -        std::find_if(members.begin(), members.end(), -                     [event](const Member& member) -> bool { return member.peer == event->peer; }); -    if (member != members.end()) { -        member->game_name = game_name; -        BroadcastRoomInformation(); +    GameInfo game_info; +    in_packet >> game_info.name; +    in_packet >> game_info.id; + +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        auto member = +            std::find_if(members.begin(), members.end(), [event](const Member& member) -> bool { +                return member.peer == event->peer; +            }); +        if (member != members.end()) { +            member->game_info = game_info; +        }      } +    BroadcastRoomInformation();  }  void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {      // Remove the client from the members list. -    members.erase(std::remove_if(members.begin(), members.end(), -                                 [client](const Member& member) { return member.peer == client; }), -                  members.end()); +    { +        std::lock_guard<std::mutex> lock(member_mutex); +        members.erase( +            std::remove_if(members.begin(), members.end(), +                           [client](const Member& member) { return member.peer == client; }), +            members.end()); +    }      // Announce the change to all clients.      enet_peer_disconnect(client, 0); @@ -437,6 +463,19 @@ const RoomInformation& Room::GetRoomInformation() const {      return room_impl->room_information;  } +std::vector<Room::Member> Room::GetRoomMemberList() const { +    std::vector<Room::Member> member_list; +    std::lock_guard<std::mutex> lock(room_impl->member_mutex); +    for (const auto& member_impl : room_impl->members) { +        Member member; +        member.nickname = member_impl.nickname; +        member.mac_address = member_impl.mac_address; +        member.game_info = member_impl.game_info; +        member_list.push_back(member); +    } +    return member_list; +}; +  void Room::Destroy() {      room_impl->state = State::Closed;      room_impl->room_thread->join(); @@ -447,7 +486,10 @@ void Room::Destroy() {      }      room_impl->room_information = {};      room_impl->server = nullptr; -    room_impl->members.clear(); +    { +        std::lock_guard<std::mutex> lock(room_impl->member_mutex); +        room_impl->members.clear(); +    }      room_impl->room_information.member_slots = 0;      room_impl->room_information.name.clear();  } diff --git a/src/network/room.h b/src/network/room.h index 65b0d008a..8285a4d0c 100644 --- a/src/network/room.h +++ b/src/network/room.h @@ -7,6 +7,7 @@  #include <array>  #include <memory>  #include <string> +#include <vector>  #include "common/common_types.h"  namespace Network { @@ -21,6 +22,11 @@ struct RoomInformation {      u32 member_slots; ///< Maximum number of members in this room  }; +struct GameInfo { +    std::string name{""}; +    u64 id{0}; +}; +  using MacAddress = std::array<u8, 6>;  /// A special MAC address that tells the room we're joining to assign us a MAC address  /// automatically. @@ -34,7 +40,7 @@ enum RoomMessageTypes : u8 {      IdJoinRequest = 1,      IdJoinSuccess,      IdRoomInformation, -    IdSetGameName, +    IdSetGameInfo,      IdWifiPacket,      IdChatMessage,      IdNameCollision, @@ -51,6 +57,12 @@ public:          Closed, ///< The room is not opened and can not accept connections.      }; +    struct Member { +        std::string nickname;   ///< The nickname of the member. +        GameInfo game_info;     ///< The current game of the member +        MacAddress mac_address; ///< The assigned mac address of the member. +    }; +      Room();      ~Room(); @@ -65,6 +77,11 @@ public:      const RoomInformation& GetRoomInformation() const;      /** +     * Gets a list of the mbmers connected to the room. +     */ +    std::vector<Member> GetRoomMemberList() const; + +    /**       * Creates the socket for this room. Will bind to default address if       * server is empty string.       */ diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index dac9bacae..f229ec6fd 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -5,6 +5,7 @@  #include <atomic>  #include <list>  #include <mutex> +#include <set>  #include <thread>  #include "common/assert.h"  #include "enet/enet.h" @@ -25,6 +26,9 @@ public:      /// Information about the room we're connected to.      RoomInformation room_information; +    /// The current game name, id and version +    GameInfo current_game_info; +      std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.      void SetState(const State new_state);      bool IsConnected() const; @@ -37,6 +41,24 @@ public:      std::unique_ptr<std::thread> loop_thread;      std::mutex send_list_mutex;  ///< Mutex that controls access to the `send_list` variable.      std::list<Packet> send_list; ///< A list that stores all packets to send the async + +    template <typename T> +    using CallbackSet = std::set<CallbackHandle<T>>; +    std::mutex callback_mutex; ///< The mutex used for handling callbacks + +    class Callbacks { +    public: +        template <typename T> +        CallbackSet<T>& Get(); + +    private: +        CallbackSet<WifiPacket> callback_set_wifi_packet; +        CallbackSet<ChatEntry> callback_set_chat_messages; +        CallbackSet<RoomInformation> callback_set_room_information; +        CallbackSet<State> callback_set_state; +    }; +    Callbacks callbacks; ///< All CallbackSets to all events +      void MemberLoop();      void StartLoop(); @@ -84,12 +106,20 @@ public:       * Disconnects the RoomMember from the Room       */      void Disconnect(); + +    template <typename T> +    void Invoke(const T& data); + +    template <typename T> +    CallbackHandle<T> Bind(std::function<void(const T&)> callback);  };  // RoomMemberImpl  void RoomMember::RoomMemberImpl::SetState(const State new_state) { -    state = new_state; -    // TODO(B3N30): Invoke the callback functions +    if (state != new_state) { +        state = new_state; +        Invoke<State>(state); +    }  }  bool RoomMember::RoomMemberImpl::IsConnected() const { @@ -195,9 +225,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev      for (auto& member : member_information) {          packet >> member.nickname;          packet >> member.mac_address; -        packet >> member.game_name; +        packet >> member.game_info.name; +        packet >> member.game_info.id;      } -    // TODO(B3N30): Invoke callbacks +    Invoke(room_information);  }  void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { @@ -209,7 +240,7 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {      // Parse the MAC Address from the packet      packet >> mac_address; -    // TODO(B3N30): Invoke callbacks +    SetState(State::Joined);  }  void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { @@ -235,7 +266,7 @@ void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {      packet >> wifi_packet.data; -    // TODO(B3N30): Invoke callbacks +    Invoke<WifiPacket>(wifi_packet);  }  void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { @@ -248,7 +279,7 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {      ChatEntry chat_entry{};      packet >> chat_entry.nickname;      packet >> chat_entry.message; -    // TODO(B3N30): Invoke callbacks +    Invoke<ChatEntry>(chat_entry);  }  void RoomMember::RoomMemberImpl::Disconnect() { @@ -276,6 +307,46 @@ void RoomMember::RoomMemberImpl::Disconnect() {      server = nullptr;  } +template <> +RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_wifi_packet; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>& +RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_state; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<RoomInformation>& +RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_room_information; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::Callbacks::Get() { +    return callback_set_chat_messages; +} + +template <typename T> +void RoomMember::RoomMemberImpl::Invoke(const T& data) { +    std::lock_guard<std::mutex> lock(callback_mutex); +    CallbackSet<T> callback_set = callbacks.Get<T>(); +    for (auto const& callback : callback_set) +        (*callback)(data); +} + +template <typename T> +RoomMember::CallbackHandle<T> RoomMember::RoomMemberImpl::Bind( +    std::function<void(const T&)> callback) { +    std::lock_guard<std::mutex> lock(callback_mutex); +    CallbackHandle<T> handle; +    handle = std::make_shared<std::function<void(const T&)>>(callback); +    callbacks.Get<T>().insert(handle); +    return handle; +} +  // RoomMember  RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {      room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); @@ -339,6 +410,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv          room_member_impl->SetState(State::Joining);          room_member_impl->StartLoop();          room_member_impl->SendJoinRequest(nick, preferred_mac); +        SendGameInfo(room_member_impl->current_game_info);      } else {          room_member_impl->SetState(State::CouldNotConnect);      } @@ -366,17 +438,53 @@ void RoomMember::SendChatMessage(const std::string& message) {      room_member_impl->Send(std::move(packet));  } -void RoomMember::SendGameName(const std::string& game_name) { +void RoomMember::SendGameInfo(const GameInfo& game_info) { +    room_member_impl->current_game_info = game_info; +    if (!IsConnected()) +        return; +      Packet packet; -    packet << static_cast<u8>(IdSetGameName); -    packet << game_name; +    packet << static_cast<u8>(IdSetGameInfo); +    packet << game_info.name; +    packet << game_info.id;      room_member_impl->Send(std::move(packet));  } +RoomMember::CallbackHandle<RoomMember::State> RoomMember::BindOnStateChanged( +    std::function<void(const RoomMember::State&)> callback) { +    return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( +    std::function<void(const WifiPacket&)> callback) { +    return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged( +    std::function<void(const RoomInformation&)> callback) { +    return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved( +    std::function<void(const ChatEntry&)> callback) { +    return room_member_impl->Bind(callback); +} + +template <typename T> +void RoomMember::Unbind(CallbackHandle<T> handle) { +    std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex); +    room_member_impl->callbacks.Get<T>().erase(handle); +} +  void RoomMember::Leave() {      room_member_impl->SetState(State::Idle);      room_member_impl->loop_thread->join();      room_member_impl->loop_thread.reset();  } +template void RoomMember::Unbind(CallbackHandle<WifiPacket>); +template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); +template void RoomMember::Unbind(CallbackHandle<RoomInformation>); +template void RoomMember::Unbind(CallbackHandle<ChatEntry>); +  } // namespace Network diff --git a/src/network/room_member.h b/src/network/room_member.h index bc1af3a7e..98770a234 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h @@ -4,6 +4,7 @@  #pragma once +#include <functional>  #include <memory>  #include <string>  #include <vector> @@ -53,12 +54,23 @@ public:      struct MemberInformation {          std::string nickname;   ///< Nickname of the member. -        std::string game_name;  ///< Name of the game they're currently playing, or empty if they're +        GameInfo game_info;     ///< Name of the game they're currently playing, or empty if they're                                  /// not playing anything.          MacAddress mac_address; ///< MAC address associated with this member.      };      using MemberList = std::vector<MemberInformation>; +    // The handle for the callback functions +    template <typename T> +    using CallbackHandle = std::shared_ptr<std::function<void(const T&)>>; + +    /** +     * Unbinds a callback function from the events. +     * @param handle The connection handle to disconnect +     */ +    template <typename T> +    void Unbind(CallbackHandle<T> handle); +      RoomMember();      ~RoomMember(); @@ -113,10 +125,49 @@ public:      void SendChatMessage(const std::string& message);      /** -     * Sends the current game name  to the room. -     * @param game_name The game name. +     * Sends the current game info to the room. +     * @param game_info The game information. +     */ +    void SendGameInfo(const GameInfo& game_info); + +    /** +     * Binds a function to an event that will be triggered every time the State of the member +     * changed. The function wil be called every time the event is triggered. The callback function +     * must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list +     */ +    CallbackHandle<State> BindOnStateChanged(std::function<void(const State&)> callback); + +    /** +     * Binds a function to an event that will be triggered every time a WifiPacket is received. +     * The function wil be called everytime the event is triggered. +     * The callback function must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list +     */ +    CallbackHandle<WifiPacket> BindOnWifiPacketReceived( +        std::function<void(const WifiPacket&)> callback); + +    /** +     * Binds a function to an event that will be triggered every time the RoomInformation changes. +     * The function wil be called every time the event is triggered. +     * The callback function must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list +     */ +    CallbackHandle<RoomInformation> BindOnRoomInformationChanged( +        std::function<void(const RoomInformation&)> callback); + +    /** +     * Binds a function to an event that will be triggered every time a ChatMessage is received. +     * The function wil be called every time the event is triggered. +     * The callback function must not bind or unbind a function. Doing so will cause a deadlock +     * @param callback The function to call +     * @return A handle used for removing the function from the registered list       */ -    void SendGameName(const std::string& game_name); +    CallbackHandle<ChatEntry> BindOnChatMessageRecieved( +        std::function<void(const ChatEntry&)> callback);      /**       * Leaves the current room. diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index bb192affd..ae67aab05 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -525,11 +525,12 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {             "float geo_factor = 1.0;\n";      // Compute fragment normals and tangents -    const std::string pertubation = -        "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; +    auto Perturbation = [&]() { +        return "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; +    };      if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {          // Bump mapping is enabled using a normal map -        out += "vec3 surface_normal = " + pertubation + ";\n"; +        out += "vec3 surface_normal = " + Perturbation() + ";\n";          // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher          // precision result @@ -543,7 +544,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";      } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {          // Bump mapping is enabled using a tangent map -        out += "vec3 surface_tangent = " + pertubation + ";\n"; +        out += "vec3 surface_tangent = " + Perturbation() + ";\n";          // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant          // computation below, which is also confirmed on 3DS. So we don't bother recomputing here          // even if 'renorm' is enabled.  | 
