From 555cf228dbb2899538e3e56b73a3bc9fd24cbd04 Mon Sep 17 00:00:00 2001 From: rock88 Date: Sat, 9 May 2020 19:13:18 +0300 Subject: [PATCH 1/2] Try to run this on a Horizon OS :C --- Makefile | 2 +- Makefile.libnx | 343 ++++++++++++++++++++++++++++ dbg.sh | 3 + libgamestream/client.c | 133 ++++++++++- moonlight.xcodeproj/project.pbxproj | 4 + run.sh | 3 + src/decoders/gl_render.c | 5 + src/moonlight_glfw.cpp | 21 +- src/moonlight_libnx.c | 40 ++++ 9 files changed, 549 insertions(+), 5 deletions(-) create mode 100644 Makefile.libnx create mode 100755 dbg.sh create mode 100755 run.sh create mode 100644 src/moonlight_libnx.c diff --git a/Makefile b/Makefile index a31f978..d77fe9b 100755 --- a/Makefile +++ b/Makefile @@ -203,7 +203,7 @@ DEFINES += -DNANOGUI_USE_OPENGL -DNVG_STB_IMAGE_IMPLEMENTATION -DNANOGUI_NO_GLFW -DHAS_SOCKLEN_T -DHAS_POLL -DHAS_FCNTL -D_GNU_SOURCE -D__LIBRETRO__ CFLAGS += -Wall -pedantic $(fpic) -std=gnu11 $(DEFINES) -CXXFLAGS += -std=gnu++17 -fno-permissive $(DEFINES) +CXXFLAGS += -std=gnu++17 $(DEFINES) LIBS += -lcrypto -lssl -lcurl -lz -lexpat -lopus \ -lavcodec -lavformat -lavutil -lavdevice -lstdc++ diff --git a/Makefile.libnx b/Makefile.libnx new file mode 100644 index 0000000..afc2980 --- /dev/null +++ b/Makefile.libnx @@ -0,0 +1,343 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) +# +# NO_ICON: if set to anything, do not use icon. +# NO_NACP: if set to anything, no .nacp file is generated. +# APP_TITLE is the name of the app stored in the .nacp file (Optional) +# APP_AUTHOR is the author of the app stored in the .nacp file (Optional) +# APP_VERSION is the version of the app stored in the .nacp file (Optional) +# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) +# ICON is the filename of the icon (.jpg), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .jpg +# - icon.jpg +# - /default_icon.jpg +# +# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .json +# - config.json +# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead +# of a homebrew executable (.nro). This is intended to be used for sysmodules. +# NACP building is skipped as well. +#--------------------------------------------------------------------------------- +TARGET := moonlight +BUILD := build +SOURCES := libgamestream src/nanogui_resources src src/decoders src/ui/windows src/ui/buttons src/ui \ + third_party/moonlight-common-c/enet third_party/moonlight-common-c/reedsolomon third_party/moonlight-common-c/src \ + third_party/nanogui/ext/nanovg/src third_party/nanogui/src +DATA := data +INCLUDES := include +#ROMFS := romfs + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE + +M_INCLUDES := \ + -I$(TOPDIR)/src -I$(TOPDIR)/src/decoders \ + -I$(TOPDIR)/src/nanogui_resources \ + -I$(TOPDIR)/src/ui -I$(TOPDIR)/src/ui/buttons -I$(TOPDIR)/src/ui/windows \ + -I$(TOPDIR)/libgamestream \ + -I$(TOPDIR)/third_party/moonlight-common-c/reedsolomon \ + -I$(TOPDIR)/third_party/moonlight-common-c/src \ + -I$(TOPDIR)/third_party/moonlight-common-c/enet/include \ + -I$(TOPDIR)/third_party/nanogui/include \ + -I$(TOPDIR)/third_party/nanogui/ext/nanovg/src \ + -I$(TOPDIR)/third_party/json/single_include/nlohmann \ + -I/Users/rock88/Documents/Projects/Switch/moonlight-switch-new/deps/include \ + -I/Users/rock88/Documents/Projects/Switch/moonlight-switch-new/third_party + +DEFINES := -DNANOGUI_USE_OPENGL -DNVG_STB_IMAGE_IMPLEMENTATION -DNANOGUI_NO_GLFW \ + -DHAS_SOCKLEN_T -DHAS_POLL -DHAS_FCNTL -D_GNU_SOURCE -D__LIBRETRO__ + +CFLAGS := -g -Wall -O0 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) $(M_INCLUDES) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto \ + -lavcodec -lavutil -lopus -lssl -lcrypto -lbz2 -lz -lexpat -lm \ + -lglad -lEGL -lglapi -ldrm_nouveau -lglfw3 \ + -lnx -lwebp -lswresample -lavformat -lvpx \ + -L/Users/rock88/Documents/Projects/Switch/moonlight-switch-new/deps/lib + +LIBGAMESTREAM_SOURCES = \ + client.c \ + http.c \ + mkcert.c \ + xml.c + +MOONLIGHT_LIBRETRO_C_SOURCES = \ + nanogui_resources.c \ + moonlight_libnx.c \ + moonlight_glfw.c \ + ffmpeg.c \ + gl_render.c \ + video_decoder.c \ + audio_decoder.c + +MOONLIGHT_LIBRETRO_CXX_SOURCES = \ + AddHostWindow.cpp \ + ContentWindow.cpp \ + MainWindow.cpp \ + StreamWindow.cpp \ + AppListWindow.cpp \ + SettingsWindow.cpp \ + AddHostButton.cpp \ + AppButton.cpp \ + HostButton.cpp \ + Application.cpp \ + LoadingOverlay.cpp \ + GameStreamClient.cpp \ + Settings.cpp \ + InputController.cpp + +MOONLIGHT_COMMON_C_SOURCES = \ + callbacks.c \ + compress.c \ + host.c \ + list.c \ + packet.c \ + peer.c \ + protocol.c \ + unix.c \ + win32.c \ + rs.c \ + AudioStream.c \ + ByteBuffer.c \ + Connection.c \ + ControlStream.c \ + FakeCallbacks.c \ + InputStream.c \ + LinkedBlockingQueue.c \ + Misc.c \ + Platform.c \ + PlatformSockets.c \ + RtpFecQueue.c \ + RtpReorderQueue.c \ + RtspConnection.c \ + RtspParser.c \ + SdpGenerator.c \ + SimpleStun.c \ + VideoDepacketizer.c \ + VideoStream.c + +NANOGUI_C_SOURCES = \ + nanovg.c + +NANOGUI_CXX_SOURCES = \ + widget.cpp \ + button.cpp \ + common.cpp \ + screen.cpp \ + checkbox.cpp \ + vscrollpanel.cpp \ + colorpicker.cpp \ + textarea.cpp \ + shader_gl.cpp \ + canvas.cpp \ + window.cpp \ + graph.cpp \ + popup.cpp \ + layout.cpp \ + texture.cpp \ + texture_gl.cpp \ + tabwidget.cpp \ + shader.cpp \ + imageview.cpp \ + progressbar.cpp \ + combobox.cpp \ + theme.cpp \ + traits.cpp \ + label.cpp \ + opengl.cpp \ + renderpass_gl.cpp \ + imagepanel.cpp \ + colorwheel.cpp \ + messagedialog.cpp \ + textbox.cpp \ + slider.cpp \ + popupbutton.cpp + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(LIBGAMESTREAM_SOURCES) $(MOONLIGHT_LIBRETRO_C_SOURCES) $(MOONLIGHT_COMMON_C_SOURCES) $(NANOGUI_C_SOURCES) +CPPFILES := $(MOONLIGHT_LIBRETRO_CXX_SOURCES) $(NANOGUI_CXX_SOURCES) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.jpg) + ifneq (,$(findstring $(TARGET).jpg,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).jpg + else + ifneq (,$(findstring icon.jpg,$(icons))) + export APP_ICON := $(TOPDIR)/icon.jpg + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_ICON)),) + export NROFLAGS += --icon=$(APP_ICON) +endif + +ifeq ($(strip $(NO_NACP)),) + export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp +endif + +ifneq ($(APP_TITLEID),) + export NACPFLAGS += --titleid=$(APP_TITLEID) +endif + +ifneq ($(ROMFS),) + export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.libnx + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... +ifeq ($(strip $(APP_JSON)),) + @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf +else + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf +endif + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(APP_JSON)),) + +all : $(OUTPUT).nro + +ifeq ($(strip $(NO_NACP)),) +$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp +else +$(OUTPUT).nro : $(OUTPUT).elf +endif + +else + +all : $(OUTPUT).nsp + +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm + +$(OUTPUT).nso : $(OUTPUT).elf + +endif + +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/dbg.sh b/dbg.sh new file mode 100755 index 0000000..355336e --- /dev/null +++ b/dbg.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/opt/devkitpro/devkitA64/bin/aarch64-none-elf-addr2line -e moonlight.elf -f -p -C -a $1 diff --git a/libgamestream/client.c b/libgamestream/client.c index 41472ac..346ec8a 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -58,6 +57,138 @@ static EVP_PKEY *privateKey; const char* gs_error; +typedef unsigned char uuid_t[16]; + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint16_t clock_seq; + uint8_t node[6]; +}; + +uid_t getuid() { + return 123; +} + +void __random_get_bytes(void *buf, size_t nbytes) { + size_t i, n = nbytes; + int fd = 1; + int lose_counter = 0; + unsigned char *cp = (unsigned char *) buf; + + if (fd >= 0) { + while (n > 0) { + ssize_t x = rand(); + if (x <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= x; + cp += x; + lose_counter = 0; + } + } + + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; +} + +void __uuid_pack(const struct uuid *uu, uuid_t ptr) { + uint32_t tmp; + unsigned char *out = ptr; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + tmp >>= 8; + out[2] = (unsigned char) tmp; + tmp >>= 8; + out[1] = (unsigned char) tmp; + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy(out+10, uu->node, 6); +} + +void __uuid_unpack(const uuid_t in, struct uuid *uu) { + const uint8_t *ptr = in; + uint32_t tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + +void __uuid_generate_random(uuid_t out) { + int nm = 1; + int *num = &nm; + uuid_t buf; + struct uuid uu; + int i, n; + + if (!num || !*num) + n = 1; + else + n = *num; + + for (i = 0; i < n; i++) { + __random_get_bytes(buf, sizeof(buf)); + __uuid_unpack(buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) + | 0x4000; + __uuid_pack(&uu, out); + out += sizeof(uuid_t); + } +} + +void __uuid_unparse(const uuid_t uu, char *out) { + struct uuid uuid; + + __uuid_unpack(uu, &uuid); + sprintf(out, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +#define uuid_generate_random __uuid_generate_random +#define uuid_unparse __uuid_unparse + int mkdirtree(const char* directory) { char buffer[PATH_MAX]; char* p = buffer; diff --git a/moonlight.xcodeproj/project.pbxproj b/moonlight.xcodeproj/project.pbxproj index 3065a95..d4e9de3 100644 --- a/moonlight.xcodeproj/project.pbxproj +++ b/moonlight.xcodeproj/project.pbxproj @@ -276,6 +276,8 @@ 3652F089245C8569001FABF3 /* ContentWindow.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContentWindow.cpp; sourceTree = ""; }; 367CD958245DE25F00A95738 /* StreamWindow.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StreamWindow.cpp; sourceTree = ""; }; 367CD959245DE25F00A95738 /* StreamWindow.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StreamWindow.hpp; sourceTree = ""; }; + 369445A82466CE2700786D0A /* Makefile.libnx */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; path = Makefile.libnx; sourceTree = ""; }; + 369445A92466E2B000786D0A /* moonlight_libnx.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = moonlight_libnx.c; sourceTree = ""; }; 36A0C0352461DBA30083289C /* AddHostButton.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AddHostButton.cpp; sourceTree = ""; }; 36A0C0362461DBA30083289C /* AddHostButton.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AddHostButton.hpp; sourceTree = ""; }; 36A0C0382461E4C00083289C /* SettingsWindow.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SettingsWindow.cpp; sourceTree = ""; }; @@ -639,6 +641,7 @@ 3652F084245C6CFC001FABF3 /* libretro.h */, 3652F085245C6CFC001FABF3 /* moonlight_libretro.cpp */, 36B406962459F460005BD903 /* moonlight_glfw.cpp */, + 369445A92466E2B000786D0A /* moonlight_libnx.c */, ); path = src; sourceTree = ""; @@ -647,6 +650,7 @@ isa = PBXGroup; children = ( 3602C3C0245DC7E300368900 /* Makefile */, + 369445A82466CE2700786D0A /* Makefile.libnx */, 36A0C03E2461FFF10083289C /* build_opus_lakka_switch.sh */, 3652F006245C2918001FABF3 /* libgamestream */, 36B406932459F41E005BD903 /* src */, diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..d0f5027 --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/opt/devkitpro/tools/bin/nxlink -s moonlight.nro diff --git a/src/decoders/gl_render.c b/src/decoders/gl_render.c index 7fb1cd3..b6121ed 100644 --- a/src/decoders/gl_render.c +++ b/src/decoders/gl_render.c @@ -1,6 +1,11 @@ #include "gl_render.h" #include "libretro.h" + +#ifdef __SWITCH__ +#include +#else #include "glsym.h" +#endif static const char *vertex_shader_string = "\ #version 140\n\ diff --git a/src/moonlight_glfw.cpp b/src/moonlight_glfw.cpp index 3cbb61d..74ca5e4 100644 --- a/src/moonlight_glfw.cpp +++ b/src/moonlight_glfw.cpp @@ -1,7 +1,5 @@ #include #include -#include -#include "glsym/glsym.h" #include "Application.hpp" #include "Settings.hpp" #include "Limelight.h" @@ -9,6 +7,14 @@ #include "libretro.h" #include "InputController.hpp" +#ifdef __SWITCH__ +#include +#else +#include "glsym/glsym.h" +#endif + +#include + extern retro_input_state_t input_state_cb; static int mouse_x = 0, mouse_y = 0; @@ -63,9 +69,14 @@ int main(int argc, const char * argv[]) { GLFWwindow* window = glfwCreateWindow(1280, 720, "Test", NULL, NULL); glfwMakeContextCurrent(window); - rglgen_resolve_symbols(glfwGetProcAddress); glfwSwapInterval(1); + #ifdef __SWITCH__ + gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); + #else + rglgen_resolve_symbols(glfwGetProcAddress); + #endif + gl_render_init(); glfwSetCursorPosCallback(window, [](GLFWwindow *w, double x, double y) { @@ -93,7 +104,11 @@ int main(int argc, const char * argv[]) { glfwGetWindowSize(window, &width, &height); glfwGetFramebufferSize(window, &fb_width, &fb_height); + #ifdef __SWITCH__ + Settings::settings()->set_working_dir("sdmc:/switch/moonlight"); + #else Settings::settings()->set_working_dir("/Users/rock88/Documents/RetroArch/system/moonlight"); + #endif nanogui::init(); nanogui::ref app = new Application(Size(width, height), Size(fb_width, fb_height)); diff --git a/src/moonlight_libnx.c b/src/moonlight_libnx.c new file mode 100644 index 0000000..c1d7ae3 --- /dev/null +++ b/src/moonlight_libnx.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include "libretro.h" + +retro_audio_sample_batch_t audio_batch_cb = NULL; +retro_input_state_t input_state_cb = NULL; + +uint32_t htonl(uint32_t hostlong) { + return __builtin_bswap32(hostlong); +} + +uint16_t htons(uint16_t hostshort) { + return __builtin_bswap16(hostshort); +} + +uint32_t ntohl(uint32_t netlong) { + return __builtin_bswap32(netlong); +} + +uint16_t ntohs(uint16_t netshort) { + return __builtin_bswap16(netshort); +} + +int sigaction(int a, const struct sigaction* b, struct sigaction* c) { + return 0; +} + +static int nxlink_sock = -1; + +void userAppInit() { + socketInitializeDefault(); + nxlink_sock = nxlinkStdio(); +} + +void userAppExit() { + if (nxlink_sock != -1) + close(nxlink_sock); + socketExit(); +} From 9ea2ab289b1d1b2efd217e7ef3f0f7c05523fd4a Mon Sep 17 00:00:00 2001 From: rock88 Date: Mon, 11 May 2020 19:30:53 +0300 Subject: [PATCH 2/2] WIP (not work) --- libgamestream/client.c | 164 +++++++++------------------------------ libgamestream/mkcert.c | 74 +++++++++--------- run.sh | 1 + src/GameStreamClient.cpp | 84 ++++++++++++++------ src/GameStreamClient.hpp | 13 ++-- src/Settings.cpp | 40 +++++++--- third_party/nanogui | 2 +- 7 files changed, 171 insertions(+), 207 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 346ec8a..10643cb 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -57,137 +57,35 @@ static EVP_PKEY *privateKey; const char* gs_error; -typedef unsigned char uuid_t[16]; +#define LOG printf("%s:%i\n", __FUNCTION__, __LINE__); -struct uuid { - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint16_t clock_seq; - uint8_t node[6]; -}; +typedef unsigned char uuid_t[16]; +typedef uuid_t uuid; uid_t getuid() { return 123; } -void __random_get_bytes(void *buf, size_t nbytes) { - size_t i, n = nbytes; - int fd = 1; - int lose_counter = 0; - unsigned char *cp = (unsigned char *) buf; - - if (fd >= 0) { - while (n > 0) { - ssize_t x = rand(); - if (x <= 0) { - if (lose_counter++ > 16) - break; - continue; - } - n -= x; - cp += x; - lose_counter = 0; - } +void simple_uuid_generate_random(uuid_t out) { + static bool once = false; + if (!once) { + srand(time(NULL)); + once = true; } - for (cp = buf, i = 0; i < nbytes; i++) - *cp++ ^= (rand() >> 7) & 0xFF; -} - -void __uuid_pack(const struct uuid *uu, uuid_t ptr) { - uint32_t tmp; - unsigned char *out = ptr; - - tmp = uu->time_low; - out[3] = (unsigned char) tmp; - tmp >>= 8; - out[2] = (unsigned char) tmp; - tmp >>= 8; - out[1] = (unsigned char) tmp; - tmp >>= 8; - out[0] = (unsigned char) tmp; - - tmp = uu->time_mid; - out[5] = (unsigned char) tmp; - tmp >>= 8; - out[4] = (unsigned char) tmp; - - tmp = uu->time_hi_and_version; - out[7] = (unsigned char) tmp; - tmp >>= 8; - out[6] = (unsigned char) tmp; - - tmp = uu->clock_seq; - out[9] = (unsigned char) tmp; - tmp >>= 8; - out[8] = (unsigned char) tmp; - - memcpy(out+10, uu->node, 6); -} - -void __uuid_unpack(const uuid_t in, struct uuid *uu) { - const uint8_t *ptr = in; - uint32_t tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - tmp = (tmp << 8) | *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->time_low = tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->time_mid = tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->time_hi_and_version = tmp; - - tmp = *ptr++; - tmp = (tmp << 8) | *ptr++; - uu->clock_seq = tmp; - - memcpy(uu->node, ptr, 6); -} - -void __uuid_generate_random(uuid_t out) { - int nm = 1; - int *num = &nm; - uuid_t buf; - struct uuid uu; - int i, n; - - if (!num || !*num) - n = 1; - else - n = *num; - - for (i = 0; i < n; i++) { - __random_get_bytes(buf, sizeof(buf)); - __uuid_unpack(buf, &uu); - - uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; - uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) - | 0x4000; - __uuid_pack(&uu, out); - out += sizeof(uuid_t); + for (int i = 0; i < 16; i++) { + out[i] = (rand() % 16) * 10 + rand() % 16; } } -void __uuid_unparse(const uuid_t uu, char *out) { - struct uuid uuid; - - __uuid_unpack(uu, &uuid); - sprintf(out, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", - uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, - uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, - uuid.node[0], uuid.node[1], uuid.node[2], - uuid.node[3], uuid.node[4], uuid.node[5]); +void simple_uuid_unparse(const uuid_t uu, char *out) { + sprintf(out, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], + uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); } -#define uuid_generate_random __uuid_generate_random -#define uuid_unparse __uuid_unparse +#define uuid_generate_random simple_uuid_generate_random +#define uuid_unparse simple_uuid_unparse int mkdirtree(const char* directory) { char buffer[PATH_MAX]; @@ -244,26 +142,32 @@ static int load_unique_id(const char* keyDirectory) { } static int load_cert(const char* keyDirectory) { + LOG char certificateFilePath[PATH_MAX]; snprintf(certificateFilePath, PATH_MAX, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME); - +LOG char keyFilePath[PATH_MAX]; snprintf(&keyFilePath[0], PATH_MAX, "%s/%s", keyDirectory, KEY_FILE_NAME); - +LOG FILE *fd = fopen(certificateFilePath, "r"); - if (fd == NULL) { - printf("Generating certificate..."); + LOG + if (fd == NULL) { + LOG + printf("Generating certificate..."); CERT_KEY_PAIR cert = mkcert_generate(); - printf("done\n"); - + LOG + printf("done\n"); +LOG char p12FilePath[PATH_MAX]; snprintf(p12FilePath, PATH_MAX, "%s/%s", keyDirectory, P12_FILE_NAME); - +LOG mkcert_save(certificateFilePath, p12FilePath, keyFilePath, cert); + LOG mkcert_free(cert); + LOG fd = fopen(certificateFilePath, "r"); } - +LOG if (fd == NULL) { gs_error = "Can't open certificate file"; return GS_FAILED; @@ -925,16 +829,18 @@ int gs_quit_app(PSERVER_DATA server) { int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) { mkdirtree(keyDirectory); + LOG if (load_unique_id(keyDirectory) != GS_OK) return GS_FAILED; - +LOG if (load_cert(keyDirectory)) return GS_FAILED; - +LOG http_init(keyDirectory, log_level); - +LOG LiInitializeServerInformation(&server->serverInfo); server->serverInfo.address = address; server->unsupported = unsupported; + LOG return load_server_status(server); } diff --git a/libgamestream/mkcert.c b/libgamestream/mkcert.c index 53c72f3..825fced 100644 --- a/libgamestream/mkcert.c +++ b/libgamestream/mkcert.c @@ -28,6 +28,8 @@ #include #endif +#define LOG printf("%s:%i\n", __FUNCTION__, __LINE__); + static const int NUM_BITS = 2048; static const int SERIAL = 0; static const int NUM_YEARS = 10; @@ -36,27 +38,28 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years); int add_ext(X509 *cert, int nid, char *value); CERT_KEY_PAIR mkcert_generate() { + LOG BIO *bio_err; X509 *x509 = NULL; EVP_PKEY *pkey = NULL; PKCS12 *p12 = NULL; - +LOG CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); - +LOG OpenSSL_add_all_algorithms(); - +LOG mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS); - +LOG p12 = PKCS12_create("limelight", "GameStream", pkey, x509, NULL, 0, 0, 0, 0, 0); - +LOG #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif CRYPTO_cleanup_all_ex_data(); - +LOG BIO_free(bio_err); - +LOG return (CERT_KEY_PAIR) {x509, pkey, p12}; } @@ -86,7 +89,7 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) { EVP_PKEY *pk; RSA *rsa; X509_NAME *name = NULL; - +LOG if (*pkeyp == NULL) { if ((pk=EVP_PKEY_new()) == NULL) { abort(); @@ -95,7 +98,7 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) { } else { pk = *pkeyp; } - +LOG if (*x509p == NULL) { if ((x = X509_new()) == NULL) { goto err; @@ -103,57 +106,58 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) { } else { x = *x509p; } - +LOG if ((rsa = RSA_new()) == NULL) goto err; - +LOG BIGNUM* bne = BN_new(); if (bne == NULL) { abort(); goto err; } - - BN_set_word(bne, RSA_F4); +LOG + BN_set_word(bne, RSA_F4);LOG if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) { + LOG abort(); goto err; } - +LOG if (!EVP_PKEY_assign_RSA(pk, rsa)) { abort(); goto err; } - - X509_set_version(x, 2); - ASN1_INTEGER_set(X509_get_serialNumber(x), serial); - X509_gmtime_adj(X509_get_notBefore(x), 0); - X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*365*years); - X509_set_pubkey(x, pk); - +LOG + X509_set_version(x, 2);LOG + ASN1_INTEGER_set(X509_get_serialNumber(x), serial);LOG + X509_gmtime_adj(X509_get_notBefore(x), 0);LOG + X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*365*years);LOG + X509_set_pubkey(x, pk);LOG +LOG name = X509_get_subject_name(x); - +LOG /* This function creates and adds the entry, working out the * correct string type and performing checks on its length. */ X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char*)"NVIDIA GameStream Client", -1, -1, 0); - +LOG /* Its self signed so set the issuer name to be the same as the * subject. */ X509_set_issuer_name(x, name); - +LOG /* Add various extensions: standard extensions */ add_ext(x, NID_key_usage, "critical,digitalSignature,keyEncipherment"); - +LOG add_ext(x, NID_subject_key_identifier, "hash"); - +LOG if (!X509_sign(x, pk, EVP_sha256())) { goto err; } - +LOG *x509p = x; *pkeyp = pk; - +LOG return(1); err: return(0); @@ -165,22 +169,22 @@ err: int add_ext(X509 *cert, int nid, char *value) { - X509_EXTENSION *ex; + X509_EXTENSION *ex;LOG X509V3_CTX ctx; /* This sets the 'context' of the extensions. */ /* No configuration database */ - X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx_nodb(&ctx);LOG /* Issuer and subject certs: both the target since it is self signed, * no request and no CRL */ - X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); - ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); + X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);LOG + ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);LOG if (!ex) { return 0; } - - X509_add_ext(cert, ex, -1); - X509_EXTENSION_free(ex); +LOG + X509_add_ext(cert, ex, -1);LOG + X509_EXTENSION_free(ex);LOG return 1; } diff --git a/run.sh b/run.sh index d0f5027..487532a 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,4 @@ #!/bin/sh +make -f Makefile.libnx /opt/devkitpro/tools/bin/nxlink -s moonlight.nro diff --git a/src/GameStreamClient.cpp b/src/GameStreamClient.cpp index 99aefa4..5eef547 100644 --- a/src/GameStreamClient.cpp +++ b/src/GameStreamClient.cpp @@ -1,41 +1,77 @@ #include "GameStreamClient.hpp" #include "Settings.hpp" -#include -#include -#include #include #include -#include #include #include #include -#include +#ifdef __SWITCH__ +#include +#else +#include +#include +#endif + +#undef LOG +#define LOG printf("%s:%i\n", __FUNCTION__, __LINE__); static std::mutex m_async_mutex; +#ifdef __SWITCH__ +static void perform(void* context) { + LOG + auto task_ptr = static_cast *>(context); + LOG + (*task_ptr)(); + LOG + delete task_ptr; + threadExit(); + LOG +} +#endif + void perform_async(std::function task) { + #ifdef __SWITCH__ + std::function* retained_task = new std::function(task); + Thread thread; + threadCreate( + &thread, + &perform, + (void *)retained_task, + NULL, + 0x10000, + 0x2C, + -2 + ); + threadStart(&thread); + #else auto thread = std::thread([task](){ std::lock_guard guard(m_async_mutex); task(); }); thread.detach(); + #endif } void GameStreamClient::connect(const std::string &address, ServerCallback callback) { + LOG m_server_data[address] = SERVER_DATA(); - + LOG perform_async([this, address, callback] { + LOG // TODO: mem leak here :( std::string key_dir = Settings::settings()->working_dir() + "/key"; + LOG int status = gs_init(&m_server_data[address], (char *)(new std::string(address))->c_str(), key_dir.c_str(), 0, false); - + LOG nanogui::async([this, address, callback, status] { + LOG if (status == GS_OK) { Settings::settings()->add_host(address); - callback(Result::success(m_server_data[address])); + callback(GameStreamResult::success(m_server_data[address])); } else { - callback(Result::failure(gs_error != NULL ? gs_error : "Unknown error...")); + callback(GameStreamResult::failure(gs_error != NULL ? gs_error : "Unknown error...")); } }); }); @@ -43,7 +79,7 @@ void GameStreamClient::connect(const std::string &address, ServerCallback callback) { if (m_server_data.count(address) == 0) { - callback(Result::failure("Firstly call connect()...")); + callback(GameStreamResult::failure("Firstly call connect()...")); return; } @@ -52,9 +88,9 @@ void GameStreamClient::pair(const std::string &address, const std::string &pin, nanogui::async([callback, status] { if (status == GS_OK) { - callback(Result::success(true)); + callback(GameStreamResult::success(true)); } else { - callback(Result::failure(gs_error != NULL ? gs_error : "Unknown error...")); + callback(GameStreamResult::failure(gs_error != NULL ? gs_error : "Unknown error...")); } }); }); @@ -62,7 +98,7 @@ void GameStreamClient::pair(const std::string &address, const std::string &pin, void GameStreamClient::applist(const std::string &address, ServerCallback callback) { if (m_server_data.count(address) == 0) { - callback(Result::failure("Firstly call connect() & pair()...")); + callback(GameStreamResult::failure("Firstly call connect() & pair()...")); return; } @@ -73,9 +109,9 @@ void GameStreamClient::applist(const std::string &address, ServerCallback::success(m_app_list[address])); + callback(GameStreamResult::success(m_app_list[address])); } else { - callback(Result::failure(gs_error != NULL ? gs_error : "Unknown error...")); + callback(GameStreamResult::failure(gs_error != NULL ? gs_error : "Unknown error...")); } }); }); @@ -83,7 +119,7 @@ void GameStreamClient::applist(const std::string &address, ServerCallback> callback) { if (m_server_data.count(address) == 0) { - callback(Result>::failure("Firstly call connect() & pair()...")); + callback(GameStreamResult>::failure("Firstly call connect() & pair()...")); return; } @@ -95,9 +131,9 @@ void GameStreamClient::app_boxart(const std::string &address, int app_id, Server nanogui::async([this, callback, data, size, status] { if (status == GS_OK) { - callback(Result>::success(std::make_pair(data, size))); + callback(GameStreamResult>::success(std::make_pair(data, size))); } else { - callback(Result>::failure(gs_error != NULL ? gs_error : "Unknown error...")); + callback(GameStreamResult>::failure(gs_error != NULL ? gs_error : "Unknown error...")); } }); }); @@ -105,7 +141,7 @@ void GameStreamClient::app_boxart(const std::string &address, int app_id, Server void GameStreamClient::start(const std::string &address, STREAM_CONFIGURATION config, int app_id, ServerCallback callback) { if (m_server_data.count(address) == 0) { - callback(Result::failure("Firstly call connect() & pair()...")); + callback(GameStreamResult::failure("Firstly call connect() & pair()...")); return; } @@ -116,9 +152,9 @@ void GameStreamClient::start(const std::string &address, STREAM_CONFIGURATION co nanogui::async([this, callback, status] { if (status == GS_OK) { - callback(Result::success(m_config)); + callback(GameStreamResult::success(m_config)); } else { - callback(Result::failure(gs_error != NULL ? gs_error : "Unknown error...")); + callback(GameStreamResult::failure(gs_error != NULL ? gs_error : "Unknown error...")); } }); }); @@ -126,7 +162,7 @@ void GameStreamClient::start(const std::string &address, STREAM_CONFIGURATION co void GameStreamClient::quit(const std::string &address, ServerCallback callback) { if (m_server_data.count(address) == 0) { - callback(Result::failure("Firstly call connect() & pair()...")); + callback(GameStreamResult::failure("Firstly call connect() & pair()...")); return; } @@ -135,9 +171,9 @@ void GameStreamClient::quit(const std::string &address, ServerCallback cal nanogui::async([this, callback, status] { if (status == GS_OK) { - callback(Result::success(true)); + callback(GameStreamResult::success(true)); } else { - callback(Result::failure(gs_error != NULL ? gs_error : "Unknown error...")); + callback(GameStreamResult::failure(gs_error != NULL ? gs_error : "Unknown error...")); } }); }); diff --git a/src/GameStreamClient.hpp b/src/GameStreamClient.hpp index 4e38eed..fe7df5a 100644 --- a/src/GameStreamClient.hpp +++ b/src/GameStreamClient.hpp @@ -13,13 +13,13 @@ extern "C" { extern void perform_async(std::function task); template -struct Result { +struct GameStreamResult { public: - static Result success(T value) { + static GameStreamResult success(T value) { return result(value, "", true); } - static Result failure(std::string error) { + static GameStreamResult failure(std::string error) { return result(T(), error, false); } @@ -36,8 +36,8 @@ public: } private: - static Result result(T value, std::string error, bool isSuccess) { - Result result; + static GameStreamResult result(T value, std::string error, bool isSuccess) { + GameStreamResult result; result._value = value; result._error = error; result._isSuccess = isSuccess; @@ -51,8 +51,7 @@ private: #define LOG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__); -template using ServerCallback = const std::function)>; -//#define ServerCallback(T) std::function)> +template using ServerCallback = const std::function)>; class GameStreamClient { public: diff --git a/src/Settings.cpp b/src/Settings.cpp index 35f1323..4382fb6 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -4,37 +4,53 @@ #include #include +#define LOG printf("%s:%i\n", __FUNCTION__, __LINE__); + #define JSON_GET_VALUE(to, json, check, type) \ if (json.check()) { \ to = json.get(); \ } void Settings::add_host(const std::string address) { + LOG if (std::find(m_hosts.begin(), m_hosts.end(), address) == m_hosts.end()) { + LOG m_hosts.push_back(address); } + LOG save(); + LOG } void Settings::load() { + LOG std::ifstream stream(m_working_dir + "/settings.json"); - + LOG if (!stream) { + LOG return; } - nlohmann::json json; - stream >> json; - - JSON_GET_VALUE(m_hosts, json["hosts"], is_array, std::vector); - JSON_GET_VALUE(m_resolution, json["settings"]["resolution"], is_number_integer, int); - JSON_GET_VALUE(m_fps, json["settings"]["fps"], is_number_integer, int); - JSON_GET_VALUE(m_video_codec, json["settings"]["video_codec"], is_number_integer, VideoCodec); - JSON_GET_VALUE(m_bitrate, json["settings"]["bitrate"], is_number_integer, int); - JSON_GET_VALUE(m_swap_ab_xy, json["settings"]["swap_ab_xy"], is_number_integer, bool); + try { + LOG + nlohmann::json json; + LOG + stream >> json; + LOG + JSON_GET_VALUE(m_hosts, json["hosts"], is_array, std::vector); + JSON_GET_VALUE(m_resolution, json["settings"]["resolution"], is_number_integer, int); + JSON_GET_VALUE(m_fps, json["settings"]["fps"], is_number_integer, int); + JSON_GET_VALUE(m_video_codec, json["settings"]["video_codec"], is_number_integer, VideoCodec); + JSON_GET_VALUE(m_bitrate, json["settings"]["bitrate"], is_number_integer, int); + JSON_GET_VALUE(m_swap_ab_xy, json["settings"]["swap_ab_xy"], is_number_integer, bool); + LOG + } catch (const std::exception &e) { + printf("Load settings error: %s\n", e.what()); + } } void Settings::save() { + LOG try { nlohmann::json json; @@ -46,9 +62,11 @@ void Settings::save() { {"bitrate", m_bitrate}, {"swap_ab_xy", m_swap_ab_xy} }; - + LOG std::ofstream stream(m_working_dir + "/settings.json"); + LOG stream << std::setw(4) << json << std::endl; + LOG } catch (const std::exception &e) { printf("Save settings error: %s\n", e.what()); } diff --git a/third_party/nanogui b/third_party/nanogui index 56eb83f..48528d3 160000 --- a/third_party/nanogui +++ b/third_party/nanogui @@ -1 +1 @@ -Subproject commit 56eb83fd44745accc765171afb46a7c60abc3067 +Subproject commit 48528d30a2c2036b7e006915984cae6f44f60f34