From 67ddd72ecf711f877e189ad2e1ad5e9cf1d6b003 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 14 Oct 2024 20:56:46 -0400 Subject: [PATCH 01/13] initial commit --- .clang-format | 74 ++++++++++++ .codeql-prebuild-cpp-Linux.sh | 23 ++++ .codeql-prebuild-cpp-Windows.sh | 26 +++++ .codeql-prebuild-cpp-macOS.sh | 18 +++ .github/workflows/ci.yml | 201 ++++++++++++++++++++++++++++++++ .gitignore | 38 ++++++ .gitmodules | 8 ++ Makefile | 8 ++ README.md | 4 +- src/main.cpp | 24 ++++ third-party/moonlight-common-c | 1 + third-party/nxdk | 1 + 12 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 .clang-format create mode 100644 .codeql-prebuild-cpp-Linux.sh create mode 100644 .codeql-prebuild-cpp-Windows.sh create mode 100644 .codeql-prebuild-cpp-macOS.sh create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 src/main.cpp create mode 160000 third-party/moonlight-common-c create mode 160000 third-party/nxdk diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e72a1e2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,74 @@ +--- +# This file is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlignTrailingComments: false +AlwaysBreakAfterReturnType: All +AlwaysBreakTemplateDeclarations: MultiLine +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterUnion: false + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 2 +Cpp11BracedListStyle: false +UseTab: Never diff --git a/.codeql-prebuild-cpp-Linux.sh b/.codeql-prebuild-cpp-Linux.sh new file mode 100644 index 0000000..fbf3c2c --- /dev/null +++ b/.codeql-prebuild-cpp-Linux.sh @@ -0,0 +1,23 @@ +# install dependencies for C++ analysis +set -e + +# install dependencies +dependencies=( + "bison" + "build-essential" + "clang" + "cmake" + "flex" + "git" + "lld" + "llvm" +) +sudo apt-get update +sudo apt-get install --no-install-recommends -y "${dependencies[@]}" + +# build +eval "$(./third-party/nxdk/bin/activate -s)" +make + +# skip autobuild +echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.codeql-prebuild-cpp-Windows.sh b/.codeql-prebuild-cpp-Windows.sh new file mode 100644 index 0000000..8897ccb --- /dev/null +++ b/.codeql-prebuild-cpp-Windows.sh @@ -0,0 +1,26 @@ +# install dependencies for C++ analysis +set -e + +# update pacman +pacman --noconfirm -Syu + +# install dependencies +dependencies=( + "make" + "cmake" + "git" + "bison" + "flex" + "mingw-w64-x86_64-gcc" + "mingw-w64-x86_64-llvm" + "mingw-w64-x86_64-clang" + "mingw-w64-x86_64-lld" +) +pacman -S --noconfirm "${dependencies[@]}" + +# build +eval "$(./third-party/nxdk/bin/activate -s)" +make + +# skip autobuild +echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.codeql-prebuild-cpp-macOS.sh b/.codeql-prebuild-cpp-macOS.sh new file mode 100644 index 0000000..0d36459 --- /dev/null +++ b/.codeql-prebuild-cpp-macOS.sh @@ -0,0 +1,18 @@ +# install dependencies for C++ analysis +set -e + +# install dependencies +dependencies=( + "cmake" + "coreutils" + "lld" + "llvm" +) +brew install "${dependencies[@]}" + +# build +eval "$(./third-party/nxdk/bin/activate -s)" +make + +# skip autobuild +echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f6bed3e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,201 @@ +--- +name: CI + +on: + pull_request: + branches: + - master + types: + - opened + - synchronize + - reopened + push: + branches: + - master + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + setup_release: + name: Setup Release + outputs: + publish_release: ${{ steps.setup_release.outputs.publish_release }} + release_body: ${{ steps.setup_release.outputs.release_body }} + release_commit: ${{ steps.setup_release.outputs.release_commit }} + release_generate_release_notes: ${{ steps.setup_release.outputs.release_generate_release_notes }} + release_tag: ${{ steps.setup_release.outputs.release_tag }} + release_version: ${{ steps.setup_release.outputs.release_version }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Release + id: setup_release + uses: LizardByte/setup-release-action@v2024.919.143601 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + build: + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + shell: "bash" + - os: ubuntu-latest + shell: "bash" + - os: windows-latest + shell: "msys2 {0}" + + name: Build (${{ matrix.os }}) + needs: setup_release + runs-on: ${{ matrix.os }} + defaults: + run: + shell: ${{ matrix.shell }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Dependencies Linux + if: runner.os == 'Linux' + run: | + dependencies=( + "bison" + "build-essential" + "clang" + "cmake" + "flex" + "git" + "lld" + "llvm" + ) + sudo apt-get update + sudo apt-get install --no-install-recommends -y "${dependencies[@]}" + + - name: Setup Dependencies macOS + if: runner.os == 'macOS' + run: | + dependencies=( + "cmake" + "coreutils" + "lld" + "llvm" + ) + brew install "${dependencies[@]}" + + - name: Setup Dependencies Windows + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + make + cmake + git + bison + flex + mingw-w64-x86_64-gcc + mingw-w64-x86_64-llvm + mingw-w64-x86_64-clang + mingw-w64-x86_64-lld + + - name: Setup python + id: setup-python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Python Path + id: python-path + run: | + if [ "${{ runner.os }}" = "Windows" ]; then + # replace backslashes with double backslashes + python_path=$(echo "${{ steps.setup-python.outputs.python-path }}" | sed 's/\\/\\\\/g') + else + python_path=${{ steps.setup-python.outputs.python-path }} + fi + + # step output + echo "python-path=${python_path}" + echo "python-path=${python_path}" >> $GITHUB_OUTPUT + + - name: Build + run: | + eval "$(./third-party/nxdk/bin/activate -s)" + make + + # move artifacts + mkdir -p artifacts + mv ./build/default.xbe ./artifacts + mv ./Moonlight.iso ./artifacts + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: moonlight-${{ runner.os }} + path: artifacts/ + + - name: Run tests + id: test + if: false + working-directory: build/tests + run: | + ./test_moonlight-xboxog --gtest_color=yes + + - name: Generate gcov report + # any except canceled or skipped + if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure') + id: test_report + working-directory: build + run: | + ${{ steps.python-path.outputs.python-path }} -m pip install gcovr + ${{ steps.python-path.outputs.python-path }} -m gcovr . -r ../src \ + --exclude-noncode-lines \ + --exclude-throw-branches \ + --exclude-unreachable-branches \ + --verbose \ + --xml-pretty \ + -o coverage.xml + + - name: Debug coverage file + if: >- + always() && + steps.test_report.outcome == 'success' + run: | + cat build/coverage.xml + + # todo: upload coverage in separate job similar to LizardByte/libdisplaydevice + - name: Upload coverage + # any except canceled or skipped + if: >- + always() && + steps.test_report.outcome == 'success' && + startsWith(github.repository, 'LizardByte/') + uses: codecov/codecov-action@v4 + with: + disable_search: true + fail_ci_if_error: true + files: ./build/coverage.xml + flags: "${{ runner.os }}" + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + + - name: Create/Update GitHub Release + if: ${{ needs.setup_release.outputs.publish_release == 'true' }} + uses: LizardByte/create-release-action@v2024.919.143026 + with: + allowUpdates: true + body: ${{ needs.setup_release.outputs.release_body }} + generateReleaseNotes: ${{ needs.setup_release.outputs.release_generate_release_notes }} + name: ${{ needs.setup_release.outputs.release_tag }} + prerelease: true + tag: ${{ needs.setup_release.outputs.release_tag }} + token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9cce0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# JetBrains IDE +.idea/ + +# VSCode IDE +.vscode/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..05645c4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,8 @@ +[submodule "third-party/moonlight-common-c"] + path = third-party/moonlight-common-c + url = https://github.com/moonlight-stream/moonlight-common-c.git + branch = master +[submodule "third-party/nxdk"] + path = third-party/nxdk + url = https://github.com/XboxDev/nxdk.git + branch = master diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1a1c5f0 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +XBE_TITLE = Moonlight +OUTPUT_DIR = $(CURDIR)/build +GEN_XISO = $(XBE_TITLE).iso +SRCS = $(CURDIR)/src/main.cpp +NXDK_DIR ?= $(CURDIR)/third-party/nxdk +NXDK_CXX = y + +include $(NXDK_DIR)/Makefile diff --git a/README.md b/README.md index fd3e658..3499796 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# template-base -Base repository template for LizardByte. +# Moonlight-XboxOG +Port of Moonlight for the Original Xbox. Unlikely to ever actually work. Do NOT use. diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0b121d8 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +int main(void) { + XVideoSetMode(640, 480, 32, REFRESH_DEFAULT); + + std::vector words; + words.emplace_back("Hello,"); + words.emplace_back(" "); + words.emplace_back("Moonlight!"); + words.emplace_back("\n"); + + while (true) { + for (auto& word : words) { + debugPrint("%s", word.c_str()); + } + Sleep(2000); + } + + return 0; +} diff --git a/third-party/moonlight-common-c b/third-party/moonlight-common-c new file mode 160000 index 0000000..8599b60 --- /dev/null +++ b/third-party/moonlight-common-c @@ -0,0 +1 @@ +Subproject commit 8599b6042a4ba27749b0f94134dd614b4328a9bc diff --git a/third-party/nxdk b/third-party/nxdk new file mode 160000 index 0000000..56cdc3d --- /dev/null +++ b/third-party/nxdk @@ -0,0 +1 @@ +Subproject commit 56cdc3d89e6a6ab00e6d7d6ecfcb533e96678107 From 02ea25d5c80e5fda8ee84432573f827e1a4770bc Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:45:50 -0400 Subject: [PATCH 02/13] use cmake --- .codeql-prebuild-cpp-Linux.sh | 12 +++++-- .codeql-prebuild-cpp-Windows.sh | 12 +++++-- .codeql-prebuild-cpp-macOS.sh | 12 +++++-- .github/workflows/ci.yml | 17 ++++++++-- .gitignore | 8 +++++ CMakeLists.txt | 59 +++++++++++++++++++++++++++++++++ Makefile | 8 ----- 7 files changed, 111 insertions(+), 17 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Makefile diff --git a/.codeql-prebuild-cpp-Linux.sh b/.codeql-prebuild-cpp-Linux.sh index fbf3c2c..493d480 100644 --- a/.codeql-prebuild-cpp-Linux.sh +++ b/.codeql-prebuild-cpp-Linux.sh @@ -16,8 +16,16 @@ sudo apt-get update sudo apt-get install --no-install-recommends -y "${dependencies[@]}" # build -eval "$(./third-party/nxdk/bin/activate -s)" -make +nxdk_dir="$(pwd)/third-party/nxdk" +eval "$(${nxdk_dir}/bin/activate -s)" +cd "${nxdk_dir}" +make NXDK_ONLY=y +make tools + +cd "${GITHUB_WORKSPACE}" +mkdir -p build +cmake -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" -B build -S . +cmake --build build # skip autobuild echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.codeql-prebuild-cpp-Windows.sh b/.codeql-prebuild-cpp-Windows.sh index 8897ccb..78ec796 100644 --- a/.codeql-prebuild-cpp-Windows.sh +++ b/.codeql-prebuild-cpp-Windows.sh @@ -19,8 +19,16 @@ dependencies=( pacman -S --noconfirm "${dependencies[@]}" # build -eval "$(./third-party/nxdk/bin/activate -s)" -make +nxdk_dir="$(pwd)/third-party/nxdk" +eval "$(${nxdk_dir}/bin/activate -s)" +cd "${nxdk_dir}" +make NXDK_ONLY=y +make tools + +cd "${GITHUB_WORKSPACE}" +mkdir -p build +cmake -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" -B build -S . +cmake --build build # skip autobuild echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.codeql-prebuild-cpp-macOS.sh b/.codeql-prebuild-cpp-macOS.sh index 0d36459..f46c16e 100644 --- a/.codeql-prebuild-cpp-macOS.sh +++ b/.codeql-prebuild-cpp-macOS.sh @@ -11,8 +11,16 @@ dependencies=( brew install "${dependencies[@]}" # build -eval "$(./third-party/nxdk/bin/activate -s)" -make +nxdk_dir="$(pwd)/third-party/nxdk" +eval "$(${nxdk_dir}/bin/activate -s)" +cd "${nxdk_dir}" +make NXDK_ONLY=y +make tools + +cd "${GITHUB_WORKSPACE}" +mkdir -p build +cmake -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" -B build -S . +cmake --build build # skip autobuild echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6bed3e..7c045cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,12 +129,23 @@ jobs: - name: Build run: | - eval "$(./third-party/nxdk/bin/activate -s)" - make + nxdk_dir="$(pwd)/third-party/nxdk" + eval "$(${nxdk_dir}/bin/activate -s)" + cd "${nxdk_dir}" + make NXDK_ONLY=y + make tools + + cd "${GITHUB_WORKSPACE}" + mkdir -p build + cmake -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" -B build -S . + cmake --build build + + # recursively list all files in build directory + ls -R build # move artifacts mkdir -p artifacts - mv ./build/default.xbe ./artifacts + mv ./build/xbe/default.xbe ./artifacts mv ./Moonlight.iso ./artifacts - name: Upload Artifacts diff --git a/.gitignore b/.gitignore index b9cce0f..ee8b55d 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,11 @@ # VSCode IDE .vscode/ + +# build directories +build/ +cmake-*/ +docs/doxyconfig* + +# iso file is created in project root +*.iso diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8dec9cd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.14) +# much of this file is borrowed from https://github.com/Ryzee119/Xenium-Tools/blob/master/CMakeLists.txt + +project(Moonlight C CXX) +set(XBE_TITLE ${CMAKE_PROJECT_NAME}) +message(STATUS "XBE_TITLE: ${XBE_TITLE}") + +set(NXDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third-party/nxdk") +message(STATUS "NXDK_DIR: ${NXDK_DIR}") + +set(XBOX_XBE_DIR "${CMAKE_CURRENT_BINARY_DIR}/xbe") +set(XBOX_ISO "${CMAKE_PROJECT_NAME}.iso") +message(STATUS "XBOX_ISO: ${XBOX_ISO}") + +# create the xbe directory if it doesn't exist +file(MAKE_DIRECTORY ${XBOX_XBE_DIR}) + +include(FindPkgConfig) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() +set(CMAKE_CXX_FLAGS_RELEASE "-O2") +set(CMAKE_C_FLAGS_RELEASE "-O2") + +# Stop lots of warning spam +add_compile_options(-Wno-builtin-macro-redefined) +add_definitions(-DXBOX -DNXDK) + +set(MOONLIGHT_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp" +) + +add_executable(${CMAKE_PROJECT_NAME} + ${MOONLIGHT_SOURCES} +) +include_directories("${NXDK_DIR}/lib") +target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC ${NXDK_DIR}/lib/libpbkit.lib) + +target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE "") + +# Post-build exe to xbe conversion +add_custom_target(cxbe_convert ALL + VERBATIM COMMAND "${CMAKE_COMMAND}" -E env ./tools/cxbe/cxbe -OUT:${XBOX_XBE_DIR}/default.xbe -TITLE:${XBE_TITLE} ${CMAKE_CURRENT_BINARY_DIR}/${XBE_TITLE}.exe + WORKING_DIRECTORY ${NXDK_DIR} + COMMENT "CXBE Conversion: [EXE -> XBE]" +) +add_dependencies(cxbe_convert ${CMAKE_PROJECT_NAME}) + +# Post-build xbe to xiso conversion +add_custom_target(xbe_iso ALL + VERBATIM COMMAND "${CMAKE_COMMAND}" -E env ${NXDK_DIR}/tools/extract-xiso/build/extract-xiso -c ${XBOX_XBE_DIR} ${XBOX_ISO} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "CXBE Conversion: [XBE -> XISO]" +) +add_dependencies(xbe_iso cxbe_convert) + +set_target_properties(cxbe_convert PROPERTIES OUTPUT_QUIET ON) +set_target_properties(xbe_iso PROPERTIES OUTPUT_QUIET ON) diff --git a/Makefile b/Makefile deleted file mode 100644 index 1a1c5f0..0000000 --- a/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -XBE_TITLE = Moonlight -OUTPUT_DIR = $(CURDIR)/build -GEN_XISO = $(XBE_TITLE).iso -SRCS = $(CURDIR)/src/main.cpp -NXDK_DIR ?= $(CURDIR)/third-party/nxdk -NXDK_CXX = y - -include $(NXDK_DIR)/Makefile From 22abcdfa7be592e415a253aea568400d189d780a Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 15 Oct 2024 23:12:26 -0400 Subject: [PATCH 03/13] add test framework --- .github/workflows/ci.yml | 3 +-- .gitmodules | 4 +++ CMakeLists.txt | 19 +++++++++++++- tests/CMakeLists.txt | 53 ++++++++++++++++++++++++++++++++++++++++ third-party/googletest | 1 + 5 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 160000 third-party/googletest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c045cc..a746424 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -156,10 +156,9 @@ jobs: - name: Run tests id: test - if: false working-directory: build/tests run: | - ./test_moonlight-xboxog --gtest_color=yes + ./test_moonlight --gtest_color=yes - name: Generate gcov report # any except canceled or skipped diff --git a/.gitmodules b/.gitmodules index 05645c4..57b4e9b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ +[submodule "third-party/googletest"] + path = third-party/googletest + url = https://github.com/google/googletest.git + branch = main [submodule "third-party/moonlight-common-c"] path = third-party/moonlight-common-c url = https://github.com/moonlight-stream/moonlight-common-c.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dec9cd..489ec9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,11 @@ set(XBOX_XBE_DIR "${CMAKE_CURRENT_BINARY_DIR}/xbe") set(XBOX_ISO "${CMAKE_PROJECT_NAME}.iso") message(STATUS "XBOX_ISO: ${XBOX_ISO}") +# +# Options +# +option(BUILD_TESTS "Build tests" ON) + # create the xbe directory if it doesn't exist file(MAKE_DIRECTORY ${XBOX_XBE_DIR}) @@ -30,15 +35,27 @@ add_definitions(-DXBOX -DNXDK) set(MOONLIGHT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp" ) +set(MOONLIGHT_EXTERNAL_LIBRARIES + "${NXDK_DIR}/lib/libpbkit.lib" +) add_executable(${CMAKE_PROJECT_NAME} ${MOONLIGHT_SOURCES} ) include_directories("${NXDK_DIR}/lib") -target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC ${NXDK_DIR}/lib/libpbkit.lib) +target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC + ${MOONLIGHT_EXTERNAL_LIBRARIES} +) target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE "") +# +# tests +# +if(BUILD_TESTS) + add_subdirectory(tests) +endif() + # Post-build exe to xbe conversion add_custom_target(cxbe_convert ALL VERBATIM COMMAND "${CMAKE_COMMAND}" -E env ./tools/cxbe/cxbe -OUT:${XBOX_XBE_DIR}/default.xbe -TITLE:${XBE_TITLE} ${CMAKE_CURRENT_BINARY_DIR}/${XBE_TITLE}.exe diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..899f4d3 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.13) +# https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md#foundational-c-support + +project(test_moonlight) + +include_directories("${CMAKE_SOURCE_DIR}") + +enable_testing() + +# Add GoogleTest directory to the project +set(GTEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/googletest") +set(INSTALL_GTEST OFF) +set(INSTALL_GMOCK OFF) +add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest") +include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}") + +# coverage +# https://gcovr.com/en/stable/guide/compiling.html#compiler-options +set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage -ggdb -O0") +set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage -ggdb -O0") + +# if windows +if (WIN32) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # cmake-lint: disable=C0103 +endif () + +file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS + ${CMAKE_SOURCE_DIR}/tests/*.h + ${CMAKE_SOURCE_DIR}/tests/*.cpp) + +# remove main.cpp from the list of sources +# TODO +# list(REMOVE_ITEM MOONLIGHT_SOURCES ${CMAKE_SOURCE_DIR}/src/main.cpp) + +add_executable(${PROJECT_NAME} + ${TEST_SOURCES} + ${MOONLIGHT_SOURCES}) + +foreach(dep ${MOONLIGHT_TARGET_DEPENDENCIES}) + add_dependencies(${PROJECT_NAME} ${dep}) # compile these before Moonlight +endforeach() + +set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20) +target_link_libraries(${PROJECT_NAME} + ${MOONLIGHT_EXTERNAL_LIBRARIES} + gtest + gtest_main) # if we use this we don't need our own main function +target_compile_definitions(${PROJECT_NAME} PUBLIC ${MOONLIGHT_DEFINITIONS} ${TEST_DEFINITIONS}) +target_compile_options(${PROJECT_NAME} PRIVATE $<$:${MOONLIGHT_COMPILE_OPTIONS}>) +target_link_options(${PROJECT_NAME} PRIVATE) + +add_test(NAME ${PROJECT_NAME} COMMAND moonlight_test) diff --git a/third-party/googletest b/third-party/googletest new file mode 160000 index 0000000..b514bdc --- /dev/null +++ b/third-party/googletest @@ -0,0 +1 @@ +Subproject commit b514bdc898e2951020cbdca1304b75f5950d1f59 From 0c2cc6987590513493846738934b19fcd8f894ae Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Wed, 16 Oct 2024 19:19:44 -0400 Subject: [PATCH 04/13] separate tests target --- .github/workflows/ci.yml | 19 ++++++++++++++++++- CMakeLists.txt | 29 ++++++++++++++++++++--------- tests/CMakeLists.txt | 22 ++++++++++++++++++++-- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a746424..e507dd9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,9 +137,25 @@ jobs: cd "${GITHUB_WORKSPACE}" mkdir -p build - cmake -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" -B build -S . + + # the main target needs to be cross compiled + echo "--- Building ALL ---" + cmake \ + -B build \ + -S . \ + -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" cmake --build build + # TODO: Tests are not building properly... + # the tests target should not be cross compiled, so we can run it on the runner + # https://stackoverflow.com/a/64335131/11214013 + # echo "--- Building tests ---" + # cmake \ + # -B build/tests \ + # -S . \ + # -DBUILD_TESTS=ON + # cmake --build build/tests --target test_moonlight + # recursively list all files in build directory ls -R build @@ -156,6 +172,7 @@ jobs: - name: Run tests id: test + if: false working-directory: build/tests run: | ./test_moonlight --gtest_color=yes diff --git a/CMakeLists.txt b/CMakeLists.txt index 489ec9f..93b7f4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ message(STATUS "XBOX_ISO: ${XBOX_ISO}") # # Options # -option(BUILD_TESTS "Build tests" ON) +option(BUILD_TESTS "Build tests" OFF) # create the xbe directory if it doesn't exist file(MAKE_DIRECTORY ${XBOX_XBE_DIR}) @@ -28,21 +28,26 @@ endif() set(CMAKE_CXX_FLAGS_RELEASE "-O2") set(CMAKE_C_FLAGS_RELEASE "-O2") -# Stop lots of warning spam -add_compile_options(-Wno-builtin-macro-redefined) -add_definitions(-DXBOX -DNXDK) - set(MOONLIGHT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp" ) set(MOONLIGHT_EXTERNAL_LIBRARIES "${NXDK_DIR}/lib/libpbkit.lib" ) +set(MOONLIGHT_INCLUDE_DIRS + "${NXDK_DIR}/lib" +) +set(MOONLIGHT_COMPILE_OPTIONS "-Wno-builtin-macro-redefined") +set(MOONLIGHT_DEFINITIONS "-DXBOX -DNXDK") + +# Stop lots of warning spam +add_compile_options(${MOONLIGHT_COMPILE_OPTIONS}) +add_definitions(${MOONLIGHT_DEFINITIONS}) add_executable(${CMAKE_PROJECT_NAME} ${MOONLIGHT_SOURCES} ) -include_directories("${NXDK_DIR}/lib") +include_directories(SYSTEM ${MOONLIGHT_INCLUDE_DIRS}) target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC ${MOONLIGHT_EXTERNAL_LIBRARIES} ) @@ -53,12 +58,16 @@ target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE "") # tests # if(BUILD_TESTS) - add_subdirectory(tests) + add_subdirectory(tests EXCLUDE_FROM_ALL) endif() # Post-build exe to xbe conversion add_custom_target(cxbe_convert ALL - VERBATIM COMMAND "${CMAKE_COMMAND}" -E env ./tools/cxbe/cxbe -OUT:${XBOX_XBE_DIR}/default.xbe -TITLE:${XBE_TITLE} ${CMAKE_CURRENT_BINARY_DIR}/${XBE_TITLE}.exe + VERBATIM COMMAND "${CMAKE_COMMAND}" -E env + ./tools/cxbe/cxbe + -OUT:${XBOX_XBE_DIR}/default.xbe + -TITLE:${XBE_TITLE} + ${CMAKE_CURRENT_BINARY_DIR}/${XBE_TITLE}.exe WORKING_DIRECTORY ${NXDK_DIR} COMMENT "CXBE Conversion: [EXE -> XBE]" ) @@ -66,7 +75,9 @@ add_dependencies(cxbe_convert ${CMAKE_PROJECT_NAME}) # Post-build xbe to xiso conversion add_custom_target(xbe_iso ALL - VERBATIM COMMAND "${CMAKE_COMMAND}" -E env ${NXDK_DIR}/tools/extract-xiso/build/extract-xiso -c ${XBOX_XBE_DIR} ${XBOX_ISO} + VERBATIM COMMAND "${CMAKE_COMMAND}" -E env + ${NXDK_DIR}/tools/extract-xiso/build/extract-xiso + -c ${XBOX_XBE_DIR} ${XBOX_ISO} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "CXBE Conversion: [XBE -> XISO]" ) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 899f4d3..89e1e0d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,18 @@ cmake_minimum_required(VERSION 3.13) project(test_moonlight) include_directories("${CMAKE_SOURCE_DIR}") +include_directories(SYSTEM ${MOONLIGHT_INCLUDE_DIRS}) + +# nxdk include dirs, these are set by the toolchain for ALL targets except this one since we don't use the toolchain +include_directories(SYSTEM + "${NXDK_DIR}/lib/libcxx/include" + "${NXDK_DIR}/lib" + "${NXDK_DIR}/lib/xboxrt/libc_extensions" + "${NXDK_DIR}/lib/pdclib/include" + "${NXDK_DIR}/lib/pdclib/platform/xbox/include" + "${NXDK_DIR}/lib/winapi" + "${NXDK_DIR}/lib/xboxrt/vcruntime" +) enable_testing() @@ -11,8 +23,8 @@ enable_testing() set(GTEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/googletest") set(INSTALL_GTEST OFF) set(INSTALL_GMOCK OFF) -add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest") -include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}") +add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest" EXCLUDE_FROM_ALL) +include_directories(SYSTEM "${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}") # coverage # https://gcovr.com/en/stable/guide/compiling.html#compiler-options @@ -33,6 +45,10 @@ file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS # TODO # list(REMOVE_ITEM MOONLIGHT_SOURCES ${CMAKE_SOURCE_DIR}/src/main.cpp) +# Stop lots of warning spam +add_compile_options(${MOONLIGHT_COMPILE_OPTIONS}) +add_definitions(${MOONLIGHT_DEFINITIONS}) + add_executable(${PROJECT_NAME} ${TEST_SOURCES} ${MOONLIGHT_SOURCES}) @@ -50,4 +66,6 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC ${MOONLIGHT_DEFINITIONS} ${TES target_compile_options(${PROJECT_NAME} PRIVATE $<$:${MOONLIGHT_COMPILE_OPTIONS}>) target_link_options(${PROJECT_NAME} PRIVATE) +add_dependencies(${PROJECT_NAME} gtest gtest_main gmock gmock_main) + add_test(NAME ${PROJECT_NAME} COMMAND moonlight_test) From 8b45b06a3917166affa5f2c2387c7970b8744d75 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:13:29 -0400 Subject: [PATCH 05/13] create archive of xbe directory --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e507dd9..f9e063f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -161,7 +161,7 @@ jobs: # move artifacts mkdir -p artifacts - mv ./build/xbe/default.xbe ./artifacts + tar -czf ./artifacts/Moonlight.tar.gz ./build/xbe mv ./Moonlight.iso ./artifacts - name: Upload Artifacts From fd4d160be6662aac679590adbdaa97503d0e2379 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:55:46 -0400 Subject: [PATCH 06/13] splash screen --- .github/workflows/ci.yml | 5 +- CMakeLists.txt | 47 ++++- README.md | 35 ++++ cmake/modules/FindNXDK.cmake | 108 +++++++++++ cmake/modules/FindNXDK_SDL2.cmake | 6 + cmake/modules/FindNXDK_SDL2_Image.cmake | 37 ++++ cmake/modules/XBEUtils.cmake | 214 ++++++++++++++++++++++ src/main.cpp | 176 ++++++++++++++++-- src/nxdk/hal/debug.h | 5 + src/nxdk/hal/video.h | 6 + src/nxdk/hal/xbox.h | 5 + src/nxdk/windows.h | 5 + src/os.h | 7 + xbe/assets/moonlight-splash-1024x768.jpg | Bin 0 -> 25736 bytes xbe/assets/moonlight-splash-1280x720.jpg | Bin 0 -> 29432 bytes xbe/assets/moonlight-splash-1920x1080.jpg | Bin 0 -> 60954 bytes xbe/assets/moonlight-splash-640x480.jpg | Bin 0 -> 12618 bytes xbe/assets/moonlight-splash-720x480.jpg | Bin 0 -> 13661 bytes xbe/assets/moonlight-splash-800x600.jpg | Bin 0 -> 17389 bytes xbe/assets/moonlight-splash.jpg | Bin 0 -> 12618 bytes 20 files changed, 628 insertions(+), 28 deletions(-) create mode 100644 cmake/modules/FindNXDK.cmake create mode 100644 cmake/modules/FindNXDK_SDL2.cmake create mode 100644 cmake/modules/FindNXDK_SDL2_Image.cmake create mode 100644 cmake/modules/XBEUtils.cmake create mode 100644 src/nxdk/hal/debug.h create mode 100644 src/nxdk/hal/video.h create mode 100644 src/nxdk/hal/xbox.h create mode 100644 src/nxdk/windows.h create mode 100644 src/os.h create mode 100644 xbe/assets/moonlight-splash-1024x768.jpg create mode 100644 xbe/assets/moonlight-splash-1280x720.jpg create mode 100644 xbe/assets/moonlight-splash-1920x1080.jpg create mode 100644 xbe/assets/moonlight-splash-640x480.jpg create mode 100644 xbe/assets/moonlight-splash-720x480.jpg create mode 100644 xbe/assets/moonlight-splash-800x600.jpg create mode 100644 xbe/assets/moonlight-splash.jpg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9e063f..8ed11c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -140,10 +140,7 @@ jobs: # the main target needs to be cross compiled echo "--- Building ALL ---" - cmake \ - -B build \ - -S . \ - -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" cmake --build build # TODO: Tests are not building properly... diff --git a/CMakeLists.txt b/CMakeLists.txt index 93b7f4d..617f5ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,25 +1,47 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.18) # much of this file is borrowed from https://github.com/Ryzee119/Xenium-Tools/blob/master/CMakeLists.txt +# and https://github.com/abaire/nxdk_pgraph_tests/blob/main/CMakeLists.txt project(Moonlight C CXX) -set(XBE_TITLE ${CMAKE_PROJECT_NAME}) -message(STATUS "XBE_TITLE: ${XBE_TITLE}") +# +# metadata +# +set(XBE_TITLE ${CMAKE_PROJECT_NAME}) set(NXDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third-party/nxdk") -message(STATUS "NXDK_DIR: ${NXDK_DIR}") - set(XBOX_XBE_DIR "${CMAKE_CURRENT_BINARY_DIR}/xbe") set(XBOX_ISO "${CMAKE_PROJECT_NAME}.iso") -message(STATUS "XBOX_ISO: ${XBOX_ISO}") # # Options # option(BUILD_TESTS "Build tests" OFF) +# add custom modules +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/" +) + +set(CMAKE_CXX_STANDARD 17) + +include(XBEUtils REQUIRED) + +find_package(NXDK REQUIRED) +find_package(NXDK_SDL2 REQUIRED) +find_package(NXDK_SDL2_Image REQUIRED) + +# add the automount_d_drive symbol to the linker flags, this is automatic with nxdk when using the Makefile option +# if this is not used, we must add some code to the main function to automount the D drive +# e.g. https://github.com/abaire/nxdk_pgraph_tests/blob/4b7934e6d612a6d17f9ec229a2d72601a5caefc4/src/main.cpp#L118-L122 +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -include:_automount_d_drive") + # create the xbe directory if it doesn't exist file(MAKE_DIRECTORY ${XBOX_XBE_DIR}) +# copy assets directory to xbe directory +file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/xbe/" DESTINATION "${XBOX_XBE_DIR}") + include(FindPkgConfig) if(NOT CMAKE_BUILD_TYPE) @@ -32,10 +54,12 @@ set(MOONLIGHT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp" ) set(MOONLIGHT_EXTERNAL_LIBRARIES - "${NXDK_DIR}/lib/libpbkit.lib" + NXDK::NXDK + NXDK::NXDK_CXX + NXDK::SDL2 + NXDK::SDL2_Image ) set(MOONLIGHT_INCLUDE_DIRS - "${NXDK_DIR}/lib" ) set(MOONLIGHT_COMPILE_OPTIONS "-Wno-builtin-macro-redefined") set(MOONLIGHT_DEFINITIONS "-DXBOX -DNXDK") @@ -47,7 +71,9 @@ add_definitions(${MOONLIGHT_DEFINITIONS}) add_executable(${CMAKE_PROJECT_NAME} ${MOONLIGHT_SOURCES} ) -include_directories(SYSTEM ${MOONLIGHT_INCLUDE_DIRS}) +include_directories(SYSTEM + ${CMAKE_CURRENT_SOURCE_DIR} + ${MOONLIGHT_INCLUDE_DIRS}) target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC ${MOONLIGHT_EXTERNAL_LIBRARIES} ) @@ -85,3 +111,6 @@ add_dependencies(xbe_iso cxbe_convert) set_target_properties(cxbe_convert PROPERTIES OUTPUT_QUIET ON) set_target_properties(xbe_iso PROPERTIES OUTPUT_QUIET ON) + +#add_xbe(xbe_file "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.exe" TITLE "${XBE_TITLE}" RESOURCE_DIRS xbe) +#add_xiso(xiso xbe_file XISO ${XBOX_ISO}) diff --git a/README.md b/README.md index 3499796..a95eddc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,37 @@ # Moonlight-XboxOG Port of Moonlight for the Original Xbox. Unlikely to ever actually work. Do NOT use. + + +## Build + +### Pre Build + +1. Install nxdk prerequisites. Then run the following from mingw64 or bash shell: + +```bash +nxdk_dir="$(pwd)/third-party/nxdk" +eval "$(${nxdk_dir}/bin/activate -s)" +cd "${nxdk_dir}" +make NXDK_ONLY=y +make tools +``` + +### Configure + +1. Create build directory + + ```bash + mkdir -p build + ``` + +2. Configure the project + + ```bash + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" + ``` + +### Build + +```bash +cmake --build build +``` diff --git a/cmake/modules/FindNXDK.cmake b/cmake/modules/FindNXDK.cmake new file mode 100644 index 0000000..66bfc00 --- /dev/null +++ b/cmake/modules/FindNXDK.cmake @@ -0,0 +1,108 @@ +if(NOT TARGET NXDK::NXDK) + add_library(nxdk STATIC IMPORTED) + set_target_properties( + nxdk + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libnxdk.lib" + ) + + add_library(nxdk_automount_d STATIC IMPORTED) + set_target_properties( + nxdk_automount_d + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libnxdk_automount_d.lib" + ) + + add_library(nxdk_hal STATIC IMPORTED) + set_target_properties( + nxdk_hal + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libnxdk_hal.lib" + ) + + add_library(nxdk_net STATIC IMPORTED) + set_target_properties( + nxdk_net + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libnxdk_net.lib" + ) + + add_library(nxdk_usb STATIC IMPORTED) + set_target_properties( + nxdk_usb + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/nxdk_usb.lib" + ) + + add_library(pbkit STATIC IMPORTED) + set_target_properties( + pbkit + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libpbkit.lib" + ) + + add_library(pdclib STATIC IMPORTED) + set_target_properties( + pdclib + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libpdclib.lib" + ) + + add_library(winapi STATIC IMPORTED) + set_target_properties( + winapi + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libwinapi.lib" + ) + + add_library(winmm STATIC IMPORTED) + set_target_properties( + winmm + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/winmm.lib" + ) + + add_library(xboxrt STATIC IMPORTED) + set_target_properties( + xboxrt + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libxboxrt.lib" + ) + + add_library(zlib STATIC IMPORTED) + set_target_properties( + zlib + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libzlib.lib" + ) + + add_library(NXDK::NXDK INTERFACE IMPORTED) + target_link_libraries( + NXDK::NXDK + INTERFACE + nxdk + nxdk_automount_d + nxdk_hal + nxdk_net + nxdk_usb + pbkit + pdclib + winapi + winmm + xboxrt + zlib + ) +endif() + +if(NOT TARGET NXDK::NXDK_CXX) + + add_library(nxdk_cxx STATIC IMPORTED) + set_target_properties( + nxdk_cxx + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libc++.lib" + ) + + add_library(NXDK::NXDK_CXX INTERFACE IMPORTED) + target_link_libraries(NXDK::NXDK_CXX INTERFACE nxdk_cxx) +endif() diff --git a/cmake/modules/FindNXDK_SDL2.cmake b/cmake/modules/FindNXDK_SDL2.cmake new file mode 100644 index 0000000..ed6d129 --- /dev/null +++ b/cmake/modules/FindNXDK_SDL2.cmake @@ -0,0 +1,6 @@ +if(NOT TARGET NXDK::SDL2) + find_package(PkgConfig REQUIRED) + pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2) + + add_library(NXDK::SDL2 ALIAS PkgConfig::SDL2) +endif() diff --git a/cmake/modules/FindNXDK_SDL2_Image.cmake b/cmake/modules/FindNXDK_SDL2_Image.cmake new file mode 100644 index 0000000..c3b5fb2 --- /dev/null +++ b/cmake/modules/FindNXDK_SDL2_Image.cmake @@ -0,0 +1,37 @@ +if (NOT TARGET NXDK::SDL2_Image) + + add_library(nxdk_sdl2_image STATIC IMPORTED) + set_target_properties( + nxdk_sdl2_image + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libSDL2_image.lib" + ) + + add_library(nxdk_jpeg STATIC IMPORTED) + set_target_properties( + nxdk_jpeg + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libjpeg.lib" + ) + + add_library(nxdk_png STATIC IMPORTED) + set_target_properties( + nxdk_png + PROPERTIES + IMPORTED_LOCATION "${NXDK_DIR}/lib/libpng.lib" + ) + + add_library(NXDK::SDL2_Image INTERFACE IMPORTED) + target_link_libraries( + NXDK::SDL2_Image + INTERFACE + nxdk_sdl2_image + nxdk_jpeg + nxdk_png + ) + target_include_directories( + NXDK::SDL2_Image + SYSTEM INTERFACE + "${NXDK_DIR}/lib/sdl/SDL2_image" + ) +endif () diff --git a/cmake/modules/XBEUtils.cmake b/cmake/modules/XBEUtils.cmake new file mode 100644 index 0000000..155b813 --- /dev/null +++ b/cmake/modules/XBEUtils.cmake @@ -0,0 +1,214 @@ +# Provides: +# +# add_xbe( +# target executable_file +# [XBENAME ="default.xbe"] +# [TITLE ={target}] +# RESOURCE_FILES +# RESOURCE_DIRS +# ) +# +# Generates an XBE file from the given executable_file. The given resources will be attached such that add_xiso will +# include them in the root of the generated iso. +# +# add_xiso(target xbe_target [XISO =.xiso]) +# Generates an xiso image for the given XBE. + +include(CMakeParseArguments) + +set(CXBE_TOOL_PATH "${NXDK_DIR}/tools/cxbe/cxbe") +set(EXTRACT_XISO_TOOL_PATH "${NXDK_DIR}/tools/extract-xiso/build/extract-xiso") + +# Makes each path in the given list into an absolute path. +function(_make_abs_paths list_name) + foreach (src "${${list_name}}") + get_filename_component(abs "${src}" ABSOLUTE) + list(APPEND ret "${abs}") + endforeach () + set(${list_name} ${ret} PARENT_SCOPE) +endfunction() + +# split_debug(executable_target) +# +# Splits debugging information from the given `executable_target`, generating a companion file ending in ".debug.exe". +function(split_debug) + if (${ARGC} LESS 1) + message(FATAL_ERROR "Missing required 'executable_target' parameter.") + endif () + + set(executable_target "${ARGV0}") + set(exe_file "${CMAKE_BINARY_DIR}/${executable_target}.exe") + get_filename_component(exe_dirname "${CMAKE_BINARY_DIR}/${executable_target}" DIRECTORY) + get_filename_component(exe_basename "${executable_target}" NAME_WE) + set(output "${exe_dirname}/${exe_basename}.debug.exe") + + add_custom_command( + TARGET "${executable_target}" + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy "${exe_file}" "${output}" + COMMAND "${CMAKE_OBJCOPY}" --strip-debug "${exe_file}" + COMMAND "${CMAKE_OBJCOPY}" "--add-gnu-debuglink=${output}" "${exe_file}" + COMMENT Splitting debug information to reduce binary size... + VERBATIM + BYPRODUCTS "${output}" + ) +endfunction() + +function(add_xbe) + cmake_parse_arguments( + PARSE_ARGV + 2 + "XBE" + "" + "XBENAME;TITLE" + "RESOURCE_FILES;RESOURCE_DIRS" + ) + + if (${ARGC} LESS 1) + message(FATAL_ERROR "Missing required 'target' parameter.") + elseif (${ARGC} LESS 2) + message(FATAL_ERROR "Missing required 'executable_file' parameter.") + endif () + + set(target "${ARGV0}") + set(exe_file "${ARGV1}") + + if (NOT XBE_XBENAME) + set(XBE_XBENAME default.xbe) + endif () + + if (NOT XBE_TITLE) + set(XBE_TITLE "${target}") + endif () + + set( + "${target}_XBE_STAGING_DIR" + "${CMAKE_CURRENT_BINARY_DIR}/xbe/${target}" + CACHE INTERNAL + "Directory into which the raw sources for an xiso have been placed." + ) + set(XBE_STAGING_DIR "${${target}_XBE_STAGING_DIR}") + + set( + "${target}_XBE_OUTPUT_PATH" + "${${target}_XBE_STAGING_DIR}/${XBE_XBENAME}" + CACHE INTERNAL + "XBE file that should be added to an xiso." + ) + set(XBE_OUTPUT_PATH "${${target}_XBE_OUTPUT_PATH}") + + add_custom_command( + OUTPUT "${XBE_STAGING_DIR}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${XBE_STAGING_DIR}" + ) + + file(MAKE_DIRECTORY "${XBE_STAGING_DIR}") + + add_custom_command( + OUTPUT "${XBE_OUTPUT_PATH}" + COMMAND "${CXBE_TOOL_PATH}" + "-TITLE:${XBE_TITLE}" + "-OUT:${XBE_OUTPUT_PATH}" + "${exe_file}" + DEPENDS "${exe_file}" + ) + + # Copy resources to the staging directory. + set( + "${target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH" + CACHE + INTERNAL + "Timestamp file indicating that resource files have been copied." + ) + if (XBE_RESOURCE_FILES) + set( + "${target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH" + "${CMAKE_CURRENT_BINARY_DIR}/xbe/.${target}_resource_files_time" + CACHE + INTERNAL + "Timestamp file indicating that resource files have been copied." + ) + set(RESOURCE_FILES_RECEIPT "${${target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH}") + _make_abs_paths(XBE_RESOURCE_FILES) + add_custom_command( + OUTPUT "${RESOURCE_FILES_RECEIPT}" + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "${XBE_RESOURCE_FILES}" "${XBE_STAGING_DIR}" + COMMAND + "${CMAKE_COMMAND}" -E touch "${RESOURCE_FILES_RECEIPT}" + DEPENDS ${XBE_RESOURCE_FILES} + ) + endif () + + set( + "${target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH" + CACHE + INTERNAL + "Timestamp file indicating that resource directories have been copied." + ) + if (XBE_RESOURCE_DIRS) + set( + "${target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH" + "${CMAKE_CURRENT_BINARY_DIR}/xbe/.${target}_resource_dirs_time" + CACHE INTERNAL + "Timestamp file indicating that resource directories have been copied." + ) + set(RESOURCE_DIRS_RECEIPT "${${target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH}") + _make_abs_paths(XBE_RESOURCE_DIRS) + add_custom_command( + OUTPUT "${RESOURCE_DIRS_RECEIPT}" + COMMAND + "${CMAKE_COMMAND}" -E env pwd + COMMAND + "${CMAKE_COMMAND}" -E copy_directory ${XBE_RESOURCE_DIRS} "${XBE_STAGING_DIR}" + COMMAND + "${CMAKE_COMMAND}" -E touch "${RESOURCE_DIRS_RECEIPT}" + DEPENDS ${XBE_RESOURCE_DIRS} + ) + endif () +endfunction() + +function(add_xiso) + cmake_parse_arguments( + PARSE_ARGV + 2 + "XISO" + "" + "XISO" + "" + ) + + if (${ARGC} LESS 1) + message(FATAL_ERROR "Missing required 'target' parameter.") + elseif (${ARGC} LESS 2) + message(FATAL_ERROR "Missing required 'xbe_target' parameter.") + endif () + set(target "${ARGV0}") + set(xbe_target "${ARGV1}") + + if (NOT XISO_XISO) + set(XISO_XISO "${target}.iso") + endif () + + set(XBE_STAGING_DIR "${${xbe_target}_XBE_STAGING_DIR}") + set(XBE_OUTPUT_PATH "${${xbe_target}_XBE_OUTPUT_PATH}") + set(XBE_RESOURCE_FILES_RECEIPT "${${xbe_target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH}") + set(XBE_RESOURCE_DIRS_RECEIPT "${${xbe_target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH}") + set(XISO_STAGING_DIR "${CMAKE_CURRENT_BINARY_DIR}/xiso/${target}") + set(XISO_OUTPUT_PATH "${XISO_XISO}") + + add_custom_command( + OUTPUT "${XISO_OUTPUT_PATH}" + COMMAND "${EXTRACT_XISO_TOOL_PATH}" -c "${XBE_STAGING_DIR}" "${XISO_OUTPUT_PATH}" + DEPENDS + "${XBE_OUTPUT_PATH}" + "${XBE_RESOURCE_FILES_RECEIPT}" + "${XBE_RESOURCE_DIRS_RECEIPT}" + ) + + add_custom_target( + "${target}" + ALL + DEPENDS + "${XISO_OUTPUT_PATH}") +endfunction() diff --git a/src/main.cpp b/src/main.cpp index 0b121d8..e0f7660 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,24 +1,170 @@ -#include -#include +// standard includes +#include +#include #include #include -#include -int main(void) { - XVideoSetMode(640, 480, 32, REFRESH_DEFAULT); +// lib includes +#include +#include - std::vector words; - words.emplace_back("Hello,"); - words.emplace_back(" "); - words.emplace_back("Moonlight!"); - words.emplace_back("\n"); +// nxdk includes +#include "src/nxdk/hal/debug.h" +#include "src/nxdk/hal/xbox.h" +#include "src/nxdk/hal/video.h" +#include "src/nxdk/windows.h" - while (true) { - for (auto& word : words) { - debugPrint("%s", word.c_str()); +// local includes +#include "src/os.h" + +static void printSDLErrorAndReboot() +{ + debugPrint("SDL_Error: %s\n", SDL_GetError()); + debugPrint("Rebooting in 5 seconds.\n"); + Sleep(5000); + XReboot(); +} + +static void printIMGErrorAndReboot() +{ + debugPrint("SDL_Image Error: %s\n", IMG_GetError()); + debugPrint("Rebooting in 5 seconds.\n"); + Sleep(5000); + XReboot(); +} + +// Screen dimension globals +static int SCREEN_WIDTH; +static int SCREEN_HEIGHT; + +void splash_screen() +{ + int done = 0; + SDL_Window *window; + SDL_Event event; + SDL_Surface *screenSurface, *imageSurface; + + // Enable standard application logging + SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + + if (SDL_VideoInit(NULL) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL video.\n"); + printSDLErrorAndReboot(); + } + + window = SDL_CreateWindow("splash", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + SCREEN_WIDTH, SCREEN_HEIGHT, + SDL_WINDOW_SHOWN); + if(window == NULL) + { + debugPrint( "Window could not be created!\n"); + SDL_VideoQuit(); + printSDLErrorAndReboot(); + } + + if (!(IMG_Init(IMG_INIT_JPG) & IMG_INIT_JPG)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't intialize SDL_image.\n"); + SDL_VideoQuit(); + printIMGErrorAndReboot(); + } + + screenSurface = SDL_GetWindowSurface(window); + if (!screenSurface) { + SDL_VideoQuit(); + printSDLErrorAndReboot(); + } + + // set string variable for splash screen path + std::string _splashScreenPath = std::string(DATA_PATH) + "assets" + PATH_SEP + "moonlight-splash-" + + std::to_string(SCREEN_WIDTH) + "x" + std::to_string(SCREEN_HEIGHT) + ".jpg"; + const char *splashScreenPath = _splashScreenPath.c_str(); + + imageSurface = IMG_Load(splashScreenPath); + if (!imageSurface) { + SDL_VideoQuit(); + printIMGErrorAndReboot(); + } + + while (!done) { + // Check for events + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + done = 1; + break; + default: + break; + } + } + + SDL_BlitSurface(imageSurface, NULL, screenSurface, NULL); + SDL_UpdateWindowSurface(window); + + Sleep(1000); + } + + SDL_VideoQuit(); +} + +int main() +{ + // create an empty list for the available video modes + std::vector availableVideoModes; + + // save the best video mode here + VIDEO_MODE bestVideoMode = {0, 0, 0, 0}; + + VIDEO_MODE vm; + int bpp = 32; // Bits per pixel + void *p = NULL; // Initialize to NULL for the first call + + // get the available video modes + while (XVideoListModes(&vm, bpp, REFRESH_DEFAULT, &p)) { + availableVideoModes.push_back(vm); + + // ensure height is equal to or better than the current best video mode + if (vm.height < bestVideoMode.height) { + continue; + } + + // ensure width is equal to or better than the current best video mode + if (vm.width < bestVideoMode.width) { + continue; + } + + // ensure bpp is equal to or better than the current best video mode + if (vm.bpp < bestVideoMode.bpp) { + continue; + } + + // ensure refresh is equal to or better than the current best video mode + if (vm.refresh < bestVideoMode.refresh) { + continue; + } + + // save the best video mode + bestVideoMode = vm; + } + + SCREEN_WIDTH = bestVideoMode.width; + SCREEN_HEIGHT = bestVideoMode.height; + + XVideoSetMode(640, 480, 32, REFRESH_DEFAULT); + + debugPrint("Available video modes:\n"); + for (VIDEO_MODE vm : availableVideoModes) { + debugPrint("Width: %d, Height: %d, BPP: %d, Refresh: %d\n", vm.width, vm.height, vm.bpp, vm.refresh); } + + debugPrint("Best video mode:\n"); + debugPrint("Width: %d, Height: %d, BPP: %d, Refresh: %d\n", bestVideoMode.width, bestVideoMode.height, bestVideoMode.bpp, bestVideoMode.refresh); + Sleep(2000); - } - return 0; + XVideoSetMode(SCREEN_WIDTH, SCREEN_HEIGHT, bestVideoMode.bpp, bestVideoMode.refresh); + + splash_screen(); + return 0; } diff --git a/src/nxdk/hal/debug.h b/src/nxdk/hal/debug.h new file mode 100644 index 0000000..92ca5c2 --- /dev/null +++ b/src/nxdk/hal/debug.h @@ -0,0 +1,5 @@ +#if defined(NXDK) +#include +#else +#define debugPrint(...) printf(__VA_ARGS__) +#endif diff --git a/src/nxdk/hal/video.h b/src/nxdk/hal/video.h new file mode 100644 index 0000000..e72d067 --- /dev/null +++ b/src/nxdk/hal/video.h @@ -0,0 +1,6 @@ +#if defined(NXDK) +#include +#else +#define XVideoSetMode(...) +#define XVideoWaitForVBlank(...) SDL_Delay(1000/60-1) +#endif diff --git a/src/nxdk/hal/xbox.h b/src/nxdk/hal/xbox.h new file mode 100644 index 0000000..8bf9a2b --- /dev/null +++ b/src/nxdk/hal/xbox.h @@ -0,0 +1,5 @@ +#if defined(NXDK) +#include +#else +#define XReboot(...) exit(555) +#endif diff --git a/src/nxdk/windows.h b/src/nxdk/windows.h new file mode 100644 index 0000000..b683674 --- /dev/null +++ b/src/nxdk/windows.h @@ -0,0 +1,5 @@ +#if defined(NXDK) +#include +#else +#define Sleep(x) SDL_Delay(x) +#endif diff --git a/src/os.h b/src/os.h new file mode 100644 index 0000000..8e1db63 --- /dev/null +++ b/src/os.h @@ -0,0 +1,7 @@ +#if defined(NXDK) +#define PATH_SEP "\\" +#define DATA_PATH "D:" PATH_SEP +#else +#define PATH_SEP "/" +#define DATA_PATH "." PATH_SEP +#endif diff --git a/xbe/assets/moonlight-splash-1024x768.jpg b/xbe/assets/moonlight-splash-1024x768.jpg new file mode 100644 index 0000000000000000000000000000000000000000..24cfaeca947008f82cdfc7f550f3fba71f35aacf GIT binary patch literal 25736 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 Uq#>jsq#>jsq~V`w2xs^H0|a_b=>Px# literal 0 HcmV?d00001 diff --git a/xbe/assets/moonlight-splash-1920x1080.jpg b/xbe/assets/moonlight-splash-1920x1080.jpg new file mode 100644 index 0000000000000000000000000000000000000000..60f6958f94d66916dc88a1f6bab2133465eb4389 GIT binary patch literal 60954 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 E0C7W>a{vGU literal 0 HcmV?d00001 diff --git a/xbe/assets/moonlight-splash-640x480.jpg b/xbe/assets/moonlight-splash-640x480.jpg new file mode 100644 index 0000000000000000000000000000000000000000..193701dea1ea8bb9993b9bc8faa8bdd0072f8af5 GIT binary patch literal 12618 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 bo=lcdtAtu540*zkCk%N)%kj4rh~M`=ipEQz literal 0 HcmV?d00001 diff --git a/xbe/assets/moonlight-splash-720x480.jpg b/xbe/assets/moonlight-splash-720x480.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7bb9e106d1e584141ea5b544f905235e5a8794ea GIT binary patch literal 13661 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 literal 0 HcmV?d00001 diff --git a/xbe/assets/moonlight-splash-800x600.jpg b/xbe/assets/moonlight-splash-800x600.jpg new file mode 100644 index 0000000000000000000000000000000000000000..484238d1c5a863acc6b3e559f34e5da1f1a98d2f GIT binary patch literal 17389 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 literal 0 HcmV?d00001 diff --git a/xbe/assets/moonlight-splash.jpg b/xbe/assets/moonlight-splash.jpg new file mode 100644 index 0000000000000000000000000000000000000000..193701dea1ea8bb9993b9bc8faa8bdd0072f8af5 GIT binary patch literal 12618 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 bo=lcdtAtu540*zkCk%N)%kj4rh~M`=ipEQz literal 0 HcmV?d00001 From f6db76e9f084ae4e59e7c86219dd760692005319 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:34:17 -0400 Subject: [PATCH 07/13] update build --- .github/workflows/ci.yml | 8 ++--- README.md | 69 ++++++++++++++++++++++++++++++++++++--- build.sh | 29 ++++++++++++++++ docs/images/loading.png | Bin 0 -> 79430 bytes 4 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 build.sh create mode 100644 docs/images/loading.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ed11c2..c8caa01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,9 +129,9 @@ jobs: - name: Build run: | - nxdk_dir="$(pwd)/third-party/nxdk" - eval "$(${nxdk_dir}/bin/activate -s)" - cd "${nxdk_dir}" + export NXDK_DIR="$(pwd)/third-party/nxdk" + eval "$(${NXDK_DIR}/bin/activate -s)" + cd "${NXDK_DIR}" make NXDK_ONLY=y make tools @@ -140,7 +140,7 @@ jobs: # the main target needs to be cross compiled echo "--- Building ALL ---" - cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${NXDK_DIR}/share/toolchain-nxdk.cmake" cmake --build build # TODO: Tests are not building properly... diff --git a/README.md b/README.md index a95eddc..00d8028 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Moonlight-XboxOG -Port of Moonlight for the Original Xbox. Unlikely to ever actually work. Do NOT use. +Port of Moonlight for the Original Xbox. Unlikely to ever actually work. Do NOT use! + +Nothing works, except the splash screen. + +![Splash Screen](./docs/images/loading.png) ## Build @@ -9,9 +13,9 @@ Port of Moonlight for the Original Xbox. Unlikely to ever actually work. Do NOT 1. Install nxdk prerequisites. Then run the following from mingw64 or bash shell: ```bash -nxdk_dir="$(pwd)/third-party/nxdk" -eval "$(${nxdk_dir}/bin/activate -s)" -cd "${nxdk_dir}" +export NXDK_DIR="$(pwd)/third-party/nxdk" +eval "$(${NXDK_DIR}/bin/activate -s)" +cd "${NXDK_DIR}" make NXDK_ONLY=y make tools ``` @@ -27,7 +31,7 @@ make tools 2. Configure the project ```bash - cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${nxdk_dir}/share/toolchain-nxdk.cmake" + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${NXDK_DIR}/share/toolchain-nxdk.cmake" ``` ### Build @@ -35,3 +39,58 @@ make tools ```bash cmake --build build ``` + +### Combined script + +This script takes care of everything, except installing the prerequisites. + +```bash +./build.sh +``` + +## Todo: + +- Build + - [x] Build in GitHub CI + - [x] Build with CMake instead of Make, see https://github.com/Ryzee119/Xenium-Tools/blob/master/CMakeLists.txt and https://github.com/abaire/nxdk_pgraph_tests/blob/4b7934e6d612a6d17f9ec229a2d72601a5caefc4/CMakeLists.txt + - [ ] Get build environment working with CLion directly instead of using external terminal + - [ ] debugger, see https://github.com/abaire/xbdm_gdb_bridge + - [ ] Add a run config for CLion, see https://github.com/Subtixx/XSampleProject + - [ ] Automatically run built xiso in Xemu + - [x] Add unit testing framework + - [x] Separate main build and unit test builds, due to cross compiling, see https://stackoverflow.com/a/64335131/11214013 + - [ ] Get tests to properly compile + - [ ] Enable codecov + - [ ] Enable sonarcloud + - [ ] Build moonlight-common-c + - [ ] Build custom enet, depends on https://github.com/XboxDev/nxdk/pull/680 or https://github.com/thrimbor/nxdk/tree/winsock/lib/winapi (seems unlikely nxdk will ever be ready for this, could definitely use some help with this) +- Menus / Screens + - [x] Loading/splash screen + - [x] Initial loading screen, see https://github.com/XboxDev/nxdk/blob/master/samples/sdl_image/main.c + - [x] Set video mode based on best available mode + - [x] dynamic splash screen (size based on current resolution) + - [ ] two images (background color, and logo) to reduce total size... stretch background color image... or don't even use an image for the background + - [ ] Main/Home + - [ ] Settings + - [ ] Add Host + - [ ] Game/App Selection + - [ ] Host Details + - [ ] App Details + - [ ] Pause/Hotkey overlay +- Streaming + - [ ] Video - https://www.xbmc4xbox.org.uk/wiki/XBMC_Features_and_Supported_Formats#Xbox_supported_video_formats_and_resolutions + - [ ] Audio + - [ ] Mono + - [ ] Stereo + - [ ] 5.1 Surround + - [ ] 7.1 Surround +- Input + - [ ] Gamepad Input + - [ ] Keyboard Input + - [ ] Mouse Input + - [ ] Mouse Emulation via Gamepad +- Misc. + - [ ] Save config and pairing states, probably use nlohmann/json + - [ ] Host pairing + - [ ] Possibly, GPU overclocking, see https://github.com/GXTX/XboxOverclock + - [ ] Docs via doxygen diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..a149e74 --- /dev/null +++ b/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status +set -e + +# Set the NXDK_DIR environment variable +export NXDK_DIR="$(pwd)/third-party/nxdk" + +# Activate the nxdk environment +eval "$(${NXDK_DIR}/bin/activate -s)" + +# Navigate to the nxdk directory +cd "${NXDK_DIR}" + +# Build nxdk with the specified options +make NXDK_ONLY=y +make tools + +# Navigate back to the project root directory +cd - + +# Create the build directory +mkdir -p build + +# Configure the project +cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${NXDK_DIR}/share/toolchain-nxdk.cmake" + +# Build the project +cmake --build build diff --git a/docs/images/loading.png b/docs/images/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..139ccbabe6c5854c786d586410030fecb9462f7f GIT binary patch literal 79430 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 z z=Wxy26YXrXM6#5AoC*g_%^ZYvn3*KZPI}GB4Q`Z!#XF&XCNDS7v@yrD6B&)nUc0oW z8^@x4jI5SmW9Op{?YU0vgk7lF54Ek=%%PGt_AlYUWr@U0N8*4oh%-xN zei@AB-L8h-U>pCV8Z80qI?uW?rJ23Wp$8M~emXYhXS=z#gGJH_rT-do=E1dDo0;gS zt>tlZvsX!Tgdjty#Vp z49Wq@EFwZoK-hQW;0-9ACkdF-#NHF;5U9Bi6mv;zN_zPx#hW*J<41wN2Wm_*{x6n~Gt zms{9hy*NG47qE_+B1KUA=@DR5_p5JZ-385VcJC+7UoX(H0<)vYv!0Vdda!ss;&)4` zga?|nLP~Q7y{wZgc|n<%y^BS=2riOb3D;tbzsLDXdp}Inz(egAJP4dE<^RojaoFsO z!Vm#yKr5w{H1Ap}MP<4TK~#}~Dw_F&OfP#5d^f;_6{$1?!e`s-kO$EgA?ra2qv5#~ z4eP=nq%7o4nN^0SJVN5pShgvIq}71S@f2>C6leBQ8I~9{wS=asA1cK#DAMw1*8#UCzbyu*J8R1Wmx9YE zydO>XJqj&-w??Y?SFHP}lh5hhQhV)Z;j_%pPP8M+$X*@B)Sh}G)n@rPzH15iJFxfQ Kp7h=Jm;ML&5Kt5V literal 0 HcmV?d00001 From 94b597e9f87f5d1bc2a923afadf1e17914be8d37 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:41:03 -0400 Subject: [PATCH 08/13] Delete XBEUtils.cmake --- cmake/modules/XBEUtils.cmake | 214 ----------------------------------- 1 file changed, 214 deletions(-) delete mode 100644 cmake/modules/XBEUtils.cmake diff --git a/cmake/modules/XBEUtils.cmake b/cmake/modules/XBEUtils.cmake deleted file mode 100644 index 155b813..0000000 --- a/cmake/modules/XBEUtils.cmake +++ /dev/null @@ -1,214 +0,0 @@ -# Provides: -# -# add_xbe( -# target executable_file -# [XBENAME ="default.xbe"] -# [TITLE ={target}] -# RESOURCE_FILES -# RESOURCE_DIRS -# ) -# -# Generates an XBE file from the given executable_file. The given resources will be attached such that add_xiso will -# include them in the root of the generated iso. -# -# add_xiso(target xbe_target [XISO =.xiso]) -# Generates an xiso image for the given XBE. - -include(CMakeParseArguments) - -set(CXBE_TOOL_PATH "${NXDK_DIR}/tools/cxbe/cxbe") -set(EXTRACT_XISO_TOOL_PATH "${NXDK_DIR}/tools/extract-xiso/build/extract-xiso") - -# Makes each path in the given list into an absolute path. -function(_make_abs_paths list_name) - foreach (src "${${list_name}}") - get_filename_component(abs "${src}" ABSOLUTE) - list(APPEND ret "${abs}") - endforeach () - set(${list_name} ${ret} PARENT_SCOPE) -endfunction() - -# split_debug(executable_target) -# -# Splits debugging information from the given `executable_target`, generating a companion file ending in ".debug.exe". -function(split_debug) - if (${ARGC} LESS 1) - message(FATAL_ERROR "Missing required 'executable_target' parameter.") - endif () - - set(executable_target "${ARGV0}") - set(exe_file "${CMAKE_BINARY_DIR}/${executable_target}.exe") - get_filename_component(exe_dirname "${CMAKE_BINARY_DIR}/${executable_target}" DIRECTORY) - get_filename_component(exe_basename "${executable_target}" NAME_WE) - set(output "${exe_dirname}/${exe_basename}.debug.exe") - - add_custom_command( - TARGET "${executable_target}" - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy "${exe_file}" "${output}" - COMMAND "${CMAKE_OBJCOPY}" --strip-debug "${exe_file}" - COMMAND "${CMAKE_OBJCOPY}" "--add-gnu-debuglink=${output}" "${exe_file}" - COMMENT Splitting debug information to reduce binary size... - VERBATIM - BYPRODUCTS "${output}" - ) -endfunction() - -function(add_xbe) - cmake_parse_arguments( - PARSE_ARGV - 2 - "XBE" - "" - "XBENAME;TITLE" - "RESOURCE_FILES;RESOURCE_DIRS" - ) - - if (${ARGC} LESS 1) - message(FATAL_ERROR "Missing required 'target' parameter.") - elseif (${ARGC} LESS 2) - message(FATAL_ERROR "Missing required 'executable_file' parameter.") - endif () - - set(target "${ARGV0}") - set(exe_file "${ARGV1}") - - if (NOT XBE_XBENAME) - set(XBE_XBENAME default.xbe) - endif () - - if (NOT XBE_TITLE) - set(XBE_TITLE "${target}") - endif () - - set( - "${target}_XBE_STAGING_DIR" - "${CMAKE_CURRENT_BINARY_DIR}/xbe/${target}" - CACHE INTERNAL - "Directory into which the raw sources for an xiso have been placed." - ) - set(XBE_STAGING_DIR "${${target}_XBE_STAGING_DIR}") - - set( - "${target}_XBE_OUTPUT_PATH" - "${${target}_XBE_STAGING_DIR}/${XBE_XBENAME}" - CACHE INTERNAL - "XBE file that should be added to an xiso." - ) - set(XBE_OUTPUT_PATH "${${target}_XBE_OUTPUT_PATH}") - - add_custom_command( - OUTPUT "${XBE_STAGING_DIR}" - COMMAND "${CMAKE_COMMAND}" -E make_directory "${XBE_STAGING_DIR}" - ) - - file(MAKE_DIRECTORY "${XBE_STAGING_DIR}") - - add_custom_command( - OUTPUT "${XBE_OUTPUT_PATH}" - COMMAND "${CXBE_TOOL_PATH}" - "-TITLE:${XBE_TITLE}" - "-OUT:${XBE_OUTPUT_PATH}" - "${exe_file}" - DEPENDS "${exe_file}" - ) - - # Copy resources to the staging directory. - set( - "${target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH" - CACHE - INTERNAL - "Timestamp file indicating that resource files have been copied." - ) - if (XBE_RESOURCE_FILES) - set( - "${target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH" - "${CMAKE_CURRENT_BINARY_DIR}/xbe/.${target}_resource_files_time" - CACHE - INTERNAL - "Timestamp file indicating that resource files have been copied." - ) - set(RESOURCE_FILES_RECEIPT "${${target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH}") - _make_abs_paths(XBE_RESOURCE_FILES) - add_custom_command( - OUTPUT "${RESOURCE_FILES_RECEIPT}" - COMMAND - "${CMAKE_COMMAND}" -E copy_if_different "${XBE_RESOURCE_FILES}" "${XBE_STAGING_DIR}" - COMMAND - "${CMAKE_COMMAND}" -E touch "${RESOURCE_FILES_RECEIPT}" - DEPENDS ${XBE_RESOURCE_FILES} - ) - endif () - - set( - "${target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH" - CACHE - INTERNAL - "Timestamp file indicating that resource directories have been copied." - ) - if (XBE_RESOURCE_DIRS) - set( - "${target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH" - "${CMAKE_CURRENT_BINARY_DIR}/xbe/.${target}_resource_dirs_time" - CACHE INTERNAL - "Timestamp file indicating that resource directories have been copied." - ) - set(RESOURCE_DIRS_RECEIPT "${${target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH}") - _make_abs_paths(XBE_RESOURCE_DIRS) - add_custom_command( - OUTPUT "${RESOURCE_DIRS_RECEIPT}" - COMMAND - "${CMAKE_COMMAND}" -E env pwd - COMMAND - "${CMAKE_COMMAND}" -E copy_directory ${XBE_RESOURCE_DIRS} "${XBE_STAGING_DIR}" - COMMAND - "${CMAKE_COMMAND}" -E touch "${RESOURCE_DIRS_RECEIPT}" - DEPENDS ${XBE_RESOURCE_DIRS} - ) - endif () -endfunction() - -function(add_xiso) - cmake_parse_arguments( - PARSE_ARGV - 2 - "XISO" - "" - "XISO" - "" - ) - - if (${ARGC} LESS 1) - message(FATAL_ERROR "Missing required 'target' parameter.") - elseif (${ARGC} LESS 2) - message(FATAL_ERROR "Missing required 'xbe_target' parameter.") - endif () - set(target "${ARGV0}") - set(xbe_target "${ARGV1}") - - if (NOT XISO_XISO) - set(XISO_XISO "${target}.iso") - endif () - - set(XBE_STAGING_DIR "${${xbe_target}_XBE_STAGING_DIR}") - set(XBE_OUTPUT_PATH "${${xbe_target}_XBE_OUTPUT_PATH}") - set(XBE_RESOURCE_FILES_RECEIPT "${${xbe_target}_RESOURCE_FILES_RECEIPT_OUTPUT_PATH}") - set(XBE_RESOURCE_DIRS_RECEIPT "${${xbe_target}_RESOURCE_DIRS_RECEIPT_OUTPUT_PATH}") - set(XISO_STAGING_DIR "${CMAKE_CURRENT_BINARY_DIR}/xiso/${target}") - set(XISO_OUTPUT_PATH "${XISO_XISO}") - - add_custom_command( - OUTPUT "${XISO_OUTPUT_PATH}" - COMMAND "${EXTRACT_XISO_TOOL_PATH}" -c "${XBE_STAGING_DIR}" "${XISO_OUTPUT_PATH}" - DEPENDS - "${XBE_OUTPUT_PATH}" - "${XBE_RESOURCE_FILES_RECEIPT}" - "${XBE_RESOURCE_DIRS_RECEIPT}" - ) - - add_custom_target( - "${target}" - ALL - DEPENDS - "${XISO_OUTPUT_PATH}") -endfunction() From 7fd44efd515ea961e830f7ab2f0772735185d118 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:44:43 -0400 Subject: [PATCH 09/13] Update CMakeLists.txt --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 617f5ba..6defac5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,8 +25,6 @@ set(CMAKE_MODULE_PATH set(CMAKE_CXX_STANDARD 17) -include(XBEUtils REQUIRED) - find_package(NXDK REQUIRED) find_package(NXDK_SDL2 REQUIRED) find_package(NXDK_SDL2_Image REQUIRED) From 8dd1d78c4f56aca867136aa30ffe810b1d6d80cd Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:49:19 -0400 Subject: [PATCH 10/13] test new clang-format rules --- .clang-format | 62 ++++++++++-- src/main.cpp | 223 +++++++++++++++++++++---------------------- src/nxdk/hal/debug.h | 4 +- src/nxdk/hal/video.h | 6 +- src/nxdk/hal/xbox.h | 4 +- src/nxdk/windows.h | 4 +- src/os.h | 8 +- 7 files changed, 172 insertions(+), 139 deletions(-) diff --git a/.clang-format b/.clang-format index e72a1e2..c6ab9f9 100644 --- a/.clang-format +++ b/.clang-format @@ -6,27 +6,34 @@ # Generated from CLion C/C++ Code Style settings BasedOnStyle: LLVM AccessModifierOffset: -2 -AlignAfterOpenBracket: DontAlign -AlignConsecutiveAssignments: false +AlignAfterOpenBracket: BlockIndent +AlignConsecutiveAssignments: None +AlignEscapedNewlines: DontAlign AlignOperands: Align AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Always +AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLambdasOnASingleLine: All +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None AllowShortLoopsOnASingleLine: true AlignTrailingComments: false -AlwaysBreakAfterReturnType: All +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: MultiLine -BreakBeforeBraces: Custom +BinPackArguments: false +BinPackParameters: false +BracedInitializerIndentWidth: 2 BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: Never AfterEnum: false + AfterExternBlock: true AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false @@ -36,39 +43,74 @@ BraceWrapping: IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: true +BreakArrays: true BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon ColumnLimit: 0 CompactNamespaces: false ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always +ExperimentalAutoDetectBinPacking: true +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentAccessModifiers: false +IndentCaseBlocks: true IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: true IndentPPDirectives: BeforeHash IndentWidth: 2 +IndentWrappedFunctionNames: true +InsertBraces: true +InsertNewlineAtEOF: true KeepEmptyLinesAtTheStartOfBlocks: false +LineEnding: LF MaxEmptyLinesToKeep: 1 NamespaceIndentation: All +ObjCBinPackProtocolList: Never ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: Never +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 1 +PenaltyBreakString: 1 +PenaltyBreakFirstLessLess: 0 +PenaltyExcessCharacter: 1000000 PointerAlignment: Right +ReferenceAlignment: Pointer ReflowComments: true +RemoveBracesLLVM: false +RemoveSemicolon: false +SeparateDefinitionBlocks: Always +SortIncludes: CaseInsensitive +SortUsingDeclarations: Lexicographic SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true +SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: true SpaceBeforeCtorInitializerColon: false SpaceBeforeInheritanceColon: false +SpaceBeforeJsonColon: false SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: Never SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Maximum: 3 + Minimum: 1 SpacesInParentheses: false SpacesInSquareBrackets: false TabWidth: 2 -Cpp11BracedListStyle: false UseTab: Never diff --git a/src/main.cpp b/src/main.cpp index e0f7660..367c3cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,161 +10,152 @@ // nxdk includes #include "src/nxdk/hal/debug.h" -#include "src/nxdk/hal/xbox.h" #include "src/nxdk/hal/video.h" +#include "src/nxdk/hal/xbox.h" #include "src/nxdk/windows.h" // local includes #include "src/os.h" -static void printSDLErrorAndReboot() -{ - debugPrint("SDL_Error: %s\n", SDL_GetError()); - debugPrint("Rebooting in 5 seconds.\n"); - Sleep(5000); - XReboot(); +static void printSDLErrorAndReboot() { + debugPrint("SDL_Error: %s\n", SDL_GetError()); + debugPrint("Rebooting in 5 seconds.\n"); + Sleep(5000); + XReboot(); } -static void printIMGErrorAndReboot() -{ - debugPrint("SDL_Image Error: %s\n", IMG_GetError()); - debugPrint("Rebooting in 5 seconds.\n"); - Sleep(5000); - XReboot(); +static void printIMGErrorAndReboot() { + debugPrint("SDL_Image Error: %s\n", IMG_GetError()); + debugPrint("Rebooting in 5 seconds.\n"); + Sleep(5000); + XReboot(); } // Screen dimension globals static int SCREEN_WIDTH; static int SCREEN_HEIGHT; -void splash_screen() -{ - int done = 0; - SDL_Window *window; - SDL_Event event; - SDL_Surface *screenSurface, *imageSurface; +void splash_screen() { + int done = 0; + SDL_Window *window; + SDL_Event event; + SDL_Surface *screenSurface, *imageSurface; - // Enable standard application logging - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + // Enable standard application logging + SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); - if (SDL_VideoInit(NULL) < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL video.\n"); - printSDLErrorAndReboot(); - } + if (SDL_VideoInit(NULL) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL video.\n"); + printSDLErrorAndReboot(); + } - window = SDL_CreateWindow("splash", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - SCREEN_WIDTH, SCREEN_HEIGHT, - SDL_WINDOW_SHOWN); - if(window == NULL) - { - debugPrint( "Window could not be created!\n"); - SDL_VideoQuit(); - printSDLErrorAndReboot(); - } + window = SDL_CreateWindow("splash", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + if (window == NULL) { + debugPrint("Window could not be created!\n"); + SDL_VideoQuit(); + printSDLErrorAndReboot(); + } - if (!(IMG_Init(IMG_INIT_JPG) & IMG_INIT_JPG)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't intialize SDL_image.\n"); - SDL_VideoQuit(); - printIMGErrorAndReboot(); - } + if (!(IMG_Init(IMG_INIT_JPG) & IMG_INIT_JPG)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't intialize SDL_image.\n"); + SDL_VideoQuit(); + printIMGErrorAndReboot(); + } - screenSurface = SDL_GetWindowSurface(window); - if (!screenSurface) { - SDL_VideoQuit(); - printSDLErrorAndReboot(); - } + screenSurface = SDL_GetWindowSurface(window); + if (!screenSurface) { + SDL_VideoQuit(); + printSDLErrorAndReboot(); + } - // set string variable for splash screen path - std::string _splashScreenPath = std::string(DATA_PATH) + "assets" + PATH_SEP + "moonlight-splash-" + - std::to_string(SCREEN_WIDTH) + "x" + std::to_string(SCREEN_HEIGHT) + ".jpg"; - const char *splashScreenPath = _splashScreenPath.c_str(); + // set string variable for splash screen path + std::string _splashScreenPath = std::string(DATA_PATH) + "assets" + PATH_SEP + "moonlight-splash-" + + std::to_string(SCREEN_WIDTH) + "x" + std::to_string(SCREEN_HEIGHT) + ".jpg"; + const char *splashScreenPath = _splashScreenPath.c_str(); - imageSurface = IMG_Load(splashScreenPath); - if (!imageSurface) { - SDL_VideoQuit(); - printIMGErrorAndReboot(); + imageSurface = IMG_Load(splashScreenPath); + if (!imageSurface) { + SDL_VideoQuit(); + printIMGErrorAndReboot(); + } + + while (!done) { + // Check for events + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + done = 1; + break; + default: + break; + } } - while (!done) { - // Check for events - while (SDL_PollEvent(&event)) { - switch (event.type) { - case SDL_QUIT: - done = 1; - break; - default: - break; - } - } - - SDL_BlitSurface(imageSurface, NULL, screenSurface, NULL); - SDL_UpdateWindowSurface(window); - - Sleep(1000); - } + SDL_BlitSurface(imageSurface, NULL, screenSurface, NULL); + SDL_UpdateWindowSurface(window); - SDL_VideoQuit(); -} + Sleep(1000); + } -int main() -{ - // create an empty list for the available video modes - std::vector availableVideoModes; + SDL_VideoQuit(); +} - // save the best video mode here - VIDEO_MODE bestVideoMode = {0, 0, 0, 0}; +int main() { + // create an empty list for the available video modes + std::vector availableVideoModes; - VIDEO_MODE vm; - int bpp = 32; // Bits per pixel - void *p = NULL; // Initialize to NULL for the first call + // save the best video mode here + VIDEO_MODE bestVideoMode = {0, 0, 0, 0}; - // get the available video modes - while (XVideoListModes(&vm, bpp, REFRESH_DEFAULT, &p)) { - availableVideoModes.push_back(vm); + VIDEO_MODE vm; + int bpp = 32; // Bits per pixel + void *p = NULL; // Initialize to NULL for the first call - // ensure height is equal to or better than the current best video mode - if (vm.height < bestVideoMode.height) { - continue; - } + // get the available video modes + while (XVideoListModes(&vm, bpp, REFRESH_DEFAULT, &p)) { + availableVideoModes.push_back(vm); - // ensure width is equal to or better than the current best video mode - if (vm.width < bestVideoMode.width) { - continue; - } + // ensure height is equal to or better than the current best video mode + if (vm.height < bestVideoMode.height) { + continue; + } - // ensure bpp is equal to or better than the current best video mode - if (vm.bpp < bestVideoMode.bpp) { - continue; - } + // ensure width is equal to or better than the current best video mode + if (vm.width < bestVideoMode.width) { + continue; + } - // ensure refresh is equal to or better than the current best video mode - if (vm.refresh < bestVideoMode.refresh) { - continue; - } + // ensure bpp is equal to or better than the current best video mode + if (vm.bpp < bestVideoMode.bpp) { + continue; + } - // save the best video mode - bestVideoMode = vm; + // ensure refresh is equal to or better than the current best video mode + if (vm.refresh < bestVideoMode.refresh) { + continue; } - SCREEN_WIDTH = bestVideoMode.width; - SCREEN_HEIGHT = bestVideoMode.height; + // save the best video mode + bestVideoMode = vm; + } - XVideoSetMode(640, 480, 32, REFRESH_DEFAULT); + SCREEN_WIDTH = bestVideoMode.width; + SCREEN_HEIGHT = bestVideoMode.height; - debugPrint("Available video modes:\n"); - for (VIDEO_MODE vm : availableVideoModes) { - debugPrint("Width: %d, Height: %d, BPP: %d, Refresh: %d\n", vm.width, vm.height, vm.bpp, vm.refresh); - } + XVideoSetMode(640, 480, 32, REFRESH_DEFAULT); + + debugPrint("Available video modes:\n"); + for (VIDEO_MODE vm : availableVideoModes) { + debugPrint("Width: %d, Height: %d, BPP: %d, Refresh: %d\n", vm.width, vm.height, vm.bpp, vm.refresh); + } - debugPrint("Best video mode:\n"); - debugPrint("Width: %d, Height: %d, BPP: %d, Refresh: %d\n", bestVideoMode.width, bestVideoMode.height, bestVideoMode.bpp, bestVideoMode.refresh); + debugPrint("Best video mode:\n"); + debugPrint("Width: %d, Height: %d, BPP: %d, Refresh: %d\n", bestVideoMode.width, bestVideoMode.height, bestVideoMode.bpp, bestVideoMode.refresh); - Sleep(2000); + Sleep(2000); - XVideoSetMode(SCREEN_WIDTH, SCREEN_HEIGHT, bestVideoMode.bpp, bestVideoMode.refresh); + XVideoSetMode(SCREEN_WIDTH, SCREEN_HEIGHT, bestVideoMode.bpp, bestVideoMode.refresh); - splash_screen(); - return 0; + splash_screen(); + return 0; } diff --git a/src/nxdk/hal/debug.h b/src/nxdk/hal/debug.h index 92ca5c2..3aa3fb2 100644 --- a/src/nxdk/hal/debug.h +++ b/src/nxdk/hal/debug.h @@ -1,5 +1,5 @@ #if defined(NXDK) -#include + #include #else -#define debugPrint(...) printf(__VA_ARGS__) + #define debugPrint(...) printf(__VA_ARGS__) #endif diff --git a/src/nxdk/hal/video.h b/src/nxdk/hal/video.h index e72d067..8d06efc 100644 --- a/src/nxdk/hal/video.h +++ b/src/nxdk/hal/video.h @@ -1,6 +1,6 @@ #if defined(NXDK) -#include + #include #else -#define XVideoSetMode(...) -#define XVideoWaitForVBlank(...) SDL_Delay(1000/60-1) + #define XVideoSetMode(...) + #define XVideoWaitForVBlank(...) SDL_Delay(1000 / 60 - 1) #endif diff --git a/src/nxdk/hal/xbox.h b/src/nxdk/hal/xbox.h index 8bf9a2b..047f10d 100644 --- a/src/nxdk/hal/xbox.h +++ b/src/nxdk/hal/xbox.h @@ -1,5 +1,5 @@ #if defined(NXDK) -#include + #include #else -#define XReboot(...) exit(555) + #define XReboot(...) exit(555) #endif diff --git a/src/nxdk/windows.h b/src/nxdk/windows.h index b683674..7fe7f47 100644 --- a/src/nxdk/windows.h +++ b/src/nxdk/windows.h @@ -1,5 +1,5 @@ #if defined(NXDK) -#include + #include #else -#define Sleep(x) SDL_Delay(x) + #define Sleep(x) SDL_Delay(x) #endif diff --git a/src/os.h b/src/os.h index 8e1db63..74c660e 100644 --- a/src/os.h +++ b/src/os.h @@ -1,7 +1,7 @@ #if defined(NXDK) -#define PATH_SEP "\\" -#define DATA_PATH "D:" PATH_SEP + #define PATH_SEP "\\" + #define DATA_PATH "D:" PATH_SEP #else -#define PATH_SEP "/" -#define DATA_PATH "." PATH_SEP + #define PATH_SEP "/" + #define DATA_PATH "." PATH_SEP #endif From 3333f2a9c00e20d780e5f0c3bea998e3e9775e1e Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 19 Oct 2024 20:23:14 -0400 Subject: [PATCH 11/13] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 00d8028..2ad1fd5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ # Moonlight-XboxOG + +[![GitHub stars](https://img.shields.io/github/stars/lizardbyte/moonlight-xboxog.svg?logo=github&style=for-the-badge)](https://github.com/LizardByte/Moonlight-XboxOG) +[![GitHub Releases](https://img.shields.io/github/downloads/lizardbyte/moonlight-xboxog/total.svg?style=for-the-badge&logo=github)](https://github.com/LizardByte/Moonlight-XboxOG/releases/latest) +[![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/moonlight-xboxog/ci.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/Moonlight-XboxOG/actions/workflows/CI.yml?query=branch%3Amaster) + Port of Moonlight for the Original Xbox. Unlikely to ever actually work. Do NOT use! Nothing works, except the splash screen. From 01ace0d298007f3470163d39c770277216969f80 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 19 Oct 2024 20:25:32 -0400 Subject: [PATCH 12/13] Create LICENSE --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9cecc1d --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program 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. + + This program 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 this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 45e90e2e6718b3f9c3235cb0703c254bfe7856c4 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 19 Oct 2024 20:30:39 -0400 Subject: [PATCH 13/13] Update ci.yml --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8caa01..4a86f6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -213,7 +213,8 @@ jobs: verbose: true - name: Create/Update GitHub Release - if: ${{ needs.setup_release.outputs.publish_release == 'true' }} + # only publish release on ubuntu-latest, is there any difference between the different builds? + if: ${{ needs.setup_release.outputs.publish_release == 'true' && matrix.os == 'ubuntu-latest' }} uses: LizardByte/create-release-action@v2024.919.143026 with: allowUpdates: true