even with 87dc83e

This commit is contained in:
R2DLiu 2020-07-25 17:39:01 -04:00
commit be07a173c3
5 changed files with 347 additions and 9 deletions

View file

@ -46,7 +46,7 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence, show the current gam
option(ENABLE_ANALYTICS "Enables opt-in Analytics collection" OFF) option(ENABLE_ANALYTICS "Enables opt-in Analytics collection" OFF)
# Enable Playback build for Slippi. TODO: turn off for normal builds # Enable Playback build for Slippi. TODO: turn off for normal builds
option(SLIPPI_PLAYBACK "Enable Playback changes" ON) option(SLIPPI_PLAYBACK "Enable Playback changes" OFF)
option(ENCODE_FRAMEDUMPS "Encode framedumps in AVI format" ON) option(ENCODE_FRAMEDUMPS "Encode framedumps in AVI format" ON)

View file

@ -0,0 +1,257 @@
#!/bin/sh
# shellcheck shell=dash
# This is just a little script that can be downloaded from the internet to
# install SmashEnabler.
#
# It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local`
# extension. Note: Most shells limit `local` to 1 var per line, contra bash.
#
# Borrow heavily and liberally from rustup's install script. ;P
set -u
usage() {
cat 1>&2 <<EOF
smashenabler-installer 0.0.1 (2020-07-20)
The installer for SmashEnabler.kext
USAGE:
[FLAGS] [OPTIONS]
EOF
}
main() {
downloader --check
local _url="https://forums.dolphin-emu.org/attachment.php?aid=16637"
say "Downloading kext from ${_url}."
local _file="SmashEnabler.kext.zip"
ensure downloader "$_url" "$_file"
say "Unzipping..."
unzip "$_file"
say "Clearing out old versions if they exist. This may require your password."
sudo rm -rf /Library/Extensions/SmashEnabler.kext
say "Installing kext. This may require your password."
sudo mv SmashEnabler.kext /Library/Extensions/
sudo chown -R root:wheel /Library/Extensions/SmashEnabler.kext
sudo touch /Library/Extensions
say "Cleaning up..."
rm "${_file}"
say "Kext installed. You should boot into recovery mode and re-enable SIP to be safe."
local _retval=$?
return "$_retval"
}
say() {
printf 'smashenabler-installer: %s\n' "$1"
}
err() {
say "$1" >&2
exit 1
}
need_cmd() {
if ! check_cmd "$1"; then
err "need '$1' (command not found)"
fi
}
check_cmd() {
command -v "$1" > /dev/null 2>&1
}
assert_nz() {
if [ -z "$1" ]; then err "assert_nz $2"; fi
}
# Run a command that should never fail. If the command fails execution
# will immediately terminate with an error showing the failing
# command.
ensure() {
if ! "$@"; then err "command failed: $*"; fi
}
# This is just for indicating that commands' results are being
# intentionally ignored. Usually, because it's being executed
# as part of error handling.
ignore() {
"$@"
}
# This wraps curl or wget. Try curl first, if not installed,
# use wget instead.
downloader() {
local _dld
local _ciphersuites
if check_cmd curl; then
_dld=curl
elif check_cmd wget; then
_dld=wget
else
_dld='curl or wget' # to be used in error message of need_cmd
fi
if [ "$1" = --check ]; then
need_cmd "$_dld"
elif [ "$_dld" = curl ]; then
get_ciphersuites_for_curl
_ciphersuites="$RETVAL"
if [ -n "$_ciphersuites" ]; then
curl --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2"
else
echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
if ! check_help_for "$3" curl --proto --tlsv1.2; then
echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
curl --silent --show-error --fail --location "$1" --output "$2"
else
curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2"
fi
fi
elif [ "$_dld" = wget ]; then
get_ciphersuites_for_wget
_ciphersuites="$RETVAL"
if [ -n "$_ciphersuites" ]; then
wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2"
else
echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
if ! check_help_for "$3" wget --https-only --secure-protocol; then
echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
wget "$1" -O "$2"
else
wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2"
fi
fi
else
err "Unknown downloader" # should not reach here
fi
}
check_help_for() {
local _arch
local _cmd
local _arg
_arch="$1"
shift
_cmd="$1"
shift
case "$_arch" in
# If we're running on OS-X, older than 10.13, then we always
# fail to find these options to force fallback
*darwin*)
if check_cmd sw_vers; then
if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then
# Older than 10.13
echo "Warning: Detected OS X platform older than 10.13"
return 1
fi
fi
;;
esac
for _arg in "$@"; do
if ! "$_cmd" --help | grep -q -- "$_arg"; then
return 1
fi
done
true # not strictly needed
}
# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
# if support by local tools is detected. Detection currently supports these curl backends:
# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
get_ciphersuites_for_curl() {
if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
# user specified custom cipher suites, assume they know what they're doing
RETVAL="$RUSTUP_TLS_CIPHERSUITES"
return
fi
local _openssl_syntax="no"
local _gnutls_syntax="no"
local _backend_supported="yes"
if curl -V | grep -q ' OpenSSL/'; then
_openssl_syntax="yes"
elif curl -V | grep -iq ' LibreSSL/'; then
_openssl_syntax="yes"
elif curl -V | grep -iq ' BoringSSL/'; then
_openssl_syntax="yes"
elif curl -V | grep -iq ' GnuTLS/'; then
_gnutls_syntax="yes"
else
_backend_supported="no"
fi
local _args_supported="no"
if [ "$_backend_supported" = "yes" ]; then
# "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then
_args_supported="yes"
fi
fi
local _cs=""
if [ "$_args_supported" = "yes" ]; then
if [ "$_openssl_syntax" = "yes" ]; then
_cs=$(get_strong_ciphersuites_for "openssl")
elif [ "$_gnutls_syntax" = "yes" ]; then
_cs=$(get_strong_ciphersuites_for "gnutls")
fi
fi
RETVAL="$_cs"
}
# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
# if support by local tools is detected. Detection currently supports these wget backends:
# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
get_ciphersuites_for_wget() {
if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
# user specified custom cipher suites, assume they know what they're doing
RETVAL="$RUSTUP_TLS_CIPHERSUITES"
return
fi
local _cs=""
if wget -V | grep -q '\-DHAVE_LIBSSL'; then
# "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
_cs=$(get_strong_ciphersuites_for "openssl")
fi
elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then
# "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
_cs=$(get_strong_ciphersuites_for "gnutls")
fi
fi
RETVAL="$_cs"
}
# Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2
# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad
# DH params often found on servers (see RFC 7919). Sequence matches or is
# similar to Firefox 68 ESR with weak cipher suites disabled via about:config.
# $1 must be openssl or gnutls.
get_strong_ciphersuites_for() {
if [ "$1" = "openssl" ]; then
# OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet.
echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
elif [ "$1" = "gnutls" ]; then
# GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't.
# Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order.
echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM"
fi
}
main "$@" || exit 1

