diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | appveyor.yml | 163 | ||||
-rw-r--r-- | dist/citra.manifest | 24 | ||||
-rw-r--r-- | src/audio_core/hle/source.cpp | 49 | ||||
-rw-r--r-- | src/audio_core/interpolate.cpp | 86 | ||||
-rw-r--r-- | src/audio_core/interpolate.h | 27 | ||||
-rw-r--r-- | src/citra/citra.rc | 8 | ||||
-rw-r--r-- | src/citra_qt/citra-qt.rc | 12 | ||||
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/hle/applets/mii_selector.cpp | 6 | ||||
-rw-r--r-- | src/core/hle/applets/mii_selector.h | 57 | ||||
-rw-r--r-- | src/core/hle/service/nwm/nwm_uds.cpp | 165 | ||||
-rw-r--r-- | src/core/hle/service/nwm/nwm_uds.h | 12 | ||||
-rw-r--r-- | src/core/hle/service/nwm/uds_beacon.cpp | 3 | ||||
-rw-r--r-- | src/core/hle/service/nwm/uds_beacon.h | 30 | ||||
-rw-r--r-- | src/core/hle/service/nwm/uds_connection.cpp | 79 | ||||
-rw-r--r-- | src/core/hle/service/nwm/uds_connection.h | 51 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 6 |
18 files changed, 566 insertions, 218 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ddba04ef9..f8060270e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,8 +129,8 @@ else() set(CMAKE_C_FLAGS_RELEASE "/O2 /GS- /MD" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" CACHE STRING "" FORCE) - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG" CACHE STRING "" FORCE) - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE) + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) endif() # Set file offset size to 64 bits. diff --git a/appveyor.yml b/appveyor.yml index 94e9969f5..1c390cdd5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,15 @@ cache: os: Visual Studio 2017 +environment: + # Tell msys2 to add mingw64 to the path + MSYSTEM: MINGW64 + # Tell msys2 to inherit the current directory when starting the shell + CHERE_INVOKING: 1 + matrix: + - BUILD_TYPE: mingw + - BUILD_TYPE: msvc + platform: - x64 @@ -15,72 +24,146 @@ configuration: install: - git submodule update --init --recursive + - ps: | + if ($env:BUILD_TYPE -eq 'mingw') { + $dependencies = "mingw64/mingw-w64-x86_64-cmake", + "mingw64/mingw-w64-x86_64-qt5", + "mingw64/mingw-w64-x86_64-curl", + "mingw64/mingw-w64-x86_64-SDL2" + # redirect err to null to prevent warnings from becoming errors + # workaround to prevent pacman from failing due to cyclical dependencies + C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw64/mingw-w64-x86_64-freetype mingw64/mingw-w64-x86_64-fontconfig" 2> $null + C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S $dependencies" 2> $null + } before_build: - - mkdir build - - cd build - - cmake -G "Visual Studio 15 2017 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCMAKE_USE_OPENSSL=0 .. + - mkdir %BUILD_TYPE%_build + - cd %BUILD_TYPE%_build + - ps: | + if ($env:BUILD_TYPE -eq 'msvc') { + # redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning + cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCMAKE_USE_OPENSSL=0 .. 2>&1 && exit 0' + } else { + C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DUSE_SYSTEM_CURL=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1" + } - cd .. -build: - project: build/citra.sln - parallel: true +build_script: + - ps: | + if ($env:BUILD_TYPE -eq 'msvc') { + # https://www.appveyor.com/docs/build-phase + msbuild msvc_build/citra.sln /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + } else { + C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -C mingw_build/ 2>&1' + } after_build: - ps: | $GITDATE = $(git show -s --date=short --format='%ad') -replace "-","" $GITREV = $(git show -s --format='%h') - $GIT_LONG_HASH = $(git rev-parse HEAD) - # Where are these spaces coming from? Regardless, let's remove them - $MSVC_BUILD_NAME = "citra-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", "" - $MSVC_BUILD_PDB = "citra-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", "" - $MSVC_SEVENZIP = "citra-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", "" - $BINTRAY_VERSION = "nightly-$GIT_LONG_HASH" -replace " ", "" - - # set the build names as env vars so the artifacts can upload them - $env:MSVC_BUILD_NAME = $MSVC_BUILD_NAME - $env:MSVC_BUILD_PDB = $MSVC_BUILD_PDB - $env:MSVC_SEVENZIP = $MSVC_SEVENZIP - $env:GITREV = $GITREV - - 7z a -tzip $MSVC_BUILD_PDB .\build\bin\release\*.pdb - rm .\build\bin\release\*.pdb # Find out which kind of release we are producing by tag name if ($env:APPVEYOR_REPO_TAG_NAME) { - $RELEASE_DIST, $RELEASE_VERSION = $env:APPVEYOR_REPO_TAG_NAME.split('-') + $RELEASE_DIST, $RELEASE_VERSION = $env:APPVEYOR_REPO_TAG_NAME.split('-') } else { - # There is no repo tag - make assumptions - $RELEASE_DIST = "head" + # There is no repo tag - make assumptions + $RELEASE_DIST = "head" } - mkdir $RELEASE_DIST - Copy-Item .\build\bin\release\* -Destination $RELEASE_DIST -Recurse - Copy-Item .\license.txt -Destination $RELEASE_DIST - Copy-Item .\README.md -Destination $RELEASE_DIST - - 7z a -tzip $MSVC_BUILD_NAME $RELEASE_DIST\* - 7z a $MSVC_SEVENZIP $RELEASE_DIST + if ($env:BUILD_TYPE -eq 'msvc') { + # Where are these spaces coming from? Regardless, let's remove them + $MSVC_BUILD_ZIP = "citra-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", "" + $MSVC_BUILD_PDB = "citra-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", "" + $MSVC_SEVENZIP = "citra-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", "" + + # set the build names as env vars so the artifacts can upload them + $env:BUILD_ZIP = $MSVC_BUILD_ZIP + $env:BUILD_SYMBOLS = $MSVC_BUILD_PDB + $env:BUILD_UPDATE = $MSVC_SEVENZIP + + 7z a -tzip $MSVC_BUILD_PDB .\msvc_build\bin\release\*.pdb + rm .\msvc_build\bin\release\*.pdb + + mkdir $RELEASE_DIST + Copy-Item .\msvc_build\bin\release\* -Destination $RELEASE_DIST -Recurse + Copy-Item .\license.txt -Destination $RELEASE_DIST + Copy-Item .\README.md -Destination $RELEASE_DIST + 7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\* + 7z a $MSVC_SEVENZIP $RELEASE_DIST + } else { + $MINGW_BUILD_ZIP = "citra-windows-mingw-$GITDATE-$GITREV.zip" -replace " ", "" + $MINGW_SEVENZIP = "citra-windows-mingw-$GITDATE-$GITREV.7z" -replace " ", "" + # not going to bother adding separate debug symbols for mingw, so just upload a README for it + # if someone wants to add them, change mingw to compile with -g and use objdump and strip to separate the symbols from the binary + $MINGW_NO_DEBUG_SYMBOLS = "README_No_Debug_Symbols.txt" + Set-Content -Path $MINGW_NO_DEBUG_SYMBOLS -Value "This is a workaround for Appveyor since msvc has debug symbols but mingw doesnt" -Force + + # store the build information in env vars so we can use them as artifacts + $env:BUILD_ZIP = $MINGW_BUILD_ZIP + $env:BUILD_SYMBOLS = $MINGW_NO_DEBUG_SYMBOLS + $env:BUILD_UPDATE = $MINGW_SEVENZIP + + $CMAKE_SOURCE_DIR = "$env:APPVEYOR_BUILD_FOLDER" + $CMAKE_BINARY_DIR = "$CMAKE_SOURCE_DIR/mingw_build" + $RELEASE_DIST = $RELEASE_DIST + "-mingw" + + mkdir $RELEASE_DIST + mkdir $RELEASE_DIST/platforms + + # copy the compiled binaries and other release files to the release folder + Get-ChildItem "$CMAKE_BINARY_DIR" -Recurse -Filter "citra*.exe" | Copy-Item -destination $RELEASE_DIST + Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST + Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST + + # copy all the dll dependencies to the release folder + # hardcoded list because we don't build static and determining the list of dlls from the binary is a pain. + $MingwDLLs = "Qt5Core.dll","Qt5Widgets.dll","Qt5Gui.dll","Qt5OpenGL.dll", + # QT dll dependencies + "libbz2-*.dll","libicudt*.dll","libicuin*.dll","libicuuc*.dll","libffi-*.dll", + "libfreetype-*.dll","libglib-*.dll","libgobject-*.dll","libgraphite2.dll","libiconv-*.dll", + "libharfbuzz-*.dll","libintl-*.dll","libpcre-*.dll","libpcre16-*.dll","libpng16-*.dll", + # Runtime/Other dependencies + "libgcc_s_seh-*.dll","libstdc++-*.dll","libwinpthread-*.dll","SDL2.dll","zlib1.dll", + # curl dependencies + "libcurl-*.dll","libnghttp2-*.dll","libeay32.dll","libgmp-*.dll","librtmp-*.dll", + "libgnutls-*.dll","libhogweed-*.dll","libnettle-*.dll","libssh2-*.dll", + "ssleay32.dll","libidn-*.dll","libp11-kit-*.dll","libtasn1-*.dll","libunistring-*.dll" + foreach ($file in $MingwDLLs) { + Copy-Item -path "C:/msys64/mingw64/bin/$file" -force -destination "$RELEASE_DIST" + } + + # copy the qt windows plugin dll to platforms + Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms" + + 7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\* + 7z a $MINGW_SEVENZIP $RELEASE_DIST + } test_script: - - cd build && ctest -VV -C Release && cd .. + - cd %BUILD_TYPE%_build + - ps: | + if ($env:BUILD_TYPE -eq 'msvc') { + ctest -VV -C Release + } else { + C:\msys64\usr\bin\bash.exe -lc "ctest -VV -C Release" + } + - cd .. artifacts: - - path: $(MSVC_BUILD_NAME) - name: msvcbuild - type: zip - - path: $(MSVC_BUILD_PDB) - name: msvcdebug + - path: $(BUILD_ZIP) + name: build type: zip - - path: $(MSVC_SEVENZIP) - name: msvcupdate + - path: $(BUILD_SYMBOLS) + name: debugsymbols + - path: $(BUILD_UPDATE) + name: update deploy: provider: GitHub release: $(appveyor_repo_tag_name) auth_token: secure: "dbpsMC/MgPKWFNJCXpQl4cR8FYhepkPLjgNp/pRMktZ8oLKTqPYErfreaIxb/4P1" - artifact: msvcupdate,msvcbuild + artifact: update,build draft: false prerelease: false on: diff --git a/dist/citra.manifest b/dist/citra.manifest new file mode 100644 index 000000000..fd30b656f --- /dev/null +++ b/dist/citra.manifest @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker" uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> + <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> + </windowsSettings> + </application> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + </application> + </compatibility> +</assembly>
\ No newline at end of file diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp index 92484c526..de4e88cae 100644 --- a/src/audio_core/hle/source.cpp +++ b/src/audio_core/hle/source.cpp @@ -244,17 +244,27 @@ void Source::GenerateFrame() { break; } - const size_t size_to_copy = - std::min(state.current_buffer.size(), current_frame.size() - frame_position); - - std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, - current_frame.begin() + frame_position); - state.current_buffer.erase(state.current_buffer.begin(), - state.current_buffer.begin() + size_to_copy); - - frame_position += size_to_copy; - state.next_sample_number += static_cast<u32>(size_to_copy); + switch (state.interpolation_mode) { + case InterpolationMode::None: + AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier, + current_frame, frame_position); + break; + case InterpolationMode::Linear: + AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier, + current_frame, frame_position); + break; + case InterpolationMode::Polyphase: + // TODO(merry): Implement polyphase interpolation + LOG_DEBUG(Audio_DSP, "Polyphase interpolation unimplemented; falling back to linear"); + AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier, + current_frame, frame_position); + break; + default: + UNIMPLEMENTED(); + break; + } } + state.next_sample_number += frame_position; state.filters.ProcessFrame(current_frame); } @@ -305,25 +315,6 @@ bool Source::DequeueBuffer() { return true; } - switch (state.interpolation_mode) { - case InterpolationMode::None: - state.current_buffer = - AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); - break; - case InterpolationMode::Linear: - state.current_buffer = - AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); - break; - case InterpolationMode::Polyphase: - // TODO(merry): Implement polyphase interpolation - state.current_buffer = - AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); - break; - default: - UNIMPLEMENTED(); - break; - } - // the first playthrough starts at play_position, loops start at the beginning of the buffer state.current_sample_number = (!buf.has_played) ? buf.play_position : 0; state.next_sample_number = state.current_sample_number; diff --git a/src/audio_core/interpolate.cpp b/src/audio_core/interpolate.cpp index 8a5d4181a..16e68bc5c 100644 --- a/src/audio_core/interpolate.cpp +++ b/src/audio_core/interpolate.cpp @@ -13,74 +13,64 @@ namespace AudioInterp { constexpr u64 scale_factor = 1 << 24; constexpr u64 scale_mask = scale_factor - 1; -/// Here we step over the input in steps of rate_multiplier, until we consume all of the input. +/// Here we step over the input in steps of rate, until we consume all of the input. /// Three adjacent samples are passed to fn each step. template <typename Function> -static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, - float rate_multiplier, Function fn) { - ASSERT(rate_multiplier > 0); +static void StepOverSamples(State& state, StereoBuffer16& input, float rate, + DSP::HLE::StereoFrame16& output, size_t& outputi, Function fn) { + ASSERT(rate > 0); - if (input.size() < 2) - return {}; + if (input.empty()) + return; - StereoBuffer16 output; - output.reserve(static_cast<size_t>(input.size() / rate_multiplier)); + input.insert(input.begin(), {state.xn2, state.xn1}); - u64 step_size = static_cast<u64>(rate_multiplier * scale_factor); + const u64 step_size = static_cast<u64>(rate * scale_factor); + u64 fposition = state.fposition; + size_t inputi = 0; - u64 fposition = 0; - const u64 max_fposition = input.size() * scale_factor; + while (outputi < output.size()) { + inputi = static_cast<size_t>(fposition / scale_factor); - while (fposition < 1 * scale_factor) { - u64 fraction = fposition & scale_mask; - - output.push_back(fn(fraction, state.xn2, state.xn1, input[0])); - - fposition += step_size; - } - - while (fposition < 2 * scale_factor) { - u64 fraction = fposition & scale_mask; - - output.push_back(fn(fraction, state.xn1, input[0], input[1])); - - fposition += step_size; - } + if (inputi + 2 >= input.size()) { + inputi = input.size() - 2; + break; + } - while (fposition < max_fposition) { u64 fraction = fposition & scale_mask; - - size_t index = static_cast<size_t>(fposition / scale_factor); - output.push_back(fn(fraction, input[index - 2], input[index - 1], input[index])); + output[outputi++] = fn(fraction, input[inputi], input[inputi + 1], input[inputi + 2]); fposition += step_size; } - state.xn2 = input[input.size() - 2]; - state.xn1 = input[input.size() - 1]; + state.xn2 = input[inputi]; + state.xn1 = input[inputi + 1]; + state.fposition = fposition - inputi * scale_factor; - return output; + input.erase(input.begin(), input.begin() + inputi + 2); } -StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { - return StepOverSamples( - state, input, rate_multiplier, +void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, + size_t& outputi) { + StepOverSamples( + state, input, rate, output, outputi, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; }); } -StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { +void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, + size_t& outputi) { // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. - return StepOverSamples(state, input, rate_multiplier, - [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { - // This is a saturated subtraction. (Verified by black-box fuzzing.) - s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); - s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); - - return std::array<s16, 2>{ - static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), - static_cast<s16>(x0[1] + fraction * delta1 / scale_factor), - }; - }); + StepOverSamples(state, input, rate, output, outputi, + [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { + // This is a saturated subtraction. (Verified by black-box fuzzing.) + s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); + s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); + + return std::array<s16, 2>{ + static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), + static_cast<s16>(x0[1] + fraction * delta1 / scale_factor), + }; + }); } } // namespace AudioInterp diff --git a/src/audio_core/interpolate.h b/src/audio_core/interpolate.h index 19a7b66cb..59f59bc14 100644 --- a/src/audio_core/interpolate.h +++ b/src/audio_core/interpolate.h @@ -6,6 +6,7 @@ #include <array> #include <vector> +#include "audio_core/hle/common.h" #include "common/common_types.h" namespace AudioInterp { @@ -14,31 +15,35 @@ namespace AudioInterp { using StereoBuffer16 = std::vector<std::array<s16, 2>>; struct State { - // Two historical samples. + /// Two historical samples. std::array<s16, 2> xn1 = {}; ///< x[n-1] std::array<s16, 2> xn2 = {}; ///< x[n-2] + /// Current fractional position. + u64 fposition = 0; }; /** * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. * @param state Interpolation state. * @param input Input buffer. - * @param rate_multiplier Stretch factor. Must be a positive non-zero value. - * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 - * performs upsampling. - * @return The resampled audio buffer. + * @param rate Stretch factor. Must be a positive non-zero value. + * rate > 1.0 performs decimation and rate < 1.0 performs upsampling. + * @param output The resampled audio buffer. + * @param outputi The index of output to start writing to. */ -StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); +void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, + size_t& outputi); /** * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. * @param state Interpolation state. * @param input Input buffer. - * @param rate_multiplier Stretch factor. Must be a positive non-zero value. - * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 - * performs upsampling. - * @return The resampled audio buffer. + * @param rate Stretch factor. Must be a positive non-zero value. + * rate > 1.0 performs decimation and rate < 1.0 performs upsampling. + * @param output The resampled audio buffer. + * @param outputi The index of output to start writing to. */ -StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); +void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, + size_t& outputi); } // namespace AudioInterp diff --git a/src/citra/citra.rc b/src/citra/citra.rc index fea603004..c490ef302 100644 --- a/src/citra/citra.rc +++ b/src/citra/citra.rc @@ -1,3 +1,4 @@ +#include "winresrc.h" ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -7,3 +8,10 @@ // remains consistent on all systems. CITRA_ICON ICON "../../dist/citra.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "../../dist/citra.manifest" diff --git a/src/citra_qt/citra-qt.rc b/src/citra_qt/citra-qt.rc index fea603004..a48a9440d 100644 --- a/src/citra_qt/citra-qt.rc +++ b/src/citra_qt/citra-qt.rc @@ -1,3 +1,4 @@ +#include "winresrc.h" ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -5,5 +6,14 @@ // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -CITRA_ICON ICON "../../dist/citra.ico" +// QT requires that the default application icon is named IDI_ICON1 +IDI_ICON1 ICON "../../dist/citra.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "../../dist/citra.manifest" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 662030782..78dec8600 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -145,6 +145,7 @@ set(SRCS hle/service/nwm/nwm_tst.cpp hle/service/nwm/nwm_uds.cpp hle/service/nwm/uds_beacon.cpp + hle/service/nwm/uds_connection.cpp hle/service/nwm/uds_data.cpp hle/service/pm_app.cpp hle/service/ptm/ptm.cpp @@ -344,6 +345,7 @@ set(HEADERS hle/service/nwm/nwm_tst.h hle/service/nwm/nwm_uds.h hle/service/nwm/uds_beacon.h + hle/service/nwm/uds_connection.h hle/service/nwm/uds_data.h hle/service/pm_app.h hle/service/ptm/ptm.h diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 705859f1e..f225c23a5 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -66,7 +66,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa // continue. MiiResult result; memset(&result, 0, sizeof(result)); - result.result_code = 0; + result.return_code = 0; // Let the application know that we're closing Service::APT::MessageParameter message; @@ -82,5 +82,5 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa } void MiiSelector::Update() {} -} -} // namespace +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h index ec00e29d2..136ce8948 100644 --- a/src/core/hle/applets/mii_selector.h +++ b/src/core/hle/applets/mii_selector.h @@ -16,51 +16,46 @@ namespace HLE { namespace Applets { struct MiiConfig { - u8 unk_000; - u8 unk_001; - u8 unk_002; - u8 unk_003; - u8 unk_004; + u8 enable_cancel_button; + u8 enable_guest_mii; + u8 show_on_top_screen; + INSERT_PADDING_BYTES(5); + u16 title[0x40]; + INSERT_PADDING_BYTES(4); + u8 show_guest_miis; INSERT_PADDING_BYTES(3); - u16 unk_008; - INSERT_PADDING_BYTES(0x82); - u8 unk_08C; - INSERT_PADDING_BYTES(3); - u16 unk_090; + u32 initially_selected_mii_index; + u8 guest_mii_whitelist[6]; + u8 user_mii_whitelist[0x64]; INSERT_PADDING_BYTES(2); - u32 unk_094; - u16 unk_098; - u8 unk_09A[0x64]; - u8 unk_0FE; - u8 unk_0FF; - u32 unk_100; + u32 magic_value; }; - static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); #define ASSERT_REG_POSITION(field_name, position) \ static_assert(offsetof(MiiConfig, field_name) == position, \ "Field " #field_name " has invalid position") -ASSERT_REG_POSITION(unk_008, 0x08); -ASSERT_REG_POSITION(unk_08C, 0x8C); -ASSERT_REG_POSITION(unk_090, 0x90); -ASSERT_REG_POSITION(unk_094, 0x94); -ASSERT_REG_POSITION(unk_0FE, 0xFE); +ASSERT_REG_POSITION(title, 0x08); +ASSERT_REG_POSITION(show_guest_miis, 0x8C); +ASSERT_REG_POSITION(initially_selected_mii_index, 0x90); +ASSERT_REG_POSITION(guest_mii_whitelist, 0x94); #undef ASSERT_REG_POSITION struct MiiResult { - u32 result_code; - u8 unk_04; - INSERT_PADDING_BYTES(7); - u8 unk_0C[0x60]; - u8 unk_6C[0x16]; + u32 return_code; + u32 is_guest_mii_selected; + u32 selected_guest_mii_index; + // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii + u8 selected_mii_data[0x5C]; INSERT_PADDING_BYTES(2); + u16 mii_data_checksum; + u16 guest_mii_name[0xC]; }; static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); #define ASSERT_REG_POSITION(field_name, position) \ static_assert(offsetof(MiiResult, field_name) == position, \ "Field " #field_name " has invalid position") -ASSERT_REG_POSITION(unk_0C, 0x0C); -ASSERT_REG_POSITION(unk_6C, 0x6C); +ASSERT_REG_POSITION(selected_mii_data, 0x0C); +ASSERT_REG_POSITION(guest_mii_name, 0x6C); #undef ASSERT_REG_POSITION class MiiSelector final : public Applet { @@ -79,5 +74,5 @@ private: MiiConfig config; }; -} -} // namespace +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 6dbdff044..893bbb1e7 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -4,6 +4,7 @@ #include <array> #include <cstring> +#include <mutex> #include <unordered_map> #include <vector> #include "common/common_types.h" @@ -15,8 +16,10 @@ #include "core/hle/result.h" #include "core/hle/service/nwm/nwm_uds.h" #include "core/hle/service/nwm/uds_beacon.h" +#include "core/hle/service/nwm/uds_connection.h" #include "core/hle/service/nwm/uds_data.h" #include "core/memory.h" +#include "network/network.h" namespace Service { namespace NWM { @@ -51,6 +54,135 @@ static NetworkInfo network_info; // Event that will generate and send the 802.11 beacon frames. static int beacon_broadcast_event; +// Mutex to synchronize access to the list of received beacons between the emulation thread and the +// network thread. +static std::mutex beacon_mutex; + +// Number of beacons to store before we start dropping the old ones. +// TODO(Subv): Find a more accurate value for this limit. +constexpr size_t MaxBeaconFrames = 15; + +// List of the last <MaxBeaconFrames> beacons received from the network. +static std::deque<Network::WifiPacket> received_beacons; + +/** + * Returns a list of received 802.11 beacon frames from the specified sender since the last call. + */ +std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { + std::lock_guard<std::mutex> lock(beacon_mutex); + // TODO(Subv): Filter by sender. + return std::move(received_beacons); +} + +/// Sends a WifiPacket to the room we're currently connected to. +void SendPacket(Network::WifiPacket& packet) { + // TODO(Subv): Implement. +} + +// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size +// limit is exceeded. +void HandleBeaconFrame(const Network::WifiPacket& packet) { + std::lock_guard<std::mutex> lock(beacon_mutex); + + received_beacons.emplace_back(packet); + + // Discard old beacons if the buffer is full. + if (received_beacons.size() > MaxBeaconFrames) + received_beacons.pop_front(); +} + +/* + * Returns an available index in the nodes array for the + * currently-hosted UDS network. + */ +static u16 GetNextAvailableNodeId() { + ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), + "Can not accept clients if we're not hosting a network"); + + for (u16 index = 0; index < connection_status.max_nodes; ++index) { + if ((connection_status.node_bitmask & (1 << index)) == 0) + return index; + } + + // Any connection attempts to an already full network should have been refused. + ASSERT_MSG(false, "No available connection slots in the network"); +} + +/* + * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11 + * authentication frame with SEQ1. + */ +void StartConnectionSequence(const MacAddress& server) { + ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); + + // TODO(Subv): Handle timeout. + + // Send an authentication frame with SEQ1 + using Network::WifiPacket; + WifiPacket auth_request; + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); + auth_request.destination_address = server; + auth_request.type = WifiPacket::PacketType::Authentication; + + SendPacket(auth_request); +} + +/// Sends an Association Response frame to the specified mac address +void SendAssociationResponseFrame(const MacAddress& address) { + ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); + + using Network::WifiPacket; + WifiPacket assoc_response; + assoc_response.channel = network_channel; + // TODO(Subv): This will cause multiple clients to end up with the same association id, but + // we're not using that for anything. + u16 association_id = 1; + assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, + network_info.network_id); + assoc_response.destination_address = address; + assoc_response.type = WifiPacket::PacketType::AssociationResponse; + + SendPacket(assoc_response); +} + +/* + * Handles the authentication request frame and sends the authentication response and association + * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds + * with an Authentication frame containing SEQ2, and immediately sends an Association response frame + * containing the details of the access point and the assigned association id for the new client. + */ +void HandleAuthenticationFrame(const Network::WifiPacket& packet) { + // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior + if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { + ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); + + // Respond with an authentication response frame with SEQ2 + using Network::WifiPacket; + WifiPacket auth_request; + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); + auth_request.destination_address = packet.transmitter_address; + auth_request.type = WifiPacket::PacketType::Authentication; + + SendPacket(auth_request); + + SendAssociationResponseFrame(packet.transmitter_address); + } +} + +/// Callback to parse and handle a received wifi packet. +void OnWifiPacketReceived(const Network::WifiPacket& packet) { + switch (packet.type) { + case Network::WifiPacket::PacketType::Beacon: + HandleBeaconFrame(packet); + break; + case Network::WifiPacket::PacketType::Authentication: + HandleAuthenticationFrame(packet); + break; + } +} + /** * NWM_UDS::Shutdown service function * Inputs: @@ -111,8 +243,7 @@ static void RecvBeaconBroadcastData(Interface* self) { u32 total_size = sizeof(BeaconDataReplyHeader); // Retrieve all beacon frames that were received from the desired mac address. - std::deque<WifiPacket> beacons = - GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address); + auto beacons = GetReceivedBeacons(mac_address); BeaconDataReplyHeader data_reply_header{}; data_reply_header.total_entries = beacons.size(); @@ -193,6 +324,9 @@ static void InitializeWithVersion(Interface* self) { rb.Push(RESULT_SUCCESS); rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); + // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of + // the room we're currently in. + LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", sharedmem_size, version, sharedmem_handle); } @@ -610,32 +744,23 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) return; - // TODO(Subv): Actually send the beacon. std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info); + using Network::WifiPacket; + WifiPacket packet; + packet.type = WifiPacket::PacketType::Beacon; + packet.data = std::move(frame); + packet.destination_address = Network::BroadcastMac; + packet.channel = network_channel; + + SendPacket(packet); + // Start broadcasting the network, send a beacon frame every 102.4ms. CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, beacon_broadcast_event, 0); } /* - * Returns an available index in the nodes array for the - * currently-hosted UDS network. - */ -static u32 GetNextAvailableNodeId() { - ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), - "Can not accept clients if we're not hosting a network"); - - for (unsigned index = 0; index < connection_status.max_nodes; ++index) { - if ((connection_status.node_bitmask & (1 << index)) == 0) - return index; - } - - // Any connection attempts to an already full network should have been refused. - ASSERT_MSG(false, "No available connection slots in the network"); -} - -/* * Called when a client connects to an UDS network we're hosting, * updates the connection status and signals the update event. * @param network_node_id Network Node Id of the connecting client. diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 141f49f9c..f1caaf974 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -42,6 +42,7 @@ using NodeList = std::vector<NodeInfo>; enum class NetworkStatus { NotConnected = 3, ConnectedAsHost = 6, + Connecting = 7, ConnectedAsClient = 9, ConnectedAsSpectator = 10, }; @@ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); +/// Additional block tag ids in the Beacon and Association Response frames +enum class TagId : u8 { + SSID = 0, + SupportedRates = 1, + DSParameterSet = 2, + TrafficIndicationMap = 5, + CountryInformation = 7, + ERPInformation = 42, + VendorSpecific = 221 +}; + class NWM_UDS final : public Interface { public: NWM_UDS(); diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp index 6332b404c..552eaf65e 100644 --- a/src/core/hle/service/nwm/uds_beacon.cpp +++ b/src/core/hle/service/nwm/uds_beacon.cpp @@ -325,8 +325,5 @@ std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeL return buffer; } -std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) { - return {}; -} } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h index caacf4c6f..50cc76da2 100644 --- a/src/core/hle/service/nwm/uds_beacon.h +++ b/src/core/hle/service/nwm/uds_beacon.h @@ -17,17 +17,6 @@ namespace NWM { using MacAddress = std::array<u8, 6>; constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; -/// Additional block tag ids in the Beacon frames -enum class TagId : u8 { - SSID = 0, - SupportedRates = 1, - DSParameterSet = 2, - TrafficIndicationMap = 5, - CountryInformation = 7, - ERPInformation = 42, - VendorSpecific = 221 -}; - /** * Internal vendor-specific tag ids as stored inside * VendorSpecific blocks in the Beacon frames. @@ -135,20 +124,6 @@ struct BeaconData { static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); -/// Information about a received WiFi packet. -/// Acts as our own 802.11 header. -struct WifiPacket { - enum class PacketType { Beacon, Data }; - - PacketType type; ///< The type of 802.11 frame, Beacon / Data. - - /// Raw 802.11 frame data, starting at the management frame header for management frames. - std::vector<u8> data; - MacAddress transmitter_address; ///< Mac address of the transmitter. - MacAddress destination_address; ///< Mac address of the receiver. - u8 channel; ///< WiFi channel where this frame was transmitted. -}; - /** * Decrypts the beacon data buffer for the network described by `network_info`. */ @@ -161,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer) */ std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); -/** - * Returns a list of received 802.11 frames from the specified sender - * matching the type since the last call. - */ -std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender); } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp new file mode 100644 index 000000000..c8a76ec2a --- /dev/null +++ b/src/core/hle/service/nwm/uds_connection.cpp @@ -0,0 +1,79 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_uds.h" +#include "core/hle/service/nwm/uds_connection.h" +#include "fmt/format.h" + +namespace Service { +namespace NWM { + +// Note: These values were taken from a packet capture of an o3DS XL +// broadcasting a Super Smash Bros. 4 lobby. +constexpr u16 DefaultExtraCapabilities = 0x0431; + +std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) { + AuthenticationFrame frame{}; + frame.auth_seq = static_cast<u16>(seq); + + std::vector<u8> data(sizeof(frame)); + std::memcpy(data.data(), &frame, sizeof(frame)); + + return data; +} + +AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) { + AuthenticationFrame frame; + std::memcpy(&frame, body.data(), sizeof(frame)); + + return static_cast<AuthenticationSeq>(frame.auth_seq); +} + +/** + * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the + * specified network id as the SSID value. + * @param network_id The network id to use. + * @returns A buffer with the SSID tag. + */ +static std::vector<u8> GenerateSSIDTag(u32 network_id) { + constexpr u8 SSIDSize = 8; + + struct { + u8 id = static_cast<u8>(TagId::SSID); + u8 size = SSIDSize; + } tag_header; + + std::vector<u8> buffer(sizeof(tag_header) + SSIDSize); + + std::memcpy(buffer.data(), &tag_header, sizeof(tag_header)); + + std::string network_name = fmt::format("{0:08X}", network_id); + + std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize); + + return buffer; +} + +std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) { + AssociationResponseFrame frame{}; + frame.capabilities = DefaultExtraCapabilities; + frame.status_code = static_cast<u16>(status); + // The association id is ORed with this magic value (0xC000) + constexpr u16 AssociationIdMagic = 0xC000; + frame.assoc_id = association_id | AssociationIdMagic; + + std::vector<u8> data(sizeof(frame)); + std::memcpy(data.data(), &frame, sizeof(frame)); + + auto ssid_tag = GenerateSSIDTag(network_id); + data.insert(data.end(), ssid_tag.begin(), ssid_tag.end()); + + // TODO(Subv): Add the SupportedRates tag. + // TODO(Subv): Add the DSParameterSet tag. + // TODO(Subv): Add the ERPInformation tag. + return data; +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h new file mode 100644 index 000000000..73f55a4fd --- /dev/null +++ b/src/core/hle/service/nwm/uds_connection.h @@ -0,0 +1,51 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +/// Sequence number of the 802.11 authentication frames. +enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 }; + +enum class AuthAlgorithm : u16 { OpenSystem = 0 }; + +enum class AuthStatus : u16 { Successful = 0 }; + +enum class AssocStatus : u16 { Successful = 0 }; + +struct AuthenticationFrame { + u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem); + u16_le auth_seq; + u16_le status_code = static_cast<u16>(AuthStatus::Successful); +}; + +static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size"); + +struct AssociationResponseFrame { + u16_le capabilities; + u16_le status_code; + u16_le assoc_id; +}; + +static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size"); + +/// Generates an 802.11 authentication frame, starting at the frame body. +std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq); + +/// Returns the sequence number from the body of an Authentication frame. +AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body); + +/// Generates an 802.11 association response frame with the specified status, association id and +/// network id, starting at the frame body. +std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); + +} // namespace NWM +} // namespace Service diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index b5f359da6..c536e61e1 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/bit_field.h" #include "common/logging/log.h" +#include "core/core.h" #include "video_core/regs_framebuffer.h" #include "video_core/regs_lighting.h" #include "video_core/regs_rasterizer.h" @@ -1156,6 +1157,11 @@ vec4 secondary_fragment_color = vec4(0.0); // Blend the fog out += "last_tex_env_out.rgb = mix(fog_color.rgb, last_tex_env_out.rgb, fog_factor);\n"; + } else if (state.fog_mode == TexturingRegs::FogMode::Gas) { + Core::Telemetry().AddField(Telemetry::FieldType::Session, "VideoCore_Pica_UseGasMode", + true); + LOG_CRITICAL(Render_OpenGL, "Unimplemented gas mode"); + UNIMPLEMENTED(); } out += "gl_FragDepth = depth;\n"; |