Add Nintendo Switch Borealis GUI (#349)

This commit is contained in:
H0neyBadger 2020-10-25 10:51:49 +01:00 committed by GitHub
parent e83cb04049
commit f7c83e8416
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 3470 additions and 124 deletions

View file

@ -1,5 +1,9 @@
name: Switch
on: [push]
on:
push:
branches:
- master
pull_request:
jobs:
build:
@ -13,4 +17,8 @@ jobs:
git submodule update
- name: Build Chiaki
run: scripts/switch/run-docker-build-chiaki.sh
- name: Upload chiaki.nro
uses: actions/upload-artifact@v1
with:
name: chiaki.nro
path: ./build_switch/switch/chiaki.nro

3
.gitmodules vendored
View file

@ -13,3 +13,6 @@
[submodule "android/app/src/main/cpp/oboe"]
path = android/app/src/main/cpp/oboe
url = https://github.com/google/oboe
[submodule "third-party/borealis"]
path = third-party/borealis
url = https://github.com/natinusala/borealis.git

View file

@ -40,17 +40,28 @@ include(CPack)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_CURRENT_SOURCE_DIR}/setsu/cmake")
# configure nintendo switch toolchain
if(CHIAKI_ENABLE_SWITCH AND CHIAKI_ENABLE_SWITCH_LINUX)
# CHIAKI_ENABLE_SWITCH_LINUX is a special testing version
# the aim is to troubleshoot nitendo switch chiaki verison
# from a x86 linux os
add_definitions(-DCHIAKI_ENABLE_SWITCH_LINUX)
set(CMAKE_BUILD_TYPE Debug)
elseif(CHIAKI_ENABLE_SWITCH)
add_definitions(-D__SWITCH__)
if(CHIAKI_ENABLE_SWITCH)
# load switch.cmake toolchain form ./cmake folder
# include(switch)
# to compile borealis third party
set(CMAKE_CXX_STANDARD 17)
# TODO check if android ... or other versions are enabled
# force mbedtls as crypto lib
set(CHIAKI_LIB_ENABLE_MBEDTLS ON)
if(CHIAKI_SWITCH_ENABLE_LINUX)
# CHIAKI_SWITCH_ENABLE_LINUX is a special testing version
# the aim is to troubleshoot nitendo switch chiaki verison
# from a x86 linux os
add_definitions(-DCHIAKI_SWITCH_ENABLE_LINUX)
add_definitions(-DBOREALIS_RESOURCES="./switch/res/")
set(CMAKE_BUILD_TYPE Debug)
add_definitions(-DDEBUG_OPENGL)
else()
add_definitions(-D__SWITCH__)
# use symbolic links to load font ..
add_definitions(-DBOREALIS_RESOURCES="romfs:/")
endif()
endif()
if(CHIAKI_USE_SYSTEM_JERASURE)
@ -120,6 +131,5 @@ if(CHIAKI_ENABLE_ANDROID)
endif()
if(CHIAKI_ENABLE_SWITCH)
#TODO
#add_subdirectory(switch)
add_subdirectory(switch)
endif()

View file

