summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--externals/CMakeLists.txt25
-rw-r--r--externals/httplib/README.md2
-rw-r--r--externals/httplib/httplib.h651
m---------externals/inih/inih0
m---------externals/libressl0
-rw-r--r--externals/lurlparser/CMakeLists.txt8
-rw-r--r--externals/lurlparser/LUrlParser.cpp265
-rw-r--r--externals/lurlparser/LUrlParser.h78
-rw-r--r--externals/lurlparser/README.md19
-rw-r--r--src/common/fiber.cpp4
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp5
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/mii/mii.cpp1
-rw-r--r--src/core/hle/service/set/set.cpp1
-rw-r--r--src/core/hle/service/set/set_sys.cpp2
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp462
-rw-r--r--src/input_common/gcadapter/gc_adapter.h146
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp128
-rw-r--r--src/tests/common/fibers.cpp71
-rw-r--r--src/video_core/CMakeLists.txt3
-rw-r--r--src/video_core/cdma_pusher.cpp2
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp2
-rw-r--r--src/video_core/command_classes/sync_manager.cpp14
-rw-r--r--src/video_core/command_classes/sync_manager.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp2
-rw-r--r--src/web_service/CMakeLists.txt2
-rw-r--r--src/web_service/web_backend.cpp26
-rw-r--r--src/yuzu/configuration/config.cpp12
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/default_ini.h4
35 files changed, 976 insertions, 983 deletions
diff --git a/.gitmodules b/.gitmodules
index 9d9356151..6931a641d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
1[submodule "inih"] 1[submodule "inih"]
2 path = externals/inih/inih 2 path = externals/inih/inih
3 url = https://github.com/svn2github/inih 3 url = https://github.com/benhoyt/inih.git
4[submodule "cubeb"] 4[submodule "cubeb"]
5 path = externals/cubeb 5 path = externals/cubeb
6 url = https://github.com/kinetiknz/cubeb.git 6 url = https://github.com/kinetiknz/cubeb.git
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index e01ff6930..421b35890 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -73,17 +73,20 @@ if (NOT LIBZIP_FOUND)
73endif() 73endif()
74 74
75if (ENABLE_WEB_SERVICE) 75if (ENABLE_WEB_SERVICE)
76 # LibreSSL 76 find_package(OpenSSL 1.1)
77 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") 77 if (OPENSSL_FOUND)
78 add_subdirectory(libressl EXCLUDE_FROM_ALL) 78 set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
79 target_include_directories(ssl INTERFACE ./libressl/include) 79 else()
80 target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP) 80 # LibreSSL
81 get_directory_property(OPENSSL_LIBRARIES 81 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
82 DIRECTORY libressl 82 set(OPENSSLDIR "/etc/ssl/")
83 DEFINITION OPENSSL_LIBS) 83 add_subdirectory(libressl EXCLUDE_FROM_ALL)
84 84 target_include_directories(ssl INTERFACE ./libressl/include)
85 # lurlparser 85 target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
86 add_subdirectory(lurlparser EXCLUDE_FROM_ALL) 86 get_directory_property(OPENSSL_LIBRARIES
87 DIRECTORY libressl
88 DEFINITION OPENSSL_LIBS)
89 endif()
87 90
88 # httplib 91 # httplib
89 add_library(httplib INTERFACE) 92 add_library(httplib INTERFACE)
diff --git a/externals/httplib/README.md b/externals/httplib/README.md
index 73037d297..1940e446c 100644
--- a/externals/httplib/README.md
+++ b/externals/httplib/README.md
@@ -1,4 +1,4 @@
1From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53 1From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
2 2
3MIT License 3MIT License
4 4
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index 5139b7f05..8982054e2 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -173,6 +173,7 @@ using socket_t = int;
173#define INVALID_SOCKET (-1) 173#define INVALID_SOCKET (-1)
174#endif //_WIN32 174#endif //_WIN32
175 175
176#include <algorithm>
176#include <array> 177#include <array>
177#include <atomic> 178#include <atomic>
178#include <cassert> 179#include <cassert>
@@ -237,6 +238,27 @@ namespace httplib {
237 238
238namespace detail { 239namespace detail {
239 240
241/*
242 * Backport std::make_unique from C++14.
243 *
244 * NOTE: This code came up with the following stackoverflow post:
245 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
246 *
247 */
248
249template <class T, class... Args>
250typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
251make_unique(Args &&... args) {
252 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
253}
254
255template <class T>
256typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
257make_unique(std::size_t n) {
258 typedef typename std::remove_extent<T>::type RT;
259 return std::unique_ptr<T>(new RT[n]);
260}
261
240struct ci { 262struct ci {
241 bool operator()(const std::string &s1, const std::string &s2) const { 263 bool operator()(const std::string &s1, const std::string &s2) const {
242 return std::lexicographical_compare( 264 return std::lexicographical_compare(
@@ -304,6 +326,10 @@ using ContentProvider =
304using ContentProviderWithoutLength = 326using ContentProviderWithoutLength =
305 std::function<bool(size_t offset, DataSink &sink)>; 327 std::function<bool(size_t offset, DataSink &sink)>;
306 328
329using ContentReceiverWithProgress =
330 std::function<bool(const char *data, size_t data_length, uint64_t offset,
331 uint64_t total_length)>;
332
307using ContentReceiver = 333using ContentReceiver =
308 std::function<bool(const char *data, size_t data_length)>; 334 std::function<bool(const char *data, size_t data_length)>;
309 335
@@ -317,14 +343,17 @@ public:
317 ContentReceiver receiver)>; 343 ContentReceiver receiver)>;
318 344
319 ContentReader(Reader reader, MultipartReader multipart_reader) 345 ContentReader(Reader reader, MultipartReader multipart_reader)
320 : reader_(reader), multipart_reader_(multipart_reader) {} 346 : reader_(std::move(reader)),
347 multipart_reader_(std::move(multipart_reader)) {}
321 348
322 bool operator()(MultipartContentHeader header, 349 bool operator()(MultipartContentHeader header,
323 ContentReceiver receiver) const { 350 ContentReceiver receiver) const {
324 return multipart_reader_(header, receiver); 351 return multipart_reader_(std::move(header), std::move(receiver));
325 } 352 }
326 353
327 bool operator()(ContentReceiver receiver) const { return reader_(receiver); } 354 bool operator()(ContentReceiver receiver) const {
355 return reader_(std::move(receiver));
356 }
328 357
329 Reader reader_; 358 Reader reader_;
330 MultipartReader multipart_reader_; 359 MultipartReader multipart_reader_;
@@ -353,7 +382,7 @@ struct Request {
353 // for client 382 // for client
354 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; 383 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
355 ResponseHandler response_handler; 384 ResponseHandler response_handler;
356 ContentReceiver content_receiver; 385 ContentReceiverWithProgress content_receiver;
357 size_t content_length = 0; 386 size_t content_length = 0;
358 ContentProvider content_provider; 387 ContentProvider content_provider;
359 Progress progress; 388 Progress progress;
@@ -475,7 +504,7 @@ public:
475 504
476 void enqueue(std::function<void()> fn) override { 505 void enqueue(std::function<void()> fn) override {
477 std::unique_lock<std::mutex> lock(mutex_); 506 std::unique_lock<std::mutex> lock(mutex_);
478 jobs_.push_back(fn); 507 jobs_.push_back(std::move(fn));
479 cond_.notify_one(); 508 cond_.notify_one();
480 } 509 }
481 510
@@ -664,8 +693,8 @@ private:
664 ContentReceiver multipart_receiver); 693 ContentReceiver multipart_receiver);
665 694
666 virtual bool process_and_close_socket(socket_t sock); 695 virtual bool process_and_close_socket(socket_t sock);
667 696
668 struct MountPointEntry { 697 struct MountPointEntry {
669 std::string mount_point; 698 std::string mount_point;
670 std::string base_dir; 699 std::string base_dir;
671 Headers headers; 700 Headers headers;
@@ -704,23 +733,27 @@ enum Error {
704 Canceled, 733 Canceled,
705 SSLConnection, 734 SSLConnection,
706 SSLLoadingCerts, 735 SSLLoadingCerts,
707 SSLServerVerification 736 SSLServerVerification,
737 UnsupportedMultipartBoundaryChars
708}; 738};
709 739
710class Result { 740class Result {
711public: 741public:
712 Result(const std::shared_ptr<Response> &res, Error err) 742 Result(std::unique_ptr<Response> res, Error err)
713 : res_(res), err_(err) {} 743 : res_(std::move(res)), err_(err) {}
714 operator bool() const { return res_ != nullptr; } 744 operator bool() const { return res_ != nullptr; }
715 bool operator==(std::nullptr_t) const { return res_ == nullptr; } 745 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
716 bool operator!=(std::nullptr_t) const { return res_ != nullptr; } 746 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
717 const Response &value() const { return *res_; } 747 const Response &value() const { return *res_; }
748 Response &value() { return *res_; }
718 const Response &operator*() const { return *res_; } 749 const Response &operator*() const { return *res_; }
750 Response &operator*() { return *res_; }
719 const Response *operator->() const { return res_.get(); } 751 const Response *operator->() const { return res_.get(); }
752 Response *operator->() { return res_.get(); }
720 Error error() const { return err_; } 753 Error error() const { return err_; }
721 754
722private: 755private:
723 std::shared_ptr<Response> res_; 756 std::unique_ptr<Response> res_;
724 Error err_; 757 Error err_;
725}; 758};
726 759
@@ -777,6 +810,8 @@ public:
777 Result Post(const char *path, const MultipartFormDataItems &items); 810 Result Post(const char *path, const MultipartFormDataItems &items);
778 Result Post(const char *path, const Headers &headers, 811 Result Post(const char *path, const Headers &headers,
779 const MultipartFormDataItems &items); 812 const MultipartFormDataItems &items);
813 Result Post(const char *path, const Headers &headers,
814 const MultipartFormDataItems &items, const std::string &boundary);
780 815
781 Result Put(const char *path); 816 Result Put(const char *path);
782 Result Put(const char *path, const std::string &body, 817 Result Put(const char *path, const std::string &body,
@@ -863,7 +898,21 @@ protected:
863 }; 898 };
864 899
865 virtual bool create_and_connect_socket(Socket &socket); 900 virtual bool create_and_connect_socket(Socket &socket);
866 virtual void close_socket(Socket &socket, bool process_socket_ret); 901
902 // All of:
903 // shutdown_ssl
904 // shutdown_socket
905 // close_socket
906 // should ONLY be called when socket_mutex_ is locked.
907 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
908 // with a DIFFERENT thread sending requests using that socket.
909 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
910 void shutdown_socket(Socket &socket);
911 void close_socket(Socket &socket);
912
913 // Similar to shutdown_ssl and close_socket, this should NOT be called
914 // concurrently with a DIFFERENT thread sending requests from the socket
915 void lock_socket_and_shutdown_and_close();
867 916
868 bool process_request(Stream &strm, const Request &req, Response &res, 917 bool process_request(Stream &strm, const Request &req, Response &res,
869 bool close_connection); 918 bool close_connection);
@@ -873,7 +922,7 @@ protected:
873 void copy_settings(const ClientImpl &rhs); 922 void copy_settings(const ClientImpl &rhs);
874 923
875 // Error state 924 // Error state
876 mutable Error error_ = Error::Success; 925 mutable std::atomic<Error> error_;
877 926
878 // Socket endoint information 927 // Socket endoint information
879 const std::string host_; 928 const std::string host_;
@@ -885,6 +934,11 @@ protected:
885 mutable std::mutex socket_mutex_; 934 mutable std::mutex socket_mutex_;
886 std::recursive_mutex request_mutex_; 935 std::recursive_mutex request_mutex_;
887 936
937 // These are all protected under socket_mutex
938 int socket_requests_in_flight_ = 0;
939 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
940 bool socket_should_be_closed_when_request_is_done_ = false;
941
888 // Default headers 942 // Default headers
889 Headers default_headers_; 943 Headers default_headers_;
890 944
@@ -942,13 +996,13 @@ private:
942 bool redirect(const Request &req, Response &res); 996 bool redirect(const Request &req, Response &res);
943 bool handle_request(Stream &strm, const Request &req, Response &res, 997 bool handle_request(Stream &strm, const Request &req, Response &res,
944 bool close_connection); 998 bool close_connection);
945 void stop_core(); 999 std::unique_ptr<Response> send_with_content_provider(
946 std::shared_ptr<Response> send_with_content_provider(
947 const char *method, const char *path, const Headers &headers, 1000 const char *method, const char *path, const Headers &headers,
948 const std::string &body, size_t content_length, 1001 const std::string &body, size_t content_length,
949 ContentProvider content_provider, const char *content_type); 1002 ContentProvider content_provider, const char *content_type);
950 1003
951 virtual bool process_socket(Socket &socket, 1004 // socket is const because this function is called when socket_mutex_ is not locked
1005 virtual bool process_socket(const Socket &socket,
952 std::function<bool(Stream &strm)> callback); 1006 std::function<bool(Stream &strm)> callback);
953 virtual bool is_ssl() const; 1007 virtual bool is_ssl() const;
954}; 1008};
@@ -1012,6 +1066,8 @@ public:
1012 Result Post(const char *path, const MultipartFormDataItems &items); 1066 Result Post(const char *path, const MultipartFormDataItems &items);
1013 Result Post(const char *path, const Headers &headers, 1067 Result Post(const char *path, const Headers &headers,
1014 const MultipartFormDataItems &items); 1068 const MultipartFormDataItems &items);
1069 Result Post(const char *path, const Headers &headers,
1070 const MultipartFormDataItems &items, const std::string &boundary);
1015 Result Put(const char *path); 1071 Result Put(const char *path);
1016 Result Put(const char *path, const std::string &body, 1072 Result Put(const char *path, const std::string &body,
1017 const char *content_type); 1073 const char *content_type);
@@ -1098,7 +1154,7 @@ public:
1098#endif 1154#endif
1099 1155
1100private: 1156private:
1101 std::shared_ptr<ClientImpl> cli_; 1157 std::unique_ptr<ClientImpl> cli_;
1102 1158
1103#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 1159#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1104 bool is_ssl_ = false; 1160 bool is_ssl_ = false;
@@ -1154,9 +1210,9 @@ public:
1154 1210
1155private: 1211private:
1156 bool create_and_connect_socket(Socket &socket) override; 1212 bool create_and_connect_socket(Socket &socket) override;
1157 void close_socket(Socket &socket, bool process_socket_ret) override; 1213 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1158 1214
1159 bool process_socket(Socket &socket, 1215 bool process_socket(const Socket &socket,
1160 std::function<bool(Stream &strm)> callback) override; 1216 std::function<bool(Stream &strm)> callback) override;
1161 bool is_ssl() const override; 1217 bool is_ssl() const override;
1162 1218
@@ -1942,9 +1998,9 @@ inline socket_t create_client_socket(const char *host, int port,
1942 bool tcp_nodelay, 1998 bool tcp_nodelay,
1943 SocketOptions socket_options, 1999 SocketOptions socket_options,
1944 time_t timeout_sec, time_t timeout_usec, 2000 time_t timeout_sec, time_t timeout_usec,
1945 const std::string &intf, Error &error) { 2001 const std::string &intf, std::atomic<Error> &error) {
1946 auto sock = create_socket( 2002 auto sock = create_socket(
1947 host, port, 0, tcp_nodelay, socket_options, 2003 host, port, 0, tcp_nodelay, std::move(socket_options),
1948 [&](socket_t sock, struct addrinfo &ai) -> bool { 2004 [&](socket_t sock, struct addrinfo &ai) -> bool {
1949 if (!intf.empty()) { 2005 if (!intf.empty()) {
1950#ifdef USE_IF2IP 2006#ifdef USE_IF2IP
@@ -2478,7 +2534,8 @@ inline bool read_headers(Stream &strm, Headers &headers) {
2478} 2534}
2479 2535
2480inline bool read_content_with_length(Stream &strm, uint64_t len, 2536inline bool read_content_with_length(Stream &strm, uint64_t len,
2481 Progress progress, ContentReceiver out) { 2537 Progress progress,
2538 ContentReceiverWithProgress out) {
2482 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2539 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2483 2540
2484 uint64_t r = 0; 2541 uint64_t r = 0;
@@ -2487,8 +2544,7 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
2487 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); 2544 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
2488 if (n <= 0) { return false; } 2545 if (n <= 0) { return false; }
2489 2546
2490 if (!out(buf, static_cast<size_t>(n))) { return false; } 2547 if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
2491
2492 r += static_cast<uint64_t>(n); 2548 r += static_cast<uint64_t>(n);
2493 2549
2494 if (progress) { 2550 if (progress) {
@@ -2510,8 +2566,10 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
2510 } 2566 }
2511} 2567}
2512 2568
2513inline bool read_content_without_length(Stream &strm, ContentReceiver out) { 2569inline bool read_content_without_length(Stream &strm,
2570 ContentReceiverWithProgress out) {
2514 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2571 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2572 uint64_t r = 0;
2515 for (;;) { 2573 for (;;) {
2516 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); 2574 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
2517 if (n < 0) { 2575 if (n < 0) {
@@ -2519,13 +2577,16 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
2519 } else if (n == 0) { 2577 } else if (n == 0) {
2520 return true; 2578 return true;
2521 } 2579 }
2522 if (!out(buf, static_cast<size_t>(n))) { return false; } 2580
2581 if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
2582 r += static_cast<uint64_t>(n);
2523 } 2583 }
2524 2584
2525 return true; 2585 return true;
2526} 2586}
2527 2587
2528inline bool read_content_chunked(Stream &strm, ContentReceiver out) { 2588inline bool read_content_chunked(Stream &strm,
2589 ContentReceiverWithProgress out) {
2529 const auto bufsiz = 16; 2590 const auto bufsiz = 16;
2530 char buf[bufsiz]; 2591 char buf[bufsiz];
2531 2592
@@ -2570,23 +2631,24 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
2570} 2631}
2571 2632
2572template <typename T, typename U> 2633template <typename T, typename U>
2573bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver, 2634bool prepare_content_receiver(T &x, int &status,
2635 ContentReceiverWithProgress receiver,
2574 bool decompress, U callback) { 2636 bool decompress, U callback) {
2575 if (decompress) { 2637 if (decompress) {
2576 std::string encoding = x.get_header_value("Content-Encoding"); 2638 std::string encoding = x.get_header_value("Content-Encoding");
2577 std::shared_ptr<decompressor> decompressor; 2639 std::unique_ptr<decompressor> decompressor;
2578 2640
2579 if (encoding.find("gzip") != std::string::npos || 2641 if (encoding.find("gzip") != std::string::npos ||
2580 encoding.find("deflate") != std::string::npos) { 2642 encoding.find("deflate") != std::string::npos) {
2581#ifdef CPPHTTPLIB_ZLIB_SUPPORT 2643#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2582 decompressor = std::make_shared<gzip_decompressor>(); 2644 decompressor = detail::make_unique<gzip_decompressor>();
2583#else 2645#else
2584 status = 415; 2646 status = 415;
2585 return false; 2647 return false;
2586#endif 2648#endif
2587 } else if (encoding.find("br") != std::string::npos) { 2649 } else if (encoding.find("br") != std::string::npos) {
2588#ifdef CPPHTTPLIB_BROTLI_SUPPORT 2650#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2589 decompressor = std::make_shared<brotli_decompressor>(); 2651 decompressor = detail::make_unique<brotli_decompressor>();
2590#else 2652#else
2591 status = 415; 2653 status = 415;
2592 return false; 2654 return false;
@@ -2595,12 +2657,14 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2595 2657
2596 if (decompressor) { 2658 if (decompressor) {
2597 if (decompressor->is_valid()) { 2659 if (decompressor->is_valid()) {
2598 ContentReceiver out = [&](const char *buf, size_t n) { 2660 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
2599 return decompressor->decompress( 2661 uint64_t off, uint64_t len) {
2600 buf, n, 2662 return decompressor->decompress(buf, n,
2601 [&](const char *buf, size_t n) { return receiver(buf, n); }); 2663 [&](const char *buf, size_t n) {
2664 return receiver(buf, n, off, len);
2665 });
2602 }; 2666 };
2603 return callback(out); 2667 return callback(std::move(out));
2604 } else { 2668 } else {
2605 status = 500; 2669 status = 500;
2606 return false; 2670 return false;
@@ -2608,18 +2672,20 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2608 } 2672 }
2609 } 2673 }
2610 2674
2611 ContentReceiver out = [&](const char *buf, size_t n) { 2675 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
2612 return receiver(buf, n); 2676 uint64_t len) {
2677 return receiver(buf, n, off, len);
2613 }; 2678 };
2614 return callback(out); 2679 return callback(std::move(out));
2615} 2680}
2616 2681
2617template <typename T> 2682template <typename T>
2618bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, 2683bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2619 Progress progress, ContentReceiver receiver, 2684 Progress progress, ContentReceiverWithProgress receiver,
2620 bool decompress) { 2685 bool decompress) {
2621 return prepare_content_receiver( 2686 return prepare_content_receiver(
2622 x, status, receiver, decompress, [&](const ContentReceiver &out) { 2687 x, status, std::move(receiver), decompress,
2688 [&](const ContentReceiverWithProgress &out) {
2623 auto ret = true; 2689 auto ret = true;
2624 auto exceed_payload_max_length = false; 2690 auto exceed_payload_max_length = false;
2625 2691
@@ -2634,7 +2700,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2634 skip_content_with_length(strm, len); 2700 skip_content_with_length(strm, len);
2635 ret = false; 2701 ret = false;
2636 } else if (len > 0) { 2702 } else if (len > 0) {
2637 ret = read_content_with_length(strm, len, progress, out); 2703 ret = read_content_with_length(strm, len, std::move(progress), out);
2638 } 2704 }
2639 } 2705 }
2640 2706
@@ -2875,7 +2941,7 @@ inline bool parse_multipart_boundary(const std::string &content_type,
2875 return !boundary.empty(); 2941 return !boundary.empty();
2876} 2942}
2877 2943
2878inline bool parse_range_header(const std::string &s, Ranges &ranges) { 2944inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
2879 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); 2945 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
2880 std::smatch m; 2946 std::smatch m;
2881 if (std::regex_match(s, m, re_first_range)) { 2947 if (std::regex_match(s, m, re_first_range)) {
@@ -2907,7 +2973,7 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
2907 return all_valid_ranges; 2973 return all_valid_ranges;
2908 } 2974 }
2909 return false; 2975 return false;
2910} 2976} catch (...) { return false; }
2911 2977
2912class MultipartFormDataParser { 2978class MultipartFormDataParser {
2913public: 2979public:
@@ -2918,7 +2984,8 @@ public:
2918 bool is_valid() const { return is_valid_; } 2984 bool is_valid() const { return is_valid_; }
2919 2985
2920 template <typename T, typename U> 2986 template <typename T, typename U>
2921 bool parse(const char *buf, size_t n, T content_callback, U header_callback) { 2987 bool parse(const char *buf, size_t n, const T &content_callback,
2988 const U &header_callback) {
2922 2989
2923 static const std::regex re_content_disposition( 2990 static const std::regex re_content_disposition(
2924 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" 2991 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
@@ -3090,8 +3157,13 @@ inline std::string make_multipart_data_boundary() {
3090 static const char data[] = 3157 static const char data[] =
3091 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 3158 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3092 3159
3160 // std::random_device might actually be deterministic on some
3161 // platforms, but due to lack of support in the c++ standard library,
3162 // doing better requires either some ugly hacks or breaking portability.
3093 std::random_device seed_gen; 3163 std::random_device seed_gen;
3094 std::mt19937 engine(seed_gen()); 3164 // Request 128 bits of entropy for initialization
3165 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
3166 std::mt19937 engine(seed_sequence);
3095 3167
3096 std::string result = "--cpp-httplib-multipart-data-"; 3168 std::string result = "--cpp-httplib-multipart-data-";
3097 3169
@@ -3114,7 +3186,7 @@ get_range_offset_and_length(const Request &req, size_t content_length,
3114 auto slen = static_cast<ssize_t>(content_length); 3186 auto slen = static_cast<ssize_t>(content_length);
3115 3187
3116 if (r.first == -1) { 3188 if (r.first == -1) {
3117 r.first = slen - r.second; 3189 r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
3118 r.second = slen - 1; 3190 r.second = slen - 1;
3119 } 3191 }
3120 3192
@@ -3451,7 +3523,7 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
3451 if (r.second != -1) { field += std::to_string(r.second); } 3523 if (r.second != -1) { field += std::to_string(r.second); }
3452 i++; 3524 i++;
3453 } 3525 }
3454 return std::make_pair("Range", field); 3526 return std::make_pair("Range", std::move(field));
3455} 3527}
3456 3528
3457inline std::pair<std::string, std::string> 3529inline std::pair<std::string, std::string>
@@ -3460,7 +3532,7 @@ make_basic_authentication_header(const std::string &username,
3460 bool is_proxy = false) { 3532 bool is_proxy = false) {
3461 auto field = "Basic " + detail::base64_encode(username + ":" + password); 3533 auto field = "Basic " + detail::base64_encode(username + ":" + password);
3462 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3534 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3463 return std::make_pair(key, field); 3535 return std::make_pair(key, std::move(field));
3464} 3536}
3465 3537
3466inline std::pair<std::string, std::string> 3538inline std::pair<std::string, std::string>
@@ -3468,7 +3540,7 @@ make_bearer_token_authentication_header(const std::string &token,
3468 bool is_proxy = false) { 3540 bool is_proxy = false) {
3469 auto field = "Bearer " + token; 3541 auto field = "Bearer " + token;
3470 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3542 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3471 return std::make_pair(key, field); 3543 return std::make_pair(key, std::move(field));
3472} 3544}
3473 3545
3474// Request implementation 3546// Request implementation
@@ -3761,60 +3833,66 @@ inline Server::Server()
3761inline Server::~Server() {} 3833inline Server::~Server() {}
3762 3834
3763inline Server &Server::Get(const char *pattern, Handler handler) { 3835inline Server &Server::Get(const char *pattern, Handler handler) {
3764 get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3836 get_handlers_.push_back(
3837 std::make_pair(std::regex(pattern), std::move(handler)));
3765 return *this; 3838 return *this;
3766} 3839}
3767 3840
3768inline Server &Server::Post(const char *pattern, Handler handler) { 3841inline Server &Server::Post(const char *pattern, Handler handler) {
3769 post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3842 post_handlers_.push_back(
3843 std::make_pair(std::regex(pattern), std::move(handler)));
3770 return *this; 3844 return *this;
3771} 3845}
3772 3846
3773inline Server &Server::Post(const char *pattern, 3847inline Server &Server::Post(const char *pattern,
3774 HandlerWithContentReader handler) { 3848 HandlerWithContentReader handler) {
3775 post_handlers_for_content_reader_.push_back( 3849 post_handlers_for_content_reader_.push_back(
3776 std::make_pair(std::regex(pattern), handler)); 3850 std::make_pair(std::regex(pattern), std::move(handler)));
3777 return *this; 3851 return *this;
3778} 3852}
3779 3853
3780inline Server &Server::Put(const char *pattern, Handler handler) { 3854inline Server &Server::Put(const char *pattern, Handler handler) {
3781 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3855 put_handlers_.push_back(
3856 std::make_pair(std::regex(pattern), std::move(handler)));
3782 return *this; 3857 return *this;
3783} 3858}
3784 3859
3785inline Server &Server::Put(const char *pattern, 3860inline Server &Server::Put(const char *pattern,
3786 HandlerWithContentReader handler) { 3861 HandlerWithContentReader handler) {
3787 put_handlers_for_content_reader_.push_back( 3862 put_handlers_for_content_reader_.push_back(
3788 std::make_pair(std::regex(pattern), handler)); 3863 std::make_pair(std::regex(pattern), std::move(handler)));
3789 return *this; 3864 return *this;
3790} 3865}
3791 3866
3792inline Server &Server::Patch(const char *pattern, Handler handler) { 3867inline Server &Server::Patch(const char *pattern, Handler handler) {
3793 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3868 patch_handlers_.push_back(
3869 std::make_pair(std::regex(pattern), std::move(handler)));
3794 return *this; 3870 return *this;
3795} 3871}
3796 3872
3797inline Server &Server::Patch(const char *pattern, 3873inline Server &Server::Patch(const char *pattern,
3798 HandlerWithContentReader handler) { 3874 HandlerWithContentReader handler) {
3799 patch_handlers_for_content_reader_.push_back( 3875 patch_handlers_for_content_reader_.push_back(
3800 std::make_pair(std::regex(pattern), handler)); 3876 std::make_pair(std::regex(pattern), std::move(handler)));
3801 return *this; 3877 return *this;
3802} 3878}
3803 3879
3804inline Server &Server::Delete(const char *pattern, Handler handler) { 3880inline Server &Server::Delete(const char *pattern, Handler handler) {
3805 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3881 delete_handlers_.push_back(
3882 std::make_pair(std::regex(pattern), std::move(handler)));
3806 return *this; 3883 return *this;
3807} 3884}
3808 3885
3809inline Server &Server::Delete(const char *pattern, 3886inline Server &Server::Delete(const char *pattern,
3810 HandlerWithContentReader handler) { 3887 HandlerWithContentReader handler) {
3811 delete_handlers_for_content_reader_.push_back( 3888 delete_handlers_for_content_reader_.push_back(
3812 std::make_pair(std::regex(pattern), handler)); 3889 std::make_pair(std::regex(pattern), std::move(handler)));
3813 return *this; 3890 return *this;
3814} 3891}
3815 3892
3816inline Server &Server::Options(const char *pattern, Handler handler) { 3893inline Server &Server::Options(const char *pattern, Handler handler) {
3817 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3894 options_handlers_.push_back(
3895 std::make_pair(std::regex(pattern), std::move(handler)));
3818 return *this; 3896 return *this;
3819} 3897}
3820 3898
@@ -3860,7 +3938,7 @@ inline void Server::set_error_handler(Handler handler) {
3860inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 3938inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
3861 3939
3862inline void Server::set_socket_options(SocketOptions socket_options) { 3940inline void Server::set_socket_options(SocketOptions socket_options) {
3863 socket_options_ = socket_options; 3941 socket_options_ = std::move(socket_options);
3864} 3942}
3865 3943
3866inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } 3944inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
@@ -4045,16 +4123,16 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
4045 } 4123 }
4046 4124
4047 if (type != detail::EncodingType::None) { 4125 if (type != detail::EncodingType::None) {
4048 std::shared_ptr<detail::compressor> compressor; 4126 std::unique_ptr<detail::compressor> compressor;
4049 4127
4050 if (type == detail::EncodingType::Gzip) { 4128 if (type == detail::EncodingType::Gzip) {
4051#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4129#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4052 compressor = std::make_shared<detail::gzip_compressor>(); 4130 compressor = detail::make_unique<detail::gzip_compressor>();
4053 res.set_header("Content-Encoding", "gzip"); 4131 res.set_header("Content-Encoding", "gzip");
4054#endif 4132#endif
4055 } else if (type == detail::EncodingType::Brotli) { 4133 } else if (type == detail::EncodingType::Brotli) {
4056#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4134#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4057 compressor = std::make_shared<detail::brotli_compressor>(); 4135 compressor = detail::make_unique<detail::brotli_compressor>();
4058 res.set_header("Content-Encoding", "brotli"); 4136 res.set_header("Content-Encoding", "brotli");
4059#endif 4137#endif
4060 } 4138 }
@@ -4136,17 +4214,17 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
4136 if (res.is_chunked_content_provider) { 4214 if (res.is_chunked_content_provider) {
4137 auto type = detail::encoding_type(req, res); 4215 auto type = detail::encoding_type(req, res);
4138 4216
4139 std::shared_ptr<detail::compressor> compressor; 4217 std::unique_ptr<detail::compressor> compressor;
4140 if (type == detail::EncodingType::Gzip) { 4218 if (type == detail::EncodingType::Gzip) {
4141#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4219#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4142 compressor = std::make_shared<detail::gzip_compressor>(); 4220 compressor = detail::make_unique<detail::gzip_compressor>();
4143#endif 4221#endif
4144 } else if (type == detail::EncodingType::Brotli) { 4222 } else if (type == detail::EncodingType::Brotli) {
4145#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4223#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4146 compressor = std::make_shared<detail::brotli_compressor>(); 4224 compressor = detail::make_unique<detail::brotli_compressor>();
4147#endif 4225#endif
4148 } else { 4226 } else {
4149 compressor = std::make_shared<detail::nocompressor>(); 4227 compressor = detail::make_unique<detail::nocompressor>();
4150 } 4228 }
4151 assert(compressor != nullptr); 4229 assert(compressor != nullptr);
4152 4230
@@ -4198,8 +4276,9 @@ inline bool Server::read_content_with_content_receiver(
4198 Stream &strm, Request &req, Response &res, ContentReceiver receiver, 4276 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
4199 MultipartContentHeader multipart_header, 4277 MultipartContentHeader multipart_header,
4200 ContentReceiver multipart_receiver) { 4278 ContentReceiver multipart_receiver) {
4201 return read_content_core(strm, req, res, receiver, multipart_header, 4279 return read_content_core(strm, req, res, std::move(receiver),
4202 multipart_receiver); 4280 std::move(multipart_header),
4281 std::move(multipart_receiver));
4203} 4282}
4204 4283
4205inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, 4284inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
@@ -4207,7 +4286,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4207 MultipartContentHeader mulitpart_header, 4286 MultipartContentHeader mulitpart_header,
4208 ContentReceiver multipart_receiver) { 4287 ContentReceiver multipart_receiver) {
4209 detail::MultipartFormDataParser multipart_form_data_parser; 4288 detail::MultipartFormDataParser multipart_form_data_parser;
4210 ContentReceiver out; 4289 ContentReceiverWithProgress out;
4211 4290
4212 if (req.is_multipart_form_data()) { 4291 if (req.is_multipart_form_data()) {
4213 const auto &content_type = req.get_header_value("Content-Type"); 4292 const auto &content_type = req.get_header_value("Content-Type");
@@ -4218,7 +4297,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4218 } 4297 }
4219 4298
4220 multipart_form_data_parser.set_boundary(std::move(boundary)); 4299 multipart_form_data_parser.set_boundary(std::move(boundary));
4221 out = [&](const char *buf, size_t n) { 4300 out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
4222 /* For debug 4301 /* For debug
4223 size_t pos = 0; 4302 size_t pos = 0;
4224 while (pos < n) { 4303 while (pos < n) {
@@ -4234,7 +4313,8 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4234 mulitpart_header); 4313 mulitpart_header);
4235 }; 4314 };
4236 } else { 4315 } else {
4237 out = receiver; 4316 out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
4317 uint64_t /*len*/) { return receiver(buf, n); };
4238 } 4318 }
4239 4319
4240 if (req.method == "DELETE" && !req.has_header("Content-Length")) { 4320 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
@@ -4271,7 +4351,7 @@ inline bool Server::handle_file_request(Request &req, Response &res,
4271 auto type = 4351 auto type =
4272 detail::find_content_type(path, file_extension_and_mimetype_map_); 4352 detail::find_content_type(path, file_extension_and_mimetype_map_);
4273 if (type) { res.set_header("Content-Type", type); } 4353 if (type) { res.set_header("Content-Type", type); }
4274 for (const auto& kv : entry.headers) { 4354 for (const auto &kv : entry.headers) {
4275 res.set_header(kv.first.c_str(), kv.second); 4355 res.set_header(kv.first.c_str(), kv.second);
4276 } 4356 }
4277 res.status = 200; 4357 res.status = 200;
@@ -4290,7 +4370,7 @@ inline socket_t
4290Server::create_server_socket(const char *host, int port, int socket_flags, 4370Server::create_server_socket(const char *host, int port, int socket_flags,
4291 SocketOptions socket_options) const { 4371 SocketOptions socket_options) const {
4292 return detail::create_socket( 4372 return detail::create_socket(
4293 host, port, socket_flags, tcp_nodelay_, socket_options, 4373 host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
4294 [](socket_t sock, struct addrinfo &ai) -> bool { 4374 [](socket_t sock, struct addrinfo &ai) -> bool {
4295 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) { 4375 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
4296 return false; 4376 return false;
@@ -4392,32 +4472,37 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4392 { 4472 {
4393 ContentReader reader( 4473 ContentReader reader(
4394 [&](ContentReceiver receiver) { 4474 [&](ContentReceiver receiver) {
4395 return read_content_with_content_receiver(strm, req, res, receiver, 4475 return read_content_with_content_receiver(
4396 nullptr, nullptr); 4476 strm, req, res, std::move(receiver), nullptr, nullptr);
4397 }, 4477 },
4398 [&](MultipartContentHeader header, ContentReceiver receiver) { 4478 [&](MultipartContentHeader header, ContentReceiver receiver) {
4399 return read_content_with_content_receiver(strm, req, res, nullptr, 4479 return read_content_with_content_receiver(strm, req, res, nullptr,
4400 header, receiver); 4480 std::move(header),
4481 std::move(receiver));
4401 }); 4482 });
4402 4483
4403 if (req.method == "POST") { 4484 if (req.method == "POST") {
4404 if (dispatch_request_for_content_reader( 4485 if (dispatch_request_for_content_reader(
4405 req, res, reader, post_handlers_for_content_reader_)) { 4486 req, res, std::move(reader),
4487 post_handlers_for_content_reader_)) {
4406 return true; 4488 return true;
4407 } 4489 }
4408 } else if (req.method == "PUT") { 4490 } else if (req.method == "PUT") {
4409 if (dispatch_request_for_content_reader( 4491 if (dispatch_request_for_content_reader(
4410 req, res, reader, put_handlers_for_content_reader_)) { 4492 req, res, std::move(reader),
4493 put_handlers_for_content_reader_)) {
4411 return true; 4494 return true;
4412 } 4495 }
4413 } else if (req.method == "PATCH") { 4496 } else if (req.method == "PATCH") {
4414 if (dispatch_request_for_content_reader( 4497 if (dispatch_request_for_content_reader(
4415 req, res, reader, patch_handlers_for_content_reader_)) { 4498 req, res, std::move(reader),
4499 patch_handlers_for_content_reader_)) {
4416 return true; 4500 return true;
4417 } 4501 }
4418 } else if (req.method == "DELETE") { 4502 } else if (req.method == "DELETE") {
4419 if (dispatch_request_for_content_reader( 4503 if (dispatch_request_for_content_reader(
4420 req, res, reader, delete_handlers_for_content_reader_)) { 4504 req, res, std::move(reader),
4505 delete_handlers_for_content_reader_)) {
4421 return true; 4506 return true;
4422 } 4507 }
4423 } 4508 }
@@ -4448,7 +4533,6 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4448 4533
4449inline bool Server::dispatch_request(Request &req, Response &res, 4534inline bool Server::dispatch_request(Request &req, Response &res,
4450 const Handlers &handlers) { 4535 const Handlers &handlers) {
4451
4452 try { 4536 try {
4453 for (const auto &x : handlers) { 4537 for (const auto &x : handlers) {
4454 const auto &pattern = x.first; 4538 const auto &pattern = x.first;
@@ -4531,7 +4615,8 @@ Server::process_request(Stream &strm, bool close_connection,
4531 if (req.has_header("Range")) { 4615 if (req.has_header("Range")) {
4532 const auto &range_header_value = req.get_header_value("Range"); 4616 const auto &range_header_value = req.get_header_value("Range");
4533 if (!detail::parse_range_header(range_header_value, req.ranges)) { 4617 if (!detail::parse_range_header(range_header_value, req.ranges)) {
4534 // TODO: error 4618 res.status = 416;
4619 return write_response(strm, close_connection, req, res);
4535 } 4620 }
4536 } 4621 }
4537 4622
@@ -4588,11 +4673,11 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
4588inline ClientImpl::ClientImpl(const std::string &host, int port, 4673inline ClientImpl::ClientImpl(const std::string &host, int port,
4589 const std::string &client_cert_path, 4674 const std::string &client_cert_path,
4590 const std::string &client_key_path) 4675 const std::string &client_key_path)
4591 : host_(host), port_(port), 4676 : error_(Error::Success), host_(host), port_(port),
4592 host_and_port_(host_ + ":" + std::to_string(port_)), 4677 host_and_port_(host_ + ":" + std::to_string(port_)),
4593 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} 4678 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
4594 4679
4595inline ClientImpl::~ClientImpl() { stop_core(); } 4680inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
4596 4681
4597inline bool ClientImpl::is_valid() const { return true; } 4682inline bool ClientImpl::is_valid() const { return true; }
4598 4683
@@ -4653,15 +4738,47 @@ inline bool ClientImpl::create_and_connect_socket(Socket &socket) {
4653 return true; 4738 return true;
4654} 4739}
4655 4740
4656inline void ClientImpl::close_socket(Socket &socket, 4741inline void ClientImpl::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
4657 bool /*process_socket_ret*/) { 4742 (void)socket;
4658 detail::close_socket(socket.sock); 4743 (void)shutdown_gracefully;
4659 socket_.sock = INVALID_SOCKET; 4744 //If there are any requests in flight from threads other than us, then it's
4745 //a thread-unsafe race because individual ssl* objects are not thread-safe.
4746 assert(socket_requests_in_flight_ == 0 ||
4747 socket_requests_are_from_thread_ == std::this_thread::get_id());
4748}
4749
4750inline void ClientImpl::shutdown_socket(Socket &socket) {
4751 if (socket.sock == INVALID_SOCKET)
4752 return;
4753 detail::shutdown_socket(socket.sock);
4754}
4755
4756inline void ClientImpl::close_socket(Socket &socket) {
4757 // If there are requests in flight in another thread, usually closing
4758 // the socket will be fine and they will simply receive an error when
4759 // using the closed socket, but it is still a bug since rarely the OS
4760 // may reassign the socket id to be used for a new socket, and then
4761 // suddenly they will be operating on a live socket that is different
4762 // than the one they intended!
4763 assert(socket_requests_in_flight_ == 0 ||
4764 socket_requests_are_from_thread_ == std::this_thread::get_id());
4765 // It is also a bug if this happens while SSL is still active
4660#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4766#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4661 socket_.ssl = nullptr; 4767 assert(socket.ssl == nullptr);
4662#endif 4768#endif
4769 if (socket.sock == INVALID_SOCKET)
4770 return;
4771 detail::close_socket(socket.sock);
4772 socket.sock = INVALID_SOCKET;
4663} 4773}
4664 4774
4775inline void ClientImpl::lock_socket_and_shutdown_and_close() {
4776 std::lock_guard<std::mutex> guard(socket_mutex_);
4777 shutdown_ssl(socket_, true);
4778 shutdown_socket(socket_);
4779 close_socket(socket_);
4780}
4781
4665inline bool ClientImpl::read_response_line(Stream &strm, Response &res) { 4782inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
4666 std::array<char, 2048> buf; 4783 std::array<char, 2048> buf;
4667 4784
@@ -4696,11 +4813,23 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4696 4813
4697 { 4814 {
4698 std::lock_guard<std::mutex> guard(socket_mutex_); 4815 std::lock_guard<std::mutex> guard(socket_mutex_);
4816 // Set this to false immediately - if it ever gets set to true by the end of the
4817 // request, we know another thread instructed us to close the socket.
4818 socket_should_be_closed_when_request_is_done_ = false;
4699 4819
4700 auto is_alive = false; 4820 auto is_alive = false;
4701 if (socket_.is_open()) { 4821 if (socket_.is_open()) {
4702 is_alive = detail::select_write(socket_.sock, 0, 0) > 0; 4822 is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
4703 if (!is_alive) { close_socket(socket_, false); } 4823 if (!is_alive) {
4824 // Attempt to avoid sigpipe by shutting down nongracefully if it seems like
4825 // the other side has already closed the connection
4826 // Also, there cannot be any requests in flight from other threads since we locked
4827 // request_mutex_, so safe to close everything immediately
4828 const bool shutdown_gracefully = false;
4829 shutdown_ssl(socket_, shutdown_gracefully);
4830 shutdown_socket(socket_);
4831 close_socket(socket_);
4832 }
4704 } 4833 }
4705 4834
4706 if (!is_alive) { 4835 if (!is_alive) {
@@ -4721,15 +4850,38 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4721 } 4850 }
4722#endif 4851#endif
4723 } 4852 }
4853
4854 // Mark the current socket as being in use so that it cannot be closed by anyone
4855 // else while this request is ongoing, even though we will be releasing the mutex.
4856 if (socket_requests_in_flight_ > 1) {
4857 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
4858 }
4859 socket_requests_in_flight_ += 1;
4860 socket_requests_are_from_thread_ = std::this_thread::get_id();
4724 } 4861 }
4725 4862
4726 auto close_connection = !keep_alive_; 4863 auto close_connection = !keep_alive_;
4727
4728 auto ret = process_socket(socket_, [&](Stream &strm) { 4864 auto ret = process_socket(socket_, [&](Stream &strm) {
4729 return handle_request(strm, req, res, close_connection); 4865 return handle_request(strm, req, res, close_connection);
4730 }); 4866 });
4731 4867
4732 if (close_connection || !ret) { stop_core(); } 4868 //Briefly lock mutex in order to mark that a request is no longer ongoing
4869 {
4870 std::lock_guard<std::mutex> guard(socket_mutex_);
4871 socket_requests_in_flight_ -= 1;
4872 if (socket_requests_in_flight_ <= 0) {
4873 assert(socket_requests_in_flight_ == 0);
4874 socket_requests_are_from_thread_ = std::thread::id();
4875 }
4876
4877 if (socket_should_be_closed_when_request_is_done_ ||
4878 close_connection ||
4879 !ret ) {
4880 shutdown_ssl(socket_, true);
4881 shutdown_socket(socket_);
4882 close_socket(socket_);
4883 }
4884 }
4733 4885
4734 if (!ret) { 4886 if (!ret) {
4735 if (error_ == Error::Success) { error_ = Error::Unknown; } 4887 if (error_ == Error::Success) { error_ = Error::Unknown; }
@@ -4973,7 +5125,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
4973 return true; 5125 return true;
4974} 5126}
4975 5127
4976inline std::shared_ptr<Response> ClientImpl::send_with_content_provider( 5128inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
4977 const char *method, const char *path, const Headers &headers, 5129 const char *method, const char *path, const Headers &headers,
4978 const std::string &body, size_t content_length, 5130 const std::string &body, size_t content_length,
4979 ContentProvider content_provider, const char *content_type) { 5131 ContentProvider content_provider, const char *content_type) {
@@ -5036,15 +5188,15 @@ inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
5036 { 5188 {
5037 if (content_provider) { 5189 if (content_provider) {
5038 req.content_length = content_length; 5190 req.content_length = content_length;
5039 req.content_provider = content_provider; 5191 req.content_provider = std::move(content_provider);
5040 } else { 5192 } else {
5041 req.body = body; 5193 req.body = body;
5042 } 5194 }
5043 } 5195 }
5044 5196
5045 auto res = std::make_shared<Response>(); 5197 auto res = detail::make_unique<Response>();
5046 5198
5047 return send(req, *res) ? res : nullptr; 5199 return send(req, *res) ? std::move(res) : nullptr;
5048} 5200}
5049 5201
5050inline bool ClientImpl::process_request(Stream &strm, const Request &req, 5202inline bool ClientImpl::process_request(Stream &strm, const Request &req,
@@ -5070,16 +5222,21 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5070 if (req.method != "HEAD" && req.method != "CONNECT") { 5222 if (req.method != "HEAD" && req.method != "CONNECT") {
5071 auto out = 5223 auto out =
5072 req.content_receiver 5224 req.content_receiver
5073 ? static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5225 ? static_cast<ContentReceiverWithProgress>(
5074 auto ret = req.content_receiver(buf, n); 5226 [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
5075 if (!ret) { error_ = Error::Canceled; } 5227 auto ret = req.content_receiver(buf, n, off, len);
5076 return ret; 5228 if (!ret) { error_ = Error::Canceled; }
5077 }) 5229 return ret;
5078 : static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5230 })
5079 if (res.body.size() + n > res.body.max_size()) { return false; } 5231 : static_cast<ContentReceiverWithProgress>(
5080 res.body.append(buf, n); 5232 [&](const char *buf, size_t n, uint64_t /*off*/,
5081 return true; 5233 uint64_t /*len*/) {
5082 }); 5234 if (res.body.size() + n > res.body.max_size()) {
5235 return false;
5236 }
5237 res.body.append(buf, n);
5238 return true;
5239 });
5083 5240
5084 auto progress = [&](uint64_t current, uint64_t total) { 5241 auto progress = [&](uint64_t current, uint64_t total) {
5085 if (!req.progress) { return true; } 5242 if (!req.progress) { return true; }
@@ -5090,7 +5247,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5090 5247
5091 int dummy_status; 5248 int dummy_status;
5092 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), 5249 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
5093 dummy_status, progress, out, decompress_)) { 5250 dummy_status, std::move(progress), std::move(out),
5251 decompress_)) {
5094 if (error_ != Error::Canceled) { error_ = Error::Read; } 5252 if (error_ != Error::Canceled) { error_ = Error::Read; }
5095 return false; 5253 return false;
5096 } 5254 }
@@ -5098,7 +5256,16 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5098 5256
5099 if (res.get_header_value("Connection") == "close" || 5257 if (res.get_header_value("Connection") == "close" ||
5100 (res.version == "HTTP/1.0" && res.reason != "Connection established")) { 5258 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
5101 stop_core(); 5259 // TODO this requires a not-entirely-obvious chain of calls to be correct
5260 // for this to be safe. Maybe a code refactor (such as moving this out to
5261 // the send function and getting rid of the recursiveness of the mutex)
5262 // could make this more obvious.
5263
5264 // This is safe to call because process_request is only called by handle_request
5265 // which is only called by send, which locks the request mutex during the process.
5266 // It would be a bug to call it from a different thread since it's a thread-safety
5267 // issue to do these things to the socket if another thread is using the socket.
5268 lock_socket_and_shutdown_and_close();
5102 } 5269 }
5103 5270
5104 // Log 5271 // Log
@@ -5108,11 +5275,11 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5108} 5275}
5109 5276
5110inline bool 5277inline bool
5111ClientImpl::process_socket(Socket &socket, 5278ClientImpl::process_socket(const Socket &socket,
5112 std::function<bool(Stream &strm)> callback) { 5279 std::function<bool(Stream &strm)> callback) {
5113 return detail::process_client_socket(socket.sock, read_timeout_sec_, 5280 return detail::process_client_socket(
5114 read_timeout_usec_, write_timeout_sec_, 5281 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5115 write_timeout_usec_, callback); 5282 write_timeout_usec_, std::move(callback));
5116} 5283}
5117 5284
5118inline bool ClientImpl::is_ssl() const { return false; } 5285inline bool ClientImpl::is_ssl() const { return false; }
@@ -5138,9 +5305,9 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5138 req.headers.insert(headers.begin(), headers.end()); 5305 req.headers.insert(headers.begin(), headers.end());
5139 req.progress = std::move(progress); 5306 req.progress = std::move(progress);
5140 5307
5141 auto res = std::make_shared<Response>(); 5308 auto res = detail::make_unique<Response>();
5142 auto ret = send(req, *res); 5309 auto ret = send(req, *res);
5143 return Result{ret ? res : nullptr, get_last_error()}; 5310 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5144} 5311}
5145 5312
5146inline Result ClientImpl::Get(const char *path, 5313inline Result ClientImpl::Get(const char *path,
@@ -5170,23 +5337,23 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5170inline Result ClientImpl::Get(const char *path, 5337inline Result ClientImpl::Get(const char *path,
5171 ResponseHandler response_handler, 5338 ResponseHandler response_handler,
5172 ContentReceiver content_receiver) { 5339 ContentReceiver content_receiver) {
5173 return Get(path, Headers(), std::move(response_handler), content_receiver, 5340 return Get(path, Headers(), std::move(response_handler),
5174 nullptr); 5341 std::move(content_receiver), nullptr);
5175} 5342}
5176 5343
5177inline Result ClientImpl::Get(const char *path, const Headers &headers, 5344inline Result ClientImpl::Get(const char *path, const Headers &headers,
5178 ResponseHandler response_handler, 5345 ResponseHandler response_handler,
5179 ContentReceiver content_receiver) { 5346 ContentReceiver content_receiver) {
5180 return Get(path, headers, std::move(response_handler), content_receiver, 5347 return Get(path, headers, std::move(response_handler),
5181 nullptr); 5348 std::move(content_receiver), nullptr);
5182} 5349}
5183 5350
5184inline Result ClientImpl::Get(const char *path, 5351inline Result ClientImpl::Get(const char *path,
5185 ResponseHandler response_handler, 5352 ResponseHandler response_handler,
5186 ContentReceiver content_receiver, 5353 ContentReceiver content_receiver,
5187 Progress progress) { 5354 Progress progress) {
5188 return Get(path, Headers(), std::move(response_handler), content_receiver, 5355 return Get(path, Headers(), std::move(response_handler),
5189 progress); 5356 std::move(content_receiver), std::move(progress));
5190} 5357}
5191 5358
5192inline Result ClientImpl::Get(const char *path, const Headers &headers, 5359inline Result ClientImpl::Get(const char *path, const Headers &headers,
@@ -5199,12 +5366,16 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5199 req.headers = default_headers_; 5366 req.headers = default_headers_;
5200 req.headers.insert(headers.begin(), headers.end()); 5367 req.headers.insert(headers.begin(), headers.end());
5201 req.response_handler = std::move(response_handler); 5368 req.response_handler = std::move(response_handler);
5202 req.content_receiver = std::move(content_receiver); 5369 req.content_receiver =
5370 [content_receiver](const char *data, size_t data_length,
5371 uint64_t /*offset*/, uint64_t /*total_length*/) {
5372 return content_receiver(data, data_length);
5373 };
5203 req.progress = std::move(progress); 5374 req.progress = std::move(progress);
5204 5375
5205 auto res = std::make_shared<Response>(); 5376 auto res = detail::make_unique<Response>();
5206 auto ret = send(req, *res); 5377 auto ret = send(req, *res);
5207 return Result{ret ? res : nullptr, get_last_error()}; 5378 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5208} 5379}
5209 5380
5210inline Result ClientImpl::Head(const char *path) { 5381inline Result ClientImpl::Head(const char *path) {
@@ -5218,9 +5389,9 @@ inline Result ClientImpl::Head(const char *path, const Headers &headers) {
5218 req.headers.insert(headers.begin(), headers.end()); 5389 req.headers.insert(headers.begin(), headers.end());
5219 req.path = path; 5390 req.path = path;
5220 5391
5221 auto res = std::make_shared<Response>(); 5392 auto res = detail::make_unique<Response>();
5222 auto ret = send(req, *res); 5393 auto ret = send(req, *res);
5223 return Result{ret ? res : nullptr, get_last_error()}; 5394 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5224} 5395}
5225 5396
5226inline Result ClientImpl::Post(const char *path) { 5397inline Result ClientImpl::Post(const char *path) {
@@ -5237,7 +5408,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
5237 const char *content_type) { 5408 const char *content_type) {
5238 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr, 5409 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
5239 content_type); 5410 content_type);
5240 return Result{ret, get_last_error()}; 5411 return Result{std::move(ret), get_last_error()};
5241} 5412}
5242 5413
5243inline Result ClientImpl::Post(const char *path, const Params &params) { 5414inline Result ClientImpl::Post(const char *path, const Params &params) {
@@ -5247,17 +5418,18 @@ inline Result ClientImpl::Post(const char *path, const Params &params) {
5247inline Result ClientImpl::Post(const char *path, size_t content_length, 5418inline Result ClientImpl::Post(const char *path, size_t content_length,
5248 ContentProvider content_provider, 5419 ContentProvider content_provider,
5249 const char *content_type) { 5420 const char *content_type) {
5250 return Post(path, Headers(), content_length, content_provider, content_type); 5421 return Post(path, Headers(), content_length, std::move(content_provider),
5422 content_type);
5251} 5423}
5252 5424
5253inline Result ClientImpl::Post(const char *path, const Headers &headers, 5425inline Result ClientImpl::Post(const char *path, const Headers &headers,
5254 size_t content_length, 5426 size_t content_length,
5255 ContentProvider content_provider, 5427 ContentProvider content_provider,
5256 const char *content_type) { 5428 const char *content_type) {
5257 auto ret = send_with_content_provider("POST", path, headers, std::string(), 5429 auto ret = send_with_content_provider(
5258 content_length, content_provider, 5430 "POST", path, headers, std::string(), content_length,
5259 content_type); 5431 std::move(content_provider), content_type);
5260 return Result{ret, get_last_error()}; 5432 return Result{std::move(ret), get_last_error()};
5261} 5433}
5262 5434
5263inline Result ClientImpl::Post(const char *path, const Headers &headers, 5435inline Result ClientImpl::Post(const char *path, const Headers &headers,
@@ -5273,7 +5445,18 @@ inline Result ClientImpl::Post(const char *path,
5273 5445
5274inline Result ClientImpl::Post(const char *path, const Headers &headers, 5446inline Result ClientImpl::Post(const char *path, const Headers &headers,
5275 const MultipartFormDataItems &items) { 5447 const MultipartFormDataItems &items) {
5276 auto boundary = detail::make_multipart_data_boundary(); 5448 return Post(path, headers, items, detail::make_multipart_data_boundary());
5449}
5450inline Result ClientImpl::Post(const char *path, const Headers &headers,
5451 const MultipartFormDataItems &items,
5452 const std::string &boundary) {
5453 for (size_t i = 0; i < boundary.size(); i++) {
5454 char c = boundary[i];
5455 if (!std::isalnum(c) && c != '-' && c != '_') {
5456 error_ = Error::UnsupportedMultipartBoundaryChars;
5457 return Result{nullptr, error_};
5458 }
5459 }
5277 5460
5278 std::string body; 5461 std::string body;
5279 5462
@@ -5311,23 +5494,24 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
5311 const char *content_type) { 5494 const char *content_type) {
5312 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr, 5495 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
5313 content_type); 5496 content_type);
5314 return Result{ret, get_last_error()}; 5497 return Result{std::move(ret), get_last_error()};
5315} 5498}
5316 5499
5317inline Result ClientImpl::Put(const char *path, size_t content_length, 5500inline Result ClientImpl::Put(const char *path, size_t content_length,
5318 ContentProvider content_provider, 5501 ContentProvider content_provider,
5319 const char *content_type) { 5502 const char *content_type) {
5320 return Put(path, Headers(), content_length, content_provider, content_type); 5503 return Put(path, Headers(), content_length, std::move(content_provider),
5504 content_type);
5321} 5505}
5322 5506
5323inline Result ClientImpl::Put(const char *path, const Headers &headers, 5507inline Result ClientImpl::Put(const char *path, const Headers &headers,
5324 size_t content_length, 5508 size_t content_length,
5325 ContentProvider content_provider, 5509 ContentProvider content_provider,
5326 const char *content_type) { 5510 const char *content_type) {
5327 auto ret = send_with_content_provider("PUT", path, headers, std::string(), 5511 auto ret = send_with_content_provider(
5328 content_length, content_provider, 5512 "PUT", path, headers, std::string(), content_length,
5329 content_type); 5513 std::move(content_provider), content_type);
5330 return Result{ret, get_last_error()}; 5514 return Result{std::move(ret), get_last_error()};
5331} 5515}
5332 5516
5333inline Result ClientImpl::Put(const char *path, const Params &params) { 5517inline Result ClientImpl::Put(const char *path, const Params &params) {
@@ -5350,23 +5534,24 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5350 const char *content_type) { 5534 const char *content_type) {
5351 auto ret = send_with_content_provider("PATCH", path, headers, body, 0, 5535 auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
5352 nullptr, content_type); 5536 nullptr, content_type);
5353 return Result{ret, get_last_error()}; 5537 return Result{std::move(ret), get_last_error()};
5354} 5538}
5355 5539
5356inline Result ClientImpl::Patch(const char *path, size_t content_length, 5540inline Result ClientImpl::Patch(const char *path, size_t content_length,
5357 ContentProvider content_provider, 5541 ContentProvider content_provider,
5358 const char *content_type) { 5542 const char *content_type) {
5359 return Patch(path, Headers(), content_length, content_provider, content_type); 5543 return Patch(path, Headers(), content_length, std::move(content_provider),
5544 content_type);
5360} 5545}
5361 5546
5362inline Result ClientImpl::Patch(const char *path, const Headers &headers, 5547inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5363 size_t content_length, 5548 size_t content_length,
5364 ContentProvider content_provider, 5549 ContentProvider content_provider,
5365 const char *content_type) { 5550 const char *content_type) {
5366 auto ret = send_with_content_provider("PATCH", path, headers, std::string(), 5551 auto ret = send_with_content_provider(
5367 content_length, content_provider, 5552 "PATCH", path, headers, std::string(), content_length,
5368 content_type); 5553 std::move(content_provider), content_type);
5369 return Result{ret, get_last_error()}; 5554 return Result{std::move(ret), get_last_error()};
5370} 5555}
5371 5556
5372inline Result ClientImpl::Delete(const char *path) { 5557inline Result ClientImpl::Delete(const char *path) {
@@ -5394,9 +5579,9 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
5394 if (content_type) { req.headers.emplace("Content-Type", content_type); } 5579 if (content_type) { req.headers.emplace("Content-Type", content_type); }
5395 req.body = body; 5580 req.body = body;
5396 5581
5397 auto res = std::make_shared<Response>(); 5582 auto res = detail::make_unique<Response>();
5398 auto ret = send(req, *res); 5583 auto ret = send(req, *res);
5399 return Result{ret ? res : nullptr, get_last_error()}; 5584 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5400} 5585}
5401 5586
5402inline Result ClientImpl::Options(const char *path) { 5587inline Result ClientImpl::Options(const char *path) {
@@ -5410,9 +5595,9 @@ inline Result ClientImpl::Options(const char *path, const Headers &headers) {
5410 req.headers.insert(headers.begin(), headers.end()); 5595 req.headers.insert(headers.begin(), headers.end());
5411 req.path = path; 5596 req.path = path;
5412 5597
5413 auto res = std::make_shared<Response>(); 5598 auto res = detail::make_unique<Response>();
5414 auto ret = send(req, *res); 5599 auto ret = send(req, *res);
5415 return Result{ret ? res : nullptr, get_last_error()}; 5600 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5416} 5601}
5417 5602
5418inline size_t ClientImpl::is_socket_open() const { 5603inline size_t ClientImpl::is_socket_open() const {
@@ -5421,18 +5606,27 @@ inline size_t ClientImpl::is_socket_open() const {
5421} 5606}
5422 5607
5423inline void ClientImpl::stop() { 5608inline void ClientImpl::stop() {
5424 stop_core();
5425 error_ = Error::Canceled;
5426}
5427
5428inline void ClientImpl::stop_core() {
5429 std::lock_guard<std::mutex> guard(socket_mutex_); 5609 std::lock_guard<std::mutex> guard(socket_mutex_);
5430 if (socket_.is_open()) { 5610 // There is no guarantee that this doesn't get overwritten later, but set it so that
5431 detail::shutdown_socket(socket_.sock); 5611 // there is a good chance that any threads stopping as a result pick up this error.
5432 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5612 error_ = Error::Canceled;
5433 close_socket(socket_, true); 5613
5434 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5614 // If there is anything ongoing right now, the ONLY thread-safe thing we can do
5615 // is to shutdown_socket, so that threads using this socket suddenly discover
5616 // they can't read/write any more and error out.
5617 // Everything else (closing the socket, shutting ssl down) is unsafe because these
5618 // actions are not thread-safe.
5619 if (socket_requests_in_flight_ > 0) {
5620 shutdown_socket(socket_);
5621 // Aside from that, we set a flag for the socket to be closed when we're done.
5622 socket_should_be_closed_when_request_is_done_ = true;
5623 return;
5435 } 5624 }
5625
5626 //Otherwise, sitll holding the mutex, we can shut everything down ourselves
5627 shutdown_ssl(socket_, true);
5628 shutdown_socket(socket_);
5629 close_socket(socket_);
5436} 5630}
5437 5631
5438inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { 5632inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
@@ -5479,7 +5673,7 @@ inline void ClientImpl::set_default_headers(Headers headers) {
5479inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 5673inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
5480 5674
5481inline void ClientImpl::set_socket_options(SocketOptions socket_options) { 5675inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
5482 socket_options_ = socket_options; 5676 socket_options_ = std::move(socket_options);
5483} 5677}
5484 5678
5485inline void ClientImpl::set_compress(bool on) { compress_ = on; } 5679inline void ClientImpl::set_compress(bool on) { compress_ = on; }
@@ -5554,9 +5748,12 @@ inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
5554} 5748}
5555 5749
5556inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, 5750inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
5557 bool process_socket_ret) { 5751 bool shutdown_gracefully) {
5558 if (process_socket_ret) { 5752 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
5559 SSL_shutdown(ssl); // shutdown only if not already closed by remote 5753 // the remote has closed the network connection
5754 // Note that it is not always possible to avoid SIGPIPE, this is merely a best-efforts.
5755 if (shutdown_gracefully) {
5756 SSL_shutdown(ssl);
5560 } 5757 }
5561 5758
5562 std::lock_guard<std::mutex> guard(ctx_mutex); 5759 std::lock_guard<std::mutex> guard(ctx_mutex);
@@ -5650,22 +5847,7 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
5650 read_timeout_usec_(read_timeout_usec), 5847 read_timeout_usec_(read_timeout_usec),
5651 write_timeout_sec_(write_timeout_sec), 5848 write_timeout_sec_(write_timeout_sec),
5652 write_timeout_usec_(write_timeout_usec) { 5849 write_timeout_usec_(write_timeout_usec) {
5653 { 5850 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
5654 timeval tv;
5655 tv.tv_sec = static_cast<long>(read_timeout_sec);
5656 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
5657
5658 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&tv),
5659 sizeof(tv));
5660 }
5661 {
5662 timeval tv;
5663 tv.tv_sec = static_cast<long>(write_timeout_sec);
5664 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
5665
5666 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&tv),
5667 sizeof(tv));
5668 }
5669} 5851}
5670 5852
5671inline SSLSocketStream::~SSLSocketStream() {} 5853inline SSLSocketStream::~SSLSocketStream() {}
@@ -5680,8 +5862,27 @@ inline bool SSLSocketStream::is_writable() const {
5680} 5862}
5681 5863
5682inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { 5864inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
5683 if (SSL_pending(ssl_) > 0 || is_readable()) { 5865 if (SSL_pending(ssl_) > 0) {
5684 return SSL_read(ssl_, ptr, static_cast<int>(size)); 5866 return SSL_read(ssl_, ptr, static_cast<int>(size));
5867 } else if (is_readable()) {
5868 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5869 if (ret < 0) {
5870 auto err = SSL_get_error(ssl_, ret);
5871 while (err == SSL_ERROR_WANT_READ) {
5872 if (SSL_pending(ssl_) > 0) {
5873 return SSL_read(ssl_, ptr, static_cast<int>(size));
5874 } else if (is_readable()) {
5875 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5876 if (ret >= 0) {
5877 return ret;
5878 }
5879 err = SSL_get_error(ssl_, ret);
5880 } else {
5881 return -1;
5882 }
5883 }
5884 }
5885 return ret;
5685 } 5886 }
5686 return -1; 5887 return -1;
5687} 5888}
@@ -5788,9 +5989,12 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
5788 }); 5989 });
5789 5990
5790 detail::ssl_delete(ctx_mutex_, ssl, ret); 5991 detail::ssl_delete(ctx_mutex_, ssl, ret);
5992 detail::shutdown_socket(sock);
5993 detail::close_socket(sock);
5791 return ret; 5994 return ret;
5792 } 5995 }
5793 5996
5997 detail::shutdown_socket(sock);
5794 detail::close_socket(sock); 5998 detail::close_socket(sock);
5795 return false; 5999 return false;
5796} 6000}
@@ -5843,6 +6047,10 @@ inline SSLClient::SSLClient(const std::string &host, int port,
5843 6047
5844inline SSLClient::~SSLClient() { 6048inline SSLClient::~SSLClient() {
5845 if (ctx_) { SSL_CTX_free(ctx_); } 6049 if (ctx_) { SSL_CTX_free(ctx_); }
6050 // Make sure to shut down SSL since shutdown_ssl will resolve to the
6051 // base function rather than the derived function once we get to the
6052 // base class destructor, and won't free the SSL (causing a leak).
6053 SSLClient::shutdown_ssl(socket_, true);
5846} 6054}
5847 6055
5848inline bool SSLClient::is_valid() const { return ctx_; } 6056inline bool SSLClient::is_valid() const { return ctx_; }
@@ -5876,11 +6084,11 @@ inline bool SSLClient::create_and_connect_socket(Socket &socket) {
5876 return is_valid() && ClientImpl::create_and_connect_socket(socket); 6084 return is_valid() && ClientImpl::create_and_connect_socket(socket);
5877} 6085}
5878 6086
6087// Assumes that socket_mutex_ is locked and that there are no requests in flight
5879inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, 6088inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5880 bool &success) { 6089 bool &success) {
5881 success = true; 6090 success = true;
5882 Response res2; 6091 Response res2;
5883
5884 if (!detail::process_client_socket( 6092 if (!detail::process_client_socket(
5885 socket.sock, read_timeout_sec_, read_timeout_usec_, 6093 socket.sock, read_timeout_sec_, read_timeout_usec_,
5886 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { 6094 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
@@ -5889,7 +6097,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5889 req2.path = host_and_port_; 6097 req2.path = host_and_port_;
5890 return process_request(strm, req2, res2, false); 6098 return process_request(strm, req2, res2, false);
5891 })) { 6099 })) {
5892 close_socket(socket, true); 6100 // Thread-safe to close everything because we are assuming there are no requests in flight
6101 shutdown_ssl(socket, true);
6102 shutdown_socket(socket);
6103 close_socket(socket);
5893 success = false; 6104 success = false;
5894 return false; 6105 return false;
5895 } 6106 }
@@ -5912,7 +6123,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5912 true)); 6123 true));
5913 return process_request(strm, req3, res3, false); 6124 return process_request(strm, req3, res3, false);
5914 })) { 6125 })) {
5915 close_socket(socket, true); 6126 // Thread-safe to close everything because we are assuming there are no requests in flight
6127 shutdown_ssl(socket, true);
6128 shutdown_socket(socket);
6129 close_socket(socket);
5916 success = false; 6130 success = false;
5917 return false; 6131 return false;
5918 } 6132 }
@@ -6005,26 +6219,30 @@ inline bool SSLClient::initialize_ssl(Socket &socket) {
6005 return true; 6219 return true;
6006 } 6220 }
6007 6221
6008 close_socket(socket, false); 6222 shutdown_socket(socket);
6223 close_socket(socket);
6009 return false; 6224 return false;
6010} 6225}
6011 6226
6012inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { 6227inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
6013 detail::close_socket(socket.sock); 6228 if (socket.sock == INVALID_SOCKET) {
6014 socket_.sock = INVALID_SOCKET; 6229 assert(socket.ssl == nullptr);
6230 return;
6231 }
6015 if (socket.ssl) { 6232 if (socket.ssl) {
6016 detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); 6233 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
6017 socket_.ssl = nullptr; 6234 socket.ssl = nullptr;
6018 } 6235 }
6236 assert(socket.ssl == nullptr);
6019} 6237}
6020 6238
6021inline bool 6239inline bool
6022SSLClient::process_socket(Socket &socket, 6240SSLClient::process_socket(const Socket &socket,
6023 std::function<bool(Stream &strm)> callback) { 6241 std::function<bool(Stream &strm)> callback) {
6024 assert(socket.ssl); 6242 assert(socket.ssl);
6025 return detail::process_client_socket_ssl( 6243 return detail::process_client_socket_ssl(
6026 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, 6244 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
6027 write_timeout_sec_, write_timeout_usec_, callback); 6245 write_timeout_sec_, write_timeout_usec_, std::move(callback));
6028} 6246}
6029 6247
6030inline bool SSLClient::is_ssl() const { return true; } 6248inline bool SSLClient::is_ssl() const { return true; }
@@ -6175,6 +6393,8 @@ inline Client::Client(const char *scheme_host_port,
6175#else 6393#else
6176 if (!scheme.empty() && scheme != "http") { 6394 if (!scheme.empty() && scheme != "http") {
6177#endif 6395#endif
6396 std::string msg = "'" + scheme + "' scheme is not supported.";
6397 throw std::invalid_argument(msg);
6178 return; 6398 return;
6179 } 6399 }
6180 6400
@@ -6187,28 +6407,28 @@ inline Client::Client(const char *scheme_host_port,
6187 6407
6188 if (is_ssl) { 6408 if (is_ssl) {
6189#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 6409#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6190 cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path, 6410 cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
6191 client_key_path); 6411 client_cert_path, client_key_path);
6192 is_ssl_ = is_ssl; 6412 is_ssl_ = is_ssl;
6193#endif 6413#endif
6194 } else { 6414 } else {
6195 cli_ = std::make_shared<ClientImpl>(host.c_str(), port, client_cert_path, 6415 cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
6196 client_key_path); 6416 client_cert_path, client_key_path);
6197 } 6417 }
6198 } else { 6418 } else {
6199 cli_ = std::make_shared<ClientImpl>(scheme_host_port, 80, client_cert_path, 6419 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
6200 client_key_path); 6420 client_cert_path, client_key_path);
6201 } 6421 }
6202} 6422}
6203 6423
6204inline Client::Client(const std::string &host, int port) 6424inline Client::Client(const std::string &host, int port)
6205 : cli_(std::make_shared<ClientImpl>(host, port)) {} 6425 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
6206 6426
6207inline Client::Client(const std::string &host, int port, 6427inline Client::Client(const std::string &host, int port,
6208 const std::string &client_cert_path, 6428 const std::string &client_cert_path,
6209 const std::string &client_key_path) 6429 const std::string &client_key_path)
6210 : cli_(std::make_shared<ClientImpl>(host, port, client_cert_path, 6430 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
6211 client_key_path)) {} 6431 client_key_path)) {}
6212 6432
6213inline Client::~Client() {} 6433inline Client::~Client() {}
6214 6434
@@ -6221,11 +6441,11 @@ inline Result Client::Get(const char *path, const Headers &headers) {
6221 return cli_->Get(path, headers); 6441 return cli_->Get(path, headers);
6222} 6442}
6223inline Result Client::Get(const char *path, Progress progress) { 6443inline Result Client::Get(const char *path, Progress progress) {
6224 return cli_->Get(path, progress); 6444 return cli_->Get(path, std::move(progress));
6225} 6445}
6226inline Result Client::Get(const char *path, const Headers &headers, 6446inline Result Client::Get(const char *path, const Headers &headers,
6227 Progress progress) { 6447 Progress progress) {
6228 return cli_->Get(path, headers, progress); 6448 return cli_->Get(path, headers, std::move(progress));
6229} 6449}
6230inline Result Client::Get(const char *path, ContentReceiver content_receiver) { 6450inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
6231 return cli_->Get(path, std::move(content_receiver)); 6451 return cli_->Get(path, std::move(content_receiver));
@@ -6262,7 +6482,8 @@ inline Result Client::Get(const char *path, ResponseHandler response_handler,
6262inline Result Client::Get(const char *path, const Headers &headers, 6482inline Result Client::Get(const char *path, const Headers &headers,
6263 ResponseHandler response_handler, 6483 ResponseHandler response_handler,
6264 ContentReceiver content_receiver, Progress progress) { 6484 ContentReceiver content_receiver, Progress progress) {
6265 return cli_->Get(path, headers, response_handler, content_receiver, progress); 6485 return cli_->Get(path, headers, std::move(response_handler),
6486 std::move(content_receiver), std::move(progress));
6266} 6487}
6267 6488
6268inline Result Client::Head(const char *path) { return cli_->Head(path); } 6489inline Result Client::Head(const char *path) { return cli_->Head(path); }
@@ -6282,13 +6503,14 @@ inline Result Client::Post(const char *path, const Headers &headers,
6282inline Result Client::Post(const char *path, size_t content_length, 6503inline Result Client::Post(const char *path, size_t content_length,
6283 ContentProvider content_provider, 6504 ContentProvider content_provider,
6284 const char *content_type) { 6505 const char *content_type) {
6285 return cli_->Post(path, content_length, content_provider, content_type); 6506 return cli_->Post(path, content_length, std::move(content_provider),
6507 content_type);
6286} 6508}
6287inline Result Client::Post(const char *path, const Headers &headers, 6509inline Result Client::Post(const char *path, const Headers &headers,
6288 size_t content_length, 6510 size_t content_length,
6289 ContentProvider content_provider, 6511 ContentProvider content_provider,
6290 const char *content_type) { 6512 const char *content_type) {
6291 return cli_->Post(path, headers, content_length, content_provider, 6513 return cli_->Post(path, headers, content_length, std::move(content_provider),
6292 content_type); 6514 content_type);
6293} 6515}
6294inline Result Client::Post(const char *path, const Params &params) { 6516inline Result Client::Post(const char *path, const Params &params) {
@@ -6306,6 +6528,11 @@ inline Result Client::Post(const char *path, const Headers &headers,
6306 const MultipartFormDataItems &items) { 6528 const MultipartFormDataItems &items) {
6307 return cli_->Post(path, headers, items); 6529 return cli_->Post(path, headers, items);
6308} 6530}
6531inline Result Client::Post(const char *path, const Headers &headers,
6532 const MultipartFormDataItems &items,
6533 const std::string &boundary) {
6534 return cli_->Post(path, headers, items, boundary);
6535}
6309inline Result Client::Put(const char *path) { return cli_->Put(path); } 6536inline Result Client::Put(const char *path) { return cli_->Put(path); }
6310inline Result Client::Put(const char *path, const std::string &body, 6537inline Result Client::Put(const char *path, const std::string &body,
6311 const char *content_type) { 6538 const char *content_type) {
@@ -6318,13 +6545,14 @@ inline Result Client::Put(const char *path, const Headers &headers,
6318inline Result Client::Put(const char *path, size_t content_length, 6545inline Result Client::Put(const char *path, size_t content_length,
6319 ContentProvider content_provider, 6546 ContentProvider content_provider,
6320 const char *content_type) { 6547 const char *content_type) {
6321 return cli_->Put(path, content_length, content_provider, content_type); 6548 return cli_->Put(path, content_length, std::move(content_provider),
6549 content_type);
6322} 6550}
6323inline Result Client::Put(const char *path, const Headers &headers, 6551inline Result Client::Put(const char *path, const Headers &headers,
6324 size_t content_length, 6552 size_t content_length,
6325 ContentProvider content_provider, 6553 ContentProvider content_provider,
6326 const char *content_type) { 6554 const char *content_type) {
6327 return cli_->Put(path, headers, content_length, content_provider, 6555 return cli_->Put(path, headers, content_length, std::move(content_provider),
6328 content_type); 6556 content_type);
6329} 6557}
6330inline Result Client::Put(const char *path, const Params &params) { 6558inline Result Client::Put(const char *path, const Params &params) {
@@ -6345,13 +6573,14 @@ inline Result Client::Patch(const char *path, const Headers &headers,
6345inline Result Client::Patch(const char *path, size_t content_length, 6573inline Result Client::Patch(const char *path, size_t content_length,
6346 ContentProvider content_provider, 6574 ContentProvider content_provider,
6347 const char *content_type) { 6575 const char *content_type) {
6348 return cli_->Patch(path, content_length, content_provider, content_type); 6576 return cli_->Patch(path, content_length, std::move(content_provider),
6577 content_type);
6349} 6578}
6350inline Result Client::Patch(const char *path, const Headers &headers, 6579inline Result Client::Patch(const char *path, const Headers &headers,
6351 size_t content_length, 6580 size_t content_length,
6352 ContentProvider content_provider, 6581 ContentProvider content_provider,
6353 const char *content_type) { 6582 const char *content_type) {
6354 return cli_->Patch(path, headers, content_length, content_provider, 6583 return cli_->Patch(path, headers, content_length, std::move(content_provider),
6355 content_type); 6584 content_type);
6356} 6585}
6357inline Result Client::Delete(const char *path) { return cli_->Delete(path); } 6586inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
@@ -6386,7 +6615,7 @@ inline void Client::set_default_headers(Headers headers) {
6386 6615
6387inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } 6616inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
6388inline void Client::set_socket_options(SocketOptions socket_options) { 6617inline void Client::set_socket_options(SocketOptions socket_options) {
6389 cli_->set_socket_options(socket_options); 6618 cli_->set_socket_options(std::move(socket_options));
6390} 6619}
6391 6620
6392inline void Client::set_connection_timeout(time_t sec, time_t usec) { 6621inline void Client::set_connection_timeout(time_t sec, time_t usec) {
diff --git a/externals/inih/inih b/externals/inih/inih
Subproject 603729dec89aaca42d7bd08f08bc333165b7d5d Subproject 1e80a47dffbda813604f0913e2ad68c7054c14e
diff --git a/externals/libressl b/externals/libressl
Subproject 7d01cb01cb1a926ecb4c9c98b107ef3c26f59df Subproject 8289d0d07de6553bf4b900bf60e808ea3f7f59d
diff --git a/externals/lurlparser/CMakeLists.txt b/externals/lurlparser/CMakeLists.txt
deleted file mode 100644
index 45046ffd3..000000000
--- a/externals/lurlparser/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
1add_library(lurlparser
2 LUrlParser.cpp
3 LUrlParser.h
4)
5
6create_target_directory_groups(lurlparser)
7
8target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/externals/lurlparser/LUrlParser.cpp b/externals/lurlparser/LUrlParser.cpp
deleted file mode 100644
index 9c134e330..000000000
--- a/externals/lurlparser/LUrlParser.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
1/*
2 * Lightweight URL & URI parser (RFC 1738, RFC 3986)
3 * https://github.com/corporateshark/LUrlParser
4 *
5 * The MIT License (MIT)
6 *
7 * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in all
17 * copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28#include "LUrlParser.h"
29
30#include <algorithm>
31#include <cstring>
32#include <stdlib.h>
33
34// check if the scheme name is valid
35static bool IsSchemeValid( const std::string& SchemeName )
36{
37 for ( auto c : SchemeName )
38 {
39 if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
40 }
41
42 return true;
43}
44
45bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
46{
47 if ( !IsValid() ) { return false; }
48
49 int Port = atoi( m_Port.c_str() );
50
51 if ( Port <= 0 || Port > 65535 ) { return false; }
52
53 if ( OutPort ) { *OutPort = Port; }
54
55 return true;
56}
57
58// based on RFC 1738 and RFC 3986
59LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
60{
61 LUrlParser::clParseURL Result;
62
63 const char* CurrentString = URL.c_str();
64
65 /*
66 * <scheme>:<scheme-specific-part>
67 * <scheme> := [a-z\+\-\.]+
68 * For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
69 */
70
71 // try to read scheme
72 {
73 const char* LocalString = strchr( CurrentString, ':' );
74
75 if ( !LocalString )
76 {
77 return clParseURL( LUrlParserError_NoUrlCharacter );
78 }
79
80 // save the scheme name
81 Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
82
83 if ( !IsSchemeValid( Result.m_Scheme ) )
84 {
85 return clParseURL( LUrlParserError_InvalidSchemeName );
86 }
87
88 // scheme should be lowercase
89 std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
90
91 // skip ':'
92 CurrentString = LocalString+1;
93 }
94
95 /*
96 * //<user>:<password>@<host>:<port>/<url-path>
97 * any ":", "@" and "/" must be normalized
98 */
99
100 // skip "//"
101 if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
102 if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
103
104 // check if the user name and password are specified
105 bool bHasUserName = false;
106
107 const char* LocalString = CurrentString;
108
109 while ( *LocalString )
110 {
111 if ( *LocalString == '@' )
112 {
113 // user name and password are specified
114 bHasUserName = true;
115 break;
116 }
117 else if ( *LocalString == '/' )
118 {
119 // end of <host>:<port> specification
120 bHasUserName = false;
121 break;
122 }
123
124 LocalString++;
125 }
126
127 // user name and password
128 LocalString = CurrentString;
129
130 if ( bHasUserName )
131 {
132 // read user name
133 while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
134
135 Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
136
137 // proceed with the current pointer
138 CurrentString = LocalString;
139
140 if ( *CurrentString == ':' )
141 {
142 // skip ':'
143 CurrentString++;
144
145 // read password
146 LocalString = CurrentString;
147
148 while ( *LocalString && *LocalString != '@' ) LocalString++;
149
150 Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
151
152 CurrentString = LocalString;
153 }
154
155 // skip '@'
156 if ( *CurrentString != '@' )
157 {
158 return clParseURL( LUrlParserError_NoAtSign );
159 }
160
161 CurrentString++;
162 }
163
164 bool bHasBracket = ( *CurrentString == '[' );
165
166 // go ahead, read the host name
167 LocalString = CurrentString;
168
169 while ( *LocalString )
170 {
171 if ( bHasBracket && *LocalString == ']' )
172 {
173 // end of IPv6 address
174 LocalString++;
175 break;
176 }
177 else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
178 {
179 // port number is specified
180 break;
181 }
182
183 LocalString++;
184 }
185
186 Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
187
188 CurrentString = LocalString;
189
190 // is port number specified?
191 if ( *CurrentString == ':' )
192 {
193 CurrentString++;
194
195 // read port number
196 LocalString = CurrentString;
197
198 while ( *LocalString && *LocalString != '/' ) LocalString++;
199
200 Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
201
202 CurrentString = LocalString;
203 }
204
205 // end of string
206 if ( !*CurrentString )
207 {
208 Result.m_ErrorCode = LUrlParserError_Ok;
209
210 return Result;
211 }
212
213 // skip '/'
214 if ( *CurrentString != '/' )
215 {
216 return clParseURL( LUrlParserError_NoSlash );
217 }
218
219 CurrentString++;
220
221 // parse the path
222 LocalString = CurrentString;
223
224 while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
225
226 Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
227
228 CurrentString = LocalString;
229
230 // check for query
231 if ( *CurrentString == '?' )
232 {
233 // skip '?'
234 CurrentString++;
235
236 // read query
237 LocalString = CurrentString;
238
239 while ( *LocalString && *LocalString != '#' ) LocalString++;
240
241 Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
242
243 CurrentString = LocalString;
244 }
245
246 // check for fragment
247 if ( *CurrentString == '#' )
248 {
249 // skip '#'
250 CurrentString++;
251
252 // read fragment
253 LocalString = CurrentString;
254
255 while ( *LocalString ) LocalString++;
256
257 Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
258
259 CurrentString = LocalString;
260 }
261
262 Result.m_ErrorCode = LUrlParserError_Ok;
263
264 return Result;
265}
diff --git a/externals/lurlparser/LUrlParser.h b/externals/lurlparser/LUrlParser.h
deleted file mode 100644
index 25d210981..000000000
--- a/externals/lurlparser/LUrlParser.h
+++ /dev/null
@@ -1,78 +0,0 @@
1/*
2 * Lightweight URL & URI parser (RFC 1738, RFC 3986)
3 * https://github.com/corporateshark/LUrlParser
4 *
5 * The MIT License (MIT)
6 *
7 * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in all
17 * copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28#pragma once
29
30#include <string>
31
32namespace LUrlParser
33{
34enum LUrlParserError
35{
36 LUrlParserError_Ok = 0,
37 LUrlParserError_Uninitialized = 1,
38 LUrlParserError_NoUrlCharacter = 2,
39 LUrlParserError_InvalidSchemeName = 3,
40 LUrlParserError_NoDoubleSlash = 4,
41 LUrlParserError_NoAtSign = 5,
42 LUrlParserError_UnexpectedEndOfLine = 6,
43 LUrlParserError_NoSlash = 7,
44};
45
46class clParseURL
47{
48public:
49 LUrlParserError m_ErrorCode;
50 std::string m_Scheme;
51 std::string m_Host;
52 std::string m_Port;
53 std::string m_Path;
54 std::string m_Query;
55 std::string m_Fragment;
56 std::string m_UserName;
57 std::string m_Password;
58
59 clParseURL()
60 : m_ErrorCode( LUrlParserError_Uninitialized )
61 {}
62
63 /// return 'true' if the parsing was successful
64 bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
65
66 /// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
67 bool GetPort( int* OutPort ) const;
68
69 /// parse the URL
70 static clParseURL ParseURL( const std::string& URL );
71
72private:
73 explicit clParseURL( LUrlParserError ErrorCode )
74 : m_ErrorCode( ErrorCode )
75 {}
76};
77
78} // namespace LUrlParser
diff --git a/externals/lurlparser/README.md b/externals/lurlparser/README.md
deleted file mode 100644
index be7f0135a..000000000
--- a/externals/lurlparser/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
1From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
2
3MIT License
4
5===
6
7Lightweight URL & URI parser (RFC 1738, RFC 3986)
8
9(C) Sergey Kosarevsky, 2015
10
11@corporateshark sk@linderdaum.com
12
13http://www.linderdaum.com
14
15http://blog.linderdaum.com
16
17=============================
18
19A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 1c1d09ccb..e186ed880 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -91,7 +91,7 @@ void Fiber::Rewind() {
91 SwitchToFiber(impl->rewind_handle); 91 SwitchToFiber(impl->rewind_handle);
92} 92}
93 93
94void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { 94void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
95 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 95 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
96 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 96 ASSERT_MSG(to != nullptr, "Next fiber is null!");
97 to->guard.lock(); 97 to->guard.lock();
@@ -199,7 +199,7 @@ void Fiber::Rewind() {
199 boost::context::detail::jump_fcontext(impl->rewind_context, this); 199 boost::context::detail::jump_fcontext(impl->rewind_context, this);
200} 200}
201 201
202void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { 202void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
203 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 203 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
204 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 204 ASSERT_MSG(to != nullptr, "Next fiber is null!");
205 to->guard.lock(); 205 to->guard.lock();
diff --git a/src/common/fiber.h b/src/common/fiber.h
index 89dde5e36..cefd61df9 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -46,7 +46,7 @@ public:
46 46
47 /// Yields control from Fiber 'from' to Fiber 'to' 47 /// Yields control from Fiber 'from' to Fiber 'to'
48 /// Fiber 'from' must be the currently running fiber. 48 /// Fiber 'from' must be the currently running fiber.
49 static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to); 49 static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
50 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); 50 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
51 51
52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter); 52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b2b5b8adf..bb3e312a7 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -86,8 +86,6 @@ struct KernelCore::Impl {
86 } 86 }
87 cores.clear(); 87 cores.clear();
88 88
89 registered_core_threads.reset();
90
91 process_list.clear(); 89 process_list.clear();
92 current_process = nullptr; 90 current_process = nullptr;
93 91
@@ -199,9 +197,7 @@ struct KernelCore::Impl {
199 const auto it = std::find(register_host_thread_keys.begin(), end, this_id); 197 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
200 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); 198 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
201 ASSERT(it == end); 199 ASSERT(it == end);
202 ASSERT(!registered_core_threads[core_id]);
203 InsertHostThread(static_cast<u32>(core_id)); 200 InsertHostThread(static_cast<u32>(core_id));
204 registered_core_threads.set(core_id);
205 } 201 }
206 202
207 void RegisterHostThread() { 203 void RegisterHostThread() {
@@ -332,7 +328,6 @@ struct KernelCore::Impl {
332 328
333 // 0-3 IDs represent core threads, >3 represent others 329 // 0-3 IDs represent core threads, >3 represent others
334 std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; 330 std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
335 std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
336 331
337 // Number of host threads is a relatively high number to avoid overflowing 332 // Number of host threads is a relatively high number to avoid overflowing
338 static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64; 333 static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index ff9d9248b..b17529dee 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <bitset> 6#include <bitset>
7#include <ctime>
7#include <memory> 8#include <memory>
8#include <random> 9#include <random>
9#include "common/alignment.h" 10#include "common/alignment.h"
@@ -123,7 +124,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
123 : kernel.CreateNewUserProcessID(); 124 : kernel.CreateNewUserProcessID();
124 process->capabilities.InitializeForMetadatalessProcess(); 125 process->capabilities.InitializeForMetadatalessProcess();
125 126
126 std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0)); 127 std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr)));
127 std::uniform_int_distribution<u64> distribution; 128 std::uniform_int_distribution<u64> distribution;
128 std::generate(process->random_entropy.begin(), process->random_entropy.end(), 129 std::generate(process->random_entropy.begin(), process->random_entropy.end(),
129 [&] { return distribution(rng); }); 130 [&] { return distribution(rng); });
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index d7a81f64a..2ce742e35 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1201,6 +1201,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1201 {151, nullptr, "TryPopFromNotificationStorageChannel"}, 1201 {151, nullptr, "TryPopFromNotificationStorageChannel"},
1202 {160, nullptr, "GetHealthWarningDisappearedSystemEvent"}, 1202 {160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
1203 {170, nullptr, "SetHdcpAuthenticationActivated"}, 1203 {170, nullptr, "SetHdcpAuthenticationActivated"},
1204 {180, nullptr, "GetLaunchRequiredVersion"},
1205 {181, nullptr, "UpgradeLaunchRequiredVersion"},
1204 {500, nullptr, "StartContinuousRecordingFlushForDebug"}, 1206 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
1205 {1000, nullptr, "CreateMovieMaker"}, 1207 {1000, nullptr, "CreateMovieMaker"},
1206 {1001, nullptr, "PrepareForJit"}, 1208 {1001, nullptr, "PrepareForJit"},
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8918946a1..50f709b25 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -260,7 +260,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
260 {404, nullptr, "HasLeftRightBattery"}, 260 {404, nullptr, "HasLeftRightBattery"},
261 {405, nullptr, "GetNpadInterfaceType"}, 261 {405, nullptr, "GetNpadInterfaceType"},
262 {406, nullptr, "GetNpadLeftRightInterfaceType"}, 262 {406, nullptr, "GetNpadLeftRightInterfaceType"},
263 {407, nullptr, "GetNpadOfHighestBatteryLevelForJoyLeft"}, 263 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
264 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, 264 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
265 {500, nullptr, "GetPalmaConnectionHandle"}, 265 {500, nullptr, "GetPalmaConnectionHandle"},
266 {501, nullptr, "InitializePalma"}, 266 {501, nullptr, "InitializePalma"},
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index b81bf6277..d7080b715 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -47,6 +47,7 @@ public:
47 {23, nullptr, "Convert"}, 47 {23, nullptr, "Convert"},
48 {24, nullptr, "ConvertCoreDataToCharInfo"}, 48 {24, nullptr, "ConvertCoreDataToCharInfo"},
49 {25, nullptr, "ConvertCharInfoToCoreData"}, 49 {25, nullptr, "ConvertCharInfoToCoreData"},
50 {26, nullptr, "Append"},
50 }; 51 };
51 // clang-format on 52 // clang-format on
52 53
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index e64777668..ffbf90b00 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -202,6 +202,7 @@ SET::SET() : ServiceFramework("set") {
202 {8, &SET::GetQuestFlag, "GetQuestFlag"}, 202 {8, &SET::GetQuestFlag, "GetQuestFlag"},
203 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, 203 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
204 {10, nullptr, "GetFirmwareVersionForDebug"}, 204 {10, nullptr, "GetFirmwareVersionForDebug"},
205 {11, nullptr, "GetDeviceNickName"},
205 }; 206 };
206 // clang-format on 207 // clang-format on
207 208
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 8bd4c7e79..080b5743e 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -300,6 +300,8 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
300 {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"}, 300 {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"},
301 {199, nullptr, "GetButtonConfigRegisteredSettings"}, 301 {199, nullptr, "GetButtonConfigRegisteredSettings"},
302 {200, nullptr, "SetButtonConfigRegisteredSettings"}, 302 {200, nullptr, "SetButtonConfigRegisteredSettings"},
303 {201, nullptr, "GetFieldTestingFlag"},
304 {202, nullptr, "SetFieldTestingFlag"},
303 }; 305 };
304 // clang-format on 306 // clang-format on
305 307
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index c95feb0d7..b912188b6 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -21,26 +21,6 @@
21 21
22namespace GCAdapter { 22namespace GCAdapter {
23 23
24// Used to loop through and assign button in poller
25constexpr std::array<PadButton, 12> PadButtonArray{
26 PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
27 PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
28 PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
29 PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
30};
31
32static void PadToState(const GCPadStatus& pad, GCState& out_state) {
33 for (const auto& button : PadButtonArray) {
34 const auto button_key = static_cast<u16>(button);
35 const auto button_value = (pad.button & button_key) != 0;
36 out_state.buttons.insert_or_assign(static_cast<s32>(button_key), button_value);
37 }
38
39 for (std::size_t i = 0; i < pad.axis_values.size(); ++i) {
40 out_state.axes.insert_or_assign(static_cast<u32>(i), pad.axis_values[i]);
41 }
42}
43
44Adapter::Adapter() { 24Adapter::Adapter() {
45 if (usb_adapter_handle != nullptr) { 25 if (usb_adapter_handle != nullptr) {
46 return; 26 return;
@@ -49,168 +29,263 @@ Adapter::Adapter() {
49 29
50 const int init_res = libusb_init(&libusb_ctx); 30 const int init_res = libusb_init(&libusb_ctx);
51 if (init_res == LIBUSB_SUCCESS) { 31 if (init_res == LIBUSB_SUCCESS) {
52 Setup(); 32 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
53 } else { 33 } else {
54 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 34 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
55 } 35 }
56} 36}
57 37
58GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { 38Adapter::~Adapter() {
59 GCPadStatus pad = {}; 39 Reset();
60 const std::size_t offset = 1 + (9 * port); 40}
41
42void Adapter::AdapterInputThread() {
43 LOG_DEBUG(Input, "GC Adapter input thread started");
44 s32 payload_size{};
45 AdapterPayload adapter_payload{};
46
47 if (adapter_scan_thread.joinable()) {
48 adapter_scan_thread.join();
49 }
50
51 while (adapter_input_thread_running) {
52 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
53 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
54 if (IsPayloadCorrect(adapter_payload, payload_size)) {
55 UpdateControllers(adapter_payload);
56 UpdateVibrations();
57 }
58 std::this_thread::yield();
59 }
61 60
62 adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); 61 if (restart_scan_thread) {
62 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
63 restart_scan_thread = false;
64 }
65}
66
67bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
68 if (payload_size != static_cast<s32>(adapter_payload.size()) ||
69 adapter_payload[0] != LIBUSB_DT_HID) {
70 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
71 adapter_payload[0]);
72 if (input_error_counter++ > 20) {
73 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
74 adapter_input_thread_running = false;
75 restart_scan_thread = true;
76 }
77 return false;
78 }
79
80 input_error_counter = 0;
81 return true;
82}
83
84void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
85 for (std::size_t port = 0; port < pads.size(); ++port) {
86 const std::size_t offset = 1 + (9 * port);
87 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
88 UpdatePadType(port, type);
89 if (DeviceConnected(port)) {
90 const u8 b1 = adapter_payload[offset + 1];
91 const u8 b2 = adapter_payload[offset + 2];
92 UpdateStateButtons(port, b1, b2);
93 UpdateStateAxes(port, adapter_payload);
94 if (configuring) {
95 UpdateYuzuSettings(port);
96 }
97 }
98 }
99}
100
101void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
102 if (pads[port].type == pad_type) {
103 return;
104 }
105 // Device changed reset device and set new type
106 ResetDevice(port);
107 pads[port].type = pad_type;
108}
109
110void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
111 if (port >= pads.size()) {
112 return;
113 }
63 114
64 static constexpr std::array<PadButton, 8> b1_buttons{ 115 static constexpr std::array<PadButton, 8> b1_buttons{
65 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, 116 PadButton::ButtonA, PadButton::ButtonB, PadButton::ButtonX, PadButton::ButtonY,
66 PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, 117 PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp,
67 PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
68 }; 118 };
69 119
70 static constexpr std::array<PadButton, 4> b2_buttons{ 120 static constexpr std::array<PadButton, 4> b2_buttons{
71 PadButton::PAD_BUTTON_START, 121 PadButton::ButtonStart,
72 PadButton::PAD_TRIGGER_Z, 122 PadButton::TriggerZ,
73 PadButton::PAD_TRIGGER_R, 123 PadButton::TriggerR,
74 PadButton::PAD_TRIGGER_L, 124 PadButton::TriggerL,
75 }; 125 };
126 pads[port].buttons = 0;
127 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
128 if ((b1 & (1U << i)) != 0) {
129 pads[port].buttons =
130 static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i]));
131 pads[port].last_button = b1_buttons[i];
132 }
133 }
76 134
135 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
136 if ((b2 & (1U << j)) != 0) {
137 pads[port].buttons =
138 static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j]));
139 pads[port].last_button = b2_buttons[j];
140 }
141 }
142}
143
144void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
145 if (port >= pads.size()) {
146 return;
147 }
148
149 const std::size_t offset = 1 + (9 * port);
77 static constexpr std::array<PadAxes, 6> axes{ 150 static constexpr std::array<PadAxes, 6> axes{
78 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX, 151 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
79 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight, 152 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
80 }; 153 };
81 154
82 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) { 155 for (const PadAxes axis : axes) {
83 // Controller may have been disconnected, recalibrate if reconnected. 156 const auto index = static_cast<std::size_t>(axis);
84 get_origin[port] = true; 157 const u8 axis_value = adapter_payload[offset + 3 + index];
158 if (pads[port].axis_origin[index] == 255) {
159 pads[port].axis_origin[index] = axis_value;
160 }
161 pads[port].axis_values[index] =
162 static_cast<s16>(axis_value - pads[port].axis_origin[index]);
85 } 163 }
164}
86 165
87 if (adapter_controllers_status[port] != ControllerTypes::None) { 166void Adapter::UpdateYuzuSettings(std::size_t port) {
88 const u8 b1 = adapter_payload[offset + 1]; 167 if (port >= pads.size()) {
89 const u8 b2 = adapter_payload[offset + 2]; 168 return;
169 }
90 170
91 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 171 constexpr u8 axis_threshold = 50;
92 if ((b1 & (1U << i)) != 0) { 172 GCPadStatus pad_status = {.port = port};
93 pad.button = static_cast<u16>(pad.button | static_cast<u16>(b1_buttons[i]));
94 }
95 }
96 173
97 for (std::size_t j = 0; j < b2_buttons.size(); ++j) { 174 if (pads[port].buttons != 0) {
98 if ((b2 & (1U << j)) != 0) { 175 pad_status.button = pads[port].last_button;
99 pad.button = static_cast<u16>(pad.button | static_cast<u16>(b2_buttons[j])); 176 pad_queue.Push(pad_status);
100 } 177 }
101 } 178
102 for (PadAxes axis : axes) { 179 // Accounting for a threshold here to ensure an intentional press
103 const auto index = static_cast<std::size_t>(axis); 180 for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
104 pad.axis_values[index] = adapter_payload[offset + 3 + index]; 181 const s16 value = pads[port].axis_values[i];
105 }
106 182
107 if (get_origin[port]) { 183 if (value > axis_threshold || value < -axis_threshold) {
108 origin_status[port].axis_values = pad.axis_values; 184 pad_status.axis = static_cast<PadAxes>(i);
109 get_origin[port] = false; 185 pad_status.axis_value = value;
186 pad_status.axis_threshold = axis_threshold;
187 pad_queue.Push(pad_status);
110 } 188 }
111 } 189 }
112 return pad;
113} 190}
114 191
115void Adapter::Read() { 192void Adapter::UpdateVibrations() {
116 LOG_DEBUG(Input, "GC Adapter Read() thread started"); 193 // Use 8 states to keep the switching between on/off fast enough for
194 // a human to not notice the difference between switching from on/off
195 // More states = more rumble strengths = slower update time
196 constexpr u8 vibration_states = 8;
117 197
118 int payload_size; 198 vibration_counter = (vibration_counter + 1) % vibration_states;
119 std::array<u8, 37> adapter_payload;
120 std::array<GCPadStatus, 4> pads;
121
122 while (adapter_thread_running) {
123 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
124 sizeof(adapter_payload), &payload_size, 16);
125
126 if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
127 LOG_ERROR(Input,
128 "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
129 payload_size, adapter_payload[0]);
130 adapter_thread_running = false; // error reading from adapter, stop reading.
131 break;
132 }
133 for (std::size_t port = 0; port < pads.size(); ++port) {
134 pads[port] = GetPadStatus(port, adapter_payload);
135 if (DeviceConnected(port) && configuring) {
136 if (pads[port].button != 0) {
137 pad_queue[port].Push(pads[port]);
138 }
139 199
140 // Accounting for a threshold here to ensure an intentional press 200 for (GCController& pad : pads) {
141 for (size_t i = 0; i < pads[port].axis_values.size(); ++i) { 201 const bool vibrate = pad.rumble_amplitude > vibration_counter;
142 const u8 value = pads[port].axis_values[i]; 202 vibration_changed |= vibrate != pad.enable_vibration;
143 const u8 origin = origin_status[port].axis_values[i]; 203 pad.enable_vibration = vibrate;
144
145 if (value > origin + pads[port].THRESHOLD ||
146 value < origin - pads[port].THRESHOLD) {
147 pads[port].axis = static_cast<PadAxes>(i);
148 pads[port].axis_value = pads[port].axis_values[i];
149 pad_queue[port].Push(pads[port]);
150 }
151 }
152 }
153 PadToState(pads[port], state[port]);
154 }
155 std::this_thread::yield();
156 } 204 }
205 SendVibrations();
157} 206}
158 207
159void Adapter::Setup() { 208void Adapter::SendVibrations() {
160 // Initialize all controllers as unplugged 209 if (!rumble_enabled || !vibration_changed) {
161 adapter_controllers_status.fill(ControllerTypes::None);
162 // Initialize all ports to store axis origin values
163 get_origin.fill(true);
164
165 // pointer to list of connected usb devices
166 libusb_device** devices{};
167
168 // populate the list of devices, get the count
169 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
170 if (device_count < 0) {
171 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
172 return; 210 return;
173 } 211 }
174 212 s32 size{};
175 if (devices != nullptr) { 213 constexpr u8 rumble_command = 0x11;
176 for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) { 214 const u8 p1 = pads[0].enable_vibration;
177 if (CheckDeviceAccess(devices[index])) { 215 const u8 p2 = pads[1].enable_vibration;
178 // GC Adapter found and accessible, registering it 216 const u8 p3 = pads[2].enable_vibration;
179 GetGCEndpoint(devices[index]); 217 const u8 p4 = pads[3].enable_vibration;
180 break; 218 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
181 } 219 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
220 static_cast<s32>(payload.size()), &size, 16);
221 if (err) {
222 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
223 if (output_error_counter++ > 5) {
224 LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
225 rumble_enabled = false;
182 } 226 }
183 libusb_free_device_list(devices, 1); 227 return;
184 } 228 }
229 output_error_counter = 0;
230 vibration_changed = false;
185} 231}
186 232
187bool Adapter::CheckDeviceAccess(libusb_device* device) { 233bool Adapter::RumblePlay(std::size_t port, f32 amplitude) {
188 libusb_device_descriptor desc; 234 amplitude = std::clamp(amplitude, 0.0f, 1.0f);
189 const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); 235 const auto raw_amp = static_cast<u8>(amplitude * 0x8);
190 if (get_descriptor_error) { 236 pads[port].rumble_amplitude = raw_amp;
191 // could not acquire the descriptor, no point in trying to use it. 237
192 LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", 238 return rumble_enabled;
193 get_descriptor_error); 239}
194 return false; 240
241void Adapter::AdapterScanThread() {
242 adapter_scan_thread_running = true;
243 adapter_input_thread_running = false;
244 if (adapter_input_thread.joinable()) {
245 adapter_input_thread.join();
246 }
247 ClearLibusbHandle();
248 ResetDevices();
249 while (adapter_scan_thread_running && !adapter_input_thread_running) {
250 Setup();
251 std::this_thread::sleep_for(std::chrono::seconds(1));
195 } 252 }
253}
196 254
197 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { 255void Adapter::Setup() {
198 // This isn't the device we are looking for. 256 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337);
199 return false; 257
258 if (usb_adapter_handle == NULL) {
259 return;
260 }
261 if (!CheckDeviceAccess()) {
262 ClearLibusbHandle();
263 return;
200 } 264 }
201 const int open_error = libusb_open(device, &usb_adapter_handle);
202 265
203 if (open_error == LIBUSB_ERROR_ACCESS) { 266 libusb_device* device = libusb_get_device(usb_adapter_handle);
204 LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", 267
205 desc.idVendor, desc.idProduct); 268 LOG_INFO(Input, "GC adapter is now connected");
206 return false; 269 // GC Adapter found and accessible, registering it
270 if (GetGCEndpoint(device)) {
271 adapter_scan_thread_running = false;
272 adapter_input_thread_running = true;
273 rumble_enabled = true;
274 input_error_counter = 0;
275 output_error_counter = 0;
276 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
207 } 277 }
208 if (open_error) { 278}
209 LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); 279
210 return false; 280bool Adapter::CheckDeviceAccess() {
281 // This fixes payload problems from offbrand GCAdapters
282 const s32 control_transfer_error =
283 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
284 if (control_transfer_error < 0) {
285 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
211 } 286 }
212 287
213 int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 288 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
214 if (kernel_driver_error == 1) { 289 if (kernel_driver_error == 1) {
215 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 290 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
216 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 291 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
@@ -236,13 +311,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
236 return true; 311 return true;
237} 312}
238 313
239void Adapter::GetGCEndpoint(libusb_device* device) { 314bool Adapter::GetGCEndpoint(libusb_device* device) {
240 libusb_config_descriptor* config = nullptr; 315 libusb_config_descriptor* config = nullptr;
241 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); 316 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
242 if (config_descriptor_return != LIBUSB_SUCCESS) { 317 if (config_descriptor_return != LIBUSB_SUCCESS) {
243 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}", 318 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
244 config_descriptor_return); 319 config_descriptor_return);
245 return; 320 return false;
246 } 321 }
247 322
248 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { 323 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
@@ -264,31 +339,51 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
264 unsigned char clear_payload = 0x13; 339 unsigned char clear_payload = 0x13;
265 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 340 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
266 sizeof(clear_payload), nullptr, 16); 341 sizeof(clear_payload), nullptr, 16);
267 342 return true;
268 adapter_thread_running = true;
269 adapter_input_thread = std::thread(&Adapter::Read, this);
270} 343}
271 344
272Adapter::~Adapter() { 345void Adapter::JoinThreads() {
273 Reset(); 346 restart_scan_thread = false;
274} 347 adapter_input_thread_running = false;
348 adapter_scan_thread_running = false;
275 349
276void Adapter::Reset() { 350 if (adapter_scan_thread.joinable()) {
277 if (adapter_thread_running) { 351 adapter_scan_thread.join();
278 adapter_thread_running = false;
279 } 352 }
353
280 if (adapter_input_thread.joinable()) { 354 if (adapter_input_thread.joinable()) {
281 adapter_input_thread.join(); 355 adapter_input_thread.join();
282 } 356 }
357}
283 358
284 adapter_controllers_status.fill(ControllerTypes::None); 359void Adapter::ClearLibusbHandle() {
285 get_origin.fill(true);
286
287 if (usb_adapter_handle) { 360 if (usb_adapter_handle) {
288 libusb_release_interface(usb_adapter_handle, 1); 361 libusb_release_interface(usb_adapter_handle, 1);
289 libusb_close(usb_adapter_handle); 362 libusb_close(usb_adapter_handle);
290 usb_adapter_handle = nullptr; 363 usb_adapter_handle = nullptr;
291 } 364 }
365}
366
367void Adapter::ResetDevices() {
368 for (std::size_t i = 0; i < pads.size(); ++i) {
369 ResetDevice(i);
370 }
371}
372
373void Adapter::ResetDevice(std::size_t port) {
374 pads[port].type = ControllerTypes::None;
375 pads[port].enable_vibration = false;
376 pads[port].rumble_amplitude = 0;
377 pads[port].buttons = 0;
378 pads[port].last_button = PadButton::Undefined;
379 pads[port].axis_values.fill(0);
380 pads[port].axis_origin.fill(255);
381}
382
383void Adapter::Reset() {
384 JoinThreads();
385 ClearLibusbHandle();
386 ResetDevices();
292 387
293 if (libusb_ctx) { 388 if (libusb_ctx) {
294 libusb_exit(libusb_ctx); 389 libusb_exit(libusb_ctx);
@@ -297,11 +392,11 @@ void Adapter::Reset() {
297 392
298std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 393std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
299 std::vector<Common::ParamPackage> devices; 394 std::vector<Common::ParamPackage> devices;
300 for (std::size_t port = 0; port < state.size(); ++port) { 395 for (std::size_t port = 0; port < pads.size(); ++port) {
301 if (!DeviceConnected(port)) { 396 if (!DeviceConnected(port)) {
302 continue; 397 continue;
303 } 398 }
304 std::string name = fmt::format("Gamecube Controller {}", port); 399 std::string name = fmt::format("Gamecube Controller {}", port + 1);
305 devices.emplace_back(Common::ParamPackage{ 400 devices.emplace_back(Common::ParamPackage{
306 {"class", "gcpad"}, 401 {"class", "gcpad"},
307 {"display", std::move(name)}, 402 {"display", std::move(name)},
@@ -318,18 +413,18 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
318 // This list also excludes any button that can't be really mapped 413 // This list also excludes any button that can't be really mapped
319 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> 414 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
320 switch_to_gcadapter_button = { 415 switch_to_gcadapter_button = {
321 std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A}, 416 std::pair{Settings::NativeButton::A, PadButton::ButtonA},
322 {Settings::NativeButton::B, PadButton::PAD_BUTTON_B}, 417 {Settings::NativeButton::B, PadButton::ButtonB},
323 {Settings::NativeButton::X, PadButton::PAD_BUTTON_X}, 418 {Settings::NativeButton::X, PadButton::ButtonX},
324 {Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y}, 419 {Settings::NativeButton::Y, PadButton::ButtonY},
325 {Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START}, 420 {Settings::NativeButton::Plus, PadButton::ButtonStart},
326 {Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT}, 421 {Settings::NativeButton::DLeft, PadButton::ButtonLeft},
327 {Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP}, 422 {Settings::NativeButton::DUp, PadButton::ButtonUp},
328 {Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT}, 423 {Settings::NativeButton::DRight, PadButton::ButtonRight},
329 {Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN}, 424 {Settings::NativeButton::DDown, PadButton::ButtonDown},
330 {Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L}, 425 {Settings::NativeButton::SL, PadButton::TriggerL},
331 {Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R}, 426 {Settings::NativeButton::SR, PadButton::TriggerR},
332 {Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z}, 427 {Settings::NativeButton::R, PadButton::TriggerZ},
333 }; 428 };
334 if (!params.Has("port")) { 429 if (!params.Has("port")) {
335 return {}; 430 return {};
@@ -352,8 +447,10 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
352 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { 447 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
353 Common::ParamPackage button_params({{"engine", "gcpad"}}); 448 Common::ParamPackage button_params({{"engine", "gcpad"}});
354 button_params.Set("port", params.Get("port", 0)); 449 button_params.Set("port", params.Get("port", 0));
355 button_params.Set("button", static_cast<int>(PadButton::PAD_STICK)); 450 button_params.Set("button", static_cast<s32>(PadButton::Stick));
356 button_params.Set("axis", static_cast<int>(gcadapter_axis)); 451 button_params.Set("axis", static_cast<s32>(gcadapter_axis));
452 button_params.Set("threshold", 0.5f);
453 button_params.Set("direction", "+");
357 mapping.insert_or_assign(switch_button, std::move(button_params)); 454 mapping.insert_or_assign(switch_button, std::move(button_params));
358 } 455 }
359 return mapping; 456 return mapping;
@@ -382,46 +479,33 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
382} 479}
383 480
384bool Adapter::DeviceConnected(std::size_t port) const { 481bool Adapter::DeviceConnected(std::size_t port) const {
385 return adapter_controllers_status[port] != ControllerTypes::None; 482 return pads[port].type != ControllerTypes::None;
386}
387
388void Adapter::ResetDeviceType(std::size_t port) {
389 adapter_controllers_status[port] = ControllerTypes::None;
390} 483}
391 484
392void Adapter::BeginConfiguration() { 485void Adapter::BeginConfiguration() {
393 get_origin.fill(true); 486 pad_queue.Clear();
394 for (auto& pq : pad_queue) {
395 pq.Clear();
396 }
397 configuring = true; 487 configuring = true;
398} 488}
399 489
400void Adapter::EndConfiguration() { 490void Adapter::EndConfiguration() {
401 for (auto& pq : pad_queue) { 491 pad_queue.Clear();
402 pq.Clear();
403 }
404 configuring = false; 492 configuring = false;
405} 493}
406 494
407std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { 495Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
408 return pad_queue; 496 return pad_queue;
409} 497}
410 498
411const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { 499const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
412 return pad_queue; 500 return pad_queue;
413} 501}
414 502
415std::array<GCState, 4>& Adapter::GetPadState() { 503GCController& Adapter::GetPadState(std::size_t port) {
416 return state; 504 return pads.at(port);
417}
418
419const std::array<GCState, 4>& Adapter::GetPadState() const {
420 return state;
421} 505}
422 506
423int Adapter::GetOriginValue(u32 port, u32 axis) const { 507const GCController& Adapter::GetPadState(std::size_t port) const {
424 return origin_status[port].axis_values[axis]; 508 return pads.at(port);
425} 509}
426 510
427} // namespace GCAdapter 511} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index 4f5f3de8e..d28dcfad3 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -19,24 +19,23 @@ struct libusb_device_handle;
19namespace GCAdapter { 19namespace GCAdapter {
20 20
21enum class PadButton { 21enum class PadButton {
22 PAD_BUTTON_LEFT = 0x0001, 22 Undefined = 0x0000,
23 PAD_BUTTON_RIGHT = 0x0002, 23 ButtonLeft = 0x0001,
24 PAD_BUTTON_DOWN = 0x0004, 24 ButtonRight = 0x0002,
25 PAD_BUTTON_UP = 0x0008, 25 ButtonDown = 0x0004,
26 PAD_TRIGGER_Z = 0x0010, 26 ButtonUp = 0x0008,
27 PAD_TRIGGER_R = 0x0020, 27 TriggerZ = 0x0010,
28 PAD_TRIGGER_L = 0x0040, 28 TriggerR = 0x0020,
29 PAD_BUTTON_A = 0x0100, 29 TriggerL = 0x0040,
30 PAD_BUTTON_B = 0x0200, 30 ButtonA = 0x0100,
31 PAD_BUTTON_X = 0x0400, 31 ButtonB = 0x0200,
32 PAD_BUTTON_Y = 0x0800, 32 ButtonX = 0x0400,
33 PAD_BUTTON_START = 0x1000, 33 ButtonY = 0x0800,
34 ButtonStart = 0x1000,
34 // Below is for compatibility with "AxisButton" type 35 // Below is for compatibility with "AxisButton" type
35 PAD_STICK = 0x2000, 36 Stick = 0x2000,
36}; 37};
37 38
38extern const std::array<PadButton, 12> PadButtonArray;
39
40enum class PadAxes : u8 { 39enum class PadAxes : u8 {
41 StickX, 40 StickX,
42 StickY, 41 StickY,
@@ -47,87 +46,122 @@ enum class PadAxes : u8 {
47 Undefined, 46 Undefined,
48}; 47};
49 48
49enum class ControllerTypes {
50 None,
51 Wired,
52 Wireless,
53};
54
50struct GCPadStatus { 55struct GCPadStatus {
51 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits 56 std::size_t port{};
52 57
53 std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes 58 PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
54 static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
55 59
56 u8 port{};
57 PadAxes axis{PadAxes::Undefined}; 60 PadAxes axis{PadAxes::Undefined};
58 u8 axis_value{255}; 61 s16 axis_value{};
62 u8 axis_threshold{50};
59}; 63};
60 64
61struct GCState { 65struct GCController {
62 std::unordered_map<int, bool> buttons; 66 ControllerTypes type{};
63 std::unordered_map<u32, u16> axes; 67 bool enable_vibration{};
68 u8 rumble_amplitude{};
69 u16 buttons{};
70 PadButton last_button{};
71 std::array<s16, 6> axis_values{};
72 std::array<u8, 6> axis_origin{};
64}; 73};
65 74
66enum class ControllerTypes { None, Wired, Wireless };
67
68class Adapter { 75class Adapter {
69public: 76public:
70 /// Initialize the GC Adapter capture and read sequence
71 Adapter(); 77 Adapter();
72
73 /// Close the adapter read thread and release the adapter
74 ~Adapter(); 78 ~Adapter();
79
80 /// Request a vibration for a controlelr
81 bool RumblePlay(std::size_t port, f32 amplitude);
82
75 /// Used for polling 83 /// Used for polling
76 void BeginConfiguration(); 84 void BeginConfiguration();
77 void EndConfiguration(); 85 void EndConfiguration();
78 86
87 Common::SPSCQueue<GCPadStatus>& GetPadQueue();
88 const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
89
90 GCController& GetPadState(std::size_t port);
91 const GCController& GetPadState(std::size_t port) const;
92
93 /// Returns true if there is a device connected to port
94 bool DeviceConnected(std::size_t port) const;
95
96 /// Used for automapping features
79 std::vector<Common::ParamPackage> GetInputDevices() const; 97 std::vector<Common::ParamPackage> GetInputDevices() const;
80 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; 98 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
81 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; 99 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
82 100
83 /// Returns true if there is a device connected to port 101private:
84 bool DeviceConnected(std::size_t port) const; 102 using AdapterPayload = std::array<u8, 37>;
85 103
86 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); 104 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
87 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; 105 void UpdateControllers(const AdapterPayload& adapter_payload);
106 void UpdateYuzuSettings(std::size_t port);
107 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
108 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
109 void UpdateVibrations();
88 110
89 std::array<GCState, 4>& GetPadState(); 111 void AdapterInputThread();
90 const std::array<GCState, 4>& GetPadState() const;
91 112
92 int GetOriginValue(u32 port, u32 axis) const; 113 void AdapterScanThread();
93 114
94private: 115 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
95 GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload); 116
117 // Updates vibration state of all controllers
118 void SendVibrations();
119
120 /// For use in initialization, querying devices to find the adapter
121 void Setup();
96 122
97 void Read(); 123 /// Resets status of all GC controller devices to a disconected state
124 void ResetDevices();
98 125
99 /// Resets status of device connected to port 126 /// Resets status of device connected to a disconected state
100 void ResetDeviceType(std::size_t port); 127 void ResetDevice(std::size_t port);
101 128
102 /// Returns true if we successfully gain access to GC Adapter 129 /// Returns true if we successfully gain access to GC Adapter
103 bool CheckDeviceAccess(libusb_device* device); 130 bool CheckDeviceAccess();
104 131
105 /// Captures GC Adapter endpoint address, 132 /// Captures GC Adapter endpoint address
106 void GetGCEndpoint(libusb_device* device); 133 /// Returns true if the endpoind was set correctly
134 bool GetGCEndpoint(libusb_device* device);
107 135
108 /// For shutting down, clear all data, join all threads, release usb 136 /// For shutting down, clear all data, join all threads, release usb
109 void Reset(); 137 void Reset();
110 138
111 /// For use in initialization, querying devices to find the adapter 139 // Join all threads
112 void Setup(); 140 void JoinThreads();
141
142 // Release usb handles
143 void ClearLibusbHandle();
113 144
114 libusb_device_handle* usb_adapter_handle = nullptr; 145 libusb_device_handle* usb_adapter_handle = nullptr;
146 std::array<GCController, 4> pads;
147 Common::SPSCQueue<GCPadStatus> pad_queue;
115 148
116 std::thread adapter_input_thread; 149 std::thread adapter_input_thread;
117 bool adapter_thread_running; 150 std::thread adapter_scan_thread;
151 bool adapter_input_thread_running;
152 bool adapter_scan_thread_running;
153 bool restart_scan_thread;
118 154
119 libusb_context* libusb_ctx; 155 libusb_context* libusb_ctx;
120 156
121 u8 input_endpoint = 0; 157 u8 input_endpoint{0};
122 u8 output_endpoint = 0; 158 u8 output_endpoint{0};
123 159 u8 input_error_counter{0};
124 bool configuring = false; 160 u8 output_error_counter{0};
161 int vibration_counter{0};
125 162
126 std::array<GCState, 4> state; 163 bool configuring{false};
127 std::array<bool, 4> get_origin; 164 bool rumble_enabled{true};
128 std::array<GCPadStatus, 4> origin_status; 165 bool vibration_changed{true};
129 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
130 std::array<ControllerTypes, 4> adapter_controllers_status{};
131}; 166};
132
133} // namespace GCAdapter 167} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 893556916..6bd6f57fc 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,22 +15,30 @@ namespace InputCommon {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(u32 port_, int button_, const GCAdapter::Adapter* adapter) 18 explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
22 22
23 bool GetStatus() const override { 23 bool GetStatus() const override {
24 if (gcadapter->DeviceConnected(port)) { 24 if (gcadapter->DeviceConnected(port)) {
25 return gcadapter->GetPadState()[port].buttons.at(button); 25 return (gcadapter->GetPadState(port).buttons & button) != 0;
26 } 26 }
27 return false; 27 return false;
28 } 28 }
29 29
30 bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
31 const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
32 const auto new_amp =
33 static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
34
35 return gcadapter->RumblePlay(port, new_amp);
36 }
37
30private: 38private:
31 const u32 port; 39 const u32 port;
32 const int button; 40 const s32 button;
33 const GCAdapter::Adapter* gcadapter; 41 GCAdapter::Adapter* gcadapter;
34}; 42};
35 43
36class GCAxisButton final : public Input::ButtonDevice { 44class GCAxisButton final : public Input::ButtonDevice {
@@ -38,13 +46,12 @@ public:
38 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_, 46 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
39 const GCAdapter::Adapter* adapter) 47 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), 48 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter), 49 gcadapter(adapter) {}
42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
43 50
44 bool GetStatus() const override { 51 bool GetStatus() const override {
45 if (gcadapter->DeviceConnected(port)) { 52 if (gcadapter->DeviceConnected(port)) {
46 const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis); 53 const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
47 const float axis_value = (current_axis_value - origin_value) / 128.0f; 54 const float axis_value = current_axis_value / 128.0f;
48 if (trigger_if_greater) { 55 if (trigger_if_greater) {
49 // TODO: Might be worthwile to set a slider for the trigger threshold. It is 56 // TODO: Might be worthwile to set a slider for the trigger threshold. It is
50 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick 57 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
@@ -61,7 +68,6 @@ private:
61 float threshold; 68 float threshold;
62 bool trigger_if_greater; 69 bool trigger_if_greater;
63 const GCAdapter::Adapter* gcadapter; 70 const GCAdapter::Adapter* gcadapter;
64 const float origin_value;
65}; 71};
66 72
67GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) 73GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
@@ -73,7 +79,7 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
73 const auto button_id = params.Get("button", 0); 79 const auto button_id = params.Get("button", 0);
74 const auto port = static_cast<u32>(params.Get("port", 0)); 80 const auto port = static_cast<u32>(params.Get("port", 0));
75 81
76 constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); 82 constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
77 83
78 // button is not an axis/stick button 84 // button is not an axis/stick button
79 if (button_id != PAD_STICK_ID) { 85 if (button_id != PAD_STICK_ID) {
@@ -106,32 +112,25 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
106 Common::ParamPackage params; 112 Common::ParamPackage params;
107 GCAdapter::GCPadStatus pad; 113 GCAdapter::GCPadStatus pad;
108 auto& queue = adapter->GetPadQueue(); 114 auto& queue = adapter->GetPadQueue();
109 for (std::size_t port = 0; port < queue.size(); ++port) { 115 while (queue.Pop(pad)) {
110 while (queue[port].Pop(pad)) { 116 // This while loop will break on the earliest detected button
111 // This while loop will break on the earliest detected button 117 params.Set("engine", "gcpad");
112 params.Set("engine", "gcpad"); 118 params.Set("port", static_cast<s32>(pad.port));
113 params.Set("port", static_cast<int>(port)); 119 if (pad.button != GCAdapter::PadButton::Undefined) {
114 for (const auto& button : GCAdapter::PadButtonArray) { 120 params.Set("button", static_cast<u16>(pad.button));
115 const u16 button_value = static_cast<u16>(button); 121 }
116 if (pad.button & button_value) {
117 params.Set("button", button_value);
118 break;
119 }
120 }
121 122
122 // For Axis button implementation 123 // For Axis button implementation
123 if (pad.axis != GCAdapter::PadAxes::Undefined) { 124 if (pad.axis != GCAdapter::PadAxes::Undefined) {
124 params.Set("axis", static_cast<u8>(pad.axis)); 125 params.Set("axis", static_cast<u8>(pad.axis));
125 params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); 126 params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
126 if (pad.axis_value > 128) { 127 params.Set("threshold", "0.25");
127 params.Set("direction", "+"); 128 if (pad.axis_value > 0) {
128 params.Set("threshold", "0.25"); 129 params.Set("direction", "+");
129 } else { 130 } else {
130 params.Set("direction", "-"); 131 params.Set("direction", "-");
131 params.Set("threshold", "-0.25");
132 }
133 break;
134 } 132 }
133 break;
135 } 134 }
136 } 135 }
137 return params; 136 return params;
@@ -152,17 +151,14 @@ public:
152 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, 151 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_,
153 const GCAdapter::Adapter* adapter, float range_) 152 const GCAdapter::Adapter* adapter, float range_)
154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 153 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))),
156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))),
157 range(range_) {} 154 range(range_) {}
158 155
159 float GetAxis(u32 axis) const { 156 float GetAxis(u32 axis) const {
160 if (gcadapter->DeviceConnected(port)) { 157 if (gcadapter->DeviceConnected(port)) {
161 std::lock_guard lock{mutex}; 158 std::lock_guard lock{mutex};
162 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
163 const auto axis_value = 159 const auto axis_value =
164 static_cast<float>(gcadapter->GetPadState()[port].axes.at(axis)); 160 static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
165 return (axis_value - origin_value) / (100.0f * range); 161 return (axis_value) / (100.0f * range);
166 } 162 }
167 return 0.0f; 163 return 0.0f;
168 } 164 }
@@ -215,8 +211,6 @@ private:
215 const u32 axis_y; 211 const u32 axis_y;
216 const float deadzone; 212 const float deadzone;
217 const GCAdapter::Adapter* gcadapter; 213 const GCAdapter::Adapter* gcadapter;
218 const float origin_value_x;
219 const float origin_value_y;
220 const float range; 214 const float range;
221 mutable std::mutex mutex; 215 mutable std::mutex mutex;
222}; 216};
@@ -254,26 +248,44 @@ void GCAnalogFactory::EndConfiguration() {
254 248
255Common::ParamPackage GCAnalogFactory::GetNextInput() { 249Common::ParamPackage GCAnalogFactory::GetNextInput() {
256 GCAdapter::GCPadStatus pad; 250 GCAdapter::GCPadStatus pad;
251 Common::ParamPackage params;
257 auto& queue = adapter->GetPadQueue(); 252 auto& queue = adapter->GetPadQueue();
258 for (std::size_t port = 0; port < queue.size(); ++port) { 253 while (queue.Pop(pad)) {
259 while (queue[port].Pop(pad)) { 254 if (pad.button != GCAdapter::PadButton::Undefined) {
260 if (pad.axis == GCAdapter::PadAxes::Undefined || 255 params.Set("engine", "gcpad");
261 std::abs((static_cast<float>(pad.axis_value) - 128.0f) / 128.0f) < 0.1f) { 256 params.Set("port", static_cast<s32>(pad.port));
262 continue; 257 params.Set("button", static_cast<u16>(pad.button));
263 } 258 return params;
264 // An analog device needs two axes, so we need to store the axis for later and wait for 259 }
265 // a second input event. The axes also must be from the same joystick. 260 if (pad.axis == GCAdapter::PadAxes::Undefined ||
266 const u8 axis = static_cast<u8>(pad.axis); 261 std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
267 if (analog_x_axis == -1) { 262 continue;
268 analog_x_axis = axis; 263 }
269 controller_number = static_cast<int>(port); 264 // An analog device needs two axes, so we need to store the axis for later and wait for
270 } else if (analog_y_axis == -1 && analog_x_axis != axis && 265 // a second input event. The axes also must be from the same joystick.
271 controller_number == static_cast<int>(port)) { 266 const u8 axis = static_cast<u8>(pad.axis);
272 analog_y_axis = axis; 267 if (axis == 0 || axis == 1) {
273 } 268 analog_x_axis = 0;
269 analog_y_axis = 1;
270 controller_number = static_cast<s32>(pad.port);
271 break;
272 }
273 if (axis == 2 || axis == 3) {
274 analog_x_axis = 2;
275 analog_y_axis = 3;
276 controller_number = static_cast<s32>(pad.port);
277 break;
278 }
279
280 if (analog_x_axis == -1) {
281 analog_x_axis = axis;
282 controller_number = static_cast<s32>(pad.port);
283 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
284 controller_number == static_cast<s32>(pad.port)) {
285 analog_y_axis = axis;
286 break;
274 } 287 }
275 } 288 }
276 Common::ParamPackage params;
277 if (analog_x_axis != -1 && analog_y_axis != -1) { 289 if (analog_x_axis != -1 && analog_y_axis != -1) {
278 params.Set("engine", "gcpad"); 290 params.Set("engine", "gcpad");
279 params.Set("port", controller_number); 291 params.Set("port", controller_number);
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4fd92428f..4757dd2b4 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -6,18 +6,40 @@
6#include <cstdlib> 6#include <cstdlib>
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <mutex>
10#include <stdexcept>
9#include <thread> 11#include <thread>
10#include <unordered_map> 12#include <unordered_map>
11#include <vector> 13#include <vector>
12 14
13#include <catch2/catch.hpp> 15#include <catch2/catch.hpp>
14#include <math.h> 16
15#include "common/common_types.h" 17#include "common/common_types.h"
16#include "common/fiber.h" 18#include "common/fiber.h"
17#include "common/spin_lock.h"
18 19
19namespace Common { 20namespace Common {
20 21
22class ThreadIds {
23public:
24 void Register(u32 id) {
25 const auto thread_id = std::this_thread::get_id();
26 std::scoped_lock lock{mutex};
27 if (ids.contains(thread_id)) {
28 throw std::logic_error{"Registering the same thread twice"};
29 }
30 ids.emplace(thread_id, id);
31 }
32
33 [[nodiscard]] u32 Get() const {
34 std::scoped_lock lock{mutex};
35 return ids.at(std::this_thread::get_id());
36 }
37
38private:
39 mutable std::mutex mutex;
40 std::unordered_map<std::thread::id, u32> ids;
41};
42
21class TestControl1 { 43class TestControl1 {
22public: 44public:
23 TestControl1() = default; 45 TestControl1() = default;
@@ -26,7 +48,7 @@ public:
26 48
27 void ExecuteThread(u32 id); 49 void ExecuteThread(u32 id);
28 50
29 std::unordered_map<std::thread::id, u32> ids; 51 ThreadIds thread_ids;
30 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; 52 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
31 std::vector<std::shared_ptr<Common::Fiber>> work_fibers; 53 std::vector<std::shared_ptr<Common::Fiber>> work_fibers;
32 std::vector<u32> items; 54 std::vector<u32> items;
@@ -39,8 +61,7 @@ static void WorkControl1(void* control) {
39} 61}
40 62
41void TestControl1::DoWork() { 63void TestControl1::DoWork() {
42 std::thread::id this_id = std::this_thread::get_id(); 64 const u32 id = thread_ids.Get();
43 u32 id = ids[this_id];
44 u32 value = items[id]; 65 u32 value = items[id];
45 for (u32 i = 0; i < id; i++) { 66 for (u32 i = 0; i < id; i++) {
46 value++; 67 value++;
@@ -50,8 +71,7 @@ void TestControl1::DoWork() {
50} 71}
51 72
52void TestControl1::ExecuteThread(u32 id) { 73void TestControl1::ExecuteThread(u32 id) {
53 std::thread::id this_id = std::this_thread::get_id(); 74 thread_ids.Register(id);
54 ids[this_id] = id;
55 auto thread_fiber = Fiber::ThreadToFiber(); 75 auto thread_fiber = Fiber::ThreadToFiber();
56 thread_fibers[id] = thread_fiber; 76 thread_fibers[id] = thread_fiber;
57 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); 77 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
@@ -98,8 +118,7 @@ public:
98 value1 += i; 118 value1 += i;
99 } 119 }
100 Fiber::YieldTo(fiber1, fiber3); 120 Fiber::YieldTo(fiber1, fiber3);
101 std::thread::id this_id = std::this_thread::get_id(); 121 const u32 id = thread_ids.Get();
102 u32 id = ids[this_id];
103 assert1 = id == 1; 122 assert1 = id == 1;
104 value2 += 5000; 123 value2 += 5000;
105 Fiber::YieldTo(fiber1, thread_fibers[id]); 124 Fiber::YieldTo(fiber1, thread_fibers[id]);
@@ -115,8 +134,7 @@ public:
115 } 134 }
116 135
117 void DoWork3() { 136 void DoWork3() {
118 std::thread::id this_id = std::this_thread::get_id(); 137 const u32 id = thread_ids.Get();
119 u32 id = ids[this_id];
120 assert2 = id == 0; 138 assert2 = id == 0;
121 value1 += 1000; 139 value1 += 1000;
122 Fiber::YieldTo(fiber3, thread_fibers[id]); 140 Fiber::YieldTo(fiber3, thread_fibers[id]);
@@ -125,14 +143,12 @@ public:
125 void ExecuteThread(u32 id); 143 void ExecuteThread(u32 id);
126 144
127 void CallFiber1() { 145 void CallFiber1() {
128 std::thread::id this_id = std::this_thread::get_id(); 146 const u32 id = thread_ids.Get();
129 u32 id = ids[this_id];
130 Fiber::YieldTo(thread_fibers[id], fiber1); 147 Fiber::YieldTo(thread_fibers[id], fiber1);
131 } 148 }
132 149
133 void CallFiber2() { 150 void CallFiber2() {
134 std::thread::id this_id = std::this_thread::get_id(); 151 const u32 id = thread_ids.Get();
135 u32 id = ids[this_id];
136 Fiber::YieldTo(thread_fibers[id], fiber2); 152 Fiber::YieldTo(thread_fibers[id], fiber2);
137 } 153 }
138 154
@@ -145,7 +161,7 @@ public:
145 u32 value2{}; 161 u32 value2{};
146 std::atomic<bool> trap{true}; 162 std::atomic<bool> trap{true};
147 std::atomic<bool> trap2{true}; 163 std::atomic<bool> trap2{true};
148 std::unordered_map<std::thread::id, u32> ids; 164 ThreadIds thread_ids;
149 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; 165 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
150 std::shared_ptr<Common::Fiber> fiber1; 166 std::shared_ptr<Common::Fiber> fiber1;
151 std::shared_ptr<Common::Fiber> fiber2; 167 std::shared_ptr<Common::Fiber> fiber2;
@@ -168,15 +184,13 @@ static void WorkControl2_3(void* control) {
168} 184}
169 185
170void TestControl2::ExecuteThread(u32 id) { 186void TestControl2::ExecuteThread(u32 id) {
171 std::thread::id this_id = std::this_thread::get_id(); 187 thread_ids.Register(id);
172 ids[this_id] = id;
173 auto thread_fiber = Fiber::ThreadToFiber(); 188 auto thread_fiber = Fiber::ThreadToFiber();
174 thread_fibers[id] = thread_fiber; 189 thread_fibers[id] = thread_fiber;
175} 190}
176 191
177void TestControl2::Exit() { 192void TestControl2::Exit() {
178 std::thread::id this_id = std::this_thread::get_id(); 193 const u32 id = thread_ids.Get();
179 u32 id = ids[this_id];
180 thread_fibers[id]->Exit(); 194 thread_fibers[id]->Exit();
181} 195}
182 196
@@ -228,24 +242,21 @@ public:
228 void DoWork1() { 242 void DoWork1() {
229 value1 += 1; 243 value1 += 1;
230 Fiber::YieldTo(fiber1, fiber2); 244 Fiber::YieldTo(fiber1, fiber2);
231 std::thread::id this_id = std::this_thread::get_id(); 245 const u32 id = thread_ids.Get();
232 u32 id = ids[this_id];
233 value3 += 1; 246 value3 += 1;
234 Fiber::YieldTo(fiber1, thread_fibers[id]); 247 Fiber::YieldTo(fiber1, thread_fibers[id]);
235 } 248 }
236 249
237 void DoWork2() { 250 void DoWork2() {
238 value2 += 1; 251 value2 += 1;
239 std::thread::id this_id = std::this_thread::get_id(); 252 const u32 id = thread_ids.Get();
240 u32 id = ids[this_id];
241 Fiber::YieldTo(fiber2, thread_fibers[id]); 253 Fiber::YieldTo(fiber2, thread_fibers[id]);
242 } 254 }
243 255
244 void ExecuteThread(u32 id); 256 void ExecuteThread(u32 id);
245 257
246 void CallFiber1() { 258 void CallFiber1() {
247 std::thread::id this_id = std::this_thread::get_id(); 259 const u32 id = thread_ids.Get();
248 u32 id = ids[this_id];
249 Fiber::YieldTo(thread_fibers[id], fiber1); 260 Fiber::YieldTo(thread_fibers[id], fiber1);
250 } 261 }
251 262
@@ -254,7 +265,7 @@ public:
254 u32 value1{}; 265 u32 value1{};
255 u32 value2{}; 266 u32 value2{};
256 u32 value3{}; 267 u32 value3{};
257 std::unordered_map<std::thread::id, u32> ids; 268 ThreadIds thread_ids;
258 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; 269 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
259 std::shared_ptr<Common::Fiber> fiber1; 270 std::shared_ptr<Common::Fiber> fiber1;
260 std::shared_ptr<Common::Fiber> fiber2; 271 std::shared_ptr<Common::Fiber> fiber2;
@@ -271,15 +282,13 @@ static void WorkControl3_2(void* control) {
271} 282}
272 283
273void TestControl3::ExecuteThread(u32 id) { 284void TestControl3::ExecuteThread(u32 id) {
274 std::thread::id this_id = std::this_thread::get_id(); 285 thread_ids.Register(id);
275 ids[this_id] = id;
276 auto thread_fiber = Fiber::ThreadToFiber(); 286 auto thread_fiber = Fiber::ThreadToFiber();
277 thread_fibers[id] = thread_fiber; 287 thread_fibers[id] = thread_fiber;
278} 288}
279 289
280void TestControl3::Exit() { 290void TestControl3::Exit() {
281 std::thread::id this_id = std::this_thread::get_id(); 291 const u32 id = thread_ids.Get();
282 u32 id = ids[this_id];
283 thread_fibers[id]->Exit(); 292 thread_fibers[id]->Exit();
284} 293}
285 294
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index fdfc885fc..abcee2a1c 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -302,7 +302,10 @@ else()
302 target_compile_options(video_core PRIVATE 302 target_compile_options(video_core PRIVATE
303 -Werror=conversion 303 -Werror=conversion
304 -Wno-error=sign-conversion 304 -Wno-error=sign-conversion
305 -Werror=pessimizing-move
306 -Werror=redundant-move
305 -Werror=switch 307 -Werror=switch
308 -Werror=type-limits
306 -Werror=unused-variable 309 -Werror=unused-variable
307 310
308 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 311 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
index d774db107..b60f86260 100644
--- a/src/video_core/cdma_pusher.cpp
+++ b/src/video_core/cdma_pusher.cpp
@@ -144,7 +144,7 @@ void CDmaPusher::ExecuteCommand(u32 offset, u32 data) {
144 } 144 }
145 case ThiMethod::SetMethod1: 145 case ThiMethod::SetMethod1:
146 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})", 146 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
147 static_cast<u32>(vic_thi_state.method_0)); 147 static_cast<u32>(vic_thi_state.method_0), data);
148 vic_processor->ProcessMethod(static_cast<Tegra::Vic::Method>(vic_thi_state.method_0), 148 vic_processor->ProcessMethod(static_cast<Tegra::Vic::Method>(vic_thi_state.method_0),
149 {data}); 149 {data});
150 break; 150 break;
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index bcbea9c05..b3e98aa9f 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -917,7 +917,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
917 if (((low_value << (offset - 1)) >> 31) != 0) { 917 if (((low_value << (offset - 1)) >> 31) != 0) {
918 const s32 current_pos = static_cast<s32>(base_stream.GetPosition()); 918 const s32 current_pos = static_cast<s32>(base_stream.GetPosition());
919 base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos); 919 base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
920 while (base_stream.GetPosition() >= 0 && PeekByte() == 0xff) { 920 while (PeekByte() == 0xff) {
921 base_stream.WriteByte(0); 921 base_stream.WriteByte(0);
922 922
923 base_stream.Seek(-2, Common::SeekOrigin::FromCurrentPos); 923 base_stream.Seek(-2, Common::SeekOrigin::FromCurrentPos);
diff --git a/src/video_core/command_classes/sync_manager.cpp b/src/video_core/command_classes/sync_manager.cpp
index a0ab44855..19dc9e0ab 100644
--- a/src/video_core/command_classes/sync_manager.cpp
+++ b/src/video_core/command_classes/sync_manager.cpp
@@ -27,22 +27,22 @@ SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {}
27SyncptIncrManager::~SyncptIncrManager() = default; 27SyncptIncrManager::~SyncptIncrManager() = default;
28 28
29void SyncptIncrManager::Increment(u32 id) { 29void SyncptIncrManager::Increment(u32 id) {
30 increments.push_back(SyncptIncr{0, id, true}); 30 increments.emplace_back(0, 0, id, true);
31 IncrementAllDone(); 31 IncrementAllDone();
32} 32}
33 33
34u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) { 34u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) {
35 const u32 handle = current_id++; 35 const u32 handle = current_id++;
36 increments.push_back(SyncptIncr{handle, class_id, id}); 36 increments.emplace_back(handle, class_id, id);
37 return handle; 37 return handle;
38} 38}
39 39
40void SyncptIncrManager::SignalDone(u32 handle) { 40void SyncptIncrManager::SignalDone(u32 handle) {
41 auto done_incr = std::find_if(increments.begin(), increments.end(), 41 const auto done_incr =
42 [handle](SyncptIncr incr) { return incr.id == handle; }); 42 std::find_if(increments.begin(), increments.end(),
43 if (done_incr != increments.end()) { 43 [handle](const SyncptIncr& incr) { return incr.id == handle; });
44 const SyncptIncr incr = *done_incr; 44 if (done_incr != increments.cend()) {
45 *done_incr = SyncptIncr{incr.id, incr.class_id, incr.syncpt_id, true}; 45 done_incr->complete = true;
46 } 46 }
47 IncrementAllDone(); 47 IncrementAllDone();
48} 48}
diff --git a/src/video_core/command_classes/sync_manager.h b/src/video_core/command_classes/sync_manager.h
index 353b67573..2c321ec58 100644
--- a/src/video_core/command_classes/sync_manager.h
+++ b/src/video_core/command_classes/sync_manager.h
@@ -32,8 +32,8 @@ struct SyncptIncr {
32 u32 syncpt_id; 32 u32 syncpt_id;
33 bool complete; 33 bool complete;
34 34
35 SyncptIncr(u32 id, u32 syncpt_id_, u32 class_id_, bool done = false) 35 SyncptIncr(u32 id_, u32 class_id_, u32 syncpt_id_, bool done = false)
36 : id(id), class_id(class_id_), syncpt_id(syncpt_id_), complete(done) {} 36 : id(id_), class_id(class_id_), syncpt_id(syncpt_id_), complete(done) {}
37}; 37};
38 38
39class SyncptIncrManager { 39class SyncptIncrManager {
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 166ee34e1..70dd0c3c6 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -317,8 +317,7 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
317 return std::nullopt; 317 return std::nullopt;
318 } 318 }
319 } 319 }
320 320 return entries;
321 return std::move(entries);
322} 321}
323 322
324void ShaderDiskCacheOpenGL::InvalidateTransferable() { 323void ShaderDiskCacheOpenGL::InvalidateTransferable() {
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index c034558a3..4e83303d8 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -844,7 +844,7 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
844 VK_SUCCESS) { 844 VK_SUCCESS) {
845 return std::nullopt; 845 return std::nullopt;
846 } 846 }
847 return std::move(properties); 847 return properties;
848} 848}
849 849
850std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( 850std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 7e484b906..ae85a72ea 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -9,4 +9,4 @@ add_library(web_service STATIC
9) 9)
10 10
11create_target_directory_groups(web_service) 11create_target_directory_groups(web_service)
12target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib lurlparser) 12target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib)
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 534960d09..67183e64c 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -7,7 +7,6 @@
7#include <mutex> 7#include <mutex>
8#include <string> 8#include <string>
9 9
10#include <LUrlParser.h>
11#include <fmt/format.h> 10#include <fmt/format.h>
12#include <httplib.h> 11#include <httplib.h>
13 12
@@ -19,9 +18,6 @@ namespace WebService {
19 18
20constexpr std::array<const char, 1> API_VERSION{'1'}; 19constexpr std::array<const char, 1> API_VERSION{'1'};
21 20
22constexpr int HTTP_PORT = 80;
23constexpr int HTTPS_PORT = 443;
24
25constexpr std::size_t TIMEOUT_SECONDS = 30; 21constexpr std::size_t TIMEOUT_SECONDS = 30;
26 22
27struct Client::Impl { 23struct Client::Impl {
@@ -67,22 +63,14 @@ struct Client::Impl {
67 const std::string& jwt = "", const std::string& username = "", 63 const std::string& jwt = "", const std::string& username = "",
68 const std::string& token = "") { 64 const std::string& token = "") {
69 if (cli == nullptr) { 65 if (cli == nullptr) {
70 const auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 66 cli = std::make_unique<httplib::Client>(host.c_str());
71 int port{}; 67 }
72 if (parsedUrl.m_Scheme == "http") { 68
73 if (!parsedUrl.GetPort(&port)) { 69 if (!cli->is_valid()) {
74 port = HTTP_PORT; 70 LOG_ERROR(WebService, "Client is invalid, skipping request!");
75 } 71 return {};
76 } else if (parsedUrl.m_Scheme == "https") {
77 if (!parsedUrl.GetPort(&port)) {
78 port = HTTPS_PORT;
79 }
80 } else {
81 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
82 return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
83 }
84 cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port);
85 } 72 }
73
86 cli->set_connection_timeout(TIMEOUT_SECONDS); 74 cli->set_connection_timeout(TIMEOUT_SECONDS);
87 cli->set_read_timeout(TIMEOUT_SECONDS); 75 cli->set_read_timeout(TIMEOUT_SECONDS);
88 cli->set_write_timeout(TIMEOUT_SECONDS); 76 cli->set_write_timeout(TIMEOUT_SECONDS);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index abbc83929..1ce62e4a6 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -515,7 +515,7 @@ void Config::ReadMotionTouchValues() {
515void Config::ReadCoreValues() { 515void Config::ReadCoreValues() {
516 qt_config->beginGroup(QStringLiteral("Core")); 516 qt_config->beginGroup(QStringLiteral("Core"));
517 517
518 ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false); 518 ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), true);
519 519
520 qt_config->endGroup(); 520 qt_config->endGroup();
521} 521}
@@ -716,12 +716,12 @@ void Config::ReadRendererValues() {
716 QStringLiteral("use_disk_shader_cache"), true); 716 QStringLiteral("use_disk_shader_cache"), true);
717 ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0); 717 ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
718 ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation, 718 ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
719 QStringLiteral("use_asynchronous_gpu_emulation"), false); 719 QStringLiteral("use_asynchronous_gpu_emulation"), true);
720 ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"), 720 ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"),
721 true); 721 true);
722 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); 722 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
723 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), 723 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
724 false); 724 true);
725 ReadSettingGlobal(Settings::values.use_asynchronous_shaders, 725 ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
726 QStringLiteral("use_asynchronous_shaders"), false); 726 QStringLiteral("use_asynchronous_shaders"), false);
727 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), 727 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
@@ -1110,7 +1110,7 @@ void Config::SaveControlValues() {
1110void Config::SaveCoreValues() { 1110void Config::SaveCoreValues() {
1111 qt_config->beginGroup(QStringLiteral("Core")); 1111 qt_config->beginGroup(QStringLiteral("Core"));
1112 1112
1113 WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); 1113 WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, true);
1114 1114
1115 qt_config->endGroup(); 1115 qt_config->endGroup();
1116} 1116}
@@ -1266,12 +1266,12 @@ void Config::SaveRendererValues() {
1266 static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)), 1266 static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
1267 Settings::values.gpu_accuracy.UsingGlobal(), 0); 1267 Settings::values.gpu_accuracy.UsingGlobal(), 0);
1268 WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"), 1268 WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
1269 Settings::values.use_asynchronous_gpu_emulation, false); 1269 Settings::values.use_asynchronous_gpu_emulation, true);
1270 WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation, 1270 WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation,
1271 true); 1271 true);
1272 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); 1272 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1273 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), 1273 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
1274 Settings::values.use_assembly_shaders, false); 1274 Settings::values.use_assembly_shaders, true);
1275 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"), 1275 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
1276 Settings::values.use_asynchronous_shaders, false); 1276 Settings::values.use_asynchronous_shaders, false);
1277 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, 1277 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 23448e747..334038ef9 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -371,7 +371,7 @@ void Config::ReadValues() {
371 371
372 // Core 372 // Core
373 Settings::values.use_multi_core.SetValue( 373 Settings::values.use_multi_core.SetValue(
374 sdl2_config->GetBoolean("Core", "use_multi_core", false)); 374 sdl2_config->GetBoolean("Core", "use_multi_core", true));
375 375
376 // Renderer 376 // Renderer
377 const int renderer_backend = sdl2_config->GetInteger( 377 const int renderer_backend = sdl2_config->GetInteger(
@@ -395,11 +395,11 @@ void Config::ReadValues() {
395 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); 395 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
396 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level)); 396 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
397 Settings::values.use_asynchronous_gpu_emulation.SetValue( 397 Settings::values.use_asynchronous_gpu_emulation.SetValue(
398 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false)); 398 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true));
399 Settings::values.use_vsync.SetValue( 399 Settings::values.use_vsync.SetValue(
400 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1))); 400 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
401 Settings::values.use_assembly_shaders.SetValue( 401 Settings::values.use_assembly_shaders.SetValue(
402 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false)); 402 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true));
403 Settings::values.use_asynchronous_shaders.SetValue( 403 Settings::values.use_asynchronous_shaders.SetValue(
404 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false)); 404 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
405 Settings::values.use_asynchronous_shaders.SetValue( 405 Settings::values.use_asynchronous_shaders.SetValue(
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index aa9e40380..796e27df4 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -94,7 +94,7 @@ udp_pad_index=
94 94
95[Core] 95[Core]
96# Whether to use multi-core for CPU emulation 96# Whether to use multi-core for CPU emulation
97# 0 (default): Disabled, 1: Enabled 97# 0: Disabled, 1 (default): Enabled
98use_multi_core= 98use_multi_core=
99 99
100[Cpu] 100[Cpu]
@@ -163,7 +163,7 @@ max_anisotropy =
163use_vsync = 163use_vsync =
164 164
165# Whether to use OpenGL assembly shaders or not. NV_gpu_program5 is required. 165# Whether to use OpenGL assembly shaders or not. NV_gpu_program5 is required.
166# 0 (default): Off, 1: On 166# 0: Off, 1 (default): On
167use_assembly_shaders = 167use_assembly_shaders =
168 168
169# Whether to allow asynchronous shader building. 169# Whether to allow asynchronous shader building.