View file

@ -157,7 +157,7 @@ if(LIBEVDEV_FOUND AND LIBUDEV_FOUND)
) )
endif() endif()
if(UNIX) if(UNIX OR WIN32)
target_sources(inputcommon PRIVATE target_sources(inputcommon PRIVATE
ControllerInterface/Pipes/Pipes.cpp ControllerInterface/Pipes/Pipes.cpp
ControllerInterface/Pipes/Pipes.h ControllerInterface/Pipes/Pipes.h

View file

@ -12,7 +12,6 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#include <vector> #include <vector>
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
@ -41,6 +40,31 @@ static double StringToDouble(const std::string& text)
void PopulateDevices() void PopulateDevices()
{ {
#ifdef _WIN32
PIPE_FD pipes[4];
// Windows has named pipes, but they're different. They don't exist on the
// local filesystem and are transient. So rather than searching the /Pipes
// directory for pipes, we just always assume there's 4 and then make them
for (uint32_t i = 0; i < 4; i++)
{
std::string pipename = "\\\\.\\pipe\\slippibot" + std::to_string(i + 1);
pipes[i] = CreateNamedPipeA(
pipename.data(), // pipe name
PIPE_ACCESS_INBOUND, // read access, inward only
PIPE_TYPE_BYTE | PIPE_NOWAIT, // byte mode, nonblocking
1, // number of clients
256, // output buffer size
256, // input buffer size
0, // timeout value
NULL // security attributes
);
// We're in nonblocking mode, so this won't wait for clients
ConnectNamedPipe(pipes[i], NULL);
std::string ui_pipe_name = "slippibot" + std::to_string(i + 1);
g_controller_interface.AddDevice(std::make_shared<PipeDevice>(pipes[i], ui_pipe_name));
}
#else
// Search the Pipes directory for files that we can open in read-only, // Search the Pipes directory for files that we can open in read-only,
// non-blocking mode. The device name is the virtual name of the file. // non-blocking mode. The device name is the virtual name of the file.
File::FSTEntry fst; File::FSTEntry fst;
@ -55,14 +79,15 @@ void PopulateDevices()
const File::FSTEntry& child = fst.children[i]; const File::FSTEntry& child = fst.children[i];
if (child.isDirectory) if (child.isDirectory)
continue; continue;
int fd = open(child.physicalName.c_str(), O_RDONLY | O_NONBLOCK); PIPE_FD fd = open(child.physicalName.c_str(), O_RDONLY | O_NONBLOCK);
if (fd < 0) if (fd < 0)
continue; continue;
g_controller_interface.AddDevice(std::make_shared<PipeDevice>(fd, child.virtualName)); g_controller_interface.AddDevice(std::make_shared<PipeDevice>(fd, child.virtualName));
} }
#endif
} }
PipeDevice::PipeDevice(int fd, const std::string& name) : m_fd(fd), m_name(name) PipeDevice::PipeDevice(PIPE_FD fd, const std::string& name) : m_fd(fd), m_name(name)
{ {
for (const auto& tok : s_button_tokens) for (const auto& tok : s_button_tokens)
{ {
@ -83,7 +108,52 @@ PipeDevice::PipeDevice(int fd, const std::string& name) : m_fd(fd), m_name(name)
PipeDevice::~PipeDevice() PipeDevice::~PipeDevice()
{ {
#ifdef _WIN32
CloseHandle(m_fd);
#else
close(m_fd); close(m_fd);
#endif
}
s32 PipeDevice::readFromPipe(PIPE_FD file_descriptor, char* in_buffer, size_t size)
{
#ifdef _WIN32
u32 bytes_available = 0;
DWORD bytesread = 0;
bool peek_success = PeekNamedPipe(
file_descriptor,
NULL,
0,
NULL,
(LPDWORD)&bytes_available,
NULL
);
if (!peek_success && (GetLastError() == ERROR_BROKEN_PIPE))
{
DisconnectNamedPipe(file_descriptor);
ConnectNamedPipe(file_descriptor, NULL);
return -1;
}
if (peek_success && (bytes_available > 0))
{
bool success = ReadFile(
file_descriptor, // pipe handle
in_buffer, // buffer to receive reply
(DWORD)std::min(bytes_available, (u32)size), // size of buffer
&bytesread, // number of bytes read
NULL); // not overlapped
if (!success)
{
return -1;
}
}
return (s32)bytesread;
#else
return read(file_descriptor, in_buffer, size);
#endif
} }
void PipeDevice::UpdateInput() void PipeDevice::UpdateInput()
@ -91,11 +161,11 @@ void PipeDevice::UpdateInput()
// Read any pending characters off the pipe. If we hit a newline, // Read any pending characters off the pipe. If we hit a newline,
// then dequeue a command off the front of m_buf and parse it. // then dequeue a command off the front of m_buf and parse it.
char buf[32]; char buf[32];
ssize_t bytes_read = read(m_fd, buf, sizeof buf); s32 bytes_read = readFromPipe(m_fd, buf, sizeof buf);
while (bytes_read > 0) while (bytes_read > 0)
{ {
m_buf.append(buf, bytes_read); m_buf.append(buf, bytes_read);
bytes_read = read(m_fd, buf, sizeof buf); bytes_read = readFromPipe(m_fd, buf, sizeof buf);
} }
std::size_t newline = m_buf.find("\n"); std::size_t newline = m_buf.find("\n");
while (newline != std::string::npos) while (newline != std::string::npos)

View file

@ -7,9 +7,19 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
namespace ciface::Pipes namespace ciface::Pipes
{ {
#ifdef _WIN32
typedef HANDLE PIPE_FD;
#else
typedef int PIPE_FD;
#endif
// To create a piped controller input, create a named pipe in the // To create a piped controller input, create a named pipe in the
// Pipes directory and write commands out to it. Commands are separated // Pipes directory and write commands out to it. Commands are separated
// by a newline character, with spaces separating command tokens. // by a newline character, with spaces separating command tokens.
@ -25,7 +35,7 @@ void PopulateDevices();
class PipeDevice : public Core::Device class PipeDevice : public Core::Device
{ {
public: public:
PipeDevice(int fd, const std::string& name); PipeDevice(PIPE_FD fd, const std::string& name);
~PipeDevice(); ~PipeDevice();
void UpdateInput() override; void UpdateInput() override;
@ -49,8 +59,9 @@ private:
void AddAxis(const std::string& name, double value); void AddAxis(const std::string& name, double value);
void ParseCommand(const std::string& command); void ParseCommand(const std::string& command);
void SetAxis(const std::string& entry, double value); void SetAxis(const std::string& entry, double value);
s32 readFromPipe(PIPE_FD file_descriptor, char* in_buffer, size_t size);
const int m_fd; const PIPE_FD m_fd;
const std::string m_name; const std::string m_name;
std::string m_buf; std::string m_buf;
std::map<std::string, PipeInput*> m_buttons; std::map<std::string, PipeInput*> m_buttons;