@ -8,7 +8,7 @@
[![Travis Build Status](https://travis-ci.com/thestr4ng3r/chiaki.svg?branch=master)](https://travis-ci.com/thestr4ng3r/chiaki) [![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/dnj9cmx5mmaaawer?svg=true)](https://ci.appveyor.com/project/thestr4ng3r/chiaki) [![builds.sr.ht Status](https://builds.sr.ht/~thestr4ng3r/chiaki.svg)](https://builds.sr.ht/~thestr4ng3r/chiaki?)
Chiaki is a Free and Open Source Software Client for PlayStation 4 Remote Play
for Linux, FreeBSD, OpenBSD, Android, macOS, Windows and potentially even more platforms.
for Linux, FreeBSD, OpenBSD, Android, macOS, Windows, Nintendo Switch and potentially even more platforms.
![Screenshot](assets/screenshot.png)
@ -37,6 +37,7 @@ You can download them [here](https://github.com/thestr4ng3r/chiaki/releases).
* **Android**: Install from [Google Play](https://play.google.com/store/apps/details?id=com.metallic.chiaki), [F-Droid](https://f-droid.org/packages/com.metallic.chiaki/) or download the APK from GitHub.
* **macOS**: Drag the application from the `.dmg` into your Applications folder.
* **Windows**: Extract the `.zip` file and execute `chiaki.exe`.
* **Switch**: Follow README specific [instructions](./switch/README.md)
### Building from Source

View file

@ -1,116 +1,79 @@
# https://github.com/nxengine/nxengine-evo
# Find DEVKITPRO
if(NOT DEFINED ENV{DEVKITPRO})
message(FATAL_ERROR "You must have defined DEVKITPRO before calling cmake.")
if(NOT DEFINED ENV{DEVKITPRO} OR NOT DEFINED ENV{PORTLIBS_PREFIX})
message(FATAL_ERROR "Please set DEVKITPRO & PORTLIBS_PREFIX env before calling cmake. https://devkitpro.org/wiki/Getting_Started")
endif()
set(DEVKITPRO $ENV{DEVKITPRO})
set(DEVKITPRO "$ENV{DEVKITPRO}")
set(PORTLIBS_PREFIX "$ENV{PORTLIBS_PREFIX}")
function(switchvar cmakevar var default)
# read or set env var
if(NOT DEFINED "ENV{$var}")
set("ENV{$var}" default)
endif()
set("$cmakevar" "ENV{$var}")
endfunction()
# include devkitpro toolchain
include("${DEVKITPRO}/switch.cmake")
# allow gcc -g to use
# aarch64-none-elf-addr2line -e build_switch/switch/chiaki -f -p -C -a 0xCCB5C
set(CMAKE_BUILD_TYPE Debug)
# Enable gcc -g, to use
# /opt/devkitpro/devkitA64/bin/aarch64-none-elf-addr2line -e build_switch/switch/chiaki -f -p -C -a 0xCCB5C
# set(CMAKE_BUILD_TYPE Debug)
# set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available" )
set( TOOL_OS_SUFFIX "" )
if( CMAKE_HOST_WIN32 )
set( TOOL_OS_SUFFIX ".exe" )
endif()
# FIXME rework this file to use the toolchain only
# https://github.com/diasurgical/devilutionX/pull/764
set(ARCH "-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -ftls-model=local-exec")
# set(CMAKE_C_FLAGS "-O2 -ffunction-sections ${ARCH}")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
# workaroud force -fPIE to avoid
# aarch64-none-elf/bin/ld: read-only segment has dynamic relocations
set(CMAKE_EXE_LINKER_FLAGS "-specs=${DEVKITPRO}/libnx/switch.specs ${ARCH} -fPIE -Wl,-Map,Output.map")
set(CMAKE_SYSTEM_PROCESSOR "armv8-a")
set(CMAKE_C_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler")
set(CMAKE_CXX_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
set(CMAKE_ASM_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-as${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
set(CMAKE_STRIP "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip")
set(CMAKE_AR "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive")
set(CMAKE_LINKER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker")
set(CMAKE_NM "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm")
set(CMAKE_OBJCOPY "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy")
set(CMAKE_OBJDUMP "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump")
set(CMAKE_RANLIB "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib")
# add portlibs to the list of include dir
include_directories("${PORTLIBS_PREFIX}/include")
# custom /opt/devkitpro/switchvars.sh
switchvar(PORTLIBS_PREFIX PORTLIBS_PREFIX "${DEVKITPRO}/portlibs/switch")
switchvar(ARCH ARCH "-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec")
switchvar(CMAKE_C_FLAGS CFLAGS "${ARCH} -O2 -ffunction-sections -fdata-sections")
switchvar(CMAKE_CXX_FLAGS CXXFLAGS "${CMAKE_C_FLAGS}")
switchvar(CMAKE_CPP_FLAGS CPPFLAGS "-D__SWITCH__ -I${PORTLIBS_PREFIX}/include -isystem${DEVKITPRO}/libnx/include")
switchvar(CMAKE_LD_FLAGS LDFLAGS "${ARCH} -L${PORTLIBS_PREFIX}/lib -L${DEVKITPRO}/libnx/lib")
switchvar(LIBS LIBS "-lnx")
# cache flags
set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" )
set( CMAKE_C_FLAGS "" CACHE STRING "c flags" )
set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" )
set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" )
set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" )
set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" )
set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" )
set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" )
set( CMAKE_EXE_LINKER_FLAGS "-mtp=soft -fPIE -L${DEVKITPRO}/portlibs/switch/lib -L${DEVKITPRO}/libnx/lib -specs=${DEVKITPRO}/libnx/switch.specs -g" CACHE STRING "executable linker flags" )
# we require the relocation table
set(CMAKE_C_FLAGS "-I/opt/devkitpro/libnx/include -D__SWITCH__ -march=armv8-a -mtune=cortex-a57 -mtp=soft -ffunction-sections -fdata-sections -fPIE")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti")
# switchvar(CMAKE_CXX_FLAGS CXXFLAGS "${CMAKE_C_FLAGS} -fno-rtti")
include_directories(${DEVKITPRO}/libnx/include ${PORTLIBS_PREFIX}/include)
# where is the target environment
set(CMAKE_FIND_ROOT_PATH
${DEVKITPRO}/devkitA64
${DEVKITPRO}/libnx
${DEVKITPRO}/portlibs/switch)
# only search for libraries and includes in toolchain
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
# troubleshoot
message(STATUS "CMAKE_FIND_ROOT_PATH = ${CMAKE_FIND_ROOT_PATH}")
message(STATUS "PKG_CONFIG_EXECUTABLE = ${PKG_CONFIG_EXECUTABLE}")
message(STATUS "CMAKE_EXE_LINKER_FLAGS = ${CMAKE_EXE_LINKER_FLAGS}")
get_property(include_directories DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
message(STATUS "INCLUDE_DIRECTORIES = ${include_directories}")
message(STATUS "CMAKE_C_COMPILER = ${CMAKE_C_COMPILER}")
message(STATUS "CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}")
find_program(ELF2NRO elf2nro ${DEVKITPRO}/tools/bin)
if (ELF2NRO)
message(STATUS "elf2nro: ${ELF2NRO} - found")
else ()
message(WARNING "elf2nro - not found")
endif ()
else()
message(WARNING "elf2nro - not found")
endif()
find_program(NACPTOOL nacptool ${DEVKITPRO}/tools/bin)
if (NACPTOOL)
message(STATUS "nacptool: ${NACPTOOL} - found")
else ()
message(WARNING "nacptool - not found")
endif ()
message(STATUS "nacptool: ${NACPTOOL} - found")
else()
message(WARNING "nacptool - not found")
endif()
function(__add_nacp target APP_TITLE APP_AUTHOR APP_VERSION)
set(__NACP_COMMAND ${NACPTOOL} --create ${APP_TITLE} ${APP_AUTHOR} ${APP_VERSION} ${CMAKE_CURRENT_BINARY_DIR}/${target})
set(__NACP_COMMAND ${NACPTOOL} --create ${APP_TITLE} ${APP_AUTHOR} ${APP_VERSION} ${CMAKE_CURRENT_BINARY_DIR}/${target})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}
COMMAND ${__NACP_COMMAND}
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
VERBATIM
)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}
COMMAND ${__NACP_COMMAND}
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
VERBATIM
)
endfunction()
function(add_nro_target target title author version icon romfs)
get_filename_component(target_we ${target} NAME_WE)
if (NOT ${target_we}.nacp)
__add_nacp(${target_we}.nacp ${title} ${author} ${version})
endif ()
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nro
COMMAND ${ELF2NRO} $<TARGET_FILE:${target}> ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nro --icon=${icon} --nacp=${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nacp
# --romfsdir=${romfs}
DEPENDS ${target} ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nacp
VERBATIM
)
add_custom_target(${target_we}_nro ALL SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nro)
get_filename_component(target_we ${target} NAME_WE)
if(NOT ${target_we}.nacp)
__add_nacp(${target_we}.nacp ${title} ${author} ${version})
endif()
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nro
COMMAND ${ELF2NRO} $<TARGET_FILE:${target}>
${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nro
--icon=${icon}
--nacp=${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nacp
--romfsdir=${romfs}
DEPENDS ${target} ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nacp
VERBATIM
)
add_custom_target(${target_we}_nro ALL SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.nro)
endfunction()

View file

@ -101,8 +101,10 @@ target_link_libraries(chiaki-lib Threads::Threads)
if(CHIAKI_LIB_ENABLE_MBEDTLS)
# provided by mbedtls-static (mbedtls-devel)
# find_package(mbedcrypto REQUIRED)
target_link_libraries(chiaki-lib mbedtls mbedx509 mbedcrypto)
find_library(MBEDTLS mbedtls)
find_library(MBEDX509 mbedx509)
find_library(MBEDCRYPTO mbedcrypto)
target_link_libraries(chiaki-lib ${MBEDTLS} ${MBEDX509} ${MBEDCRYPTO})
elseif(CHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT)
target_link_libraries(chiaki-lib OpenSSL_Crypto)
else()
@ -111,11 +113,6 @@ else()
target_link_libraries(chiaki-lib OpenSSL::Crypto)
endif()
if(CHIAKI_ENABLE_SWITCH AND NOT CHIAKI_ENABLE_SWITCH_LINUX)
# to provides csrngGetRandomBytes
target_link_libraries(chiaki-lib nx)
endif()
target_link_libraries(chiaki-lib protobuf-nanopb-static)
target_link_libraries(chiaki-lib Jerasure::Jerasure)

View file

@ -3,15 +3,14 @@
set -xveo pipefail
arg1=$1
CHIAKI_ENABLE_SWITCH_LINUX="ON"
CHIAKI_SWITCH_ENABLE_LINUX="ON"
build="./build"
if [ "$arg1" != "linux" ]; then
CHIAKI_ENABLE_SWITCH_LINUX="OFF"
source /opt/devkitpro/switchvars.sh
toolchain=../cmake/switch.cmake # TODO: devkitpro ships a toolchain in /opt/devkitpro/switch.cmake, but it's broken.
export CC=${TOOL_PREFIX}gcc
export CXX=${TOOL_PREFIX}g++
CHIAKI_SWITCH_ENABLE_LINUX="OFF"
# source /opt/devkitpro/switchvars.sh
# toolchain="${DEVKITPRO}/switch.cmake"
toolchain="cmake/switch.cmake"
export PORTLIBS_PREFIX="$(${DEVKITPRO}/portlibs_prefix.sh switch)"
build="./build_switch"
fi
@ -22,14 +21,17 @@ build_chiaki (){
pushd "${BASEDIR}"
#rm -rf ./build
cmake -B "${build}" -DCMAKE_TOOLCHAIN_FILE=${toolchain} \
cmake -B "${build}" \
-DCMAKE_TOOLCHAIN_FILE=${toolchain} \
-DCHIAKI_ENABLE_TESTS=OFF \
-DCHIAKI_ENABLE_CLI=OFF \
-DCHIAKI_ENABLE_GUI=OFF \
-DCHIAKI_ENABLE_ANDROID=OFF \
-DCHIAKI_ENABLE_SWITCH=ON \
-DCHIAKI_ENABLE_SWITCH_LINUX="${CHIAKI_ENABLE_SWITCH_LINUX}" \
-DCHIAKI_LIB_ENABLE_MBEDTLS=ON
-DCHIAKI_SWITCH_ENABLE_LINUX="${CHIAKI_SWITCH_ENABLE_LINUX}" \
-DCHIAKI_LIB_ENABLE_MBEDTLS=ON \
# -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
# -DCMAKE_FIND_DEBUG_MODE=ON
pushd "${BASEDIR}/${build}"
make -j8

View file

@ -0,0 +1,10 @@
#!/bin/bash
cd "`dirname $(readlink -f ${0})`/../.."
docker run \
-v "`pwd`:/build/chiaki" \
-p 28771:28771 -ti \
chiaki-switch \
-c "/opt/devkitpro/tools/bin/nxlink -a $@ -s /build/chiaki/build_switch/switch/chiaki.nro"

View file

@ -4,7 +4,8 @@ cd "`dirname $(readlink -f ${0})`/../.."
docker run \
-v "`pwd`:/build/chiaki" \
-w "/build/chiaki" \
-t \
thestr4ng3r/chiaki-build-switch \
-c "cd /build/chiaki && scripts/switch/build.sh"
-c "scripts/switch/build.sh"

46
switch/CMakeLists.txt Normal file
View file

@ -0,0 +1,46 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(FFMPEG REQUIRED COMPONENTS avcodec avutil swscale)
find_library(SDL2 SDL2)
find_library(SWRESAMPLE swresample)
# find -type f | grep -P '\.(h|cpp)$' | sed 's#\./#\t\t#g'
add_executable(chiaki WIN32
src/discoverymanager.cpp
src/settings.cpp
src/io.cpp
src/host.cpp
src/main.cpp
src/gui.cpp)
target_include_directories(chiaki PRIVATE include)
target_link_libraries(chiaki
chiaki-lib
borealis
${SDL2}
FFMPEG::avcodec
FFMPEG::avutil
FFMPEG::swscale
${SWRESAMPLE}
${SWSCALE})
# OS links
if(CHIAKI_SWITCH_ENABLE_LINUX)
target_link_libraries(chiaki dl)
else()
# libnx is forced by the switch toolchain
find_library(Z z)
find_library(GLAPI glapi)
find_library(DRM_NOUVEAU drm_nouveau)
target_link_libraries(chiaki ${Z} ${GLAPI} ${DRM_NOUVEAU})
endif()
install(TARGETS chiaki
RUNTIME DESTINATION bin
BUNDLE DESTINATION bin)
if(CHIAKI_ENABLE_SWITCH AND NOT CHIAKI_SWITCH_ENABLE_LINUX)
add_nro_target(chiaki "chiaki" "Chiaki team" "${CHIAKI_VERSION}" "${CMAKE_SOURCE_DIR}/switch/res/icon.jpg" "${CMAKE_SOURCE_DIR}/switch/res")
endif()

64
switch/README.md Normal file
View file

@ -0,0 +1,64 @@
Nintendo Switch build instructions
==================================
this project requires the devkitpro toolchain.
you can use your personal computer to install devkitpro
but the easiest way is to use the following container.
Build container image
---------------------
```
bash scripts/switch/build-docker-image.sh
```
Run container
-------------
from the project's [root folder](../)
```
docker run -it --rm \
-v "$(pwd):/build" \
-p 28771:28771 \
chiaki-switch
```
Build Project
-------------
```
bash scripts/switch/run-docker-build-chiaki.sh
```
tools
-----
Push to homebrew Netloader
```
# where X.X.X.X is the IP of your switch
scripts/switch/push-docker-build-chiaki.sh 192.168.0.200
```
Troubleshoot
```
# replace 0xCCB5C with the backtrace adress (PC - Backtrace Start Address)
aarch64-none-elf-addr2line \
-e ./build_switch/switch/chiaki \
-f -p -C -a 0xCCB5C
```
Chiaki config file
------------------
The **chiaki.conf** is generated by the application.
this file contains sensitive data. (do not share this file)
```ini
# required: PS4-XXX (PS4 local name)
# name from PS4 Settings > System > system information
[PS4-XXX]
# required: lan PS4 IP address
# IP from PS4 Settings > System > system information
host_ip = X.X.X.X
# required: sony oline id (login)
psn_online_id = ps4_online_id
# required (PS4>7.0 Only): https://github.com/thestr4ng3r/chiaki#obtaining-your-psn-accountid
psn_account_id = ps4_base64_account_id
# optional(default 60): remote play fps (must be 30 or 60)
video_fps = 60
# optional(default 720p): remote play resolution (must be 720p, 540p or 360p)
video_resolution = 720p
```

View file

@ -0,0 +1,61 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_DISCOVERYMANAGER_H
#define CHIAKI_DISCOVERYMANAGER_H
#include <map>
#include <string>
#include <chiaki/log.h>
#include <chiaki/discoveryservice.h>
#include "host.h"
#include "settings.h"
static void Discovery(ChiakiDiscoveryHost*, void*);
class DiscoveryManager
{
private:
Settings * settings;
ChiakiLog * log;
ChiakiDiscoveryService service;
ChiakiDiscovery discovery;
struct sockaddr * host_addr;
size_t host_addr_len;
uint32_t GetIPv4BroadcastAddr();
bool service_enable;
public:
typedef enum hoststate
{
UNKNOWN,
READY,
STANDBY,
SHUTDOWN,
} HostState;
DiscoveryManager(Settings *settings);
~DiscoveryManager();
void SetService(bool);
int Send();
int Send(struct sockaddr *host_addr, size_t host_addr_len);
int Send(const char *);
void DiscoveryCB(ChiakiDiscoveryHost*);
};
#endif //CHIAKI_DISCOVERYMANAGER_H

View file

@ -0,0 +1,35 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_EXCEPTION_H
#define CHIAKI_EXCEPTION_H
#include <exception>
#include <string>
class Exception : public std::exception
{
private:
const char *msg;
public:
explicit Exception(const char *msg) : msg(msg) {}
const char *what() const noexcept override { return msg; }
};
#endif // CHIAKI_EXCEPTION_H

102
switch/include/gui.h Normal file
View file

@ -0,0 +1,102 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_GUI_H
#define CHIAKI_GUI_H
#include <glad.h>
#include <GLFW/glfw3.h>
#include "nanovg.h"
#include "nanovg_gl.h"
#include "nanovg_gl_utils.h"
#include <borealis.hpp>
#include <thread>
#include <map>
#include "host.h"
#include "settings.h"
#include "discoverymanager.h"
#include "io.h"
class HostInterface
{
private:
brls::TabFrame * root;
IO * io;
Host * host;
Settings * settings;
brls::List * hostList;
bool connected = false;
public:
HostInterface(brls::List * hostList, IO * io, Host * host, Settings * settings);
~HostInterface();
void Register(bool pin_incorrect);
void Wakeup(brls::View * view);
void Connect(brls::View * view);
void ConnectSession();
void Disconnect();
bool Stream();
bool CloseStream(ChiakiQuitEvent * quit);
};
class MainApplication
{
private:
ChiakiLog * log;
std::map<std::string, Host> * hosts;
Settings * settings;
DiscoveryManager * discoverymanager;
IO * io;
brls::TabFrame * rootFrame;
std::map<Host *, brls::List *> host_menuitems;
bool BuildConfigurationMenu(brls::List *, Host * host = nullptr);
public:
MainApplication(std::map<std::string, Host> * hosts,
Settings * settings, DiscoveryManager * discoverymanager,
IO * io, ChiakiLog * log);
~MainApplication();
bool Load();
};
class PS4RemotePlay : public brls::View
{
private:
brls::AppletFrame * frame;
// to display stream on screen
IO * io;
// to send gamepad inputs
Host * host;
brls::Label * label;
ChiakiControllerState state = { 0 };
// FPS calculation
// double base_time;
// int frame_counter = 0;
// int fps = 0;
public:
PS4RemotePlay(IO * io, Host * host);
~PS4RemotePlay();
void draw(NVGcontext * vg, int x, int y, unsigned width, unsigned height, brls::Style * style, brls::FrameContext * ctx) override;
};
#endif // CHIAKI_GUI_H

129
switch/include/host.h Normal file
View file

@ -0,0 +1,129 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_HOST_H
#define CHIAKI_HOST_H
#include <string>
#include <map>
#include <netinet/in.h>
#include <chiaki/log.h>
#include <chiaki/regist.h>
#include <chiaki/discovery.h>
#include <chiaki/opusdecoder.h>
#include <chiaki/controller.h>
#include "exception.h"
#include "io.h"
#include "settings.h"
class DiscoveryManager;
static void Discovery(ChiakiDiscoveryHost*, void *);
static void InitAudioCB(unsigned int channels, unsigned int rate, void * user);
static bool VideoCB(uint8_t * buf, size_t buf_size, void * user);
static void AudioCB(int16_t * buf, size_t samples_count, void * user);
static void RegistEventCB(ChiakiRegistEvent * event, void * user);
static void EventCB(ChiakiEvent * event, void * user);
enum RegistError
{
HOST_REGISTER_OK,
HOST_REGISTER_ERROR_SETTING_PSNACCOUNTID,
HOST_REGISTER_ERROR_SETTING_PSNONLINEID
};
class Settings;
class Host
{
private:
ChiakiLog * log = nullptr;
Settings * settings = nullptr;
//video config
ChiakiVideoResolutionPreset video_resolution = CHIAKI_VIDEO_RESOLUTION_PRESET_720p;
ChiakiVideoFPSPreset video_fps = CHIAKI_VIDEO_FPS_PRESET_60;
// info from discovery manager
int device_discovery_protocol_version = 0;
std::string host_type;
// user info
std::string psn_online_id = "";
std::string psn_account_id = "";
// info from regist/settings
ChiakiRegist regist = {};
ChiakiRegistInfo regist_info = {};
std::function<void()> chiaki_regist_event_type_finished_canceled = nullptr;
std::function<void()> chiaki_regist_event_type_finished_failed = nullptr;
std::function<void()> chiaki_regist_event_type_finished_success = nullptr;
std::string ap_ssid;
std::string ap_bssid;
std::string ap_key;
std::string ap_name;
std::string ps4_nickname;
// mac address = 48 bits
uint8_t ps4_mac[6] = {0};
char rp_regist_key[CHIAKI_SESSION_AUTH_SIZE] = {0};
uint32_t rp_key_type = 0;
uint8_t rp_key[0x10] = {0};
// manage stream session
bool session_init = false;
ChiakiSession session;
ChiakiOpusDecoder opus_decoder;
ChiakiConnectVideoProfile video_profile;
ChiakiControllerState keyboard_state;
friend class DiscoveryManager;
friend class Settings;
public:
// internal state
ChiakiDiscoveryHostState state = CHIAKI_DISCOVERY_HOST_STATE_UNKNOWN;
bool discovered = false;
bool registered = false;
// rp_key_data is true when rp_key, rp_regist_key, rp_key_type
bool rp_key_data = false;
std::string host_name;
int system_version = 0;
// sony's host_id == mac addr without colon
std::string host_id;
std::string host_addr;
Host(ChiakiLog * log, Settings * settings, std::string host_name);
~Host();
bool GetVideoResolution(int * ret_width, int * ret_height);
int Register(std::string pin);
int Wakeup();
int InitSession(IO *);
int FiniSession();
void StopSession();
void StartSession();
void SendFeedbackState(ChiakiControllerState*);
void RegistCB(ChiakiRegistEvent *);
void SetRegistEventTypeFinishedCanceled(std::function<void()> chiaki_regist_event_type_finished_canceled)
{
this->chiaki_regist_event_type_finished_canceled = chiaki_regist_event_type_finished_canceled;
};
void SetRegistEventTypeFinishedFailed(std::function<void()> chiaki_regist_event_type_finished_failed)
{
this->chiaki_regist_event_type_finished_failed = chiaki_regist_event_type_finished_failed;
};
void SetRegistEventTypeFinishedSuccess(std::function<void()> chiaki_regist_event_type_finished_success)
{
this->chiaki_regist_event_type_finished_success = chiaki_regist_event_type_finished_success;
};
};
#endif

136
switch/include/io.h Normal file
View file

@ -0,0 +1,136 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_IO_H
#define CHIAKI_IO_H
#include <functional>
#include <cstdint>
#include <SDL2/SDL.h>
#include <glad.h> // glad library (OpenGL loader)
#include <chiaki/session.h>
/*
https://github.com/devkitPro/switch-glad/blob/master/include/glad/glad.h
https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D4.3&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_EXT_texture_filter_anisotropic
Language/Generator: C/C++
Specification: gl
APIs: gl=4.3
Profile: core
Extensions:
GL_EXT_texture_compression_s3tc,
GL_EXT_texture_filter_anisotropic
Loader: False
Local files: False
Omit khrplatform: False
Reproducible: False
*/
#include <mutex>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#include <chiaki/log.h>
#include <chiaki/controller.h>
#include "exception.h"
#define PLANES_COUNT 3
#define SDL_JOYSTICK_COUNT 2
class IO
{
private:
ChiakiLog * log;
int video_width;
int video_height;
bool quit = false;
// opengl reader writer
std::mutex mtx;
// default nintendo switch res
int screen_width = 1280;
int screen_height = 720;
std::function<bool()> chiaki_event_connected_cb = nullptr;
std::function<bool(bool)> chiaki_even_login_pin_request_cb = nullptr;
std::function<bool(ChiakiQuitEvent *)> chiaki_event_quit_cb = nullptr;
AVCodec * codec;
AVCodecContext * codec_context;
AVFrame * frame;
SDL_AudioDeviceID sdl_audio_device_id = 0;
SDL_Event sdl_event;
SDL_Joystick * sdl_joystick_ptr[SDL_JOYSTICK_COUNT] = {0};
GLuint vao;
GLuint vbo;
GLuint tex[PLANES_COUNT];
GLuint pbo[PLANES_COUNT];
GLuint vert;
GLuint frag;
GLuint prog;
private:
bool InitAVCodec();
bool InitOpenGl();
bool InitOpenGlTextures();
bool InitOpenGlShader();
void OpenGlDraw();
#ifdef DEBUG_OPENGL
void CheckGLError(const char * func, const char * file, int line);
void DumpShaderError(GLuint prog, const char * func, const char * file, int line);
void DumpProgramError(GLuint prog, const char * func, const char * file, int line);
#endif
GLuint CreateAndCompileShader(GLenum type, const char * source);
void SetOpenGlYUVPixels(AVFrame * frame);
bool ReadGameKeys(SDL_Event * event, ChiakiControllerState * state);
bool ReadGameTouchScreen(ChiakiControllerState * state);
public:
IO(ChiakiLog * log);
~IO();
void SetMesaConfig();
bool VideoCB(uint8_t * buf, size_t buf_size);
void SetEventConnectedCallback(std::function<bool()> chiaki_event_connected_cb)
{
this->chiaki_event_connected_cb = chiaki_event_connected_cb;
};
void SetEventLoginPinRequestCallback(std::function<bool(bool)> chiaki_even_login_pin_request_cb)
{
this->chiaki_even_login_pin_request_cb = chiaki_even_login_pin_request_cb;
};
void SetEventQuitCallback(std::function<bool(ChiakiQuitEvent *)> chiaki_event_quit_cb)
{
this->chiaki_event_quit_cb = chiaki_event_quit_cb;
};
void InitAudioCB(unsigned int channels, unsigned int rate);
void AudioCB(int16_t * buf, size_t samples_count);
void EventCB(ChiakiEvent *event);
bool InitVideo(int video_width, int video_height, int screen_width, int screen_height);
bool FreeVideo();
bool InitJoystick();
bool FreeJoystick();
bool ReadUserKeyboard(char * buffer, size_t buffer_size);
bool MainLoop(ChiakiControllerState * state);
};
#endif //CHIAKI_IO_H

105
switch/include/settings.h Normal file
View file

@ -0,0 +1,105 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_SETTINGS_H
#define CHIAKI_SETTINGS_H
#include <regex>
#include "host.h"
#include <chiaki/log.h>
// mutual host and settings
class Host;
class Settings
{
private:
ChiakiLog * log;
const char * filename = "chiaki.conf";
std::map<std::string, Host> *hosts;
// global_settings from psedo INI file
ChiakiVideoResolutionPreset global_video_resolution = CHIAKI_VIDEO_RESOLUTION_PRESET_720p;
ChiakiVideoFPSPreset global_video_fps = CHIAKI_VIDEO_FPS_PRESET_60;
std::string global_psn_online_id = "";
std::string global_psn_account_id = "";
typedef enum configurationitem
{
UNKNOWN,
HOST_NAME,
HOST_IP,
PSN_ONLINE_ID,
PSN_ACCOUNT_ID,
RP_KEY,
RP_KEY_TYPE,
RP_REGIST_KEY,
VIDEO_RESOLUTION,
VIDEO_FPS,
} ConfigurationItem;
// dummy parser implementation
// the aim is not to have bulletproof parser
// the goal is to read/write inernal flat configuration file
const std::map<Settings::ConfigurationItem, std::regex> re_map = {
{ HOST_NAME, std::regex("^\\[\\s*(.+)\\s*\\]") },
{ HOST_IP, std::regex("^\\s*host_ip\\s*=\\s*\"?(\\d+\\.\\d+\\.\\d+\\.\\d+)\"?") },
{ PSN_ONLINE_ID, std::regex("^\\s*psn_online_id\\s*=\\s*\"?(\\w+)\"?") },
{ PSN_ACCOUNT_ID, std::regex("^\\s*psn_account_id\\s*=\\s*\"?([\\w/=+]+)\"?") },
{ RP_KEY, std::regex("^\\s*rp_key\\s*=\\s*\"?([\\w/=+]+)\"?") },
{ RP_KEY_TYPE, std::regex("^\\s*rp_key_type\\s*=\\s*\"?(\\d)\"?") },
{ RP_REGIST_KEY, std::regex("^\\s*rp_regist_key\\s*=\\s*\"?([\\w/=+]+)\"?") },
{ VIDEO_RESOLUTION, std::regex("^\\s*video_resolution\\s*=\\s*\"?(1080p|720p|540p|360p)\"?") },
{ VIDEO_FPS, std::regex("^\\s*video_fps\\s*=\\s*\"?(60|30)\"?") },
};
ConfigurationItem ParseLine(std::string * line, std::string * value);
size_t GetB64encodeSize(size_t);
public:
Settings(ChiakiLog * log, std::map<std::string, Host> * hosts): log(log), hosts(hosts){};
Host * GetOrCreateHost(std::string * host_name);
ChiakiLog* GetLogger();
std::string GetPSNOnlineID(Host * host);
std::string GetPSNAccountID(Host * host);
void SetPSNOnlineID(Host * host, std::string psn_online_id);
void SetPSNAccountID(Host * host, std::string psn_account_id);
ChiakiVideoResolutionPreset GetVideoResolution(Host * host);
ChiakiVideoFPSPreset GetVideoFPS(Host * host);
std::string ResolutionPresetToString(ChiakiVideoResolutionPreset resolution);
std::string FPSPresetToString(ChiakiVideoFPSPreset fps);
ChiakiVideoResolutionPreset StringToResolutionPreset(std::string value);
ChiakiVideoFPSPreset StringToFPSPreset(std::string value);
int ResolutionPresetToInt(ChiakiVideoResolutionPreset resolution);
int FPSPresetToInt(ChiakiVideoFPSPreset fps);
void SetVideoResolution(Host * host, ChiakiVideoResolutionPreset value);
void SetVideoFPS(Host * host, ChiakiVideoFPSPreset value);
void SetVideoResolution(Host * host, std::string value);
void SetVideoFPS(Host * host, std::string value);
std::string GetHostIPAddr(Host * host);
std::string GetHostName(Host * host);
bool SetHostRPKeyType(Host * host, std::string value);
int GetHostRPKeyType(Host * host);
std::string GetHostRPKey(Host * host);
std::string GetHostRPRegistKey(Host * host);
bool SetHostRPKey(Host * host, std::string rp_key_b64);
bool SetHostRPRegistKey(Host * host, std::string rp_regist_key_b64);
void ParseFile();
int WriteFile();
};
#endif // CHIAKI_SETTINGS_H

1
switch/res/add-24px.svg Symbolic link
View file

@ -0,0 +1 @@
../../gui/res/add-24px.svg

1
switch/res/console.svg Symbolic link
View file

@ -0,0 +1 @@
../../assets/console.svg

View file

@ -0,0 +1 @@
../../gui/res/discover-24px.svg

View file

@ -0,0 +1 @@
../../gui/res/discover-off-24px.svg

1
switch/res/i18n/en-US Symbolic link
View file

@ -0,0 +1 @@
../../../third-party/borealis/resources/i18n/en-US

BIN
switch/res/icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

1
switch/res/inter Symbolic link
View file

@ -0,0 +1 @@
../../third-party/borealis/resources/inter

1
switch/res/material Symbolic link
View file

@ -0,0 +1 @@
../../third-party/borealis/resources/material

View file

@ -0,0 +1 @@
../../gui/res/settings-20px.svg

View file

@ -0,0 +1,259 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef __SWITCH__
#include <switch.h>
#endif
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <discoverymanager.h>
#define PING_MS 500
#define HOSTS_MAX 16
#define DROP_PINGS 3
static void Discovery(ChiakiDiscoveryHost * discovered_hosts, size_t hosts_count, void * user)
{
DiscoveryManager * dm = (DiscoveryManager *) user;
for(size_t i=0; i < hosts_count; i++)
{
dm->DiscoveryCB(discovered_hosts+i);
}
}
DiscoveryManager::DiscoveryManager(Settings *settings)
: settings(settings), host_addr(nullptr), host_addr_len(0)
{
this->log = this->settings->GetLogger();
}
DiscoveryManager::~DiscoveryManager()
{
// join discovery thread
if(this->service_enable)
{
SetService(false);
}
chiaki_discovery_fini(&this->discovery);
}
void DiscoveryManager::SetService(bool enable)
{
if(this->service_enable == enable)
{
return;
}
this->service_enable = enable;
if(enable)
{
ChiakiDiscoveryServiceOptions options;
options.ping_ms = PING_MS;
options.hosts_max = HOSTS_MAX;
options.host_drop_pings = DROP_PINGS;
options.cb = Discovery;
options.cb_user = this;
sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(CHIAKI_DISCOVERY_PORT);
addr.sin_addr.s_addr = GetIPv4BroadcastAddr();
options.send_addr = reinterpret_cast<sockaddr *>(&addr);
options.send_addr_size = sizeof(addr);
ChiakiErrorCode err = chiaki_discovery_service_init(&this->service, &options, log);
if(err != CHIAKI_ERR_SUCCESS)
{
this->service_enable = false;
CHIAKI_LOGE(this->log, "DiscoveryManager failed to init Discovery Service");
return;
}
}
else
{
chiaki_discovery_service_fini(&this->service);
}
}
uint32_t DiscoveryManager::GetIPv4BroadcastAddr()
{
#ifdef __SWITCH__
uint32_t current_addr, subnet_mask;
// init nintendo net interface service
Result rc = nifmInitialize(NifmServiceType_User);
if (R_SUCCEEDED(rc))
{
// read current IP and netmask
rc = nifmGetCurrentIpConfigInfo(
&current_addr, &subnet_mask,
NULL, NULL, NULL);
nifmExit();
}
else
{
CHIAKI_LOGE(this->log, "Failed to get nintendo nifmGetCurrentIpConfigInfo");
return 1;
}
return current_addr | (~subnet_mask);
#else
return 0xffffffff;
#endif
}
int DiscoveryManager::Send(struct sockaddr *host_addr, size_t host_addr_len)
{
if(!host_addr)
{
CHIAKI_LOGE(log, "Null sockaddr");
return 1;
}
((struct sockaddr_in *)host_addr)->sin_port = htons(CHIAKI_DISCOVERY_PORT);
ChiakiDiscoveryPacket packet;
memset(&packet, 0, sizeof(packet));
packet.cmd = CHIAKI_DISCOVERY_CMD_SRCH;
chiaki_discovery_send(&this->discovery, &packet, this->host_addr, this->host_addr_len);
return 0;
}
int DiscoveryManager::Send(const char * discover_ip_dest)
{
struct addrinfo * host_addrinfos;
int r = getaddrinfo(discover_ip_dest, NULL, NULL, &host_addrinfos);
if(r != 0)
{
CHIAKI_LOGE(log, "getaddrinfo failed");
return 1;
}
struct sockaddr * host_addr = nullptr;
socklen_t host_addr_len = 0;
for(struct addrinfo *ai=host_addrinfos; ai; ai=ai->ai_next)
{
if(ai->ai_protocol != IPPROTO_UDP)
continue;
if(ai->ai_family != AF_INET) // TODO: IPv6
continue;
this->host_addr_len = ai->ai_addrlen;
this->host_addr = (struct sockaddr *)malloc(host_addr_len);
if(!this->host_addr)
break;
memcpy(this->host_addr, ai->ai_addr, this->host_addr_len);
}
freeaddrinfo(host_addrinfos);
if(!this->host_addr)
{
CHIAKI_LOGE(log, "Failed to get addr for hostname");
return 1;
}
return DiscoveryManager::Send(this->host_addr, this->host_addr_len);
}
int DiscoveryManager::Send()
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = GetIPv4BroadcastAddr();
addr.sin_port = htons(CHIAKI_DISCOVERY_PORT);
this->host_addr_len = sizeof(sockaddr_in);
this->host_addr = (struct sockaddr *)malloc(host_addr_len);
memcpy(this->host_addr, &addr, this->host_addr_len);
return DiscoveryManager::Send(this->host_addr, this->host_addr_len);
}
void DiscoveryManager::DiscoveryCB(ChiakiDiscoveryHost * discovered_host)
{
// the user ptr is passed as
// chiaki_discovery_thread_start arg
std::string key = discovered_host->host_name;
Host *host = this->settings->GetOrCreateHost(&key);
CHIAKI_LOGI(this->log, "--");
CHIAKI_LOGI(this->log, "Discovered Host:");
CHIAKI_LOGI(this->log, "State: %s", chiaki_discovery_host_state_string(discovered_host->state));
/*
host attr
uint32_t host_addr;
int system_version;
int device_discovery_protocol_version;
std::string host_name;
std::string host_type;
std::string host_id;
*/
host->state = discovered_host->state;
// add host ptr to list
if(discovered_host->system_version)
{
// example: 07020001
host->system_version = atoi(discovered_host->system_version);
CHIAKI_LOGI(this->log, "System Version: %s", discovered_host->system_version);
}
if(discovered_host->device_discovery_protocol_version)
{
host->device_discovery_protocol_version = atoi(discovered_host->device_discovery_protocol_version);
CHIAKI_LOGI(this->log, "Device Discovery Protocol Version: %s", discovered_host->device_discovery_protocol_version);
}
if(discovered_host->host_request_port)
CHIAKI_LOGI(this->log, "Request Port: %hu", (unsigned short)discovered_host->host_request_port);
if(discovered_host->host_addr)
{
host->host_addr = discovered_host->host_addr;
CHIAKI_LOGI(this->log, "Host Addr: %s", discovered_host->host_addr);
}
if(discovered_host->host_name)
{
host->host_name = discovered_host->host_name;
CHIAKI_LOGI(this->log, "Host Name: %s", discovered_host->host_name);
}
if(discovered_host->host_type)
CHIAKI_LOGI(this->log, "Host Type: %s", discovered_host->host_type);
if(discovered_host->host_id)
CHIAKI_LOGI(this->log, "Host ID: %s", discovered_host->host_id);
if(discovered_host->running_app_titleid)
CHIAKI_LOGI(this->log, "Running App Title ID: %s", discovered_host->running_app_titleid);
if(discovered_host->running_app_name)
CHIAKI_LOGI(this->log, "Running App Name: %s", discovered_host->running_app_name);
CHIAKI_LOGI(this->log, "--");
}

514
switch/src/gui.cpp Normal file
View file

@ -0,0 +1,514 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#include <chiaki/log.h>
#include "gui.h"
#define SCREEN_W 1280
#define SCREEN_H 720
// TODO
using namespace brls::i18n::literals; // for _i18n
#define DIALOG(dialog, r) \
brls::Dialog* d_##dialog = new brls::Dialog(r); \
brls::GenericEvent::Callback cb_##dialog = [d_##dialog](brls::View* view) \
{ \
d_##dialog->close(); \
}; \
d_##dialog->addButton("Ok", cb_##dialog); \
d_##dialog->setCancelable(false); \
d_##dialog->open(); \
brls::Logger::info("Dialog: {0}", r);
HostInterface::HostInterface(brls::List * hostList, IO * io, Host * host, Settings * settings)
: hostList(hostList), io(io), host(host), settings(settings)
{
brls::ListItem* connect = new brls::ListItem("Connect");
connect->getClickEvent()->subscribe(std::bind(&HostInterface::Connect, this, std::placeholders::_1));
this->hostList->addView(connect);
brls::ListItem* wakeup = new brls::ListItem("Wakeup");
wakeup->getClickEvent()->subscribe(std::bind(&HostInterface::Wakeup, this, std::placeholders::_1));
this->hostList->addView(wakeup);
// message delimiter
brls::Label* info = new brls::Label(brls::LabelStyle::REGULAR,
"Host configuration", true);
this->hostList->addView(info);
// push opengl chiaki stream
// when the host is connected
this->io->SetEventConnectedCallback(std::bind(&HostInterface::Stream, this));
this->io->SetEventQuitCallback(std::bind(&HostInterface::CloseStream, this, std::placeholders::_1));
}
HostInterface::~HostInterface()
{
Disconnect();
}
void HostInterface::Register(bool pin_incorrect)
{
if(pin_incorrect)
{
DIALOG(srfpvyps, "Session Registration Failed, Please verify your PS4 settings");
return;
}
// the host is not registered yet
brls::Dialog* peprpc = new brls::Dialog("Please enter your PS4 registration PIN code");
brls::GenericEvent::Callback cb_peprpc = [this, peprpc](brls::View* view)
{
bool pin_provided = false;
char pin_input[9] = {0};
std::string error_message;
// use callback to ensure that the message is showed on screen
// before the the ReadUserKeyboard
peprpc->close();
pin_provided = this->io->ReadUserKeyboard(pin_input, sizeof(pin_input));
if(pin_provided)
{
// prevent users form messing with the gui
brls::Application::blockInputs();
int ret = this->host->Register(pin_input);
if(ret != HOST_REGISTER_OK)
{
switch(ret)
{
// account not configured
case HOST_REGISTER_ERROR_SETTING_PSNACCOUNTID:
brls::Application::notify("No PSN Account ID provided");
brls::Application::unblockInputs();
break;
case HOST_REGISTER_ERROR_SETTING_PSNONLINEID:
brls::Application::notify("No PSN Online ID provided");
brls::Application::unblockInputs();
break;
}
}
}
};
peprpc->addButton("Ok", cb_peprpc);
peprpc->setCancelable(false);
peprpc->open();
}
void HostInterface::Wakeup(brls::View * view)
{
if(!this->host->rp_key_data)
{
// the host is not registered yet
DIALOG(prypf, "Please register your PS4 first");
}
else
{
int r = host->Wakeup();
if(r == 0)
{
brls::Application::notify("PS4 Wakeup packet sent");
}
else
{
brls::Application::notify("PS4 Wakeup packet failed");
}
}
}
void HostInterface::Connect(brls::View * view)
{
// check that all requirements are met
if(this->host->state != CHIAKI_DISCOVERY_HOST_STATE_READY)
{
// host in standby mode
DIALOG(ptoyp, "Please turn on your PS4");
return;
}
if(!this->host->rp_key_data)
{
// user must provide psn id for registration
std::string account_id = this->settings->GetPSNAccountID(this->host);
std::string online_id = this->settings->GetPSNOnlineID(this->host);
if(this->host->system_version >= 7000000 && account_id.length() <= 0)
{
// PS4 firmware > 7.0
DIALOG(upaid, "Undefined PSN Account ID (Please configure a valid psn_account_id)");
return;
}
else if( this->host->system_version < 7000000 && this->host->system_version > 0 && online_id.length() <= 0)
{
// use oline ID for ps4 < 7.0
DIALOG(upoid, "Undefined PSN Online ID (Please configure a valid psn_online_id)");
return;
}
// add HostConnected function to regist_event_type_finished_success
auto event_type_finished_success_cb = [this]()
{
// save RP keys
this->settings->WriteFile();
// FIXME: may raise a connection refused
// when the connection is triggered
// just after the register success
sleep(2);
ConnectSession();
// decrement block input token number
brls::Application::unblockInputs();
};
this->host->SetRegistEventTypeFinishedSuccess(event_type_finished_success_cb);
auto event_type_finished_failed_cb = [this]()
{
// unlock user inputs
brls::Application::unblockInputs();
brls::Application::notify("Registration failed");
};
this->host->SetRegistEventTypeFinishedFailed(event_type_finished_failed_cb);
this->Register(false);
}
else
{
// the host is already registered
// start session directly
ConnectSession();
}
}
void HostInterface::ConnectSession()
{
// ignore all user inputs (avoid double connect)
// user inputs are restored with the CloseStream
brls::Application::blockInputs();
// connect host sesssion
this->host->InitSession(this->io);
this->host->StartSession();
}
void HostInterface::Disconnect()
{
if(this->connected)
{
brls::Application::popView();
this->host->StopSession();
this->connected = false;
}
this->host->FiniSession();
}
bool HostInterface::Stream()
{
this->connected = true;
// https://github.com/natinusala/borealis/issues/59
// disable 60 fps limit
brls::Application::setMaximumFPS(0);
// show FPS counter
// brls::Application::setDisplayFramerate(true);
// push raw opengl stream over borealis
brls::Application::pushView(new PS4RemotePlay(this->io, this->host));
return true;
}
bool HostInterface::CloseStream(ChiakiQuitEvent * quit)
{
// session QUIT call back
brls::Application::unblockInputs();
// restore 60 fps limit
brls::Application::setMaximumFPS(60);
// brls::Application::setDisplayFramerate(false);
/*
DIALOG(sqrs, chiaki_quit_reason_string(quit->reason));
*/
brls::Application::notify(chiaki_quit_reason_string(quit->reason));
Disconnect();
return false;
}
MainApplication::MainApplication(std::map<std::string, Host> * hosts,
Settings * settings, DiscoveryManager * discoverymanager,
IO * io, ChiakiLog * log)
: hosts(hosts), settings(settings), discoverymanager(discoverymanager),
io(io), log(log)
{
}
MainApplication::~MainApplication()
{
this->discoverymanager->SetService(false);
//this->io->FreeJoystick();
this->io->FreeVideo();
}
bool MainApplication::Load()
{
this->discoverymanager->SetService(true);
// Init the app
brls::Logger::setLogLevel(brls::LogLevel::DEBUG);
brls::i18n::loadTranslations();
if (!brls::Application::init("Chiaki Remote play"))
{
brls::Logger::error("Unable to init Borealis application");
return false;
}
// init chiaki gl after borealis
// let borealis manage the main screen/window
if(!io->InitVideo(0, 0, SCREEN_W, SCREEN_H))
{
brls::Logger::error("Failed to initiate Video");
}
brls::Logger::info("Load sdl joysticks");
if(!io->InitJoystick())
{
brls::Logger::error("Faled to initiate Joysticks");
}
// Create a view
this->rootFrame = new brls::TabFrame();
this->rootFrame->setTitle("Chiaki: Open Source PS4 Remote Play Client");
this->rootFrame->setIcon(BOREALIS_ASSET("icon.jpg"));
brls::List* config = new brls::List();
BuildConfigurationMenu(config);
this->rootFrame->addTab("Configuration", config);
// ----------------
this->rootFrame->addSeparator();
// Add the root view to the stack
brls::Application::pushView(this->rootFrame);
while(brls::Application::mainLoop())
{
for(auto it = this->hosts->begin(); it != this->hosts->end(); it++)
{
if(this->host_menuitems.find(&it->second) == this->host_menuitems.end())
{
brls::List* new_host = new brls::List();
this->host_menuitems[&it->second] = new_host;
// create host if udefined
HostInterface host_menu = HostInterface(new_host, this->io, &it->second, this->settings);
BuildConfigurationMenu(new_host, &it->second);
this->rootFrame->addTab(it->second.host_name.c_str(), new_host);
}
}
}
return true;
}
bool MainApplication::BuildConfigurationMenu(brls::List * ls, Host * host)
{
std::string psn_online_id_string = this->settings->GetPSNOnlineID(host);
brls::ListItem* psn_online_id = new brls::ListItem("PSN Online ID");
psn_online_id->setValue(psn_online_id_string.c_str());
auto psn_online_id_cb = [this, host, psn_online_id](brls::View * view)
{
char online_id[256] = {0};
bool input = this->io->ReadUserKeyboard(online_id, sizeof(online_id));
if(input)
{
// update gui
psn_online_id->setValue(online_id);
// push in setting
this->settings->SetPSNOnlineID(host, online_id);
// write on disk
this->settings->WriteFile();
}
};
psn_online_id->getClickEvent()->subscribe(psn_online_id_cb);
ls->addView(psn_online_id);
std::string psn_account_id_string = this->settings->GetPSNAccountID(host);
brls::ListItem* psn_account_id = new brls::ListItem("PSN Account ID", "v7.0 and greater");
psn_account_id->setValue(psn_account_id_string.c_str());
auto psn_account_id_cb = [this, host, psn_account_id](brls::View * view)
{
char account_id[CHIAKI_PSN_ACCOUNT_ID_SIZE * 2] = {0};
bool input = this->io->ReadUserKeyboard(account_id, sizeof(account_id));
if(input)
{
// update gui
psn_account_id->setValue(account_id);
// push in setting
this->settings->SetPSNAccountID(host, account_id);
// write on disk
this->settings->WriteFile();
}
};
psn_account_id->getClickEvent()->subscribe(psn_account_id_cb);
ls->addView(psn_account_id);
int value;
ChiakiVideoResolutionPreset resolution_preset = this->settings->GetVideoResolution(host);
switch(resolution_preset)
{
case CHIAKI_VIDEO_RESOLUTION_PRESET_720p:
value = 0;
break;
case CHIAKI_VIDEO_RESOLUTION_PRESET_540p:
value = 1;
break;
case CHIAKI_VIDEO_RESOLUTION_PRESET_360p:
value = 2;
break;
}
brls::SelectListItem* resolution = new brls::SelectListItem(
"Resolution", { "720p", "540p", "360p" }, value);
auto resolution_cb = [this, host](int result)
{
ChiakiVideoResolutionPreset value = CHIAKI_VIDEO_RESOLUTION_PRESET_720p;
switch(result)
{
case 0:
value = CHIAKI_VIDEO_RESOLUTION_PRESET_720p;
break;
case 1:
value = CHIAKI_VIDEO_RESOLUTION_PRESET_540p;
break;
case 2:
value = CHIAKI_VIDEO_RESOLUTION_PRESET_360p;
break;
}
this->settings->SetVideoResolution(host, value);
this->settings->WriteFile();
};
resolution->getValueSelectedEvent()->subscribe(resolution_cb);
ls->addView(resolution);
ChiakiVideoFPSPreset fps_preset = this->settings->GetVideoFPS(host);
switch(fps_preset)
{
case CHIAKI_VIDEO_FPS_PRESET_60:
value = 0;
break;
case CHIAKI_VIDEO_FPS_PRESET_30:
value = 1;
break;
}
brls::SelectListItem* fps = new brls::SelectListItem(
"FPS", { "60", "30"}, value);
auto fps_cb = [this, host](int result)
{
ChiakiVideoFPSPreset value = CHIAKI_VIDEO_FPS_PRESET_60;
switch(result)
{
case 0:
value = CHIAKI_VIDEO_FPS_PRESET_60;
break;
case 1:
value = CHIAKI_VIDEO_FPS_PRESET_30;
break;
}
this->settings->SetVideoFPS(host, value);
this->settings->WriteFile();
};
fps->getValueSelectedEvent()->subscribe(fps_cb);
ls->addView(fps);
if(host != nullptr)
{
// message delimiter
brls::Label* info = new brls::Label(brls::LabelStyle::REGULAR,
"Host information", true);
ls->addView(info);
std::string host_name_string = this->settings->GetHostName(host);
brls::ListItem* host_name = new brls::ListItem("PS4 Hostname");
host_name->setValue(host_name_string.c_str());
ls->addView(host_name);
std::string host_ipaddr_string = settings->GetHostIPAddr(host);
brls::ListItem* host_ipaddr = new brls::ListItem("PS4 IP Address");
host_ipaddr->setValue(host_ipaddr_string.c_str());
ls->addView(host_ipaddr);
std::string host_rp_regist_key_string = settings->GetHostRPRegistKey(host);
brls::ListItem* host_rp_regist_key = new brls::ListItem("RP Register Key");
host_rp_regist_key->setValue(host_rp_regist_key_string.c_str());
ls->addView(host_rp_regist_key);
std::string host_rp_key_string = settings->GetHostRPKey(host);
brls::ListItem* host_rp_key = new brls::ListItem("RP Key");
host_rp_key->setValue(host_rp_key_string.c_str());
ls->addView(host_rp_key);
std::string host_rp_key_type_string = std::to_string(settings->GetHostRPKeyType(host));
brls::ListItem* host_rp_key_type = new brls::ListItem("RP Key type");
host_rp_key_type->setValue(host_rp_key_type_string.c_str());
ls->addView(host_rp_key_type);
}
return true;
}
PS4RemotePlay::PS4RemotePlay(IO * io, Host * host)
: io(io), host(host)
{
// store joycon/touchpad keys
for(int x=0; x < CHIAKI_CONTROLLER_TOUCHES_MAX; x++)
// start touchpad as "untouched"
this->state.touches[x].id = -1;
// this->base_time=glfwGetTime();
}
void PS4RemotePlay::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{
this->io->MainLoop(&this->state);
this->host->SendFeedbackState(&state);
// FPS calculation
// this->frame_counter += 1;
// double frame_time = glfwGetTime();
// if((frame_time - base_time) >= 1.0)
// {
// base_time += 1;
// //printf("FPS: %d\n", this->frame_counter);
// this->fps = this->frame_counter;
// this->frame_counter = 0;
// }
// nvgBeginPath(vg);
// nvgFillColor(vg, nvgRGBA(255,192,0,255));
// nvgFontFaceId(vg, ctx->fontStash->regular);
// nvgFontSize(vg, style->Label.smallFontSize);
// nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
// char fps_str[9] = {0};
// sprintf(fps_str, "FPS: %000d", this->fps);
// nvgText(vg, 5,10, fps_str, NULL);
}
PS4RemotePlay::~PS4RemotePlay()
{
}

287
switch/src/host.cpp Normal file
View file

@ -0,0 +1,287 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <chiaki/base64.h>
#include "io.h"
#include "host.h"
static void InitAudioCB(unsigned int channels, unsigned int rate, void * user)
{
IO * io = (IO *) user;
io->InitAudioCB(channels, rate);
}
static bool VideoCB(uint8_t * buf, size_t buf_size, void * user)
{
IO * io = (IO *) user;
return io->VideoCB(buf, buf_size);
}
static void AudioCB(int16_t * buf, size_t samples_count, void * user)
{
IO * io = (IO *) user;
io->AudioCB(buf, samples_count);
}
static void EventCB(ChiakiEvent * event, void * user)
{
IO * io = (IO *) user;
io->EventCB(event);
}
static void RegistEventCB(ChiakiRegistEvent * event, void * user)
{
Host * host = (Host *) user;
host->RegistCB(event);
}
Host::Host(ChiakiLog * log, Settings * settings, std::string host_name)
: log(log), settings(settings), host_name(host_name)
{
}
Host::~Host()
{
}
int Host::Wakeup()
{
if(strlen(this->rp_regist_key) > 8)
{
CHIAKI_LOGE(this->log, "Given registkey is too long");
return 1;
}
else if (strlen(this->rp_regist_key) <=0)
{
CHIAKI_LOGE(this->log, "Given registkey is not defined");
return 2;
}
uint64_t credential = (uint64_t)strtoull(this->rp_regist_key, NULL, 16);
ChiakiErrorCode ret = chiaki_discovery_wakeup(this->log, NULL, host_addr.c_str(), credential);
if(ret == CHIAKI_ERR_SUCCESS)
{
//FIXME
}
return ret;
}
int Host::Register(std::string pin)
{
// use pin and accont_id to negociate secrets for session
//
// convert psn_account_id into uint8_t[CHIAKI_PSN_ACCOUNT_ID_SIZE]
// CHIAKI_PSN_ACCOUNT_ID_SIZE == 8
std::string account_id = this->settings->GetPSNAccountID(this);
std::string online_id = this->settings->GetPSNOnlineID(this);
size_t account_id_size = sizeof(uint8_t[CHIAKI_PSN_ACCOUNT_ID_SIZE]);
// PS4 firmware > 7.0
if(this->system_version >= 7000000)
{
// use AccountID for ps4 > 7.0
if(account_id.length() > 0)
{
chiaki_base64_decode(account_id.c_str(), account_id.length(),
regist_info.psn_account_id, &(account_id_size));
regist_info.psn_online_id = nullptr;
}
else
{
CHIAKI_LOGE(this->log, "Undefined PSN Account ID (Please configure a valid psn_account_id)");
return HOST_REGISTER_ERROR_SETTING_PSNACCOUNTID;
}
if(this->system_version >= 8000000)
regist_info.target = CHIAKI_TARGET_PS4_10;
else
regist_info.target = CHIAKI_TARGET_PS4_9;
}
else if( this->system_version < 7000000 && this->system_version > 0)
{
// use oline ID for ps4 < 7.0
if(online_id.length() > 0)
{
regist_info.psn_online_id = this->psn_online_id.c_str();
// regist_info.psn_account_id = {0};
}
else
{
CHIAKI_LOGE(this->log, "Undefined PSN Online ID (Please configure a valid psn_online_id)");
return HOST_REGISTER_ERROR_SETTING_PSNONLINEID;
}
regist_info.target = CHIAKI_TARGET_PS4_8;
}
else
{
CHIAKI_LOGE(this->log, "Undefined PS4 system version (please run discover first)");
}
this->regist_info.pin = atoi(pin.c_str());
this->regist_info.host = this->host_addr.c_str();
this->regist_info.broadcast = false;
CHIAKI_LOGI(this->log, "Registering to host `%s` `%s` with PSN AccountID `%s` pin `%s`",
this->host_name.c_str(), this->host_addr.c_str(), psn_account_id.c_str(), pin.c_str());
chiaki_regist_start(&this->regist, this->log, &this->regist_info, RegistEventCB, this);
return HOST_REGISTER_OK;
}
int Host::InitSession(IO * user)
{
chiaki_connect_video_profile_preset(&(this->video_profile),
this->video_resolution, this->video_fps);
// Build chiaki ps4 stream session
chiaki_opus_decoder_init(&(this->opus_decoder), this->log);
ChiakiAudioSink audio_sink;
ChiakiConnectInfo chiaki_connect_info;
chiaki_connect_info.host = this->host_addr.c_str();
chiaki_connect_info.video_profile = this->video_profile;
memcpy(chiaki_connect_info.regist_key, this->rp_regist_key, sizeof(chiaki_connect_info.regist_key));
memcpy(chiaki_connect_info.morning, this->rp_key, sizeof(chiaki_connect_info.morning));
// set keybord state to 0
memset(&(this->keyboard_state), 0, sizeof(keyboard_state));
ChiakiErrorCode err = chiaki_session_init(&(this->session), &chiaki_connect_info, this->log);
if(err != CHIAKI_ERR_SUCCESS)
throw Exception(chiaki_error_string(err));
this->session_init = true;
// audio setting_cb and frame_cb
chiaki_opus_decoder_set_cb(&this->opus_decoder, InitAudioCB, AudioCB, user);
chiaki_opus_decoder_get_sink(&this->opus_decoder, &audio_sink);
chiaki_session_set_audio_sink(&(this->session), &audio_sink);
chiaki_session_set_video_sample_cb(&(this->session), VideoCB, user);
chiaki_session_set_event_cb(&(this->session), EventCB, user);
return 0;
}
int Host::FiniSession()
{
if(this->session_init)
{
chiaki_session_join(&this->session);
chiaki_session_fini(&this->session);
chiaki_opus_decoder_fini(&this->opus_decoder);
}
return 0;
}
void Host::StopSession()
{
chiaki_session_stop(&this->session);
}
void Host::StartSession()
{
ChiakiErrorCode err = chiaki_session_start(&this->session);
if(err != CHIAKI_ERR_SUCCESS)
{
chiaki_session_fini(&this->session);
throw Exception("Chiaki Session Start failed");
}
}
void Host::SendFeedbackState(ChiakiControllerState * state)
{
// send controller/joystick key
chiaki_session_set_controller_state(&this->session, state);
}
void Host::RegistCB(ChiakiRegistEvent * event)
{
// Chiaki callback fuction
// fuction called by lib chiaki regist
// durring client pin code registration
//
// read data from lib and record secrets into Host object
this->registered = false;
switch(event->type)
{
case CHIAKI_REGIST_EVENT_TYPE_FINISHED_CANCELED:
CHIAKI_LOGI(this->log, "Register event CHIAKI_REGIST_EVENT_TYPE_FINISHED_CANCELED");
if(this->chiaki_regist_event_type_finished_canceled != nullptr)
{
this->chiaki_regist_event_type_finished_canceled();
}
break;
case CHIAKI_REGIST_EVENT_TYPE_FINISHED_FAILED:
CHIAKI_LOGI(this->log, "Register event CHIAKI_REGIST_EVENT_TYPE_FINISHED_FAILED");
if(this->chiaki_regist_event_type_finished_failed != nullptr)
{
this->chiaki_regist_event_type_finished_failed();
}
break;
case CHIAKI_REGIST_EVENT_TYPE_FINISHED_SUCCESS:
{
ChiakiRegisteredHost *r_host = event->registered_host;
CHIAKI_LOGI(this->log, "Register event CHIAKI_REGIST_EVENT_TYPE_FINISHED_SUCCESS");
// copy values form ChiakiRegisteredHost object
this->ap_ssid = r_host->ap_ssid;
this->ap_key = r_host->ap_key;
this->ap_name = r_host->ap_name;
memcpy( &(this->ps4_mac), &(r_host->ps4_mac), sizeof(this->ps4_mac) );
this->ps4_nickname = r_host->ps4_nickname;
memcpy( &(this->rp_regist_key), &(r_host->rp_regist_key), sizeof(this->rp_regist_key) );
this->rp_key_type = r_host->rp_key_type;
memcpy( &(this->rp_key), &(r_host->rp_key), sizeof(this->rp_key) );
// mark host as registered
this->registered = true;
this->rp_key_data = true;
CHIAKI_LOGI(this->log, "Register Success %s", this->host_name.c_str());
if(this->chiaki_regist_event_type_finished_success != nullptr)
this->chiaki_regist_event_type_finished_success();
break;
}
}
// close registration socket
chiaki_regist_stop(&this->regist);
chiaki_regist_fini(&this->regist);
}
bool Host::GetVideoResolution(int * ret_width, int * ret_height)
{
switch(this->video_resolution)
{
case CHIAKI_VIDEO_RESOLUTION_PRESET_360p:
*ret_width = 640;
*ret_height = 360;
break;
case CHIAKI_VIDEO_RESOLUTION_PRESET_540p:
*ret_width = 950;
*ret_height = 540;
break;
case CHIAKI_VIDEO_RESOLUTION_PRESET_720p:
*ret_width = 1280;
*ret_height = 720;
break;
case CHIAKI_VIDEO_RESOLUTION_PRESET_1080p:
*ret_width = 1920;
*ret_height = 1080;
break;
default:
return false;
}
return true;
}

797
switch/src/io.cpp Normal file
View file

@ -0,0 +1,797 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef __SWITCH__
#include <switch.h>
#else
#include <iostream>
#endif
#include "io.h"
// https://github.com/matlo/GIMX/blob/3af491c3b6a89c6a76c9831f1f022a1b73a00752/shared/gimxcontroller/include/ds4.h#L112
#define DS4_TRACKPAD_MAX_X 1919
#define DS4_TRACKPAD_MAX_Y 919
#define SWITCH_TOUCHSCREEN_MAX_X 1280
#define SWITCH_TOUCHSCREEN_MAX_Y 720
// source:
// https://github.com/thestr4ng3r/chiaki/blob/master/gui/src/avopenglwidget.cpp
//
// examples :
// https://www.roxlu.com/2014/039/decoding-h264-and-yuv420p-playback
// https://gist.github.com/roxlu/9329339
// use OpenGl to decode YUV
// the aim is to spare CPU load on nintendo switch
static const char* shader_vert_glsl = R"glsl(
#version 150 core
in vec2 pos_attr;
out vec2 uv_var;
void main()
{
uv_var = pos_attr;
gl_Position = vec4(pos_attr * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.0, 1.0);
}
)glsl";
static const char *yuv420p_shader_frag_glsl = R"glsl(
#version 150 core
uniform sampler2D plane1; // Y
uniform sampler2D plane2; // U
uniform sampler2D plane3; // V
in vec2 uv_var;
out vec4 out_color;
void main()
{
vec3 yuv = vec3(
(texture(plane1, uv_var).r - (16.0 / 255.0)) / ((235.0 - 16.0) / 255.0),
(texture(plane2, uv_var).r - (16.0 / 255.0)) / ((240.0 - 16.0) / 255.0) - 0.5,
(texture(plane3, uv_var).r - (16.0 / 255.0)) / ((240.0 - 16.0) / 255.0) - 0.5);
vec3 rgb = mat3(
1.0, 1.0, 1.0,
0.0, -0.21482, 2.12798,
1.28033, -0.38059, 0.0) * yuv;
out_color = vec4(rgb, 1.0);
}
)glsl";
static const float vert_pos[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
IO::IO(ChiakiLog * log)
: log(log)
{
}
IO::~IO()
{
//FreeJoystick();
if(this->sdl_audio_device_id <= 0)
{
SDL_CloseAudioDevice(this->sdl_audio_device_id);
}
FreeVideo();
}
void IO::SetMesaConfig()
{
//TRACE("%s", "Mesaconfig");
//setenv("MESA_GL_VERSION_OVERRIDE", "3.3", 1);
//setenv("MESA_GLSL_VERSION_OVERRIDE", "330", 1);
// Uncomment below to disable error checking and save CPU time (useful for production):
//setenv("MESA_NO_ERROR", "1", 1);
#ifdef DEBUG_OPENGL
// Uncomment below to enable Mesa logging:
setenv("EGL_LOG_LEVEL", "debug", 1);
setenv("MESA_VERBOSE", "all", 1);
setenv("NOUVEAU_MESA_DEBUG", "1", 1);
// Uncomment below to enable shader debugging in Nouveau:
//setenv("NV50_PROG_OPTIMIZE", "0", 1);
setenv("NV50_PROG_DEBUG", "1", 1);
//setenv("NV50_PROG_CHIPSET", "0x120", 1);
#endif
}
#ifdef DEBUG_OPENGL
#define D(x){ (x); CheckGLError(__func__, __FILE__, __LINE__); }
void IO::CheckGLError(const char* func, const char* file, int line)
{
GLenum err;
while( (err = glGetError()) != GL_NO_ERROR )
{
CHIAKI_LOGE(this->log, "glGetError: %x function: %s from %s line %d", err, func, file, line);
//GL_INVALID_VALUE, 0x0501
// Given when a value parameter is not a legal value for that function. T
// his is only given for local problems;
// if the spec allows the value in certain circumstances,
// where other parameters or state dictate those circumstances,
// then GL_INVALID_OPERATION is the result instead.
}
}
#define DS(x){ DumpShaderError(x, __func__, __FILE__, __LINE__); }
void IO::DumpShaderError(GLuint shader, const char* func, const char* file, int line)
{
GLchar str[512+1];
GLsizei len = 0;
glGetShaderInfoLog(shader, 512, &len, str);
if (len > 512) len = 512;
str[len] = '\0';
CHIAKI_LOGE(this->log, "glGetShaderInfoLog: %s function: %s from %s line %d", str, func, file, line);
}
#define DP(x){ DumpProgramError(x, __func__, __FILE__, __LINE__); }
void IO::DumpProgramError(GLuint prog, const char* func, const char* file, int line)
{
GLchar str[512+1];
GLsizei len = 0;
glGetProgramInfoLog(prog, 512, &len, str);
if (len > 512) len = 512;
str[len] = '\0';
CHIAKI_LOGE(this->log, "glGetProgramInfoLog: %s function: %s from %s line %d", str, func, file, line);
}
#else
// do nothing
#define D(x){ (x); }
#define DS(x){ }
#define DP(x){ }
#endif
bool IO::VideoCB(uint8_t * buf, size_t buf_size)
{
// callback function to decode video buffer
AVPacket packet;
av_init_packet(&packet);
packet.data = buf;
packet.size = buf_size;
AVFrame * frame = av_frame_alloc();
if(!frame)
{
CHIAKI_LOGE(this->log, "UpdateFrame Failed to alloc AVFrame");
av_packet_unref(&packet);
return false;
}
send_packet:
// Push
int r = avcodec_send_packet(this->codec_context, &packet);
if(r != 0)
{
if(r == AVERROR(EAGAIN))
{
CHIAKI_LOGE(this->log, "AVCodec internal buffer is full removing frames before pushing");
r = avcodec_receive_frame(this->codec_context, frame);
// send decoded frame for sdl texture update
if(r != 0)
{
CHIAKI_LOGE(this->log, "Failed to pull frame");
av_frame_free(&frame);
av_packet_unref(&packet);
return false;
}
goto send_packet;
}
else
{
char errbuf[128];
av_make_error_string(errbuf, sizeof(errbuf), r);
CHIAKI_LOGE(this->log, "Failed to push frame: %s", errbuf);
av_frame_free(&frame);
av_packet_unref(&packet);
return false;
}
}
this->mtx.lock();
// Pull
r = avcodec_receive_frame(this->codec_context, this->frame);
this->mtx.unlock();
if(r != 0)
CHIAKI_LOGE(this->log, "Failed to pull frame");
av_frame_free(&frame);
av_packet_unref(&packet);
return true;
}
void IO::InitAudioCB(unsigned int channels, unsigned int rate)
{
SDL_AudioSpec want, have, test;
SDL_memset(&want, 0, sizeof(want));
//source
//[I] Audio Header:
//[I] channels = 2
//[I] bits = 16
//[I] rate = 48000
//[I] frame size = 480
//[I] unknown = 1
want.freq = rate;
want.format = AUDIO_S16SYS;
// 2 == stereo
want.channels = channels;
want.samples = 1024;
want.callback = NULL;
if(this->sdl_audio_device_id <= 0)
{
// the chiaki session might be called many times
// open the audio device only once
this->sdl_audio_device_id = SDL_OpenAudioDevice(NULL, 0, &want, NULL, 0);
}
if(this->sdl_audio_device_id <= 0)
{
CHIAKI_LOGE(this->log, "SDL_OpenAudioDevice failed: %s\n", SDL_GetError());
}
else
{
SDL_PauseAudioDevice(this->sdl_audio_device_id, 0);
}
}
void IO::AudioCB(int16_t * buf, size_t samples_count)
{
//int az = SDL_GetQueuedAudioSize(host->audio_device_id);
// len the number of bytes (not samples!) to which (data) points
int success = SDL_QueueAudio(this->sdl_audio_device_id, buf, sizeof(int16_t)*samples_count*2);
if(success != 0)
CHIAKI_LOGE(this->log, "SDL_QueueAudio failed: %s\n", SDL_GetError());
}
bool IO::InitVideo(int video_width, int video_height, int screen_width, int screen_height)
{
CHIAKI_LOGV(this->log, "load InitVideo");
this->video_width = video_width;
this->video_height = video_height;
this->screen_width = screen_width;
this->screen_height = screen_height;
this->frame = av_frame_alloc();
if(!InitAVCodec())
{
throw Exception("Failed to initiate libav codec");
}
if(!InitOpenGl())
{
throw Exception("Failed to initiate OpenGl");
}
return true;
}
void IO::EventCB(ChiakiEvent *event)
{
switch(event->type)
{
case CHIAKI_EVENT_CONNECTED:
CHIAKI_LOGI(this->log, "EventCB CHIAKI_EVENT_CONNECTED");
if(this->chiaki_event_connected_cb != nullptr)
this->quit = !this->chiaki_event_connected_cb();
else
this->quit = false;
break;
case CHIAKI_EVENT_LOGIN_PIN_REQUEST:
CHIAKI_LOGI(this->log, "EventCB CHIAKI_EVENT_LOGIN_PIN_REQUEST");
if(this->chiaki_even_login_pin_request_cb != nullptr)
this->quit = !this->chiaki_even_login_pin_request_cb(event->login_pin_request.pin_incorrect);
break;
case CHIAKI_EVENT_QUIT:
CHIAKI_LOGI(this->log, "EventCB CHIAKI_EVENT_QUIT");
if(this->chiaki_event_quit_cb != nullptr)
this->quit = !this->chiaki_event_quit_cb(&event->quit);
else
this->quit = true;
break;
}
}
bool IO::FreeVideo()
{
bool ret = true;
if(this->frame)
av_frame_free(&this->frame);
// avcodec_alloc_context3(codec);
if(this->codec_context)
{
avcodec_close(this->codec_context);
avcodec_free_context(&this->codec_context);
}
return ret;
}
bool IO::ReadUserKeyboard(char *buffer, size_t buffer_size)
{
#ifdef CHIAKI_SWITCH_ENABLE_LINUX
// use cin to get user input from linux
std::cin.getline(buffer, buffer_size);
CHIAKI_LOGI(this->log, "Got user input: %s\n", buffer);
#else
// https://kvadevack.se/post/nintendo-switch-virtual-keyboard/
SwkbdConfig kbd;
Result rc = swkbdCreate(&kbd, 0);
if (R_SUCCEEDED(rc))
{
swkbdConfigMakePresetDefault(&kbd);
rc = swkbdShow(&kbd, buffer, buffer_size);
if (R_SUCCEEDED(rc))
{
CHIAKI_LOGI(this->log, "Got user input: %s\n", buffer);
}
else
{
CHIAKI_LOGE(this->log, "swkbdShow() error: %u\n", rc);
return false;
}
swkbdClose(&kbd);
}
else
{
CHIAKI_LOGE(this->log, "swkbdCreate() error: %u\n", rc);
return false;
}
#endif
return true;
}
bool IO::ReadGameTouchScreen(ChiakiControllerState *state)
{
#ifdef __SWITCH__
hidScanInput();
int touch_count = hidTouchCount();
bool ret = false;
if(!touch_count)
{
for(int i=0; i < CHIAKI_CONTROLLER_TOUCHES_MAX; i++)
{
if(state->touches[i].id != -1)
{
state->touches[i].x = 0;
state->touches[i].y = 0;
state->touches[i].id = -1;
state->buttons &= ~CHIAKI_CONTROLLER_BUTTON_TOUCHPAD; // touchscreen release
// the state changed
ret = true;
}
}
return ret;
}
touchPosition touch;
for(int i=0; i < touch_count && i < CHIAKI_CONTROLLER_TOUCHES_MAX; i++)
{
hidTouchRead(&touch, i);
// 1280×720 px (16:9)
// ps4 controller aspect ratio looks closer to 29:10
uint16_t x = touch.px * (DS4_TRACKPAD_MAX_X / SWITCH_TOUCHSCREEN_MAX_X);
uint16_t y = touch.py * (DS4_TRACKPAD_MAX_Y / SWITCH_TOUCHSCREEN_MAX_Y);
// use nintendo switch border's 5% to
if(x <= (SWITCH_TOUCHSCREEN_MAX_X * 0.05) || x >= (SWITCH_TOUCHSCREEN_MAX_X * 0.95)
|| y <= (SWITCH_TOUCHSCREEN_MAX_Y * 0.05) || y >= (SWITCH_TOUCHSCREEN_MAX_Y * 0.95))
{
state->buttons |= CHIAKI_CONTROLLER_BUTTON_TOUCHPAD; // touchscreen
// printf("CHIAKI_CONTROLLER_BUTTON_TOUCHPAD\n");
}
else
{
state->buttons &= ~CHIAKI_CONTROLLER_BUTTON_TOUCHPAD; // touchscreen release
}
state->touches[i].x = x;
state->touches[i].y = y;
state->touches[i].id = i;
// printf("[point_id=%d] px=%03d, py=%03d, dx=%03d, dy=%03d, angle=%03d\n",
// i, touch.px, touch.py, touch.dx, touch.dy, touch.angle);
ret = true;
}
return ret;
#else
return false;
#endif
}
bool IO::ReadGameKeys(SDL_Event *event, ChiakiControllerState *state)
{
// return true if an event changed (gamepad input)
// TODO
// share vs PS button
// Gyro ?
// rumble ?
bool ret = true;
switch(event->type)
{
case SDL_JOYAXISMOTION:
if(event->jaxis.which == 0)
{
// left joystick
if(event->jaxis.axis == 0)
// Left-right movement
state->left_x = event->jaxis.value;
else if(event->jaxis.axis == 1)
// Up-Down movement
state->left_y = event->jaxis.value;
else if(event->jaxis.axis == 2)
// Left-right movement
state->right_x = event->jaxis.value;
else if(event->jaxis.axis == 3)
// Up-Down movement
state->right_y = event->jaxis.value;
else
ret = false;
}
else if (event->jaxis.which == 1)
{
// right joystick
if(event->jaxis.axis == 0)
// Left-right movement
state->right_x = event->jaxis.value;
else if(event->jaxis.axis == 1)
// Up-Down movement
state->right_y = event->jaxis.value;
else
ret = false;
}
else
ret = false;
break;
case SDL_JOYBUTTONDOWN:
// printf("Joystick %d button %d DOWN\n",
// event->jbutton.which, event->jbutton.button);
switch(event->jbutton.button)
{
case 0: state->buttons |= CHIAKI_CONTROLLER_BUTTON_MOON; break; // KEY_A
case 1: state->buttons |= CHIAKI_CONTROLLER_BUTTON_CROSS; break; // KEY_B
case 2: state->buttons |= CHIAKI_CONTROLLER_BUTTON_PYRAMID; break; // KEY_X
case 3: state->buttons |= CHIAKI_CONTROLLER_BUTTON_BOX; break; // KEY_Y
case 12: state->buttons |= CHIAKI_CONTROLLER_BUTTON_DPAD_LEFT; break; // KEY_DLEFT
case 14: state->buttons |= CHIAKI_CONTROLLER_BUTTON_DPAD_RIGHT; break; // KEY_DRIGHT
case 13: state->buttons |= CHIAKI_CONTROLLER_BUTTON_DPAD_UP; break; // KEY_DUP
case 15: state->buttons |= CHIAKI_CONTROLLER_BUTTON_DPAD_DOWN; break; // KEY_DDOWN
case 6: state->buttons |= CHIAKI_CONTROLLER_BUTTON_L1; break; // KEY_L
case 7: state->buttons |= CHIAKI_CONTROLLER_BUTTON_R1; break; // KEY_R
case 8: state->l2_state = 0xff; break; // KEY_ZL
case 9: state->r2_state = 0xff; break; // KEY_ZR
case 4: state->buttons |= CHIAKI_CONTROLLER_BUTTON_L3; break; // KEY_LSTICK
case 5: state->buttons |= CHIAKI_CONTROLLER_BUTTON_R3; break; // KEY_RSTICK
case 10: state->buttons |= CHIAKI_CONTROLLER_BUTTON_OPTIONS; break; // KEY_PLUS
// FIXME
// case 11: state->buttons |= CHIAKI_CONTROLLER_BUTTON_SHARE; break; // KEY_MINUS
case 11: state->buttons |= CHIAKI_CONTROLLER_BUTTON_PS; break; // KEY_MINUS
default:
ret = false;
}
break;
case SDL_JOYBUTTONUP:
// printf("Joystick %d button %d UP\n",
// event->jbutton.which, event->jbutton.button);
switch(event->jbutton.button)
{
case 0: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_MOON; break; // KEY_A
case 1: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_CROSS; break; // KEY_B
case 2: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_PYRAMID; break; // KEY_X
case 3: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_BOX; break; // KEY_Y
case 12: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_DPAD_LEFT; break; // KEY_DLEFT
case 14: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_DPAD_RIGHT; break; // KEY_DRIGHT
case 13: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_DPAD_UP; break; // KEY_DUP
case 15: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_DPAD_DOWN; break; // KEY_DDOWN
case 6: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_L1; break; // KEY_L
case 7: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_R1; break; // KEY_R
case 8: state->l2_state = 0x00; break; // KEY_ZL
case 9: state->r2_state = 0x00; break; // KEY_ZR
case 4: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_L3; break; // KEY_LSTICK
case 5: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_R3; break; // KEY_RSTICK
case 10: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_OPTIONS; break; // KEY_PLUS
//case 11: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_SHARE; break; // KEY_MINUS
case 11: state->buttons ^= CHIAKI_CONTROLLER_BUTTON_PS; break; // KEY_MINUS
default:
ret = false;
}
break;
default:
ret = false;
}
return ret;
}
bool IO::InitAVCodec()
{
CHIAKI_LOGV(this->log, "loading AVCodec");
// set libav video context
this->codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if(!this->codec)
throw Exception("H264 Codec not available");
this->codec_context = avcodec_alloc_context3(codec);
if(!this->codec_context)
throw Exception("Failed to alloc codec context");
// use rock88's mooxlight-nx optimization
// https://github.com/rock88/moonlight-nx/blob/698d138b9fdd4e483c998254484ccfb4ec829e95/src/streaming/ffmpeg/FFmpegVideoDecoder.cpp#L63
// this->codec_context->skip_loop_filter = AVDISCARD_ALL;
this->codec_context->flags |= AV_CODEC_FLAG_LOW_DELAY;
this->codec_context->flags2 |= AV_CODEC_FLAG2_FAST;
// this->codec_context->flags2 |= AV_CODEC_FLAG2_CHUNKS;
this->codec_context->thread_type = FF_THREAD_SLICE;
this->codec_context->thread_count = 4;
if(avcodec_open2(this->codec_context, this->codec, nullptr) < 0)
{
avcodec_free_context(&this->codec_context);
throw Exception("Failed to open codec context");
}
return true;
}
bool IO::InitOpenGl()
{
CHIAKI_LOGV(this->log, "loading OpenGL");
if(!InitOpenGlShader())
return false;
if(!InitOpenGlTextures())
return false;
return true;
}
bool IO::InitOpenGlTextures()
{
CHIAKI_LOGV(this->log, "loading OpenGL textrures");
D(glGenTextures(PLANES_COUNT, this->tex));
D(glGenBuffers(PLANES_COUNT, this->pbo));
uint8_t uv_default[] = {0x7f, 0x7f};
for(int i=0; i < PLANES_COUNT; i++)
{
D(glBindTexture(GL_TEXTURE_2D, this->tex[i]));
D(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
D(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
D(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
D(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
D(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, i > 0 ? uv_default : nullptr));
}
D(glUseProgram(this->prog));
// bind only as many planes as we need
const char *plane_names[] = {"plane1", "plane2", "plane3"};
for(int i=0; i < PLANES_COUNT; i++)
D(glUniform1i(glGetUniformLocation(this->prog, plane_names[i]), i));
D(glGenVertexArrays(1, &this->vao));
D(glBindVertexArray(this->vao));
D(glGenBuffers(1, &this->vbo));
D(glBindBuffer(GL_ARRAY_BUFFER, this->vbo));
D(glBufferData(GL_ARRAY_BUFFER, sizeof(vert_pos), vert_pos, GL_STATIC_DRAW));
D(glBindBuffer(GL_ARRAY_BUFFER, this->vbo));
D(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
D(glEnableVertexAttribArray(0));
D(glCullFace(GL_BACK));
D(glEnable(GL_CULL_FACE));
D(glClearColor(0.5, 0.5, 0.5, 1.0));
return true;
}
GLuint IO::CreateAndCompileShader(GLenum type, const char* source)
{
GLint success;
GLchar msg[512];
GLuint handle;
D(handle = glCreateShader(type));
if (!handle)
{
CHIAKI_LOGE(this->log, "%u: cannot create shader", type);
DP(this->prog);
}
D(glShaderSource(handle, 1, &source, nullptr));
D(glCompileShader(handle));
D(glGetShaderiv(handle, GL_COMPILE_STATUS, &success));
if (!success)
{
D(glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg));
CHIAKI_LOGE(this->log, "%u: %s\n", type, msg);
D(glDeleteShader(handle));
}
return handle;
}
bool IO::InitOpenGlShader()
{
CHIAKI_LOGV(this->log, "loading OpenGl Shaders");
D(this->vert = CreateAndCompileShader(GL_VERTEX_SHADER, shader_vert_glsl));
D(this->frag = CreateAndCompileShader(GL_FRAGMENT_SHADER, yuv420p_shader_frag_glsl));
D(this->prog = glCreateProgram());
D(glAttachShader(this->prog, this->vert));
D(glAttachShader(this->prog, this->frag));
D(glBindAttribLocation(this->prog, 0, "pos_attr"));
D(glLinkProgram(this->prog));
GLint success;
D(glGetProgramiv(this->prog, GL_LINK_STATUS, &success));
if (!success)
{
char buf[512];
glGetProgramInfoLog(this->prog, sizeof(buf), nullptr, buf);
CHIAKI_LOGE(this->log, "OpenGL link error: %s", buf);
return false;
}
D(glDeleteShader(this->vert));
D(glDeleteShader(this->frag));
return true;
}
inline void IO::SetOpenGlYUVPixels(AVFrame * frame)
{
D(glUseProgram(this->prog));
int planes[][3] = {
// { width_divide, height_divider, data_per_pixel }
{ 1, 1, 1 }, // Y
{ 2, 2, 1 }, // U
{ 2, 2, 1 } // V
};
this->mtx.lock();
for(int i = 0; i < PLANES_COUNT; i++)
{
int width = frame->width / planes[i][0];
int height = frame->height / planes[i][1];
int size = width * height * planes[i][2];
uint8_t * buf;
D(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->pbo[i]));
D(glBufferData(GL_PIXEL_UNPACK_BUFFER, size, nullptr, GL_STREAM_DRAW));
D(buf = reinterpret_cast<uint8_t *>(glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)));
if(!buf)
{
GLint data;
D(glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &data));
CHIAKI_LOGE(this->log, "AVOpenGLFrame failed to map PBO");
CHIAKI_LOGE(this->log, "Info buf == %p. size %d frame %d * %d, divs %d, %d, pbo %d GL_BUFFER_SIZE %x",
buf, size, frame->width, frame->height, planes[i][0], planes[i][1], pbo[i], data);
continue;
}
if(frame->linesize[i] == width)
{
// Y
memcpy(buf, frame->data[i], size);
}
else
{
// UV
for(int l=0; l<height; l++)
memcpy(buf + width * l * planes[i][2],
frame->data[i] + frame->linesize[i] * l,
width * planes[i][2]);
}
D(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
D(glBindTexture(GL_TEXTURE_2D, tex[i]));
D(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr));
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
this->mtx.unlock();
glFinish();
}
inline void IO::OpenGlDraw()
{
glClear(GL_COLOR_BUFFER_BIT);
// send to OpenGl
SetOpenGlYUVPixels(this->frame);
//avcodec_flush_buffers(this->codec_context);
D(glBindVertexArray(this->vao));
for(int i=0; i< PLANES_COUNT; i++)
{
D(glActiveTexture(GL_TEXTURE0 + i));
D(glBindTexture(GL_TEXTURE_2D, this->tex[i]));
}
D(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
D(glBindVertexArray(0));
D(glFinish());
}
bool IO::InitJoystick()
{
// https://github.com/switchbrew/switch-examples/blob/master/graphics/sdl2/sdl2-simple/source/main.cpp#L57
// open CONTROLLER_PLAYER_1 and CONTROLLER_PLAYER_2
// when railed, both joycons are mapped to joystick #0,
// else joycons are individually mapped to joystick #0, joystick #1, ...
for (int i = 0; i < SDL_JOYSTICK_COUNT; i++)
{
this->sdl_joystick_ptr[i] = SDL_JoystickOpen(i);
if (sdl_joystick_ptr[i] == nullptr)
{
CHIAKI_LOGE(this->log, "SDL_JoystickOpen: %s\n", SDL_GetError());
return false;
}
}
return true;
}
bool IO::FreeJoystick()
{
for (int i = 0; i < SDL_JOYSTICK_COUNT; i++)
{
if(SDL_JoystickGetAttached(sdl_joystick_ptr[i]))
SDL_JoystickClose(sdl_joystick_ptr[i]);
}
return true;
}
bool IO::MainLoop(ChiakiControllerState * state)
{
D(glUseProgram(this->prog));
// handle SDL events
while(SDL_PollEvent(&this->sdl_event))
{
this->ReadGameKeys(&this->sdl_event, state);
switch(this->sdl_event.type)
{
case SDL_QUIT:
return false;
}
}
ReadGameTouchScreen(state);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
OpenGlDraw();
return !this->quit;
}

175
switch/src/main.cpp Normal file
View file

@ -0,0 +1,175 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
// chiaki modules
#include <chiaki/log.h>
#include <chiaki/discovery.h>
// discover and wakeup ps4 host
// from local network
#include "discoverymanager.h"
#include "settings.h"
#include "io.h"
#include "gui.h"
#ifdef __SWITCH__
#include <switch.h>
#else
bool appletMainLoop()
{
return true;
}
#endif
#ifndef CHIAKI_SWITCH_ENABLE_LINUX
#define CHIAKI_ENABLE_SWITCH_NXLINK 1
#endif
#ifdef __SWITCH__
// use a custom nintendo switch socket config
// chiaki requiers many threads with udp/tcp sockets
static const SocketInitConfig g_chiakiSocketInitConfig = {
.bsdsockets_version = 1,
.tcp_tx_buf_size = 0x8000,
.tcp_rx_buf_size = 0x10000,
.tcp_tx_buf_max_size = 0x40000,
.tcp_rx_buf_max_size = 0x40000,
.udp_tx_buf_size = 0x40000,
.udp_rx_buf_size = 0x40000,
.sb_efficiency = 8,
.num_bsd_sessions = 16,
.bsd_service_type = BsdServiceType_User,
};
#endif // __SWITCH__
#ifdef CHIAKI_ENABLE_SWITCH_NXLINK
static int s_nxlinkSock = -1;
static void initNxLink()
{
// use chiaki socket config initialization
if (R_FAILED(socketInitialize(&g_chiakiSocketInitConfig)))
return;
s_nxlinkSock = nxlinkStdio();
if (s_nxlinkSock >= 0)
printf("initNxLink");
else
socketExit();
}
static void deinitNxLink()
{
if (s_nxlinkSock >= 0)
{
close(s_nxlinkSock);
s_nxlinkSock = -1;
}
}
#endif // CHIAKI_ENABLE_SWITCH_NXLINK
#ifdef __SWITCH__
extern "C" void userAppInit()
{
#ifdef CHIAKI_ENABLE_SWITCH_NXLINK
initNxLink();
#endif
// to load gui resources
romfsInit();
plInitialize(PlServiceType_User);
// load socket custom config
socketInitialize(&g_chiakiSocketInitConfig);
setsysInitialize();
}
extern "C" void userAppExit()
{
#ifdef CHIAKI_ENABLE_SWITCH_NXLINK
deinitNxLink();
#endif // CHIAKI_ENABLE_SWITCH_NXLINK
socketExit();
/* Cleanup tesla required services. */
hidsysExit();
pmdmntExit();
plExit();
/* Cleanup default services. */
fsExit();
hidExit();
appletExit();
setsysExit();
smExit();
}
#endif // __SWITCH__
int main(int argc, char* argv[])
{
// init chiaki lib
ChiakiLog log;
#if defined(CHIAKI_ENABLE_SWITCH_NXLINK) || defined(CHIAKI_SWITCH_ENABLE_LINUX)
#ifdef __SWITCH__
chiaki_log_init(&log, CHIAKI_LOG_ALL ^ CHIAKI_LOG_VERBOSE, chiaki_log_cb_print, NULL);
#else
chiaki_log_init(&log, CHIAKI_LOG_ALL, chiaki_log_cb_print, NULL);
#endif
#else
// null log for switch version
chiaki_log_init(&log, 0, chiaki_log_cb_print, NULL);
#endif
// load chiaki lib
CHIAKI_LOGI(&log, "Loading chaki lib");
ChiakiErrorCode err = chiaki_lib_init();
if(err != CHIAKI_ERR_SUCCESS)
{
CHIAKI_LOGE(&log, "Chiaki lib init failed: %s\n", chiaki_error_string(err));
return 1;
}
CHIAKI_LOGI(&log, "Loading SDL audio / joystick");
if(SDL_Init( SDL_INIT_AUDIO | SDL_INIT_JOYSTICK ))
{
CHIAKI_LOGE(&log, "SDL initialization failed: %s", SDL_GetError());
return 1;
}
// build sdl OpenGl and AV decoders graphical interface
IO io = IO(&log); // open Input Output class
// manage ps4 setting discovery wakeup and registration
std::map<std::string, Host> hosts;
// create host objects form config file
Settings settings = Settings(&log, &hosts);
CHIAKI_LOGI(&log, "Read chiaki settings file");
// FIXME use GUI for config
settings.ParseFile();
Host * host = nullptr;
DiscoveryManager discoverymanager = DiscoveryManager(&settings);
MainApplication app = MainApplication(&hosts, &settings, &discoverymanager, &io, &log);
app.Load();
CHIAKI_LOGI(&log, "Quit applet");
SDL_Quit();
return 0;
}

561
switch/src/settings.cpp Normal file
View file

@ -0,0 +1,561 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <chiaki/base64.h>
#include "settings.h"
Host * Settings::GetOrCreateHost(std::string *host_name)
{
bool created = false;
// update of create Host instance
if(this->hosts->find(*host_name) == hosts->end())
{
// create host if udefined
Host h = Host(this->log, this, *host_name);
this->hosts->emplace( *host_name, h);
created = true;
}
Host *host = &(this->hosts->at(*host_name));
if(created)
{
// copy default settings
// to the newly created host
this->SetPSNOnlineID(host, this->global_psn_online_id);
this->SetPSNAccountID(host, this->global_psn_account_id);
this->SetVideoResolution(host, this->global_video_resolution);
this->SetVideoFPS(host, this->global_video_fps);
}
return host;
}
Settings::ConfigurationItem Settings::ParseLine(std::string *line, std::string *value)
{
Settings::ConfigurationItem ci;
std::smatch m;
for(auto it = re_map.begin(); it != re_map.end(); it++)
{
if(regex_search(*line, m, it->second))
{
ci = it->first;
*value = m[1];
return ci;
}
}
return UNKNOWN;
}
ChiakiLog* Settings::GetLogger()
{
return this->log;
}
size_t Settings::GetB64encodeSize(size_t in)
{
// calculate base64 buffer size after encode
return ((4 * in / 3) + 3) & ~3;
}
std::string Settings::GetPSNOnlineID(Host * host)
{
if(host == nullptr || host->psn_online_id.length() == 0 )
return this->global_psn_online_id;
else
return host->psn_online_id;
}
std::string Settings::GetPSNAccountID(Host * host)
{
if(host == nullptr || host->psn_account_id.length() == 0 )
return this->global_psn_account_id;
else
return host->psn_account_id;
}
void Settings::SetPSNOnlineID(Host * host, std::string psn_online_id)
{
if(host == nullptr)
this->global_psn_online_id = psn_online_id;
else
host->psn_online_id = psn_online_id;
}
void Settings::SetPSNAccountID(Host * host, std::string psn_account_id)
{
if(host == nullptr)
this->global_psn_account_id = psn_account_id;
else
host->psn_account_id = psn_account_id;
}
std::string Settings::ResolutionPresetToString(ChiakiVideoResolutionPreset resolution)
{
switch(resolution)
{
case CHIAKI_VIDEO_RESOLUTION_PRESET_360p:
return "360p";
case CHIAKI_VIDEO_RESOLUTION_PRESET_540p:
return "540p";
case CHIAKI_VIDEO_RESOLUTION_PRESET_720p:
return "720p";
case CHIAKI_VIDEO_RESOLUTION_PRESET_1080p:
return "1080p";
}
return "UNKNOWN";
}
std::string Settings::FPSPresetToString(ChiakiVideoFPSPreset fps)
{
switch(fps)
{
case CHIAKI_VIDEO_FPS_PRESET_30:
return "30";
case CHIAKI_VIDEO_FPS_PRESET_60:
return "60";
}
return "UNKNOWN";
}
ChiakiVideoResolutionPreset Settings::StringToResolutionPreset(std::string value)
{
if (value.compare("1080p") == 0)
return CHIAKI_VIDEO_RESOLUTION_PRESET_1080p;
else if (value.compare("720p") == 0)
return CHIAKI_VIDEO_RESOLUTION_PRESET_720p;
else if (value.compare("540p") == 0)
return CHIAKI_VIDEO_RESOLUTION_PRESET_540p;
else if (value.compare("360p") == 0)
return CHIAKI_VIDEO_RESOLUTION_PRESET_360p;
// default
CHIAKI_LOGE(this->log, "Unable to parse String resolution: %s",
value.c_str());
return CHIAKI_VIDEO_RESOLUTION_PRESET_720p;
}
ChiakiVideoFPSPreset Settings::StringToFPSPreset(std::string value)
{
if (value.compare("60") == 0)
return CHIAKI_VIDEO_FPS_PRESET_60;
else if (value.compare("30") == 0)
return CHIAKI_VIDEO_FPS_PRESET_30;
// default
CHIAKI_LOGE(this->log, "Unable to parse String fps: %s",
value.c_str());
return CHIAKI_VIDEO_FPS_PRESET_30;
}
int Settings::FPSPresetToInt(ChiakiVideoFPSPreset fps)
{
switch(fps)
{
case CHIAKI_VIDEO_FPS_PRESET_30:
return 30;
case CHIAKI_VIDEO_FPS_PRESET_60:
return 60;
}
return 0;
}
int Settings::ResolutionPresetToInt(ChiakiVideoResolutionPreset resolution)
{
switch(resolution)
{
case CHIAKI_VIDEO_RESOLUTION_PRESET_360p:
return 360;
case CHIAKI_VIDEO_RESOLUTION_PRESET_540p:
return 540;
case CHIAKI_VIDEO_RESOLUTION_PRESET_720p:
return 720;
case CHIAKI_VIDEO_RESOLUTION_PRESET_1080p:
return 1080;
}
return 0;
}
ChiakiVideoResolutionPreset Settings::GetVideoResolution(Host * host)
{
if(host == nullptr)
return this->global_video_resolution;
else
return host->video_resolution;
}
ChiakiVideoFPSPreset Settings::GetVideoFPS(Host * host)
{
if(host == nullptr)
return this->global_video_fps;
else
return host->video_fps;
}
void Settings::SetVideoResolution(Host * host, ChiakiVideoResolutionPreset value)
{
if(host == nullptr)
this->global_video_resolution = value;
else
host->video_resolution = value;
}
void Settings::SetVideoResolution(Host * host, std::string value)
{
ChiakiVideoResolutionPreset p = StringToResolutionPreset(value);
this->SetVideoResolution(host, p);
}
void Settings::SetVideoFPS(Host * host, ChiakiVideoFPSPreset value)
{
if(host == nullptr)
this->global_video_fps = value;
else
host->video_fps = value;
}
void Settings::SetVideoFPS(Host * host, std::string value)
{
ChiakiVideoFPSPreset p = StringToFPSPreset(value);
this->SetVideoFPS(host, p);
}
#ifdef CHIAKI_ENABLE_SWITCH_OVERCLOCK
int Settings::GetCPUOverclock(Host * host)
{
if(host == nullptr)
return this->global_cpu_overclock;
else
return host->cpu_overclock;
}
void Settings::SetCPUOverclock(Host * host, int value)
{
int oc = OC_1326;
if(value > OC_1580)
// max OC
oc = OC_1785;
else if(OC_1580 >= value && value > OC_1326)
oc = OC_1580;
else if(OC_1326 >= value && value > OC_1220)
oc = OC_1326;
else if(OC_1220 >= value && value > OC_1020)
oc = OC_1220;
else if(OC_1020 >= value)
// no overclock
// default nintendo switch value
oc = OC_1020;
if(host == nullptr)
this->global_cpu_overclock = oc;
else
host->cpu_overclock = oc;
}
void Settings::SetCPUOverclock(Host * host, std::string value)
{
int v = atoi(value.c_str());
this->SetCPUOverclock(host, v);
}
#endif
std::string Settings::GetHostIPAddr(Host * host)
{
if(host != nullptr)
return host->host_addr;
else
CHIAKI_LOGE(this->log, "Cannot GetHostIPAddr from nullptr host");
return "";
}
std::string Settings::GetHostName(Host * host)
{
if(host != nullptr)
return host->host_name;
else
CHIAKI_LOGE(this->log, "Cannot GetHostName from nullptr host");
return "";
}
int Settings::GetHostRPKeyType(Host * host)
{
if(host != nullptr)
return host->rp_key_type;
CHIAKI_LOGE(this->log, "Cannot GetHostRPKeyType from nullptr host");
return 0;
}
bool Settings::SetHostRPKeyType(Host * host, std::string value)
{
if(host != nullptr)
{
// TODO Check possible rp_type values
host->rp_key_type = std::atoi(value.c_str());
return true;
}
return false;
}
std::string Settings::GetHostRPKey(Host * host)
{
if(host != nullptr)
{
if(host->rp_key_data || host->registered)
{
size_t rp_key_b64_sz = this->GetB64encodeSize(0x10);
char rp_key_b64[rp_key_b64_sz + 1] = {0};
ChiakiErrorCode err;
err = chiaki_base64_encode(
host->rp_key, 0x10,
rp_key_b64, sizeof(rp_key_b64));
if(CHIAKI_ERR_SUCCESS == err)
return rp_key_b64;
else
CHIAKI_LOGE(this->log, "Failed to encode rp_key to base64");
}
}
else
CHIAKI_LOGE(this->log, "Cannot GetHostRPKey from nullptr host");
return "";
}
std::string Settings::GetHostRPRegistKey(Host * host)
{
if(host != nullptr)
{
if(host->rp_key_data || host->registered)
{
size_t rp_regist_key_b64_sz = this->GetB64encodeSize(CHIAKI_SESSION_AUTH_SIZE);
char rp_regist_key_b64[rp_regist_key_b64_sz + 1] = {0};
ChiakiErrorCode err;
err = chiaki_base64_encode(
(uint8_t *) host->rp_regist_key, CHIAKI_SESSION_AUTH_SIZE,
rp_regist_key_b64, sizeof(rp_regist_key_b64));
if(CHIAKI_ERR_SUCCESS == err)
return rp_regist_key_b64;
else
CHIAKI_LOGE(this->log, "Failed to encode rp_regist_key to base64");
}
}
else
CHIAKI_LOGE(this->log, "Cannot GetHostRPRegistKey from nullptr host");
return "";
}
bool Settings::SetHostRPKey(Host * host, std::string rp_key_b64)
{
if(host != nullptr)
{
size_t rp_key_sz = sizeof(host->rp_key);
ChiakiErrorCode err = chiaki_base64_decode(
rp_key_b64.c_str(), rp_key_b64.length(),
host->rp_key, &rp_key_sz);
if(CHIAKI_ERR_SUCCESS != err)
CHIAKI_LOGE(this->log, "Failed to parse RP_KEY %s (it must be a base64 encoded)", rp_key_b64.c_str());
else
return true;
}
else
CHIAKI_LOGE(this->log, "Cannot SetHostRPKey from nullptr host");
return false;
}
bool Settings::SetHostRPRegistKey(Host * host, std::string rp_regist_key_b64)
{
if(host != nullptr)
{
size_t rp_regist_key_sz = sizeof(host->rp_regist_key);
ChiakiErrorCode err = chiaki_base64_decode(
rp_regist_key_b64.c_str(), rp_regist_key_b64.length(),
(uint8_t*) host->rp_regist_key, &rp_regist_key_sz);
if(CHIAKI_ERR_SUCCESS != err)
CHIAKI_LOGE(this->log, "Failed to parse RP_REGIST_KEY %s (it must be a base64 encoded)", rp_regist_key_b64.c_str());
else
return true;
}
else
CHIAKI_LOGE(this->log, "Cannot SetHostRPKey from nullptr host");
return false;
}
void Settings::ParseFile()
{
CHIAKI_LOGI(this->log, "Parse config file %s", this->filename);
std::fstream config_file;
config_file.open(this->filename, std::fstream::in);
std::string line;
std::string value;
bool rp_key_b, rp_regist_key_b, rp_key_type_b;
Host *current_host = nullptr;
if(config_file.is_open())
{
CHIAKI_LOGV(this->log, "Config file opened");
Settings::ConfigurationItem ci;
while(getline(config_file, line))
{
CHIAKI_LOGV(this->log, "Parse config line `%s`", line.c_str());
// for each line loop over config regex
ci = this->ParseLine(&line, &value);
switch(ci)
{
// got to next line
case UNKNOWN: CHIAKI_LOGV(this->log, "UNKNOWN config"); break;
case HOST_NAME:
CHIAKI_LOGV(this->log, "HOST_NAME %s", value.c_str());
// current host is in context
current_host = this->GetOrCreateHost(&value);
// all following case will edit the current_host config
break;
case HOST_IP:
CHIAKI_LOGV(this->log, "HOST_IP %s", value.c_str());
if(current_host != nullptr)
current_host->host_addr = value;
// reset bool flags
rp_key_b=false;
rp_regist_key_b=false;
rp_key_type_b=false;
break;
case PSN_ONLINE_ID:
CHIAKI_LOGV(this->log, "PSN_ONLINE_ID %s", value.c_str());
// current_host == nullptr
// means we are in global ini section
// update default setting
this->SetPSNOnlineID(current_host, value);
break;
case PSN_ACCOUNT_ID:
CHIAKI_LOGV(this->log, "PSN_ACCOUNT_ID %s", value.c_str());
this->SetPSNAccountID(current_host, value);
break;
case RP_KEY:
CHIAKI_LOGV(this->log, "RP_KEY %s", value.c_str());
if(current_host != nullptr)
rp_key_b = this->SetHostRPKey(current_host, value);
break;
case RP_KEY_TYPE:
CHIAKI_LOGV(this->log, "RP_KEY_TYPE %s", value.c_str());
if(current_host != nullptr)
// TODO Check possible rp_type values
rp_key_type_b = this->SetHostRPKeyType(current_host, value);
break;
case RP_REGIST_KEY:
CHIAKI_LOGV(this->log, "RP_REGIST_KEY %s", value.c_str());
if(current_host != nullptr)
rp_regist_key_b = this->SetHostRPRegistKey(current_host, value);
break;
case VIDEO_RESOLUTION:
this->SetVideoResolution(current_host, value);
break;
case VIDEO_FPS:
this->SetVideoFPS(current_host, value);
break;
} // ci switch
if(rp_key_b && rp_regist_key_b && rp_key_type_b)
// the current host contains rp key data
current_host->rp_key_data = true;
} // is_open
config_file.close();
}
return;
}
int Settings::WriteFile()
{
std::fstream config_file;
CHIAKI_LOGI(this->log, "Write config file %s", this->filename);
// flush file (trunc)
// the config file is completely overwritten
config_file.open(this->filename, std::fstream::out | std::ofstream::trunc);
std::string line;
std::string value;
if(this->hosts == nullptr)
return -1;
if(config_file.is_open())
{
// save global settings
CHIAKI_LOGD(this->log, "Write Global config file %s", this->filename);
if(this->global_video_resolution)
config_file << "video_resolution = \""
<< this->ResolutionPresetToString(this->GetVideoResolution(nullptr))
<< "\"\n";
if(this->global_video_fps)
config_file << "video_fps = "
<< this->FPSPresetToString(this->GetVideoFPS(nullptr))
<< "\n";
if(this->global_psn_online_id.length())
config_file << "psn_online_id = \"" << this->global_psn_online_id << "\"\n";
if(this->global_psn_account_id.length())
config_file << "psn_account_id = \"" << this->global_psn_account_id << "\"\n";
// write host config in file
// loop over all configured
for(auto it = this->hosts->begin(); it != this->hosts->end(); it++ )
{
// first is std::string
// second is Host
CHIAKI_LOGD(this->log, "Write Host config file %s", it->first.c_str());
config_file << "[" << it->first << "]\n"
<< "host_ip = \"" << it->second.host_addr << "\"\n";
if(it->second.video_resolution)
config_file << "video_resolution = \""
<< this->ResolutionPresetToString(this->GetVideoResolution(&it->second))
<< "\"\n";
if(it->second.video_fps)
config_file << "video_fps = "
<< this->FPSPresetToString(this->GetVideoFPS(&it->second))
<< "\n";
if(it->second.psn_online_id.length())
config_file << "psn_online_id = \"" << it->second.psn_online_id << "\"\n";
if(it->second.psn_account_id.length())
config_file << "psn_account_id = \"" << it->second.psn_account_id << "\"\n";
if(it->second.rp_key_data || it->second.registered)
{
char rp_key_type[33] = { 0 };
snprintf(rp_key_type, sizeof(rp_key_type), "%d", it->second.rp_key_type);
// save registered rp key for auto login
config_file << "rp_key = \"" << this->GetHostRPKey(&it->second) << "\"\n"
<< "rp_regist_key = \"" << this->GetHostRPRegistKey(&it->second) << "\"\n"
<< "rp_key_type = " << rp_key_type << "\n";
} //
config_file << "\n";
} // for host
} // is_open
config_file.close();
return 0;
}

View file

@ -50,3 +50,74 @@ if(NOT CHIAKI_USE_SYSTEM_JERASURE)
add_library(Jerasure::Jerasure ALIAS jerasure)
endif()
##################
# borealis
##################
if(CHIAKI_ENABLE_SWITCH)
# do not include
# borealis/library/lib/switch_wrapper.c
# switch functions are in switch/src/main.cpp
set(BOREALIS_SOURCE
borealis/library/lib/extern/libretro-common/features/features_cpu.c
borealis/library/lib/extern/libretro-common/encodings/encoding_utf.c
borealis/library/lib/extern/libretro-common/compat/compat_strl.c
borealis/library/lib/extern/nxfmtwrapper/format.cpp
borealis/library/lib/extern/nanovg/nanovg.c
borealis/library/lib/extern/glad/glad.c
borealis/library/lib/scroll_view.cpp
borealis/library/lib/style.cpp
borealis/library/lib/table.cpp
borealis/library/lib/task_manager.cpp
borealis/library/lib/progress_display.cpp
borealis/library/lib/staged_applet_frame.cpp
borealis/library/lib/applet_frame.cpp
borealis/library/lib/hint.cpp
borealis/library/lib/image.cpp
borealis/library/lib/logger.cpp
borealis/library/lib/swkbd.cpp
borealis/library/lib/crash_frame.cpp
borealis/library/lib/header.cpp
borealis/library/lib/progress_spinner.cpp
borealis/library/lib/layer_view.cpp
borealis/library/lib/notification_manager.cpp
borealis/library/lib/rectangle.cpp
borealis/library/lib/application.cpp
borealis/library/lib/box_layout.cpp
borealis/library/lib/sidebar.cpp
borealis/library/lib/dropdown.cpp
borealis/library/lib/popup_frame.cpp
borealis/library/lib/repeating_task.cpp
borealis/library/lib/absolute_layout.cpp
borealis/library/lib/i18n.cpp
borealis/library/lib/tab_frame.cpp
borealis/library/lib/thumbnail_frame.cpp
borealis/library/lib/animations.cpp
borealis/library/lib/dialog.cpp
borealis/library/lib/view.cpp
borealis/library/lib/list.cpp
borealis/library/lib/button.cpp
borealis/library/lib/label.cpp
borealis/library/lib/theme.cpp
borealis/library/lib/material_icon.cpp)
add_library(borealis STATIC ${BOREALIS_SOURCE})
target_include_directories(borealis PUBLIC
borealis/library/include
borealis/library/include/borealis/extern
borealis/library/include/borealis/extern/glad
borealis/library/include/borealis/extern/nanovg
borealis/library/include/borealis/extern/libretro-common
borealis/library/lib/extern/fmt/include)
find_package(glfw3 REQUIRED)
find_library(EGL EGL)
find_library(GLAPI glapi)
find_library(DRM_NOUVEAU drm_nouveau)
target_link_libraries(borealis
glfw
${EGL}
${GLAPI}
${DRM_NOUVEAU})
endif()

1
third-party/borealis vendored Submodule

@ -0,0 +1 @@
Subproject commit 205e97ab45922fa7f5c9fa6a85d5d686cd50b669