diff --git a/README.md b/README.md index cd2f8cce6..4f9b35284 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1744. +This is the source code for early-access 1746. ## Legal Notice diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 6c3685927..b5254dd75 100755 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -105,7 +105,7 @@ ResultVal SaveDataFactory::Open(SaveDataSpaceId space, auto out = dir->GetDirectoryRelative(save_directory); - if (out == nullptr && ShouldSaveDataBeAutomaticallyCreated(space, meta)) { + if (out == nullptr && (ShouldSaveDataBeAutomaticallyCreated(space, meta) && auto_create)) { return Create(space, meta); } @@ -199,4 +199,8 @@ void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 us size_file->WriteObject(new_value); } +void SaveDataFactory::SetAutoCreate(bool state) { + auto_create = state; +} + } // namespace FileSys diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 86c9f5350..1d8dc981f 100755 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -104,9 +104,12 @@ public: void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value) const; + void SetAutoCreate(bool state); + private: VirtualDir dir; Core::System& system; + bool auto_create{true}; }; } // namespace FileSys diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 0c5d2b3b0..7a047803e 100755 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -27,6 +27,10 @@ struct AnalogProperties { float range; float threshold; }; +template +struct InputCallback { + std::function on_change; +}; /// An abstract class template for an input device (a button, an analog input, etc.). template @@ -50,6 +54,17 @@ public: [[maybe_unused]] f32 freq_high) const { return {}; } + void SetCallback(InputCallback callback_) { + callback = std::move(callback_); + } + void TriggerOnChange() { + if (callback.on_change) { + callback.on_change(GetStatus()); + } + } + +private: + InputCallback callback; }; /// An abstract class template for a factory that can create input devices. diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 4a1908bcb..3c16fe6c7 100755 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -721,6 +721,10 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const { return bis_factory->GetBCATDirectory(title_id); } +void FileSystemController::SetAutoSaveDataCreation(bool enable) { + save_data_factory->SetAutoCreate(enable); +} + void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 7102d3f9a..b6b1b9220 100755 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -120,6 +120,8 @@ public: FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; + void SetAutoSaveDataCreation(bool enable); + // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index d9e8020b4..3af9881c2 100755 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -764,7 +764,7 @@ FSP_SRV::FSP_SRV(Core::System& system_) {1000, nullptr, "SetBisRootForHost"}, {1001, nullptr, "SetSaveDataSize"}, {1002, nullptr, "SetSaveDataRootPath"}, - {1003, nullptr, "DisableAutoSaveDataCreation"}, + {1003, &FSP_SRV::DisableAutoSaveDataCreation, "DisableAutoSaveDataCreation"}, {1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"}, {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, {1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"}, @@ -1030,6 +1030,15 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface(std::move(storage)); } +void FSP_SRV::DisableAutoSaveDataCreation(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + fsc.SetAutoSaveDataCreation(false); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; log_mode = rp.PopEnum(); diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index b01b924eb..ff7455a20 100755 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -50,6 +50,7 @@ private: void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenDataStorageWithProgramIndex(Kernel::HLERequestContext& ctx); + void DisableAutoSaveDataCreation(Kernel::HLERequestContext& ctx); void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index f8ec179d0..100138d11 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp @@ -21,104 +21,153 @@ public: : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), modifier_angle(modifier_angle_) { - update_thread_running.store(true); - update_thread = std::thread(&Analog::UpdateStatus, this); + Input::InputCallback callbacks{ + [this]([[maybe_unused]] bool status) { UpdateStatus(); }}; + up->SetCallback(callbacks); + down->SetCallback(callbacks); + left->SetCallback(callbacks); + right->SetCallback(callbacks); } - ~Analog() override { - if (update_thread_running.load()) { - update_thread_running.store(false); - if (update_thread.joinable()) { - update_thread.join(); - } - } - } - - void MoveToDirection(bool enable, float to_angle) { - if (!enable) { - return; - } + bool IsAngleGreater(float old_angle, float new_angle) const { constexpr float TAU = Common::PI * 2.0f; // Use wider angle to ease the transition. constexpr float aperture = TAU * 0.15f; - const float top_limit = to_angle + aperture; - const float bottom_limit = to_angle - aperture; + const float top_limit = new_angle + aperture; + return (old_angle > new_angle && old_angle <= top_limit) || + (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); + } - if ((angle > to_angle && angle <= top_limit) || - (angle + TAU > to_angle && angle + TAU <= top_limit)) { - angle -= modifier_angle; - if (angle < 0) { - angle += TAU; + bool IsAngleSmaller(float old_angle, float new_angle) const { + constexpr float TAU = Common::PI * 2.0f; + // Use wider angle to ease the transition. + constexpr float aperture = TAU * 0.15f; + const float bottom_limit = new_angle - aperture; + return (old_angle >= bottom_limit && old_angle < new_angle) || + (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); + } + + float GetAngle(std::chrono::time_point now) const { + constexpr float TAU = Common::PI * 2.0f; + float new_angle = angle; + + auto time_difference = static_cast( + std::chrono::duration_cast(now - last_update).count()); + time_difference /= 1000.0f * 1000.0f; + if (time_difference > 0.5f) { + time_difference = 0.5f; + } + + if (IsAngleGreater(new_angle, goal_angle)) { + new_angle -= modifier_angle * time_difference; + if (new_angle < 0) { + new_angle += TAU; } - } else if ((angle >= bottom_limit && angle < to_angle) || - (angle - TAU >= bottom_limit && angle - TAU < to_angle)) { - angle += modifier_angle; - if (angle >= TAU) { - angle -= TAU; + if (!IsAngleGreater(new_angle, goal_angle)) { + return goal_angle; + } + } else if (IsAngleSmaller(new_angle, goal_angle)) { + new_angle += modifier_angle * time_difference; + if (new_angle >= TAU) { + new_angle -= TAU; + } + if (!IsAngleSmaller(new_angle, goal_angle)) { + return goal_angle; } } else { - angle = to_angle; + return goal_angle; + } + return new_angle; + } + + void SetGoalAngle(bool r, bool l, bool u, bool d) { + // Move to the right + if (r && !u && !d) { + goal_angle = 0.0f; + } + + // Move to the upper right + if (r && u && !d) { + goal_angle = Common::PI * 0.25f; + } + + // Move up + if (u && !l && !r) { + goal_angle = Common::PI * 0.5f; + } + + // Move to the upper left + if (l && u && !d) { + goal_angle = Common::PI * 0.75f; + } + + // Move to the left + if (l && !u && !d) { + goal_angle = Common::PI; + } + + // Move to the bottom left + if (l && !u && d) { + goal_angle = Common::PI * 1.25f; + } + + // Move down + if (d && !l && !r) { + goal_angle = Common::PI * 1.5f; + } + + // Move to the bottom right + if (r && !u && d) { + goal_angle = Common::PI * 1.75f; } } void UpdateStatus() { - while (update_thread_running.load()) { - const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; + const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; - bool r = right->GetStatus(); - bool l = left->GetStatus(); - bool u = up->GetStatus(); - bool d = down->GetStatus(); + bool r = right->GetStatus(); + bool l = left->GetStatus(); + bool u = up->GetStatus(); + bool d = down->GetStatus(); - // Eliminate contradictory movements - if (r && l) { - r = false; - l = false; - } - if (u && d) { - u = false; - d = false; - } - - // Move to the right - MoveToDirection(r && !u && !d, 0.0f); - - // Move to the upper right - MoveToDirection(r && u && !d, Common::PI * 0.25f); - - // Move up - MoveToDirection(u && !l && !r, Common::PI * 0.5f); - - // Move to the upper left - MoveToDirection(l && u && !d, Common::PI * 0.75f); - - // Move to the left - MoveToDirection(l && !u && !d, Common::PI); - - // Move to the bottom left - MoveToDirection(l && !u && d, Common::PI * 1.25f); - - // Move down - MoveToDirection(d && !l && !r, Common::PI * 1.5f); - - // Move to the bottom right - MoveToDirection(r && !u && d, Common::PI * 1.75f); - - // Move if a key is pressed - if (r || l || u || d) { - amplitude = coef; - } else { - amplitude = 0; - } - - // Delay the update rate to 100hz - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // Eliminate contradictory movements + if (r && l) { + r = false; + l = false; } + if (u && d) { + u = false; + d = false; + } + + // Move if a key is pressed + if (r || l || u || d) { + amplitude = coef; + } else { + amplitude = 0; + } + + const auto now = std::chrono::steady_clock::now(); + const auto time_difference = static_cast( + std::chrono::duration_cast(now - last_update).count()); + + if (time_difference < 10) { + // Disable analog mode if inputs are too fast + SetGoalAngle(r, l, u, d); + angle = goal_angle; + } else { + angle = GetAngle(now); + SetGoalAngle(r, l, u, d); + } + + last_update = now; } std::tuple GetStatus() const override { if (Settings::values.emulate_analog_keyboard) { - return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude); + const auto now = std::chrono::steady_clock::now(); + float angle_ = GetAngle(now); + return std::make_tuple(std::cos(angle_) * amplitude, std::sin(angle_) * amplitude); } constexpr float SQRT_HALF = 0.707106781f; int x = 0, y = 0; @@ -166,9 +215,9 @@ private: float modifier_scale; float modifier_angle; float angle{}; + float goal_angle{}; float amplitude{}; - std::thread update_thread; - std::atomic update_thread_running{}; + std::chrono::time_point last_update; }; std::unique_ptr AnalogFromButton::Create(const Common::ParamPackage& params) { @@ -179,7 +228,7 @@ std::unique_ptr AnalogFromButton::Create(const Common::Para auto right = Input::CreateDevice(params.Get("right", null_engine)); auto modifier = Input::CreateDevice(params.Get("modifier", null_engine)); auto modifier_scale = params.Get("modifier_scale", 0.5f); - auto modifier_angle = params.Get("modifier_angle", 0.035f); + auto modifier_angle = params.Get("modifier_angle", 5.5f); return std::make_unique(std::move(up), std::move(down), std::move(left), std::move(right), std::move(modifier), modifier_scale, modifier_angle); diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index c467ff4c5..8261e76fd 100755 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp @@ -75,6 +75,7 @@ public: } else { pair.key_button->UnlockButton(); } + pair.key_button->TriggerOnChange(); } } } diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index 61ba91cef..f50cda2f3 100755 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -85,6 +85,8 @@ void PlayerControlPreview::SetConnectedStatus(bool checked) { led_color[1] = led_pattern.position2 ? colors.led_on : colors.led_off; led_color[2] = led_pattern.position3 ? colors.led_on : colors.led_off; led_color[3] = led_pattern.position4 ? colors.led_on : colors.led_off; + is_enabled = checked; + ResetInputs(); } void PlayerControlPreview::SetControllerType(const Settings::ControllerType type) { @@ -108,6 +110,7 @@ void PlayerControlPreview::EndMapping() { analog_mapping_index = Settings::NativeAnalog::NumAnalogs; mapping_active = false; blink_counter = 0; + ResetInputs(); } void PlayerControlPreview::UpdateColors() { @@ -156,7 +159,23 @@ void PlayerControlPreview::UpdateColors() { // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right); } +void PlayerControlPreview::ResetInputs() { + for (std::size_t index = 0; index < button_values.size(); ++index) { + button_values[index] = false; + } + + for (std::size_t index = 0; index < axis_values.size(); ++index) { + axis_values[index].properties = {0, 1, 0}; + axis_values[index].value = {0, 0}; + axis_values[index].raw_value = {0, 0}; + } + update(); +} + void PlayerControlPreview::UpdateInput() { + if (!is_enabled && !mapping_active) { + return; + } bool input_changed = false; const auto& button_state = buttons; for (std::size_t index = 0; index < button_values.size(); ++index) { diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h index 51bb84eb6..5fc16d8af 100755 --- a/src/yuzu/configuration/configure_input_player_widget.h +++ b/src/yuzu/configuration/configure_input_player_widget.h @@ -100,6 +100,7 @@ private: static LedPattern GetColorPattern(std::size_t index, bool player_on); void UpdateColors(); + void ResetInputs(); // Draw controller functions void DrawHandheldController(QPainter& p, QPointF center); @@ -176,6 +177,7 @@ private: using StickArray = std::array, Settings::NativeAnalog::NUM_STICKS_HID>; + bool is_enabled{}; bool mapping_active{}; int blink_counter{}; QColor button_color{}; diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp index d85408ac6..c1fc69578 100755 --- a/src/yuzu/debugger/controller.cpp +++ b/src/yuzu/debugger/controller.cpp @@ -28,6 +28,7 @@ ControllerDialog::ControllerDialog(QWidget* parent) : QWidget(parent, Qt::Dialog // Configure focus so that widget is focusable and the dialog automatically forwards focus to // it. setFocusProxy(widget); + widget->SetConnectedStatus(false); widget->setFocusPolicy(Qt::StrongFocus); widget->setFocus(); } @@ -36,9 +37,8 @@ void ControllerDialog::refreshConfiguration() { const auto& players = Settings::values.players.GetValue(); constexpr std::size_t player = 0; widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs); - widget->SetConnectedStatus(players[player].connected); widget->SetControllerType(players[player].controller_type); - widget->repaint(); + widget->SetConnectedStatus(players[player].connected); } QAction* ControllerDialog::toggleViewAction() { @@ -56,6 +56,7 @@ void ControllerDialog::showEvent(QShowEvent* ev) { if (toggle_view_action) { toggle_view_action->setChecked(isVisible()); } + refreshConfiguration(); QWidget::showEvent(ev); } @@ -63,5 +64,6 @@ void ControllerDialog::hideEvent(QHideEvent* ev) { if (toggle_view_action) { toggle_view_action->setChecked(isVisible()); } + widget->SetConnectedStatus(false); QWidget::hideEvent(ev); }