mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-03-11 07:56:15 +01:00
Merge branch 'master' into windows-clang
This commit is contained in:
commit
572e3dd350
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
git config --global --add safe.directory '*'
|
||||
|
||||
CPU_ARCH="${1:-x86_64}"
|
||||
MSYS2="${2:-clang64}"
|
||||
|
||||
# Pull all the submodules except some
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
|
|
@ -23,7 +26,7 @@ else
|
|||
fi
|
||||
|
||||
cmake .. \
|
||||
-DCMAKE_PREFIX_PATH=/clang64 \
|
||||
-DCMAKE_PREFIX_PATH=/"${MSYS2}" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF \
|
||||
|
|
@ -44,8 +47,8 @@ cmake .. \
|
|||
-DUSE_DISCORD_RPC=ON \
|
||||
-DOpenGL_GL_PREFERENCE=LEGACY \
|
||||
-DWITH_LLVM=ON \
|
||||
-DLLVM_DIR=/clang64/lib/cmake/llvm \
|
||||
-DVulkan_LIBRARY=/clang64/lib/libvulkan-1.dll.a \
|
||||
-DLLVM_DIR=/"${MSYS2}"/lib/cmake/llvm \
|
||||
-DVulkan_LIBRARY=/"${MSYS2}"/lib/libvulkan-1.dll.a \
|
||||
-DSTATIC_LINK_LLVM=ON \
|
||||
-DBUILD_RPCS3_TESTS=OFF \
|
||||
-DRUN_RPCS3_TESTS=OFF \
|
||||
|
|
@ -57,5 +60,5 @@ cd ..
|
|||
|
||||
# If it compiled succesfully let's deploy.
|
||||
if [ "$build_status" -eq 0 ]; then
|
||||
echo "Build succeeded"
|
||||
.ci/deploy-windows-clang.sh "${CPU_ARCH}" "${MSYS2}"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@
|
|||
cd build || exit 1
|
||||
|
||||
CPU_ARCH="${1:-x86_64}"
|
||||
MSYS2="${2:-clang64}"
|
||||
|
||||
echo "Deploying rpcs3 windows clang $CPU_ARCH"
|
||||
|
||||
# BUILD_blablabla is CI specific, so we wrap it for portability
|
||||
ARTIFACT_DIR=$(cygpath -u "$BUILD_ARTIFACTSTAGINGDIRECTORY")
|
||||
MSYS2_CLANG_BIN=$(cygpath -w /clang64/bin)
|
||||
MSYS2_CLANG_BIN=$(cygpath -w /"${MSYS2}"/bin)
|
||||
MSYS2_USR_BIN=$(cygpath -w /usr/bin)
|
||||
|
||||
echo "Installing dependencies of: ./bin/rpcs3.exe (MSYS2 dir is '$MSYS2_CLANG_BIN', usr dir is '$MSYS2_USR_BIN')"
|
||||
|
|
|
|||
59
.github/workflows/rpcs3.yml
vendored
59
.github/workflows/rpcs3.yml
vendored
|
|
@ -333,16 +333,24 @@ jobs:
|
|||
Windows_Build_MSYS2:
|
||||
# Only run push event on master branch of main repo, but run all PRs
|
||||
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
|
||||
runs-on: windows-2025
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- msys2: clang64
|
||||
compiler: clang
|
||||
arch: win64
|
||||
arch: x86_64
|
||||
os: windows-2025
|
||||
name: X64
|
||||
- msys2: clangarm64
|
||||
compiler: clang
|
||||
arch: aarch64
|
||||
os: windows-11-arm
|
||||
name: ARM64
|
||||
env:
|
||||
CCACHE_DIR: 'C:\ccache'
|
||||
name: RPCS3 Windows Clang (MSYS2)
|
||||
name: RPCS3 Windows Clang ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@main
|
||||
|
|
@ -356,23 +364,25 @@ jobs:
|
|||
update: true
|
||||
cache: true
|
||||
install: |
|
||||
mingw-w64-clang-x86_64-clang
|
||||
mingw-w64-clang-x86_64-ccache
|
||||
mingw-w64-clang-x86_64-cmake
|
||||
mingw-w64-clang-x86_64-lld
|
||||
mingw-w64-clang-x86_64-ninja
|
||||
mingw-w64-clang-x86_64-llvm
|
||||
mingw-w64-clang-x86_64-ffmpeg
|
||||
mingw-w64-clang-x86_64-opencv
|
||||
mingw-w64-clang-x86_64-glew
|
||||
mingw-w64-clang-x86_64-vulkan
|
||||
mingw-w64-clang-x86_64-vulkan-headers
|
||||
mingw-w64-clang-x86_64-vulkan-loader
|
||||
mingw-w64-clang-x86_64-gtest
|
||||
mingw-w64-clang-x86_64-qt6-base
|
||||
mingw-w64-clang-x86_64-qt6-declarative
|
||||
mingw-w64-clang-x86_64-qt6-multimedia
|
||||
mingw-w64-clang-x86_64-qt6-svg
|
||||
mingw-w64-clang-${{ matrix.arch }}-clang
|
||||
mingw-w64-clang-${{ matrix.arch }}-ccache
|
||||
mingw-w64-clang-${{ matrix.arch }}-cmake
|
||||
mingw-w64-clang-${{ matrix.arch }}-lld
|
||||
mingw-w64-clang-${{ matrix.arch }}-ninja
|
||||
mingw-w64-clang-${{ matrix.arch }}-llvm
|
||||
mingw-w64-clang-${{ matrix.arch }}-ffmpeg
|
||||
mingw-w64-clang-${{ matrix.arch }}-opencv
|
||||
mingw-w64-clang-${{ matrix.arch }}-glew
|
||||
mingw-w64-clang-${{ matrix.arch }}-vulkan
|
||||
mingw-w64-clang-${{ matrix.arch }}-vulkan-headers
|
||||
mingw-w64-clang-${{ matrix.arch }}-vulkan-loader
|
||||
mingw-w64-clang-${{ matrix.arch }}-gtest
|
||||
mingw-w64-clang-${{ matrix.arch }}-qt6-base
|
||||
mingw-w64-clang-${{ matrix.arch }}-qt6-declarative
|
||||
mingw-w64-clang-${{ matrix.arch }}-qt6-multimedia
|
||||
mingw-w64-clang-${{ matrix.arch }}-qt6-svg
|
||||
mingw-w64-clang-${{ matrix.arch }}-qt6-tools
|
||||
mingw-w64-clang-${{ matrix.arch }}-qt6-translations
|
||||
base-devel
|
||||
curl
|
||||
git
|
||||
|
|
@ -383,17 +393,16 @@ jobs:
|
|||
id: restore-build-ccache
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-
|
||||
key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ matrix.arch }}-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ matrix.arch }}-
|
||||
|
||||
- name: Build RPCS3
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
export CCACHE_DIR=$(cygpath -u "$CCACHE_DIR")
|
||||
echo "CCACHE_DIR=$CCACHE_DIR"
|
||||
.ci/build-windows-clang.sh
|
||||
.ci/setup-windows-ci-vars.sh ${{ matrix.arch }} ${{ matrix.compiler }}
|
||||
.ci/deploy-windows-${{ matrix.compiler }}.sh
|
||||
.ci/build-windows-clang.sh ${{ matrix.arch }} ${{ matrix.msys2 }}
|
||||
|
||||
- name: Save build Ccache
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
|
@ -405,7 +414,7 @@ jobs:
|
|||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: RPCS3 for Windows (${{ runner.arch }}, ${{ matrix.compiler }})
|
||||
name: RPCS3 for Windows (${{ matrix.name }}, clang)
|
||||
path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}
|
||||
compression-level: 0
|
||||
if-no-files-found: error
|
||||
|
|
|
|||
2
3rdparty/FAudio
vendored
2
3rdparty/FAudio
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 4ea8afea6ba857c24e40877f487d000d559b196d
|
||||
Subproject commit 633bdb772a593104414b4b103ec752567d57c3c1
|
||||
2
3rdparty/ffmpeg
vendored
2
3rdparty/ffmpeg
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit ec6367d3ba9d0d57b9d22d4b87da8144acaf428f
|
||||
Subproject commit ce81114ed99e5510f6cd983f59a1eac9f33bb73c
|
||||
2
3rdparty/libsdl-org/SDL
vendored
2
3rdparty/libsdl-org/SDL
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit f5e5f6588921eed3d7d048ce43d9eb1ff0da0ffc
|
||||
Subproject commit a962f40bbba175e9716557a25d5d7965f134a3d3
|
||||
36
3rdparty/libsdl-org/SDL.vcxproj
vendored
36
3rdparty/libsdl-org/SDL.vcxproj
vendored
|
|
@ -23,6 +23,7 @@
|
|||
<ClInclude Include="SDL\include\SDL3\SDL_clipboard.h" />
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_copying.h" />
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_cpuinfo.h" />
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_dlopennote.h" />
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_egl.h" />
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_endian.h" />
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_error.h" />
|
||||
|
|
@ -102,6 +103,7 @@
|
|||
<ClInclude Include="SDL\src\audio\wasapi\SDL_wasapi.h" />
|
||||
<ClInclude Include="SDL\src\camera\SDL_camera_c.h" />
|
||||
<ClInclude Include="SDL\src\camera\SDL_syscamera.h" />
|
||||
<ClInclude Include="SDL\src\core\SDL_core_unsupported.h" />
|
||||
<ClInclude Include="SDL\src\core\windows\SDL_directx.h" />
|
||||
<ClInclude Include="SDL\src\core\windows\SDL_gameinput.h" />
|
||||
<ClInclude Include="SDL\src\core\windows\SDL_hid.h" />
|
||||
|
|
@ -130,6 +132,8 @@
|
|||
<ClInclude Include="SDL\src\filesystem\SDL_sysfilesystem.h" />
|
||||
<ClInclude Include="SDL\src\gpu\SDL_sysgpu.h" />
|
||||
<ClInclude Include="SDL\src\gpu\vulkan\SDL_gpu_vulkan_vkfuncs.h" />
|
||||
<ClInclude Include="SDL\src\haptic\hidapi\SDL_hidapihaptic.h" />
|
||||
<ClInclude Include="SDL\src\haptic\hidapi\SDL_hidapihaptic_c.h" />
|
||||
<ClInclude Include="SDL\src\io\SDL_asyncio_c.h" />
|
||||
<ClInclude Include="SDL\src\io\SDL_sysasyncio.h" />
|
||||
<ClInclude Include="SDL\src\haptic\SDL_haptic_c.h" />
|
||||
|
|
@ -140,7 +144,11 @@
|
|||
<ClInclude Include="SDL\src\hidapi\SDL_hidapi_c.h" />
|
||||
<ClInclude Include="SDL\src\joystick\controller_type.h" />
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_flydigi.h" />
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_nintendo.h" />
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_sinput.h" />
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_report_descriptor.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_gamepad_c.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_gamepad_db.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_joystick_c.h" />
|
||||
|
|
@ -156,6 +164,7 @@
|
|||
<ClInclude Include="SDL\src\libm\math_private.h" />
|
||||
<ClInclude Include="SDL\src\locale\SDL_syslocale.h" />
|
||||
<ClInclude Include="SDL\src\main\SDL_main_callbacks.h" />
|
||||
<ClInclude Include="SDL\src\misc\SDL_libusb.h" />
|
||||
<ClInclude Include="SDL\src\misc\SDL_sysurl.h" />
|
||||
<ClInclude Include="SDL\src\power\SDL_syspower.h" />
|
||||
<ClInclude Include="SDL\src\render\direct3d11\SDL_shaders_d3d11.h" />
|
||||
|
|
@ -175,7 +184,6 @@
|
|||
<ClInclude Include="SDL\src\render\software\SDL_drawline.h" />
|
||||
<ClInclude Include="SDL\src\render\software\SDL_drawpoint.h" />
|
||||
<ClInclude Include="SDL\src\render\software\SDL_render_sw_c.h" />
|
||||
<ClInclude Include="SDL\src\render\software\SDL_rotate.h" />
|
||||
<ClInclude Include="SDL\src\render\software\SDL_triangle.h" />
|
||||
<ClInclude Include="SDL\src\render\vulkan\SDL_shaders_vulkan.h" />
|
||||
<ClInclude Include="SDL\src\SDL_assert_c.h" />
|
||||
|
|
@ -184,20 +192,35 @@
|
|||
<ClCompile Include="SDL\src\camera\dummy\SDL_camera_dummy.c" />
|
||||
<ClCompile Include="SDL\src\camera\mediafoundation\SDL_camera_mediafoundation.c" />
|
||||
<ClCompile Include="SDL\src\camera\SDL_camera.c" />
|
||||
<ClCompile Include="SDL\src\core\windows\pch_cpp.cpp" />
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_gameinput.cpp" />
|
||||
<ClCompile Include="SDL\src\dialog\SDL_dialog.c" />
|
||||
<ClCompile Include="SDL\src\dialog\SDL_dialog_utils.c" />
|
||||
<ClCompile Include="SDL\src\filesystem\SDL_filesystem.c" />
|
||||
<ClCompile Include="SDL\src\filesystem\windows\SDL_sysfsops.c" />
|
||||
<ClCompile Include="SDL\src\haptic\hidapi\SDL_hidapihaptic.c" />
|
||||
<ClCompile Include="SDL\src\haptic\hidapi\SDL_hidapihaptic_lg4ff.c" />
|
||||
<ClCompile Include="SDL\src\io\windows\SDL_asyncio_windows_ioring.c" />
|
||||
<ClCompile Include="SDL\src\gpu\SDL_gpu.c" />
|
||||
<ClCompile Include="SDL\src\gpu\d3d12\SDL_gpu_d3d12.c" />
|
||||
<ClCompile Include="SDL\src\gpu\vulkan\SDL_gpu_vulkan.c" />
|
||||
<ClCompile Include="SDL\src\io\generic\SDL_asyncio_generic.c" />
|
||||
<ClCompile Include="SDL\src\io\SDL_asyncio.c" />
|
||||
<ClCompile Include="SDL\src\joystick\gdk\SDL_gameinputjoystick.cpp" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_8bitdo.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_flydigi.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_gip.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_sinput.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steam_triton.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_switch2.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_zuiki.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_report_descriptor.c" />
|
||||
<ClCompile Include="SDL\src\main\generic\SDL_sysmain_callbacks.c" />
|
||||
<ClCompile Include="SDL\src\main\SDL_main_callbacks.c" />
|
||||
<ClCompile Include="SDL\src\main\SDL_runapp.c" />
|
||||
<ClCompile Include="SDL\src\main\windows\SDL_sysmain_runapp.c" />
|
||||
<ClCompile Include="SDL\src\misc\SDL_libusb.c" />
|
||||
<ClCompile Include="SDL\src\render\vulkan\SDL_render_vulkan.c" />
|
||||
<ClCompile Include="SDL\src\render\vulkan\SDL_shaders_vulkan.c" />
|
||||
<ClCompile Include="SDL\src\SDL_guid.c" />
|
||||
|
|
@ -241,6 +264,7 @@
|
|||
<ClInclude Include="SDL\src\video\khronos\vulkan\vulkan_xcb.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vulkan\vulkan_xlib.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vulkan\vulkan_xlib_xrandr.h" />
|
||||
<ClInclude Include="SDL\src\video\miniz.h" />
|
||||
<ClInclude Include="SDL\src\video\offscreen\SDL_offscreenevents_c.h" />
|
||||
<ClInclude Include="SDL\src\video\offscreen\SDL_offscreenframebuffer_c.h" />
|
||||
<ClInclude Include="SDL\src\video\offscreen\SDL_offscreenopengles.h" />
|
||||
|
|
@ -256,13 +280,14 @@
|
|||
<ClInclude Include="SDL\src\video\SDL_pixels_c.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_rect_c.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_RLEaccel_c.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_rotate.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_stb_c.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_surface_c.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_sysvideo.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_video_unsupported.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_vulkan_internal.h" />
|
||||
<ClInclude Include="SDL\src\video\SDL_yuv_c.h" />
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_msctf.h" />
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_surface_utils.h" />
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_windowsclipboard.h" />
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_windowsevents.h" />
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_windowsframebuffer.h" />
|
||||
|
|
@ -303,7 +328,6 @@
|
|||
<ClCompile Include="SDL\src\audio\SDL_wave.c" />
|
||||
<ClCompile Include="SDL\src\audio\wasapi\SDL_wasapi.c" />
|
||||
<ClCompile Include="SDL\src\core\SDL_core_unsupported.c" />
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_gameinput.c" />
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_hid.c" />
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_immdevice.c" />
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_windows.c" />
|
||||
|
|
@ -333,7 +357,6 @@
|
|||
<ClCompile Include="SDL\src\hidapi\SDL_hidapi.c" />
|
||||
<ClCompile Include="SDL\src\joystick\controller_type.c" />
|
||||
<ClCompile Include="SDL\src\joystick\dummy\SDL_sysjoystick.c" />
|
||||
<ClCompile Include="SDL\src\joystick\gdk\SDL_gameinputjoystick.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapijoystick.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_combined.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_gamecube.c" />
|
||||
|
|
@ -393,7 +416,6 @@
|
|||
<ClCompile Include="SDL\src\render\software\SDL_drawline.c" />
|
||||
<ClCompile Include="SDL\src\render\software\SDL_drawpoint.c" />
|
||||
<ClCompile Include="SDL\src\render\software\SDL_render_sw.c" />
|
||||
<ClCompile Include="SDL\src\render\software\SDL_rotate.c" />
|
||||
<ClCompile Include="SDL\src\render\software\SDL_triangle.c" />
|
||||
<ClCompile Include="SDL\src\SDL.c" />
|
||||
<ClCompile Include="SDL\src\SDL_assert.c" />
|
||||
|
|
@ -464,6 +486,7 @@
|
|||
<ClCompile Include="SDL\src\video\SDL_pixels.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_rect.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_RLEaccel.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_rotate.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_stb.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_stretch.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_surface.c" />
|
||||
|
|
@ -471,12 +494,11 @@
|
|||
<ClCompile Include="SDL\src\video\SDL_video_unsupported.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_vulkan_utils.c" />
|
||||
<ClCompile Include="SDL\src\video\SDL_yuv.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_surface_utils.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsclipboard.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsevents.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsframebuffer.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsgameinput.cpp" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowskeyboard.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsgameinput.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsmessagebox.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsmodes.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsmouse.c" />
|
||||
|
|
|
|||
147
3rdparty/libsdl-org/SDL.vcxproj.filters
vendored
147
3rdparty/libsdl-org/SDL.vcxproj.filters
vendored
|
|
@ -214,6 +214,9 @@
|
|||
<Filter Include="io\windows">
|
||||
<UniqueIdentifier>{000028b2ea36d7190d13777a4dc70000}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="haptic\hidapi">
|
||||
<UniqueIdentifier>{695ffc61-5497-4227-b415-15e9bdd5b6bf}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_begin_code.h">
|
||||
|
|
@ -699,9 +702,6 @@
|
|||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std_func.h">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_surface_utils.h">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_windowsclipboard.h">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -831,9 +831,6 @@
|
|||
<ClInclude Include="SDL\src\render\software\SDL_render_sw_c.h">
|
||||
<Filter>render\software</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\render\software\SDL_rotate.h">
|
||||
<Filter>render\software</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\render\software\SDL_triangle.h">
|
||||
<Filter>render\software</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -911,12 +908,6 @@
|
|||
<ClInclude Include="SDL\src\hidapi\SDL_hidapi_c.h" />
|
||||
<ClInclude Include="SDL\src\thread\generic\SDL_sysrwlock_c.h" />
|
||||
<ClInclude Include="SDL\src\thread\generic\SDL_sysrwlock_c.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_common.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_internal.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx_func.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std.h" />
|
||||
<ClInclude Include="SDL\src\render\vulkan\SDL_shaders_vulkan.h">
|
||||
<Filter>render\vulkan</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -950,6 +941,60 @@
|
|||
<ClInclude Include="SDL\include\SDL3\SDL_storage.h" />
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_time.h" />
|
||||
<ClInclude Include="SDL\src\events\SDL_categories_c.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std.h">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_common.h">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_internal.h">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.h">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx_func.h">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.h">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\miniz.h">
|
||||
<Filter>video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\SDL_rotate.h">
|
||||
<Filter>video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\video\SDL_video_unsupported.h">
|
||||
<Filter>video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\misc\SDL_libusb.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\haptic\hidapi\SDL_hidapihaptic.h">
|
||||
<Filter>haptic\hidapi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\haptic\hidapi\SDL_hidapihaptic_c.h">
|
||||
<Filter>haptic\hidapi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\core\SDL_core_unsupported.h">
|
||||
<Filter>core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_flydigi.h">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_nintendo.h">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_sinput.h">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_report_descriptor.h">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SDL\include\SDL3\SDL_dlopennote.h">
|
||||
<Filter>API Headers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="SDL\src\audio\wasapi\SDL_wasapi.c" />
|
||||
|
|
@ -1037,9 +1082,6 @@
|
|||
<ClCompile Include="SDL\src\core\SDL_core_unsupported.c">
|
||||
<Filter>core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_gameinput.c">
|
||||
<Filter>core\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_hid.c">
|
||||
<Filter>core\windows</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -1166,9 +1208,6 @@
|
|||
<ClCompile Include="SDL\src\joystick\dummy\SDL_sysjoystick.c">
|
||||
<Filter>joystick\dummy</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\gdk\SDL_gameinputjoystick.c">
|
||||
<Filter>joystick\gdk</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_combined.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -1328,9 +1367,6 @@
|
|||
<ClCompile Include="SDL\src\video\dummy\SDL_nullvideo.c">
|
||||
<Filter>video\dummy</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_surface_utils.c">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsclipboard.c">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -1343,9 +1379,6 @@
|
|||
<ClCompile Include="SDL\src\video\windows\SDL_windowskeyboard.c">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsgameinput.c">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsmessagebox.c">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -1508,9 +1541,6 @@
|
|||
<ClCompile Include="SDL\src\render\software\SDL_render_sw.c">
|
||||
<Filter>render\software</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\render\software\SDL_rotate.c">
|
||||
<Filter>render\software</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\render\software\SDL_triangle.c">
|
||||
<Filter>render\software</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -1535,9 +1565,6 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\thread\generic\SDL_sysrwlock.c" />
|
||||
<ClCompile Include="SDL\src\thread\generic\SDL_sysrwlock.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_std.c" />
|
||||
<ClCompile Include="SDL\src\render\vulkan\SDL_render_vulkan.c">
|
||||
<Filter>render\vulkan</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -1579,6 +1606,66 @@
|
|||
<ClCompile Include="SDL\src\storage\generic\SDL_genericstorage.c" />
|
||||
<ClCompile Include="SDL\src\storage\steam\SDL_steamstorage.c" />
|
||||
<ClCompile Include="SDL\src\storage\SDL_storage.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsgameinput.cpp">
|
||||
<Filter>video\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.c">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.c">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_std.c">
|
||||
<Filter>video\yuv2rgb</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\video\SDL_rotate.c">
|
||||
<Filter>video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\misc\SDL_libusb.c">
|
||||
<Filter>misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\haptic\hidapi\SDL_hidapihaptic.c">
|
||||
<Filter>haptic\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\haptic\hidapi\SDL_hidapihaptic_lg4ff.c">
|
||||
<Filter>haptic\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\core\windows\pch_cpp.cpp">
|
||||
<Filter>core\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\core\windows\SDL_gameinput.cpp">
|
||||
<Filter>core\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\gdk\SDL_gameinputjoystick.cpp">
|
||||
<Filter>joystick\gdk</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_8bitdo.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_flydigi.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_gip.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_lg4ff.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_sinput.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steam_triton.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_switch2.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_zuiki.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_report_descriptor.c">
|
||||
<Filter>joystick\hidapi</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="SDL\src\core\windows\version.rc" />
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ namespace fs
|
|||
// Virtual device
|
||||
struct device_base
|
||||
{
|
||||
const std::string fs_prefix;
|
||||
std::string fs_prefix;
|
||||
|
||||
device_base();
|
||||
virtual ~device_base();
|
||||
|
|
@ -257,6 +257,8 @@ namespace fs
|
|||
// Open file with specified mode
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
|
||||
file(std::unique_ptr<file_base>&& ptr) : m_file(std::move(ptr)) {}
|
||||
|
||||
static file from_native_handle(native_handle handle);
|
||||
|
||||
// Open memory for read
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<OutDir>$(SolutionDir)build\lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<LibraryPath>$(SolutionDir)build\lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
|
||||
<IntDir>$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
<GTestPath>$(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.targets</GTestPath>
|
||||
<GTestPath>$(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.targets</GTestPath>
|
||||
<GTestInstalled Condition="Exists('$(GTestPath)')">true</GTestInstalled>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ target_sources(rpcs3_emu PRIVATE
|
|||
../Loader/PSF.cpp
|
||||
../Loader/PUP.cpp
|
||||
../Loader/TAR.cpp
|
||||
../Loader/ISO.cpp
|
||||
../Loader/TROPUSR.cpp
|
||||
../Loader/TRP.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -211,6 +211,7 @@ struct vdec_context final
|
|||
lf_queue<vdec_cmd> in_cmd;
|
||||
|
||||
AVRational log_time_base{}; // Used to reduce log spam
|
||||
AVRational log_framerate{}; // Used to reduce log spam
|
||||
|
||||
vdec_context(s32 type, u32 /*profile*/, u32 addr, u32 size, vm::ptr<CellVdecCbMsg> func, u32 arg)
|
||||
: type(type)
|
||||
|
|
@ -231,6 +232,7 @@ struct vdec_context final
|
|||
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
break;
|
||||
}
|
||||
case CELL_VDEC_CODEC_TYPE_MPEG4:
|
||||
case CELL_VDEC_CODEC_TYPE_DIVX:
|
||||
{
|
||||
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG4);
|
||||
|
|
@ -292,6 +294,19 @@ struct vdec_context final
|
|||
sws_freeContext(sws);
|
||||
}
|
||||
|
||||
static u32 freq_to_framerate_code(f64 freq)
|
||||
{
|
||||
if (std::abs(freq - 23.976) < 0.002) return CELL_VDEC_FRC_24000DIV1001;
|
||||
if (std::abs(freq - 24.000) < 0.001) return CELL_VDEC_FRC_24;
|
||||
if (std::abs(freq - 25.000) < 0.001) return CELL_VDEC_FRC_25;
|
||||
if (std::abs(freq - 29.970) < 0.002) return CELL_VDEC_FRC_30000DIV1001;
|
||||
if (std::abs(freq - 30.000) < 0.001) return CELL_VDEC_FRC_30;
|
||||
if (std::abs(freq - 50.000) < 0.001) return CELL_VDEC_FRC_50;
|
||||
if (std::abs(freq - 59.940) < 0.002) return CELL_VDEC_FRC_60000DIV1001;
|
||||
if (std::abs(freq - 60.000) < 0.001) return CELL_VDEC_FRC_60;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exec(ppu_thread& ppu, u32 vid)
|
||||
{
|
||||
perf_meter<"VDEC"_u32> perf0;
|
||||
|
|
@ -341,6 +356,7 @@ struct vdec_context final
|
|||
|
||||
out_queue.clear(); // Flush image queue
|
||||
log_time_base = {};
|
||||
log_framerate = {};
|
||||
|
||||
frc_set = 0; // TODO: ???
|
||||
next_pts = 0;
|
||||
|
|
@ -471,10 +487,10 @@ struct vdec_context final
|
|||
frame.userdata = au_usrd;
|
||||
frame.attr = attr;
|
||||
|
||||
u64 amend = 0;
|
||||
|
||||
if (frc_set)
|
||||
{
|
||||
u64 amend = 0;
|
||||
|
||||
switch (frc_set)
|
||||
{
|
||||
case CELL_VDEC_FRC_24000DIV1001: amend = 1001 * 90000 / 24000; break;
|
||||
|
|
@ -491,62 +507,45 @@ struct vdec_context final
|
|||
}
|
||||
}
|
||||
|
||||
next_pts += amend;
|
||||
next_dts += amend;
|
||||
frame.frc = frc_set;
|
||||
}
|
||||
else if (ctx->time_base.num == 0)
|
||||
else if (ctx->time_base.den && ctx->time_base.num)
|
||||
{
|
||||
if (log_time_base.den != ctx->time_base.den || log_time_base.num != ctx->time_base.num)
|
||||
const auto freq = 1. * ctx->time_base.den / ctx->time_base.num / ticks_per_frame;
|
||||
|
||||
frame.frc = freq_to_framerate_code(freq);
|
||||
if (frame.frc)
|
||||
{
|
||||
cellVdec.error("time_base.num is 0 (handle=0x%x, seq_id=%d, cmd_id=%d, %d/%d, tpf=%d framerate=%d/%d)", handle, cmd->seq_id, cmd->id, ctx->time_base.num, ctx->time_base.den, ticks_per_frame, ctx->framerate.num, ctx->framerate.den);
|
||||
amend = u64{90000} * ctx->time_base.num * ticks_per_frame / ctx->time_base.den;
|
||||
}
|
||||
}
|
||||
else if (ctx->framerate.den && ctx->framerate.num)
|
||||
{
|
||||
const auto freq = ctx->framerate.num / static_cast<f64>(ctx->framerate.den);
|
||||
|
||||
frame.frc = freq_to_framerate_code(freq);
|
||||
if (frame.frc)
|
||||
{
|
||||
amend = u64{90000} * ctx->framerate.den / ctx->framerate.num;
|
||||
}
|
||||
}
|
||||
|
||||
if (amend == 0 || frame.frc == 0)
|
||||
{
|
||||
if (log_time_base.den != ctx->time_base.den || log_time_base.num != ctx->time_base.num || log_framerate.den != ctx->framerate.den || log_framerate.num != ctx->framerate.num)
|
||||
{
|
||||
cellVdec.error("Invalid frequency (handle=0x%x, seq_id=%d, cmd_id=%d, timebase=%d/%d, tpf=%d framerate=%d/%d)", handle, cmd->seq_id, cmd->id, ctx->time_base.num, ctx->time_base.den, ticks_per_frame, ctx->framerate.num, ctx->framerate.den);
|
||||
log_time_base = ctx->time_base;
|
||||
log_framerate = ctx->framerate;
|
||||
}
|
||||
|
||||
// Hack
|
||||
const u64 amend = u64{90000} / 30;
|
||||
amend = u64{90000} / 30;
|
||||
frame.frc = CELL_VDEC_FRC_30;
|
||||
next_pts += amend;
|
||||
next_dts += amend;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 amend = u64{90000} * ctx->time_base.num * ticks_per_frame / ctx->time_base.den;
|
||||
const auto freq = 1. * ctx->time_base.den / ctx->time_base.num / ticks_per_frame;
|
||||
|
||||
if (std::abs(freq - 23.976) < 0.002)
|
||||
frame.frc = CELL_VDEC_FRC_24000DIV1001;
|
||||
else if (std::abs(freq - 24.000) < 0.001)
|
||||
frame.frc = CELL_VDEC_FRC_24;
|
||||
else if (std::abs(freq - 25.000) < 0.001)
|
||||
frame.frc = CELL_VDEC_FRC_25;
|
||||
else if (std::abs(freq - 29.970) < 0.002)
|
||||
frame.frc = CELL_VDEC_FRC_30000DIV1001;
|
||||
else if (std::abs(freq - 30.000) < 0.001)
|
||||
frame.frc = CELL_VDEC_FRC_30;
|
||||
else if (std::abs(freq - 50.000) < 0.001)
|
||||
frame.frc = CELL_VDEC_FRC_50;
|
||||
else if (std::abs(freq - 59.940) < 0.002)
|
||||
frame.frc = CELL_VDEC_FRC_60000DIV1001;
|
||||
else if (std::abs(freq - 60.000) < 0.001)
|
||||
frame.frc = CELL_VDEC_FRC_60;
|
||||
else
|
||||
{
|
||||
if (log_time_base.den != ctx->time_base.den || log_time_base.num != ctx->time_base.num)
|
||||
{
|
||||
// 1/1000 usually means that the time stamps are written in 1ms units and that the frame rate may vary.
|
||||
cellVdec.error("Unsupported time_base (handle=0x%x, seq_id=%d, cmd_id=%d, %d/%d, tpf=%d framerate=%d/%d)", handle, cmd->seq_id, cmd->id, ctx->time_base.num, ctx->time_base.den, ticks_per_frame, ctx->framerate.num, ctx->framerate.den);
|
||||
log_time_base = ctx->time_base;
|
||||
}
|
||||
|
||||
// Hack
|
||||
amend = u64{90000} / 30;
|
||||
frame.frc = CELL_VDEC_FRC_30;
|
||||
}
|
||||
|
||||
next_pts += amend;
|
||||
next_dts += amend;
|
||||
}
|
||||
next_pts += amend;
|
||||
next_dts += amend;
|
||||
|
||||
cellVdec.trace("Got picture (handle=0x%x, seq_id=%d, cmd_id=%d, pts=0x%llx[0x%llx], dts=0x%llx[0x%llx])", handle, cmd->seq_id, cmd->id, frame.pts, frame->pts, frame.dts, frame->pkt_dts);
|
||||
|
||||
|
|
@ -680,7 +679,15 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
cellVdec.warning("cellVdecQueryAttr: AVC (profile=%d)", profile);
|
||||
|
||||
//const vm::ptr<CellVdecAvcSpecificInfo> sinfo = vm::cast(spec_addr);
|
||||
const vm::ptr<CellVdecAvcSpecificInfo> sinfo = vm::cast(spec_addr);
|
||||
|
||||
if (sinfo)
|
||||
{
|
||||
if (sinfo->thisSize != sizeof(CellVdecAvcSpecificInfo))
|
||||
{
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid AVC specific info size %d", sinfo->thisSize };
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: sinfo
|
||||
|
||||
|
|
@ -699,7 +706,7 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
case CELL_VDEC_AVC_LEVEL_4P0: memSize = new_sdk ? 0x33A5FFD : 0x36A527D; break;
|
||||
case CELL_VDEC_AVC_LEVEL_4P1: memSize = new_sdk ? 0x33A5FFD : 0x36A527D; break;
|
||||
case CELL_VDEC_AVC_LEVEL_4P2: memSize = new_sdk ? 0x33A5FFD : 0x36A527D; break;
|
||||
default: return CELL_VDEC_ERROR_ARG;
|
||||
default: return { CELL_VDEC_ERROR_ARG, "Invalid AVC profile level %d", profile };
|
||||
}
|
||||
|
||||
decoderVerLower = 0x11300;
|
||||
|
|
@ -715,7 +722,7 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
if (sinfo->thisSize != sizeof(CellVdecMpeg2SpecificInfo))
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid MPEG2 specific info size %d", sinfo->thisSize };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -730,7 +737,7 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
if (maxDecW > 352 || maxDecH > 288)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
|
||||
}
|
||||
|
||||
memSize = new_sdk ? 0x11290B : 0x2A610B;
|
||||
|
|
@ -740,7 +747,7 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
if (maxDecW > 720 || maxDecH > 576)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
|
||||
}
|
||||
|
||||
memSize = new_sdk ? 0x2DFB8B : 0x47110B;
|
||||
|
|
@ -750,7 +757,7 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
if (maxDecW > 1440 || maxDecH > 1152)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
|
||||
}
|
||||
|
||||
memSize = new_sdk ? 0xA0270B : 0xB8F90B;
|
||||
|
|
@ -760,18 +767,19 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
if (maxDecW > 1920 || maxDecH > 1152)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
|
||||
}
|
||||
|
||||
memSize = new_sdk ? 0xD2F40B : 0xEB990B;
|
||||
break;
|
||||
}
|
||||
default: return CELL_VDEC_ERROR_ARG;
|
||||
default: return { CELL_VDEC_ERROR_ARG, "Invalid MPEG2 profile %d", profile };
|
||||
}
|
||||
|
||||
decoderVerLower = 0x1030000;
|
||||
break;
|
||||
}
|
||||
case CELL_VDEC_CODEC_TYPE_MPEG4:
|
||||
case CELL_VDEC_CODEC_TYPE_DIVX:
|
||||
{
|
||||
cellVdec.warning("cellVdecQueryAttr: DivX (profile=%d)", profile);
|
||||
|
|
@ -780,9 +788,9 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
|
||||
if (sinfo)
|
||||
{
|
||||
if (sinfo->thisSize != sizeof(CellVdecDivxSpecificInfo2))
|
||||
if (sinfo->thisSize != sizeof(CellVdecDivxSpecificInfo) && sinfo->thisSize != sizeof(CellVdecDivxSpecificInfo2))
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid DIVX specific info size %d", sinfo->thisSize };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -790,7 +798,7 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
|
||||
//const u32 maxDecH = sinfo ? +sinfo->maxDecodedFrameHeight : 0;
|
||||
//const u32 maxDecW = sinfo ? +sinfo->maxDecodedFrameWidth : 0;
|
||||
u32 nrOfBuf = sinfo ? +sinfo->numberOfDecodedFrameBuffer : 0;
|
||||
u32 nrOfBuf = sinfo && sinfo->thisSize == sizeof(CellVdecDivxSpecificInfo2) ? +sinfo->numberOfDecodedFrameBuffer : 0;
|
||||
|
||||
if (nrOfBuf == 0)
|
||||
{
|
||||
|
|
@ -800,12 +808,12 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
if (profile != CELL_VDEC_DIVX_QMOBILE && profile != CELL_VDEC_DIVX_MOBILE)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid number of decoded frame buffers %d for DIVX profile %d", nrOfBuf, profile };
|
||||
}
|
||||
}
|
||||
else if (nrOfBuf != 4 && nrOfBuf != 3)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "Invalid number of decoded frame buffers %d for DIVX", nrOfBuf };
|
||||
}
|
||||
|
||||
// TODO: change memSize based on buffercount.
|
||||
|
|
@ -814,16 +822,17 @@ static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0
|
|||
{
|
||||
case CELL_VDEC_DIVX_QMOBILE : memSize = new_sdk ? 0x11B720 : 0x1DEF30; break;
|
||||
case CELL_VDEC_DIVX_MOBILE : memSize = new_sdk ? 0x19A740 : 0x26DED0; break;
|
||||
case CELL_VDEC_MPEG4_SIMPLE_PROFILE: // just a guess based on the profile used by singstar before and after update
|
||||
case CELL_VDEC_DIVX_HOME_THEATER: memSize = new_sdk ? 0x386A60 : 0x498060; break;
|
||||
case CELL_VDEC_DIVX_HD_720 : memSize = new_sdk ? 0x692070 : 0x805690; break;
|
||||
case CELL_VDEC_DIVX_HD_1080 : memSize = new_sdk ? 0xD78100 : 0xFC9870; break;
|
||||
default: return CELL_VDEC_ERROR_ARG;
|
||||
default: return { CELL_VDEC_ERROR_ARG, "Invalid DIVX profile %d", profile };
|
||||
}
|
||||
|
||||
decoderVerLower = 0x30806;
|
||||
break;
|
||||
}
|
||||
default: return CELL_VDEC_ERROR_ARG;
|
||||
default: return { CELL_VDEC_ERROR_ARG, "Invalid codec type %d", type };
|
||||
}
|
||||
|
||||
attr->decoderVerLower = decoderVerLower;
|
||||
|
|
@ -839,7 +848,7 @@ error_code cellVdecQueryAttr(vm::cptr<CellVdecType> type, vm::ptr<CellVdecAttr>
|
|||
|
||||
if (!type || !attr)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "type=%d, attr=%d", !!type, !!attr };
|
||||
}
|
||||
|
||||
return vdecQueryAttr(type->codecType, type->profileLevel, 0, attr.get_ptr());
|
||||
|
|
@ -851,7 +860,7 @@ error_code cellVdecQueryAttrEx(vm::cptr<CellVdecTypeEx> type, vm::ptr<CellVdecAt
|
|||
|
||||
if (!type || !attr)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "type=%d, attr=%d", !!type, !!attr };
|
||||
}
|
||||
|
||||
return vdecQueryAttr(type->codecType, type->profileLevel, type->codecSpecificInfo_addr, attr.get_ptr());
|
||||
|
|
@ -862,13 +871,14 @@ static error_code vdecOpen(ppu_thread& ppu, T type, U res, vm::cptr<CellVdecCb>
|
|||
{
|
||||
if (!type || !res || !cb || !handle || !cb->cbFunc)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "type=%d, res=%d, cb=%d, handle=%d, cbFunc=%d", !!type, !!res, !!cb, !!handle, cb && cb->cbFunc };
|
||||
}
|
||||
|
||||
if (!res->memAddr || res->ppuThreadPriority + 0u >= 3072 || res->spuThreadPriority + 0u >= 256 || res->ppuThreadStackSize < 4096
|
||||
|| type->codecType + 0u >= 0xe)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "memAddr=%d, ppuThreadPriority=%d, spuThreadPriority=%d, ppuThreadStackSize=%d, codecType=%d",
|
||||
res->memAddr, res->ppuThreadPriority, res->spuThreadPriority, res->ppuThreadStackSize, type->codecType };
|
||||
}
|
||||
|
||||
u32 spec_addr = 0;
|
||||
|
|
@ -878,11 +888,16 @@ static error_code vdecOpen(ppu_thread& ppu, T type, U res, vm::cptr<CellVdecCb>
|
|||
spec_addr = type->codecSpecificInfo_addr;
|
||||
}
|
||||
|
||||
if (CellVdecAttr attr{};
|
||||
vdecQueryAttr(type->codecType, type->profileLevel, spec_addr, &attr) != CELL_OK ||
|
||||
attr.memSize > res->memSize)
|
||||
CellVdecAttr attr{};
|
||||
const error_code err = vdecQueryAttr(type->codecType, type->profileLevel, spec_addr, &attr);
|
||||
if (err != CELL_OK)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (attr.memSize > res->memSize)
|
||||
{
|
||||
return { CELL_VDEC_ERROR_ARG, "attr.memSize=%d, res->memSize=%d", attr.memSize, res->memSize };
|
||||
}
|
||||
|
||||
// Create decoder context
|
||||
|
|
@ -953,7 +968,7 @@ error_code cellVdecClose(ppu_thread& ppu, u32 handle)
|
|||
|
||||
if (!vdec)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "vdec is nullptr" };
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -991,7 +1006,7 @@ error_code cellVdecClose(ppu_thread& ppu, u32 handle)
|
|||
if (!idm::remove_verify<vdec_context>(handle, std::move(vdec)))
|
||||
{
|
||||
// Other thread removed it beforehead
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "remove_verify failed" };
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
|
|
@ -1007,7 +1022,7 @@ error_code cellVdecStartSeq(ppu_thread& ppu, u32 handle)
|
|||
|
||||
if (!vdec)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "vdec is nullptr" };
|
||||
}
|
||||
|
||||
sequence_state old_state{};
|
||||
|
|
@ -1059,7 +1074,7 @@ error_code cellVdecEndSeq(ppu_thread& ppu, u32 handle)
|
|||
|
||||
if (!vdec)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "vdec is nullptr" };
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -1196,7 +1211,7 @@ error_code cellVdecGetPictureExt(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecP
|
|||
|
||||
if (!vdec || !format)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "vdec=%d, format=%d", !!vdec, !!format };
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -1210,12 +1225,12 @@ error_code cellVdecGetPictureExt(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecP
|
|||
|
||||
if (format->formatType > 4 || (format->formatType <= CELL_VDEC_PICFMT_RGBA32_ILV && format->colorMatrixType > CELL_VDEC_COLOR_MATRIX_TYPE_BT709))
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return {CELL_VDEC_ERROR_ARG, "formatType=%d, colorMatrixType=%d", +format->formatType, +format->colorMatrixType};
|
||||
}
|
||||
|
||||
if (arg4 && arg4 != 8 && arg4 != 0xc)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "arg4=0x%x", arg4 };
|
||||
}
|
||||
|
||||
if (arg4 || format->unk0 || format->unk1)
|
||||
|
|
@ -1335,7 +1350,7 @@ error_code cellVdecGetPicture(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecPicF
|
|||
|
||||
if (!format)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "format is nullptr" };
|
||||
}
|
||||
|
||||
vm::var<CellVdecPicFormat2> format2;
|
||||
|
|
@ -1358,7 +1373,7 @@ error_code cellVdecGetPicItem(ppu_thread& ppu, u32 handle, vm::pptr<CellVdecPicI
|
|||
|
||||
if (!vdec || !picItem)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "vdec=%d, picItem=%d", !!vdec, !!picItem };
|
||||
}
|
||||
|
||||
u64 sequence_id{};
|
||||
|
|
@ -1510,7 +1525,7 @@ error_code cellVdecGetPicItem(ppu_thread& ppu, u32 handle, vm::pptr<CellVdecPicI
|
|||
avc->reserved[0] = 0;
|
||||
avc->reserved[1] = 0;
|
||||
}
|
||||
else if (vdec->type == CELL_VDEC_CODEC_TYPE_DIVX)
|
||||
else if (vdec->type == CELL_VDEC_CODEC_TYPE_MPEG4 || vdec->type == CELL_VDEC_CODEC_TYPE_DIVX)
|
||||
{
|
||||
const vm::ptr<CellVdecDivxInfo> dvx = picinfo_addr;
|
||||
|
||||
|
|
@ -1601,7 +1616,7 @@ error_code cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frameRateCode)
|
|||
// 0x80 seems like a common prefix
|
||||
if (!vdec || (frameRateCode & 0xf8) != 0x80)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "vdec=%d, frameRateCode=0x%x", !!vdec, +frameRateCode };
|
||||
}
|
||||
|
||||
std::lock_guard lock{vdec->mutex};
|
||||
|
|
@ -1625,7 +1640,7 @@ error_code cellVdecOpenExt(ppu_thread& ppu, vm::cptr<CellVdecType> type, vm::cpt
|
|||
|
||||
if (!res)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "res is nullptr" };
|
||||
}
|
||||
|
||||
vm::var<CellVdecResource> tmp = vm::make_var<CellVdecResource>({});
|
||||
|
|
@ -1678,7 +1693,7 @@ error_code cellVdecSetPts(u32 handle, vm::ptr<void> unk)
|
|||
|
||||
if (!vdec || !unk)
|
||||
{
|
||||
return CELL_VDEC_ERROR_ARG;
|
||||
return { CELL_VDEC_ERROR_ARG, "vdec=%d, unk=%d", !!vdec, !!unk };
|
||||
}
|
||||
|
||||
std::lock_guard lock{vdec->mutex};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ enum CellVdecCodecType : s32
|
|||
{
|
||||
CELL_VDEC_CODEC_TYPE_MPEG2 = 0,
|
||||
CELL_VDEC_CODEC_TYPE_AVC = 1,
|
||||
CELL_VDEC_CODEC_TYPE_MPEG4 = 2,
|
||||
CELL_VDEC_CODEC_TYPE_DIVX = 5,
|
||||
CELL_VDEC_CODEC_TYPE_MAX
|
||||
};
|
||||
|
|
@ -388,6 +389,7 @@ struct CellVdecAvcInfo
|
|||
// DIVX Profile
|
||||
enum DIVX_level : u8
|
||||
{
|
||||
CELL_VDEC_MPEG4_SIMPLE_PROFILE = 4,
|
||||
CELL_VDEC_DIVX_QMOBILE = 10,
|
||||
CELL_VDEC_DIVX_MOBILE = 11,
|
||||
CELL_VDEC_DIVX_HOME_THEATER = 12,
|
||||
|
|
|
|||
|
|
@ -745,7 +745,7 @@ error_code sceNpDrmIsAvailable(ppu_thread& ppu, vm::cptr<u8> k_licensee_addr, vm
|
|||
lv2_obj::sleep(ppu);
|
||||
|
||||
const auto ret = npDrmIsAvailable(k_licensee_addr, drm_path);
|
||||
lv2_sleep(25'000, &ppu);
|
||||
lv2_sleep(5'000, &ppu);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1035,7 +1035,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
|||
|
||||
error_code sys_fs_open(ppu_thread& ppu, vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32 mode, vm::cptr<void> arg, u64 size)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_open(path=%s, flags=%#o, fd=*0x%x, mode=%#o, arg=*0x%x, size=0x%llx)", path, flags, fd, mode, arg, size);
|
||||
|
||||
|
|
@ -1083,7 +1083,7 @@ error_code sys_fs_open(ppu_thread& ppu, vm::cptr<char> path, s32 flags, vm::ptr<
|
|||
|
||||
error_code sys_fs_read(ppu_thread& ppu, u32 fd, vm::ptr<void> buf, u64 nbytes, vm::ptr<u64> nread)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_read(fd=%d, buf=*0x%x, nbytes=0x%llx, nread=*0x%x)", fd, buf, nbytes, nread);
|
||||
|
||||
|
|
@ -1156,7 +1156,7 @@ error_code sys_fs_read(ppu_thread& ppu, u32 fd, vm::ptr<void> buf, u64 nbytes, v
|
|||
|
||||
error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr<void> buf, u64 nbytes, vm::ptr<u64> nwrite)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_write(fd=%d, buf=*0x%x, nbytes=0x%llx, nwrite=*0x%x)", fd, buf, nbytes, nwrite);
|
||||
|
||||
|
|
@ -1238,7 +1238,7 @@ error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr<void> buf, u64 nbytes,
|
|||
|
||||
error_code sys_fs_close(ppu_thread& ppu, u32 fd)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
const auto file = idm::get_unlocked<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
|
|
@ -1314,7 +1314,7 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
|
|||
|
||||
error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_opendir(path=%s, fd=*0x%x)", path, fd);
|
||||
|
||||
|
|
@ -1433,7 +1433,7 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
|||
|
||||
error_code sys_fs_readdir(ppu_thread& ppu, u32 fd, vm::ptr<CellFsDirent> dir, vm::ptr<u64> nread)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_readdir(fd=%d, dir=*0x%x, nread=*0x%x)", fd, dir, nread);
|
||||
|
||||
|
|
@ -1490,7 +1490,7 @@ error_code sys_fs_readdir(ppu_thread& ppu, u32 fd, vm::ptr<CellFsDirent> dir, vm
|
|||
|
||||
error_code sys_fs_closedir(ppu_thread& ppu, u32 fd)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_closedir(fd=%d)", fd);
|
||||
|
||||
|
|
@ -1504,7 +1504,6 @@ error_code sys_fs_closedir(ppu_thread& ppu, u32 fd)
|
|||
|
||||
error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat> sb)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_stat(path=%s, sb=*0x%x)", path, sb);
|
||||
|
|
@ -1618,7 +1617,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
|||
|
||||
error_code sys_fs_fstat(ppu_thread& ppu, u32 fd, vm::ptr<CellFsStat> sb)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_fstat(fd=%d, sb=*0x%x)", fd, sb);
|
||||
|
||||
|
|
@ -1673,7 +1672,7 @@ error_code sys_fs_link(ppu_thread&, vm::cptr<char> from, vm::cptr<char> to)
|
|||
|
||||
error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_mkdir(path=%s, mode=%#o)", path, mode);
|
||||
|
||||
|
|
@ -1734,7 +1733,7 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
|
|||
|
||||
error_code sys_fs_rename(ppu_thread& ppu, vm::cptr<char> from, vm::cptr<char> to)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_rename(from=%s, to=%s)", from, to);
|
||||
|
||||
|
|
@ -1799,7 +1798,7 @@ error_code sys_fs_rename(ppu_thread& ppu, vm::cptr<char> from, vm::cptr<char> to
|
|||
|
||||
error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr<char> path)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_rmdir(path=%s)", path);
|
||||
|
||||
|
|
@ -1854,7 +1853,7 @@ error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr<char> path)
|
|||
|
||||
error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_unlink(path=%s)", path);
|
||||
|
||||
|
|
@ -1919,7 +1918,7 @@ error_code sys_fs_access(ppu_thread&, vm::cptr<char> path, s32 mode)
|
|||
|
||||
error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32 _size)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_fcntl(fd=%d, op=0x%x, arg=*0x%x, size=0x%x)", fd, op, _arg, _size);
|
||||
|
||||
|
|
@ -2591,7 +2590,7 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
|||
|
||||
error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr<u64> pos)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_lseek(fd=%d, offset=0x%llx, whence=0x%x, pos=*0x%x)", fd, offset, whence, pos);
|
||||
|
||||
|
|
@ -2636,7 +2635,7 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
|
|||
|
||||
error_code sys_fs_fdatasync(ppu_thread& ppu, u32 fd)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_fdadasync(fd=%d)", fd);
|
||||
|
||||
|
|
@ -2662,7 +2661,7 @@ error_code sys_fs_fdatasync(ppu_thread& ppu, u32 fd)
|
|||
|
||||
error_code sys_fs_fsync(ppu_thread& ppu, u32 fd)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_fsync(fd=%d)", fd);
|
||||
|
||||
|
|
@ -2688,7 +2687,7 @@ error_code sys_fs_fsync(ppu_thread& ppu, u32 fd)
|
|||
|
||||
error_code sys_fs_fget_block_size(ppu_thread& ppu, u32 fd, vm::ptr<u64> sector_size, vm::ptr<u64> block_size, vm::ptr<u64> arg4, vm::ptr<s32> out_flags)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_fget_block_size(fd=%d, sector_size=*0x%x, block_size=*0x%x, arg4=*0x%x, out_flags=*0x%x)", fd, sector_size, block_size, arg4, out_flags);
|
||||
|
||||
|
|
@ -2712,7 +2711,7 @@ error_code sys_fs_fget_block_size(ppu_thread& ppu, u32 fd, vm::ptr<u64> sector_s
|
|||
|
||||
error_code sys_fs_get_block_size(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u64> sector_size, vm::ptr<u64> block_size, vm::ptr<u64> arg4)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_get_block_size(path=%s, sector_size=*0x%x, block_size=*0x%x, arg4=*0x%x)", path, sector_size, block_size, arg4);
|
||||
|
||||
|
|
@ -2762,7 +2761,7 @@ error_code sys_fs_get_block_size(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u
|
|||
|
||||
error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr<char> path, u64 size)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_truncate(path=%s, size=0x%llx)", path, size);
|
||||
|
||||
|
|
@ -2813,7 +2812,7 @@ error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr<char> path, u64 size)
|
|||
|
||||
error_code sys_fs_ftruncate(ppu_thread& ppu, u32 fd, u64 size)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_ftruncate(fd=%d, size=0x%llx)", fd, size);
|
||||
|
||||
|
|
@ -2939,7 +2938,6 @@ error_code sys_fs_chown(ppu_thread&, vm::cptr<char> path, s32 uid, s32 gid)
|
|||
|
||||
error_code sys_fs_disk_free(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u64> total_free, vm::ptr<u64> avail_free)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_disk_free(path=%s total_free=*0x%x avail_free=*0x%x)", path, total_free, avail_free);
|
||||
|
|
@ -3018,7 +3016,7 @@ error_code sys_fs_disk_free(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u64> t
|
|||
|
||||
error_code sys_fs_utime(ppu_thread& ppu, vm::cptr<char> path, vm::cptr<CellFsUtimbuf> timep)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_utime(path=%s, timep=*0x%x)", path, timep);
|
||||
sys_fs.warning("** actime=%u, modtime=%u", timep->actime, timep->modtime);
|
||||
|
|
@ -3215,7 +3213,7 @@ error_code sys_fs_get_mount_info(ppu_thread&, vm::ptr<CellFsMountInfo> info, u64
|
|||
|
||||
error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, s32 unk1, vm::cptr<char> str1)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_newfs(dev_name=%s, file_system=%s, unk1=0x%x, str1=%s)", dev_name, file_system, unk1, str1);
|
||||
|
||||
|
|
@ -3263,7 +3261,7 @@ error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
|||
|
||||
error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, vm::cptr<char> path, s32 unk1, s32 prot, s32 unk2, vm::cptr<char> str1, u32 str_len)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_mount(dev_name=%s, file_system=%s, path=%s, unk1=0x%x, prot=%d, unk3=0x%x, str1=%s, str_len=%d)", dev_name, file_system, path, unk1, prot, unk2, str1, str_len);
|
||||
|
||||
|
|
@ -3366,7 +3364,7 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
|||
|
||||
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 force)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_unmount(path=%s, unk1=0x%x, force=%d)", path, unk1, force);
|
||||
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ static const std::map<logitech_personality,
|
|||
};
|
||||
|
||||
// ref: https://github.com/libsdl-org/SDL/issues/7941, need to use SDL_HAPTIC_STEERING_AXIS for some windows drivers
|
||||
static const SDL_HapticDirection STEERING_DIRECTION =
|
||||
static constexpr SDL_HapticDirection STEERING_DIRECTION =
|
||||
{
|
||||
.type = SDL_HAPTIC_STEERING_AXIS,
|
||||
.dir = {0, 0, 0}
|
||||
|
|
@ -1486,8 +1486,8 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
|
|||
new_effect.condition.direction = STEERING_DIRECTION;
|
||||
new_effect.condition.length = SDL_HAPTIC_INFINITY;
|
||||
const u16 saturation = logitech_g27_clip_to_saturation(buf[4]);
|
||||
const u16 deadband = 2 * 0xFFFF / 255;
|
||||
s16 center = 0;
|
||||
constexpr u16 deadband = 2 * 0xFFFF / 255;
|
||||
constexpr s16 center = 0;
|
||||
s16 left_coeff = 0;
|
||||
s16 right_coeff = 0;
|
||||
if (buf[1] == 0x03)
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ pad_capabilities PadHandlerBase::get_capabilities(const std::string& /*pad_id*/)
|
|||
.has_rumble = b_has_rumble,
|
||||
.has_accel = b_has_motion,
|
||||
.has_gyro = b_has_motion,
|
||||
.has_pressure_sensitivity = b_has_pressure_intensity_button
|
||||
.has_pressure_intensity_button = b_has_pressure_intensity_button
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ struct pad_capabilities
|
|||
bool has_rumble = false;
|
||||
bool has_accel = false;
|
||||
bool has_gyro = false;
|
||||
bool has_pressure_sensitivity = false;
|
||||
bool has_pressure_intensity_button = true;
|
||||
};
|
||||
|
||||
using pad_preview_values = std::array<int, 6>;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ namespace rsx
|
|||
|
||||
private:
|
||||
// Members
|
||||
block_list *block;
|
||||
block_list* block = nullptr;
|
||||
list_iterator list_it = {};
|
||||
size_type idx = u32{umax};
|
||||
size_type array_idx = 0;
|
||||
|
|
@ -705,9 +705,9 @@ namespace rsx
|
|||
private:
|
||||
// Members
|
||||
address_range32 range;
|
||||
section_bounds bounds;
|
||||
section_bounds bounds {};
|
||||
|
||||
block_type *block = nullptr;
|
||||
block_type* block = nullptr;
|
||||
bool needs_overlap_check = true;
|
||||
bool unowned_remaining = false;
|
||||
unowned_iterator unowned_it = {};
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool /*
|
|||
static_cast<gl::framebuffer_holder*>(m_draw_fbo)->release();
|
||||
}
|
||||
|
||||
for (auto &fbo : m_framebuffer_cache)
|
||||
for (auto& fbo : m_framebuffer_cache)
|
||||
{
|
||||
if (fbo.matches(color_targets, depth_stencil_target))
|
||||
{
|
||||
|
|
@ -264,6 +264,8 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool /*
|
|||
}
|
||||
}
|
||||
|
||||
ensure(m_draw_fbo);
|
||||
|
||||
switch (rsx::method_registers.surface_color_target())
|
||||
{
|
||||
case rsx::surface_target::none: break;
|
||||
|
|
|
|||
|
|
@ -289,6 +289,8 @@ namespace gl
|
|||
void* copy_image_to_buffer(gl::command_context& cmd, const pixel_buffer_layout& pack_info, const gl::texture* src, gl::buffer* dst,
|
||||
u32 dst_offset, const int src_level, const coord3u& src_region, image_memory_requirements* mem_info)
|
||||
{
|
||||
ensure(src && dst);
|
||||
|
||||
auto initialize_scratch_mem = [&]() -> bool // skip_transform
|
||||
{
|
||||
const u64 max_mem = (mem_info->memory_required) ? mem_info->memory_required : mem_info->image_size_in_bytes;
|
||||
|
|
|
|||
|
|
@ -669,8 +669,8 @@ namespace gl
|
|||
}
|
||||
else
|
||||
{
|
||||
//TODO: More tests on byte order
|
||||
//ARGB8+native+unswizzled is confirmed with Dark Souls II character preview
|
||||
// TODO: More tests on byte order
|
||||
// ARGB8+native+unswizzled is confirmed with Dark Souls II character preview
|
||||
switch (gcm_format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_A8R8G8B8:
|
||||
|
|
@ -697,8 +697,7 @@ namespace gl
|
|||
fmt::throw_exception("Unexpected gcm format 0x%X", gcm_format);
|
||||
}
|
||||
|
||||
//NOTE: Protection is handled by the caller
|
||||
cached.set_dimensions(width, height, depth, (rsx_range.length() / height));
|
||||
// NOTE: Protection is handled by the caller
|
||||
no_access_range = cached.get_min_max(no_access_range, rsx::section_bounds::locked_range);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace gl
|
|||
class shader
|
||||
{
|
||||
std::string source;
|
||||
::glsl::program_domain type;
|
||||
::glsl::program_domain type {};
|
||||
GLuint m_id = GL_NONE;
|
||||
|
||||
fence m_compiled_fence;
|
||||
|
|
|
|||
|
|
@ -376,15 +376,12 @@ namespace gl
|
|||
|
||||
GLuint get_bound_texture(GLuint layer, GLenum target)
|
||||
{
|
||||
ensure(layer < 48);
|
||||
return bound_textures[layer][target];
|
||||
return ::at32(bound_textures, layer)[target];
|
||||
}
|
||||
|
||||
void bind_texture(GLuint layer, GLenum target, GLuint name, GLboolean force = GL_FALSE)
|
||||
{
|
||||
ensure(layer < 48);
|
||||
|
||||
auto& bound = bound_textures[layer][target];
|
||||
auto& bound = ::at32(bound_textures, layer)[target];
|
||||
if (bound != name || force)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + layer);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include "Loader/PSF.h"
|
||||
#include "Loader/TAR.h"
|
||||
#include "Loader/ISO.h"
|
||||
#include "Loader/ELF.h"
|
||||
#include "Loader/disc.h"
|
||||
|
||||
|
|
@ -782,7 +783,7 @@ bool Emulator::BootRsxCapture(const std::string& path)
|
|||
std::unique_ptr<rsx::frame_capture_data> frame = std::make_unique<rsx::frame_capture_data>();
|
||||
utils::serial load;
|
||||
load.set_reading_state();
|
||||
|
||||
|
||||
const std::string lower = fmt::to_lower(path);
|
||||
|
||||
if (lower.ends_with(".gz") || lower.ends_with(".zst"))
|
||||
|
|
@ -931,6 +932,7 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string&
|
|||
|
||||
if (result != game_boot_result::no_errors)
|
||||
{
|
||||
unload_iso();
|
||||
GetCallbacks().close_gs_frame();
|
||||
}
|
||||
}
|
||||
|
|
@ -1078,6 +1080,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
sys_log.notice("Path: %s", m_path);
|
||||
|
||||
std::string inherited_ps3_game_path;
|
||||
bool launching_from_disc_archive = false;
|
||||
|
||||
{
|
||||
Init();
|
||||
|
|
@ -1169,12 +1172,16 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
std::string disc_info;
|
||||
m_ar->serialize(argv.emplace_back(), disc_info, klic.emplace_back(), m_game_dir, hdd1);
|
||||
|
||||
launching_from_disc_archive = is_file_iso(disc_info);
|
||||
|
||||
sys_log.notice("Savestate: is iso archive = %d ('%s')", launching_from_disc_archive, disc_info);
|
||||
|
||||
if (!klic[0])
|
||||
{
|
||||
klic.clear();
|
||||
}
|
||||
|
||||
if (!disc_info.empty() && disc_info[0] != '/')
|
||||
if (!launching_from_disc_archive && !disc_info.empty() && disc_info[0] != '/')
|
||||
{
|
||||
// Restore disc path for disc games (must exist in games.yml i.e. your game library)
|
||||
m_title_id = disc_info;
|
||||
|
|
@ -1182,6 +1189,11 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
// Load /dev_bdvd/ from game list if available
|
||||
if (std::string game_path = m_games_config.get_path(m_title_id); !game_path.empty())
|
||||
{
|
||||
if (is_file_iso(game_path))
|
||||
{
|
||||
game_path = iso_device::virtual_device_name + "/PS3_GAME/./";
|
||||
}
|
||||
|
||||
if (game_path.ends_with("/./"))
|
||||
{
|
||||
// Marked as PS3_GAME directory
|
||||
|
|
@ -1286,6 +1298,16 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
|
||||
m_path_old = m_path;
|
||||
resolve_path_as_vfs_path = true;
|
||||
|
||||
if (launching_from_disc_archive)
|
||||
{
|
||||
sys_log.notice("Savestate: Loading iso archive");
|
||||
|
||||
load_iso(disc_info);
|
||||
m_path = iso_device::virtual_device_name + "/" + argv[0];
|
||||
|
||||
resolve_path_as_vfs_path = false;
|
||||
}
|
||||
}
|
||||
else if (m_path.starts_with(vfs_boot_prefix))
|
||||
{
|
||||
|
|
@ -1421,6 +1443,33 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
}
|
||||
|
||||
const std::string resolved_path = GetCallbacks().resolve_path(m_path);
|
||||
if (!launching_from_disc_archive && is_file_iso(m_path))
|
||||
{
|
||||
sys_log.notice("Loading iso archive '%s'", m_path);
|
||||
|
||||
load_iso(m_path);
|
||||
|
||||
launching_from_disc_archive = true;
|
||||
|
||||
std::string path = iso_device::virtual_device_name + "/";
|
||||
|
||||
// ISOs that are install discs will error if set to EBOOT.BIN
|
||||
// so this should cover both of them
|
||||
if (fs::exists(path + "PS3_GAME/USRDIR/EBOOT.BIN"))
|
||||
{
|
||||
path = path + "PS3_GAME/USRDIR/EBOOT.BIN";
|
||||
}
|
||||
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
sys_log.notice("Load: is iso archive = %d (m_path='%s')", launching_from_disc_archive, m_path);
|
||||
|
||||
if (launching_from_disc_archive)
|
||||
{
|
||||
m_dir = "/dev_bdvd/PS3_GAME/";
|
||||
m_cat = "DG"sv;
|
||||
}
|
||||
|
||||
const std::string elf_dir = fs::get_parent_dir(m_path);
|
||||
|
||||
|
|
@ -1595,8 +1644,14 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
}
|
||||
}
|
||||
|
||||
// ISO PKG INSTALL HACK!
|
||||
if (launching_from_disc_archive && fs::is_dir(m_path))
|
||||
{
|
||||
bdvd_dir = m_path;
|
||||
}
|
||||
|
||||
// Special boot mode (directory scan)
|
||||
if (fs::is_dir(m_path))
|
||||
if (!launching_from_disc_archive && fs::is_dir(m_path))
|
||||
{
|
||||
m_state = system_state::ready;
|
||||
GetCallbacks().on_ready();
|
||||
|
|
@ -1788,6 +1843,10 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
sys_log.error("Failed to move disc game %s to '%s' (%s)", m_title_id, dst_dir, fs::g_tls_error);
|
||||
return game_boot_result::wrong_disc_location;
|
||||
}
|
||||
else if (launching_from_disc_archive)
|
||||
{
|
||||
bdvd_dir = iso_device::virtual_device_name + "/";
|
||||
}
|
||||
}
|
||||
|
||||
if (bdvd_dir.empty() && disc.empty() && !is_disc_patch)
|
||||
|
|
@ -1802,6 +1861,15 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
// Load /dev_bdvd/ from game list if available
|
||||
if (std::string game_path = m_games_config.get_path(m_title_id); !game_path.empty())
|
||||
{
|
||||
if (is_file_iso(game_path))
|
||||
{
|
||||
sys_log.notice("Loading iso archive for patch ('%s')", game_path);
|
||||
|
||||
load_iso(game_path);
|
||||
launching_from_disc_archive = true;
|
||||
game_path = iso_device::virtual_device_name + "/PS3_GAME/./";
|
||||
}
|
||||
|
||||
if (game_path.ends_with("/./"))
|
||||
{
|
||||
// Marked as PS3_GAME directory
|
||||
|
|
@ -1929,21 +1997,28 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
}
|
||||
|
||||
// TODO: Verify timestamps and error codes with sys_fs
|
||||
vfs::mount("/dev_bdvd", bdvd_dir);
|
||||
|
||||
vfs::mount("/dev_bdvd/PS3_GAME", inherited_ps3_game_path.empty() ? hdd0_game + m_path.substr(hdd0_game.size(), 10) : inherited_ps3_game_path);
|
||||
|
||||
const std::string new_ps3_game = vfs::get("/dev_bdvd/PS3_GAME");
|
||||
sys_log.notice("Game: %s", new_ps3_game);
|
||||
|
||||
// Store /dev_bdvd/PS3_GAME location
|
||||
if (games_config::result res = m_games_config.add_game(m_title_id, new_ps3_game + "/./"); res == games_config::result::success)
|
||||
if (!new_ps3_game.starts_with(iso_device::virtual_device_name))
|
||||
{
|
||||
sys_log.notice("Registered BDVD/PS3_GAME game directory for title '%s': %s", m_title_id, new_ps3_game);
|
||||
// Store /dev_bdvd/PS3_GAME location
|
||||
if (games_config::result res = m_games_config.add_game(m_title_id, new_ps3_game + "/./"); res == games_config::result::success)
|
||||
{
|
||||
sys_log.notice("Registered BDVD/PS3_GAME game directory for title '%s': %s", m_title_id, new_ps3_game);
|
||||
}
|
||||
else if (res == games_config::result::failure)
|
||||
{
|
||||
sys_log.error("Failed to save BDVD/PS3_GAME location of title '%s' (error=%s)", m_title_id, fs::g_tls_error);
|
||||
}
|
||||
|
||||
vfs::mount("/dev_bdvd", bdvd_dir);
|
||||
}
|
||||
else if (res == games_config::result::failure)
|
||||
else
|
||||
{
|
||||
sys_log.error("Failed to save BDVD/PS3_GAME location of title '%s' (error=%s)", m_title_id, fs::g_tls_error);
|
||||
vfs::mount("/dev_bdvd", iso_device::virtual_device_name + "/");
|
||||
}
|
||||
}
|
||||
else if (disc.empty())
|
||||
|
|
@ -2076,6 +2151,13 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
}
|
||||
}
|
||||
|
||||
// ISO has no USRDIR/EBOOT.BIN, and we've examined its PKGDIR and extras.
|
||||
// time to wrap up
|
||||
if (launching_from_disc_archive && fs::is_dir(m_path))
|
||||
{
|
||||
return game_boot_result::nothing_to_boot;
|
||||
}
|
||||
|
||||
// Check firmware version
|
||||
if (const std::string_view game_fw_version = psf::get_string(_psf, "PS3_SYSTEM_VER", ""); !game_fw_version.empty())
|
||||
{
|
||||
|
|
@ -2184,7 +2266,8 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
// Check game updates
|
||||
if (const std::string hdd0_boot = hdd0_game + m_title_id + "/USRDIR/EBOOT.BIN"; !m_ar
|
||||
&& recursion_count == 0 && disc.empty() && !bdvd_dir.empty() && !m_title_id.empty()
|
||||
&& resolved_path == GetCallbacks().resolve_path(vfs::get("/dev_bdvd/PS3_GAME/USRDIR/EBOOT.BIN"))
|
||||
&& (launching_from_disc_archive ||
|
||||
resolved_path == GetCallbacks().resolve_path(vfs::get("/dev_bdvd/PS3_GAME/USRDIR/EBOOT.BIN")))
|
||||
&& resolved_path != GetCallbacks().resolve_path(hdd0_boot) && fs::is_file(hdd0_boot)
|
||||
&& ppu_exec == elf_error::ok)
|
||||
{
|
||||
|
|
@ -2295,8 +2378,9 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
else if (!bdvd_dir.empty() && fs::is_dir(bdvd_dir))
|
||||
{
|
||||
// Disc games are on /dev_bdvd/
|
||||
const usz pos = resolved_path.rfind(m_game_dir);
|
||||
argv[0] = "/dev_bdvd/PS3_GAME/" + unescape(resolved_path.substr(pos + m_game_dir.size() + 1));
|
||||
const std::string& disc_path = launching_from_disc_archive ? m_path : resolved_path;
|
||||
const usz pos = disc_path.rfind(m_game_dir);
|
||||
argv[0] = "/dev_bdvd/PS3_GAME/" + unescape(disc_path.substr(pos + m_game_dir.size() + 1));
|
||||
m_dir = "/dev_bdvd/PS3_GAME/";
|
||||
}
|
||||
else if (from_dev_flash)
|
||||
|
|
@ -3368,7 +3452,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
|
|||
{
|
||||
if (spu.first->pc != spu.second || spu.first->unsavable)
|
||||
{
|
||||
std::string dump;
|
||||
std::string dump;
|
||||
spu.first->dump_all(dump);
|
||||
|
||||
sys_log.error("SPU thread continued after being paused. (old_pc=0x%x, pc=0x%x, unsavable=%d)", spu.second, spu.first->pc, spu.first->unsavable);
|
||||
|
|
@ -3546,7 +3630,18 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
|
|||
|
||||
ar(std::array<u8, 32>{}); // Reserved for future use
|
||||
|
||||
if (auto dir = vfs::get("/dev_bdvd/PS3_GAME"); fs::is_dir(dir) && !fs::is_file(fs::get_parent_dir(dir) + "/PS3_DISC.SFB"))
|
||||
// Game mounted from archive
|
||||
if (m_path.starts_with(iso_device::virtual_device_name + "/"))
|
||||
{
|
||||
const auto device = fs::get_virtual_device(iso_device::virtual_device_name + "/");
|
||||
ensure(device);
|
||||
|
||||
const auto iso_dev = dynamic_cast<const iso_device*>(device.get());
|
||||
|
||||
ar(m_path.substr(iso_device::virtual_device_name.size() + 1));
|
||||
ar(iso_dev->get_loaded_iso());
|
||||
}
|
||||
else if (auto dir = vfs::get("/dev_bdvd/PS3_GAME"); fs::is_dir(dir) && !fs::is_file(fs::get_parent_dir(dir) + "/PS3_DISC.SFB"))
|
||||
{
|
||||
// Fake /dev_bdvd/PS3_GAME detected, use HDD0 for m_path restoration
|
||||
ensure(vfs::unmount("/dev_bdvd/PS3_GAME"));
|
||||
|
|
@ -3840,6 +3935,11 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
|
|||
m_emu_state_close_pending = false;
|
||||
m_precompilation_option = {};
|
||||
|
||||
if (!m_continuous_mode)
|
||||
{
|
||||
unload_iso();
|
||||
}
|
||||
|
||||
initialize_timebased_time(0, true);
|
||||
|
||||
// Complete the operation
|
||||
|
|
@ -4039,13 +4139,18 @@ u32 Emulator::AddGamesFromDir(const std::string& path)
|
|||
{
|
||||
auto dir_entry = std::move(*path_it);
|
||||
|
||||
if (!dir_entry.is_directory || dir_entry.name == "." || dir_entry.name == "..")
|
||||
if (dir_entry.name == "." || dir_entry.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string dir_path = path + '/' + dir_entry.name;
|
||||
|
||||
if (!dir_entry.is_directory && !is_file_iso(dir_path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const game_boot_result error = AddGame(dir_path); error == game_boot_result::no_errors)
|
||||
{
|
||||
games_added++;
|
||||
|
|
@ -4143,10 +4248,17 @@ game_boot_result Emulator::AddGameToYml(const std::string& path)
|
|||
return error;
|
||||
}
|
||||
|
||||
std::unique_ptr<iso_archive> archive;
|
||||
if (is_file_iso(path))
|
||||
{
|
||||
archive = std::make_unique<iso_archive>(path);
|
||||
}
|
||||
|
||||
// Load PARAM.SFO
|
||||
const std::string elf_dir = fs::get_parent_dir(path);
|
||||
std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(fs::get_parent_dir(elf_dir));
|
||||
const psf::registry _psf = psf::load_object(sfo_dir + "/PARAM.SFO");
|
||||
std::string sfo_dir = archive ? "PS3_GAME" : rpcs3::utils::get_sfo_dir_from_game_path(fs::get_parent_dir(elf_dir));
|
||||
const std::string sfo_path = sfo_dir + "/PARAM.SFO";
|
||||
const psf::registry _psf = archive ? archive->open_psf(sfo_path) : psf::load_object(sfo_path);
|
||||
|
||||
const std::string title_id = std::string(psf::get_string(_psf, "TITLE_ID"));
|
||||
const std::string cat = std::string(psf::get_string(_psf, "CATEGORY"));
|
||||
|
|
@ -4169,6 +4281,23 @@ game_boot_result Emulator::AddGameToYml(const std::string& path)
|
|||
return game_boot_result::invalid_file_or_folder;
|
||||
}
|
||||
|
||||
// Add ISO game
|
||||
if (archive)
|
||||
{
|
||||
if (cat == "DG")
|
||||
{
|
||||
std::string iso_path = path;
|
||||
switch (m_games_config.add_external_hdd_game(title_id, iso_path))
|
||||
{
|
||||
case games_config::result::failure: return game_boot_result::generic_error;
|
||||
case games_config::result::success: return game_boot_result::no_errors;
|
||||
case games_config::result::exists: return game_boot_result::already_added;
|
||||
}
|
||||
|
||||
return game_boot_result::generic_error;
|
||||
}
|
||||
}
|
||||
|
||||
// Set bdvd_dir
|
||||
std::string bdvd_dir;
|
||||
std::string game_dir;
|
||||
|
|
@ -4331,7 +4460,7 @@ const std::string& Emulator::GetFakeCat() const
|
|||
{
|
||||
const std::string mount_point = vfs::get("/dev_bdvd");
|
||||
|
||||
if (mount_point.empty() || !IsPathInsideDir(m_path, mount_point))
|
||||
if (mount_point.empty() || (!IsPathInsideDir(m_path, mount_point) && !m_path.starts_with(iso_device::virtual_device_name)))
|
||||
{
|
||||
static const std::string s_hg = "HG";
|
||||
return s_hg;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include "util/yaml.hpp"
|
||||
#include "Utilities/File.h"
|
||||
|
||||
#include "Loader/ISO.h"
|
||||
|
||||
LOG_CHANNEL(cfg_log, "CFG");
|
||||
|
||||
games_config::games_config()
|
||||
|
|
@ -44,6 +46,15 @@ std::string games_config::get_path(const std::string& title_id) const
|
|||
|
||||
games_config::result games_config::add_game(const std::string& key, const std::string& path)
|
||||
{
|
||||
if (path == iso_device::virtual_device_name + "/")
|
||||
{
|
||||
const auto device = fs::get_virtual_device(iso_device::virtual_device_name + "/");
|
||||
if (!device) return result::failure;
|
||||
|
||||
const auto iso_dev = dynamic_cast<const iso_device*>(device.get());
|
||||
return add_game(key, iso_dev->get_loaded_iso());
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
// Access or create node if does not exist
|
||||
|
|
|
|||
|
|
@ -102,6 +102,11 @@ bool sdl_instance::initialize_impl()
|
|||
set_hint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1");
|
||||
#endif
|
||||
|
||||
// Disable LG4FF driver on windows (only needed for SDL 3.4.0)
|
||||
#if _WIN32 && SDL_VERSION_ATLEAST(3, 4, 0)
|
||||
set_hint(SDL_HINT_JOYSTICK_HIDAPI_LG4FF, "0");
|
||||
#endif
|
||||
|
||||
if (!SDL_Init(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC | SDL_INIT_CAMERA))
|
||||
{
|
||||
sdl_log.error("Could not initialize! SDL Error: %s", SDL_GetError());
|
||||
|
|
|
|||
|
|
@ -738,7 +738,7 @@ pad_capabilities sdl_pad_handler::get_capabilities(const std::string& pad_id)
|
|||
capabilities.has_rumble &= dev->sdl.has_rumble;
|
||||
capabilities.has_accel &= dev->sdl.has_accel;
|
||||
capabilities.has_gyro &= dev->sdl.has_gyro;
|
||||
capabilities.has_pressure_sensitivity &= dev->sdl.is_ds3_with_pressure_buttons;
|
||||
capabilities.has_pressure_intensity_button &= !dev->sdl.is_ds3_with_pressure_buttons; // Only allow if there's not pressure sensitivity
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
|
|
|||
617
rpcs3/Loader/ISO.cpp
Normal file
617
rpcs3/Loader/ISO.cpp
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "ISO.h"
|
||||
#include "Emu/VFS.h"
|
||||
|
||||
#include <codecvt>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <stack>
|
||||
|
||||
LOG_CHANNEL(sys_log, "SYS");
|
||||
|
||||
bool is_file_iso(const std::string& path)
|
||||
{
|
||||
if (path.empty()) return false;
|
||||
if (fs::is_dir(path)) return false;
|
||||
|
||||
return is_file_iso(fs::file(path));
|
||||
}
|
||||
|
||||
bool is_file_iso(const fs::file& file)
|
||||
{
|
||||
if (!file) return false;
|
||||
if (file.size() < 32768 + 6) return false;
|
||||
|
||||
file.seek(32768);
|
||||
|
||||
char magic[5];
|
||||
file.read_at(32768 + 1, magic, 5);
|
||||
|
||||
return magic[0] == 'C' && magic[1] == 'D'
|
||||
&& magic[2] == '0' && magic[3] == '0'
|
||||
&& magic[4] == '1';
|
||||
}
|
||||
|
||||
const int ISO_BLOCK_SIZE = 2048;
|
||||
|
||||
template<typename T>
|
||||
inline T read_both_endian_int(fs::file& file)
|
||||
{
|
||||
T out;
|
||||
|
||||
if (std::endian::little == std::endian::native)
|
||||
{
|
||||
out = file.read<T>();
|
||||
file.seek(sizeof(T), fs::seek_cur);
|
||||
}
|
||||
else
|
||||
{
|
||||
file.seek(sizeof(T), fs::seek_cur);
|
||||
out = file.read<T>();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// assumed that directory_entry is at file head
|
||||
std::optional<iso_fs_metadata> iso_read_directory_entry(fs::file& file, bool names_in_ucs2 = false)
|
||||
{
|
||||
const auto start_pos = file.pos();
|
||||
const u8 entry_length = file.read<u8>();
|
||||
|
||||
if (entry_length == 0) return std::nullopt;
|
||||
|
||||
file.seek(1, fs::seek_cur);
|
||||
const u32 start_sector = read_both_endian_int<u32>(file);
|
||||
const u32 file_size = read_both_endian_int<u32>(file);
|
||||
|
||||
std::tm file_date = {};
|
||||
file_date.tm_year = file.read<u8>();
|
||||
file_date.tm_mon = file.read<u8>() - 1;
|
||||
file_date.tm_mday = file.read<u8>();
|
||||
file_date.tm_hour = file.read<u8>();
|
||||
file_date.tm_min = file.read<u8>();
|
||||
file_date.tm_sec = file.read<u8>();
|
||||
const s16 timezone_value = file.read<u8>();
|
||||
const s16 timezone_offset = (timezone_value - 50) * 15 * 60;
|
||||
|
||||
const std::time_t date_time = std::mktime(&file_date) + timezone_offset;
|
||||
|
||||
const u8 flags = file.read<u8>();
|
||||
|
||||
// 2nd flag bit indicates whether a given fs node is a directory
|
||||
const bool is_directory = flags & 0b00000010;
|
||||
const bool has_more_extents = flags & 0b10000000;
|
||||
|
||||
file.seek(6, fs::seek_cur);
|
||||
|
||||
const u8 file_name_length = file.read<u8>();
|
||||
|
||||
std::string file_name;
|
||||
file.read(file_name, file_name_length);
|
||||
|
||||
if (file_name_length == 1 && file_name[0] == 0)
|
||||
{
|
||||
file_name = ".";
|
||||
}
|
||||
else if (file_name == "\1")
|
||||
{
|
||||
file_name = "..";
|
||||
}
|
||||
else if (names_in_ucs2) // for strings in joliet descriptor
|
||||
{
|
||||
// characters are stored in big endian format.
|
||||
std::u16string utf16;
|
||||
utf16.resize(file_name_length / 2);
|
||||
|
||||
const u16* raw = reinterpret_cast<const u16*>(file_name.data());
|
||||
for (size_t i = 0; i < utf16.size(); ++i, raw++)
|
||||
{
|
||||
utf16[i] = *reinterpret_cast<const be_t<u16>*>(raw);
|
||||
}
|
||||
|
||||
file_name = utf16_to_utf8(utf16);
|
||||
}
|
||||
|
||||
if (file_name.ends_with(";1"))
|
||||
{
|
||||
file_name.erase(file_name.end() - 2, file_name.end());
|
||||
}
|
||||
|
||||
if (file_name_length > 1 && file_name.ends_with("."))
|
||||
{
|
||||
file_name.pop_back();
|
||||
}
|
||||
|
||||
// skip the rest of the entry.
|
||||
file.seek(entry_length + start_pos);
|
||||
|
||||
return iso_fs_metadata
|
||||
{
|
||||
.name = std::move(file_name),
|
||||
.time = date_time,
|
||||
.is_directory = is_directory,
|
||||
.has_multiple_extents = has_more_extents,
|
||||
.extents =
|
||||
{
|
||||
iso_extent_info
|
||||
{
|
||||
.start = start_sector,
|
||||
.size = file_size
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void iso_form_hierarchy(fs::file& file, iso_fs_node& node, bool use_ucs2_decoding = false, const std::string& parent_path = "")
|
||||
{
|
||||
if (!node.metadata.is_directory) return;
|
||||
|
||||
std::vector<usz> multi_extent_node_indices;
|
||||
|
||||
// assuming the directory spans a single extent
|
||||
const auto& directory_extent = node.metadata.extents[0];
|
||||
|
||||
file.seek(directory_extent.start * ISO_BLOCK_SIZE);
|
||||
|
||||
const u64 end_pos = directory_extent.size + (directory_extent.start * ISO_BLOCK_SIZE);
|
||||
|
||||
while(file.pos() < end_pos)
|
||||
{
|
||||
auto entry = iso_read_directory_entry(file, use_ucs2_decoding);
|
||||
if (!entry)
|
||||
{
|
||||
const u64 new_sector = (file.pos() / ISO_BLOCK_SIZE) + 1;
|
||||
file.seek(new_sector * ISO_BLOCK_SIZE);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool extent_added = false;
|
||||
|
||||
// find previous extent and merge into it, otherwise we push this node's index
|
||||
for (usz index : multi_extent_node_indices)
|
||||
{
|
||||
auto& selected_node = ::at32(node.children, index);
|
||||
if (selected_node->metadata.name == entry->name)
|
||||
{
|
||||
// merge into selected_node
|
||||
selected_node->metadata.extents.push_back(entry->extents[0]);
|
||||
|
||||
extent_added = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (extent_added) continue;
|
||||
|
||||
if (entry->has_multiple_extents)
|
||||
{
|
||||
// haven't pushed entry to node.children yet so node.children::size() == entry_index
|
||||
multi_extent_node_indices.push_back(node.children.size());
|
||||
}
|
||||
|
||||
node.children.push_back(std::make_unique<iso_fs_node>(iso_fs_node{
|
||||
.metadata = std::move(*entry)
|
||||
}));
|
||||
}
|
||||
|
||||
for (auto& child_node : node.children)
|
||||
{
|
||||
if (child_node->metadata.name != "." && child_node->metadata.name != "..")
|
||||
{
|
||||
iso_form_hierarchy(file, *child_node, use_ucs2_decoding, parent_path + "/" + node.metadata.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u64 iso_fs_metadata::size() const
|
||||
{
|
||||
u64 total_size = 0;
|
||||
for (const auto& extent : extents)
|
||||
{
|
||||
total_size += extent.size;
|
||||
}
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
iso_archive::iso_archive(const std::string& path)
|
||||
{
|
||||
m_path = path;
|
||||
m_file = fs::file(path);
|
||||
|
||||
if (!is_file_iso(m_file))
|
||||
{
|
||||
// not iso... TODO: throw something??
|
||||
return;
|
||||
}
|
||||
|
||||
u8 descriptor_type = -2;
|
||||
bool use_ucs2_decoding = false;
|
||||
|
||||
do
|
||||
{
|
||||
const auto descriptor_start = m_file.pos();
|
||||
|
||||
descriptor_type = m_file.read<u8>();
|
||||
|
||||
// 1 = primary vol descriptor, 2 = joliet SVD
|
||||
if (descriptor_type == 1 || descriptor_type == 2)
|
||||
{
|
||||
use_ucs2_decoding = descriptor_type == 2;
|
||||
|
||||
// skip the rest of descriptor's data
|
||||
m_file.seek(155, fs::seek_cur);
|
||||
|
||||
m_root = iso_fs_node
|
||||
{
|
||||
.metadata = iso_read_directory_entry(m_file, use_ucs2_decoding).value(),
|
||||
};
|
||||
}
|
||||
|
||||
m_file.seek(descriptor_start + ISO_BLOCK_SIZE);
|
||||
}
|
||||
while (descriptor_type != 255);
|
||||
|
||||
iso_form_hierarchy(m_file, m_root, use_ucs2_decoding);
|
||||
}
|
||||
|
||||
iso_fs_node* iso_archive::retrieve(const std::string& passed_path)
|
||||
{
|
||||
if (passed_path.empty()) return nullptr;
|
||||
|
||||
const std::string path = std::filesystem::path(passed_path).string();
|
||||
const std::string_view path_sv = path;
|
||||
|
||||
size_t start = 0;
|
||||
size_t end = path_sv.find_first_of(fs::delim);
|
||||
|
||||
std::stack<iso_fs_node*> search_stack;
|
||||
search_stack.push(&m_root);
|
||||
|
||||
do
|
||||
{
|
||||
if (search_stack.empty()) return nullptr;
|
||||
const auto* top_entry = search_stack.top();
|
||||
|
||||
if (end == umax)
|
||||
{
|
||||
end = path.size();
|
||||
}
|
||||
|
||||
const std::string_view path_component = path_sv.substr(start, end-start);
|
||||
|
||||
bool found = false;
|
||||
|
||||
if (path_component == ".")
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else if (path_component == "..")
|
||||
{
|
||||
search_stack.pop();
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& entry : top_entry->children)
|
||||
{
|
||||
if (entry->metadata.name == path_component)
|
||||
{
|
||||
search_stack.push(entry.get());
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) return nullptr;
|
||||
|
||||
start = end + 1;
|
||||
end = path_sv.find_first_of(fs::delim, start);
|
||||
}
|
||||
while (start < path.size());
|
||||
|
||||
if (search_stack.empty()) return nullptr;
|
||||
|
||||
return search_stack.top();
|
||||
}
|
||||
|
||||
bool iso_archive::exists(const std::string& path)
|
||||
{
|
||||
return retrieve(path) != nullptr;
|
||||
}
|
||||
|
||||
bool iso_archive::is_file(const std::string& path)
|
||||
{
|
||||
const auto file_node = retrieve(path);
|
||||
if (!file_node) return false;
|
||||
|
||||
return !file_node->metadata.is_directory;
|
||||
}
|
||||
|
||||
iso_file iso_archive::open(const std::string& path)
|
||||
{
|
||||
return iso_file(fs::file(m_path), *ensure(retrieve(path)));
|
||||
}
|
||||
|
||||
psf::registry iso_archive::open_psf(const std::string& path)
|
||||
{
|
||||
auto* archive_file = retrieve(path);
|
||||
if (!archive_file) return psf::registry();
|
||||
|
||||
const fs::file psf_file(std::make_unique<iso_file>(fs::file(m_path), *archive_file));
|
||||
|
||||
return psf::load_object(psf_file, path);
|
||||
}
|
||||
|
||||
iso_file::iso_file(fs::file&& iso_handle, const iso_fs_node& node)
|
||||
: m_file(std::move(iso_handle)), m_meta(node.metadata)
|
||||
{
|
||||
m_file.seek(ISO_BLOCK_SIZE * node.metadata.extents[0].start);
|
||||
}
|
||||
|
||||
fs::stat_t iso_file::get_stat()
|
||||
{
|
||||
return fs::stat_t
|
||||
{
|
||||
.is_directory = false,
|
||||
.is_symlink = false,
|
||||
.is_writable = false,
|
||||
.size = size(),
|
||||
.atime = m_meta.time,
|
||||
.mtime = m_meta.time,
|
||||
.ctime = m_meta.time
|
||||
};
|
||||
}
|
||||
|
||||
bool iso_file::trunc(u64 /*length*/)
|
||||
{
|
||||
fs::g_tls_error = fs::error::readonly;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<u64, iso_extent_info> iso_file::get_extent_pos(u64 pos) const
|
||||
{
|
||||
ensure(!m_meta.extents.empty());
|
||||
|
||||
auto it = m_meta.extents.begin();
|
||||
|
||||
while (pos >= it->size && it != m_meta.extents.end() - 1)
|
||||
{
|
||||
pos -= it->size;
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
return {pos, *it};
|
||||
}
|
||||
|
||||
// assumed valid and in bounds.
|
||||
u64 iso_file::file_offset(u64 pos) const
|
||||
{
|
||||
const auto [local_pos, extent] = get_extent_pos(pos);
|
||||
|
||||
return (extent.start * ISO_BLOCK_SIZE) + local_pos;
|
||||
}
|
||||
|
||||
u64 iso_file::local_extent_remaining(u64 pos) const
|
||||
{
|
||||
const auto [local_pos, extent] = get_extent_pos(pos);
|
||||
|
||||
return extent.size - local_pos;
|
||||
}
|
||||
|
||||
u64 iso_file::local_extent_size(u64 pos) const
|
||||
{
|
||||
return get_extent_pos(pos).second.size;
|
||||
}
|
||||
|
||||
u64 iso_file::read(void* buffer, u64 size)
|
||||
{
|
||||
const auto r = read_at(m_pos, buffer, size);
|
||||
m_pos += r;
|
||||
return r;
|
||||
}
|
||||
|
||||
u64 iso_file::read_at(u64 offset, void* buffer, u64 size)
|
||||
{
|
||||
const u64 local_remaining = local_extent_remaining(offset);
|
||||
|
||||
const u64 total_read = m_file.read_at(file_offset(offset), buffer, std::min(size, local_remaining));
|
||||
|
||||
const auto total_size = this->size();
|
||||
|
||||
if (size > total_read && (offset + total_read) < total_size)
|
||||
{
|
||||
const u64 second_total_read = read_at(offset + total_read, reinterpret_cast<u8*>(buffer) + total_read, size - total_read);
|
||||
|
||||
return total_read + second_total_read;
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
u64 iso_file::write(const void* /*buffer*/, u64 /*size*/)
|
||||
{
|
||||
fs::g_tls_error = fs::error::readonly;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 iso_file::seek(s64 offset, fs::seek_mode whence)
|
||||
{
|
||||
const s64 total_size = size();
|
||||
const s64 new_pos =
|
||||
whence == fs::seek_set ? offset :
|
||||
whence == fs::seek_cur ? offset + m_pos :
|
||||
whence == fs::seek_end ? offset + total_size : -1;
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const u64 result = m_file.seek(file_offset(m_pos));
|
||||
if (result == umax) return umax;
|
||||
|
||||
m_pos = new_pos;
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
u64 iso_file::size()
|
||||
{
|
||||
u64 extent_sizes = 0;
|
||||
for (const auto& extent : m_meta.extents)
|
||||
{
|
||||
extent_sizes += extent.size;
|
||||
}
|
||||
|
||||
return extent_sizes;
|
||||
}
|
||||
|
||||
void iso_file::release()
|
||||
{
|
||||
m_file.release();
|
||||
}
|
||||
|
||||
bool iso_dir::read(fs::dir_entry& entry)
|
||||
{
|
||||
if (m_pos < m_node.children.size())
|
||||
{
|
||||
const auto& selected = m_node.children[m_pos].get()->metadata;
|
||||
|
||||
entry.name = selected.name;
|
||||
entry.atime = selected.time;
|
||||
entry.mtime = selected.time;
|
||||
entry.ctime = selected.time;
|
||||
entry.is_directory = selected.is_directory;
|
||||
entry.is_symlink = false;
|
||||
entry.is_writable = false;
|
||||
entry.size = selected.size();
|
||||
|
||||
m_pos++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool iso_device::stat(const std::string& path, fs::stat_t& info)
|
||||
{
|
||||
const auto relative_path = std::filesystem::relative(std::filesystem::path(path), std::filesystem::path(fs_prefix)).string();
|
||||
|
||||
const auto node = m_archive.retrieve(relative_path);
|
||||
if (!node)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& meta = node->metadata;
|
||||
|
||||
info = fs::stat_t
|
||||
{
|
||||
.is_directory = meta.is_directory,
|
||||
.is_symlink = false,
|
||||
.is_writable = false,
|
||||
.size = meta.size(),
|
||||
.atime = meta.time,
|
||||
.mtime = meta.time,
|
||||
.ctime = meta.time
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool iso_device::statfs(const std::string& path, fs::device_stat& info)
|
||||
{
|
||||
const auto relative_path = std::filesystem::relative(std::filesystem::path(path), std::filesystem::path(fs_prefix)).string();
|
||||
|
||||
const auto node = m_archive.retrieve(relative_path);
|
||||
if (!node)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& meta = node->metadata;
|
||||
const u64 size = meta.size();
|
||||
|
||||
info = fs::device_stat
|
||||
{
|
||||
.block_size = size,
|
||||
.total_size = size,
|
||||
.total_free = 0,
|
||||
.avail_free = 0
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<fs::file_base> iso_device::open(const std::string& path, bs_t<fs::open_mode> mode)
|
||||
{
|
||||
const auto relative_path = std::filesystem::relative(std::filesystem::path(path), std::filesystem::path(fs_prefix)).string();
|
||||
|
||||
const auto node = m_archive.retrieve(relative_path);
|
||||
if (!node)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (node->metadata.is_directory)
|
||||
{
|
||||
fs::g_tls_error = fs::error::isdir;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<iso_file>(fs::file(iso_path), *node);
|
||||
}
|
||||
|
||||
std::unique_ptr<fs::dir_base> iso_device::open_dir(const std::string& path)
|
||||
{
|
||||
const auto relative_path = std::filesystem::relative(std::filesystem::path(path), std::filesystem::path(fs_prefix)).string();
|
||||
|
||||
const auto node = m_archive.retrieve(relative_path);
|
||||
if (!node)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!node->metadata.is_directory)
|
||||
{
|
||||
// fs::dir::open -> ::readdir should return ENOTDIR when path is
|
||||
// pointing to a file instead of a folder, which translates to error::unknown.
|
||||
// doing the same here.
|
||||
fs::g_tls_error = fs::error::unknown;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<iso_dir>(*node);
|
||||
}
|
||||
|
||||
void iso_dir::rewind()
|
||||
{
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
void load_iso(const std::string& path)
|
||||
{
|
||||
sys_log.notice("Loading iso '%s'", path);
|
||||
|
||||
fs::set_virtual_device("iso_overlay_fs_dev", stx::make_shared<iso_device>(path));
|
||||
|
||||
vfs::mount("/dev_bdvd/"sv, iso_device::virtual_device_name + "/");
|
||||
}
|
||||
|
||||
void unload_iso()
|
||||
{
|
||||
sys_log.notice("Unoading iso");
|
||||
|
||||
fs::set_virtual_device("iso_overlay_fs_dev", stx::shared_ptr<iso_device>());
|
||||
}
|
||||
121
rpcs3/Loader/ISO.h
Normal file
121
rpcs3/Loader/ISO.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include "Loader/PSF.h"
|
||||
|
||||
#include "Utilities/File.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
bool is_file_iso(const std::string& path);
|
||||
bool is_file_iso(const fs::file& path);
|
||||
|
||||
void load_iso(const std::string& path);
|
||||
void unload_iso();
|
||||
|
||||
struct iso_extent_info
|
||||
{
|
||||
u64 start;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct iso_fs_metadata
|
||||
{
|
||||
std::string name;
|
||||
s64 time;
|
||||
bool is_directory;
|
||||
bool has_multiple_extents;
|
||||
std::vector<iso_extent_info> extents;
|
||||
|
||||
u64 size() const;
|
||||
};
|
||||
|
||||
struct iso_fs_node
|
||||
{
|
||||
iso_fs_metadata metadata;
|
||||
std::vector<std::unique_ptr<iso_fs_node>> children;
|
||||
};
|
||||
|
||||
class iso_file : public fs::file_base
|
||||
{
|
||||
private:
|
||||
fs::file m_file;
|
||||
iso_fs_metadata m_meta;
|
||||
u64 m_pos = 0;
|
||||
|
||||
std::pair<u64, iso_extent_info> get_extent_pos(u64 pos) const;
|
||||
u64 file_offset(u64 pos) const;
|
||||
u64 local_extent_remaining(u64 pos) const;
|
||||
u64 local_extent_size(u64 pos) const;
|
||||
|
||||
public:
|
||||
iso_file(fs::file&& iso_handle, const iso_fs_node& node);
|
||||
|
||||
fs::stat_t get_stat() override;
|
||||
bool trunc(u64 length) override;
|
||||
u64 read(void* buffer, u64 size) override;
|
||||
u64 read_at(u64 offset, void* buffer, u64 size) override;
|
||||
u64 write(const void* buffer, u64 size) override;
|
||||
u64 seek(s64 offset, fs::seek_mode whence) override;
|
||||
u64 size() override;
|
||||
|
||||
void release() override;
|
||||
};
|
||||
|
||||
class iso_dir : public fs::dir_base
|
||||
{
|
||||
private:
|
||||
const iso_fs_node& m_node;
|
||||
u64 m_pos = 0;
|
||||
|
||||
public:
|
||||
iso_dir(const iso_fs_node& node)
|
||||
: m_node(node)
|
||||
{}
|
||||
|
||||
bool read(fs::dir_entry&) override;
|
||||
void rewind() override;
|
||||
};
|
||||
|
||||
// represents the .iso file itself.
|
||||
class iso_archive
|
||||
{
|
||||
private:
|
||||
std::string m_path;
|
||||
iso_fs_node m_root;
|
||||
fs::file m_file;
|
||||
|
||||
public:
|
||||
iso_archive(const std::string& path);
|
||||
|
||||
iso_fs_node* retrieve(const std::string& path);
|
||||
bool exists(const std::string& path);
|
||||
bool is_file(const std::string& path);
|
||||
|
||||
iso_file open(const std::string& path);
|
||||
|
||||
psf::registry open_psf(const std::string& path);
|
||||
};
|
||||
|
||||
class iso_device : public fs::device_base
|
||||
{
|
||||
private:
|
||||
iso_archive m_archive;
|
||||
std::string iso_path;
|
||||
|
||||
public:
|
||||
inline static std::string virtual_device_name = "/vfsv0_virtual_iso_overlay_fs_dev";
|
||||
|
||||
iso_device(const std::string& iso_path, const std::string& device_name = virtual_device_name)
|
||||
: m_archive(iso_path), iso_path(iso_path)
|
||||
{
|
||||
fs_prefix = device_name;
|
||||
}
|
||||
~iso_device() override = default;
|
||||
|
||||
const std::string& get_loaded_iso() const { return iso_path; }
|
||||
|
||||
bool stat(const std::string& path, fs::stat_t& info) override;
|
||||
bool statfs(const std::string& path, fs::device_stat& info) override;
|
||||
|
||||
std::unique_ptr<fs::file_base> open(const std::string& path, bs_t<fs::open_mode> mode) override;
|
||||
std::unique_ptr<fs::dir_base> open_dir(const std::string& path) override;
|
||||
};
|
||||
|
|
@ -540,6 +540,7 @@
|
|||
<ClCompile Include="Loader\PSF.cpp" />
|
||||
<ClCompile Include="Loader\PUP.cpp" />
|
||||
<ClCompile Include="Loader\TAR.cpp" />
|
||||
<ClCompile Include="Loader\ISO.cpp" />
|
||||
<ClCompile Include="Loader\mself.cpp" />
|
||||
<ClCompile Include="Loader\TROPUSR.cpp" />
|
||||
<ClCompile Include="Loader\TRP.cpp" />
|
||||
|
|
@ -1021,6 +1022,7 @@
|
|||
<ClInclude Include="Loader\PSF.h" />
|
||||
<ClInclude Include="Loader\PUP.h" />
|
||||
<ClInclude Include="Loader\TAR.h" />
|
||||
<ClInclude Include="Loader\ISO.h" />
|
||||
<ClInclude Include="Loader\TROPUSR.h" />
|
||||
<ClInclude Include="Loader\TRP.h" />
|
||||
<ClInclude Include="rpcs3_version.h" />
|
||||
|
|
@ -1096,4 +1098,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ public:
|
|||
{
|
||||
int row{};
|
||||
int col{};
|
||||
|
||||
operator bool() const { return row >= 0 && col >= 0; }
|
||||
};
|
||||
|
||||
explicit flow_layout(QWidget* parent, int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "flow_widget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QStyleOption>
|
||||
|
|
@ -48,14 +49,24 @@ void flow_widget::clear()
|
|||
m_flow_layout->clear();
|
||||
}
|
||||
|
||||
flow_widget_item* flow_widget::selected_item() const
|
||||
std::set<flow_widget_item*> flow_widget::selected_items() const
|
||||
{
|
||||
if (m_selected_index >= 0 && static_cast<usz>(m_selected_index) < m_widgets.size())
|
||||
std::set<flow_widget_item*> items;
|
||||
|
||||
for (s64 index : m_selected_items)
|
||||
{
|
||||
return ::at32(m_widgets, m_selected_index);
|
||||
if (index >= 0 && static_cast<usz>(index) < m_widgets.size())
|
||||
{
|
||||
items.insert(::at32(m_widgets, index));
|
||||
|
||||
if (!m_allow_multi_selection)
|
||||
{
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return items;
|
||||
}
|
||||
|
||||
void flow_widget::paintEvent(QPaintEvent* /*event*/)
|
||||
|
|
@ -69,7 +80,7 @@ void flow_widget::paintEvent(QPaintEvent* /*event*/)
|
|||
|
||||
s64 flow_widget::find_item(const flow_layout::position& pos)
|
||||
{
|
||||
if (pos.row < 0 || pos.col < 0)
|
||||
if (!pos)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -109,7 +120,7 @@ flow_layout::position flow_widget::find_item(flow_widget_item* item)
|
|||
|
||||
flow_layout::position flow_widget::find_next_item(flow_layout::position current_pos, flow_navigation value)
|
||||
{
|
||||
if (current_pos.row >= 0 && current_pos.col >= 0 && m_flow_layout->rows() > 0 && m_flow_layout->cols() > 0)
|
||||
if (current_pos && m_flow_layout->rows() > 0 && m_flow_layout->cols() > 0)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
|
|
@ -183,54 +194,160 @@ flow_layout::position flow_widget::find_next_item(flow_layout::position current_
|
|||
return current_pos;
|
||||
}
|
||||
|
||||
void flow_widget::select_item(flow_widget_item* item)
|
||||
void flow_widget::select_items(const std::set<flow_widget_item*>& selected_items, flow_widget_item* current_item)
|
||||
{
|
||||
const flow_layout::position selected_pos = find_item(item);
|
||||
const s64 selected_index = find_item(selected_pos);
|
||||
m_selected_items.clear();
|
||||
|
||||
if (selected_index < 0 || static_cast<usz>(selected_index) >= items().size())
|
||||
for (flow_widget_item* item : selected_items)
|
||||
{
|
||||
m_selected_index = -1;
|
||||
return;
|
||||
}
|
||||
const flow_layout::position selected_pos = find_item(item);
|
||||
const s64 selected_index = find_item(selected_pos);
|
||||
|
||||
m_selected_index = selected_index;
|
||||
Q_EMIT ItemSelectionChanged(m_selected_index);
|
||||
if (selected_index < 0 || static_cast<usz>(selected_index) >= items().size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_selected_items.insert(selected_index);
|
||||
|
||||
if (!m_allow_multi_selection)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (usz i = 0; i < items().size(); i++)
|
||||
{
|
||||
if (flow_widget_item* item = items().at(i))
|
||||
{
|
||||
// We need to polish the widgets in order to re-apply any stylesheet changes for the selected property.
|
||||
item->selected = m_selected_index >= 0 && i == static_cast<usz>(m_selected_index);
|
||||
item->selected = m_selected_items.contains(i);
|
||||
item->polish_style();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we see the focused widget
|
||||
m_scroll_area->ensureWidgetVisible(::at32(items(), m_selected_index));
|
||||
if (m_selected_items.empty())
|
||||
{
|
||||
m_last_selected_item = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_selected_items.contains(m_last_selected_item))
|
||||
{
|
||||
m_last_selected_item = *m_selected_items.cbegin();
|
||||
}
|
||||
|
||||
s64 selected_item = m_last_selected_item;
|
||||
s64 focused_item = selected_item;
|
||||
|
||||
if (current_item)
|
||||
{
|
||||
if (const flow_layout::position selected_pos = find_item(current_item))
|
||||
{
|
||||
focused_item = find_item(selected_pos);
|
||||
|
||||
if (current_item->selected)
|
||||
{
|
||||
selected_item = focused_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we see the focused widget
|
||||
m_scroll_area->ensureWidgetVisible(::at32(items(), focused_item));
|
||||
|
||||
Q_EMIT ItemSelectionChanged(selected_item);
|
||||
}
|
||||
}
|
||||
|
||||
void flow_widget::update_selection(flow_widget_item* current_item)
|
||||
{
|
||||
std::set<flow_widget_item*> selected_items;
|
||||
const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
|
||||
|
||||
if (current_item)
|
||||
{
|
||||
if (!m_allow_multi_selection || !current_item->selected || modifiers != Qt::ControlModifier)
|
||||
{
|
||||
selected_items.insert(current_item);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_allow_multi_selection)
|
||||
{
|
||||
flow_layout::position selected_pos;
|
||||
flow_layout::position last_selected_pos;
|
||||
|
||||
if (modifiers == Qt::ShiftModifier && m_last_selected_item >= 0 && static_cast<usz>(m_last_selected_item) < items().size())
|
||||
{
|
||||
selected_pos = find_item(current_item);
|
||||
last_selected_pos = find_item(::at32(m_widgets, m_last_selected_item));
|
||||
}
|
||||
|
||||
flow_layout::position pos_min = last_selected_pos;
|
||||
flow_layout::position pos_max = selected_pos;
|
||||
|
||||
if (pos_min.row > pos_max.row)
|
||||
{
|
||||
std::swap(pos_min, pos_max);
|
||||
}
|
||||
|
||||
// Check if the item is between the last and the current selection
|
||||
const auto item_between = [this, &pos_min, &pos_max](flow_widget_item* item)
|
||||
{
|
||||
if (!pos_min || !pos_max) return false;
|
||||
|
||||
const flow_layout::position pos = find_item(item);
|
||||
if (!pos) return false; // pos invalid
|
||||
if (pos.row < pos_min.row || pos.row > pos_max.row) return false; // not in any relevant row
|
||||
if (pos.row > pos_min.row && pos.row < pos_max.row) return true; // in a row between the items -> match
|
||||
|
||||
const bool in_min_row = pos.row == pos_min.row && pos.col >= pos_min.col;
|
||||
const bool in_max_row = pos.row == pos_max.row && pos.col <= pos_max.col;
|
||||
|
||||
if (pos_min.row == pos_max.row) return in_min_row && in_max_row; // in the only row at a relevant col -> match
|
||||
|
||||
return in_min_row || in_max_row; // in either min or max row at a relevant col -> match
|
||||
};
|
||||
|
||||
for (usz i = 0; i < items().size(); i++)
|
||||
{
|
||||
if (flow_widget_item* item = items().at(i))
|
||||
{
|
||||
if (item == current_item) continue;
|
||||
|
||||
if (modifiers == Qt::ControlModifier && item->selected)
|
||||
{
|
||||
selected_items.insert(item);
|
||||
}
|
||||
else if (modifiers == Qt::ShiftModifier && item_between(item))
|
||||
{
|
||||
selected_items.insert(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select_items(selected_items, current_item);
|
||||
}
|
||||
|
||||
void flow_widget::on_item_focus()
|
||||
{
|
||||
select_item(static_cast<flow_widget_item*>(QObject::sender()));
|
||||
update_selection(static_cast<flow_widget_item*>(QObject::sender()));
|
||||
}
|
||||
|
||||
void flow_widget::on_navigate(flow_navigation value)
|
||||
{
|
||||
const flow_layout::position selected_pos = find_next_item(find_item(static_cast<flow_widget_item*>(QObject::sender())), value);
|
||||
const s64 selected_index = find_item(selected_pos);
|
||||
if (selected_index < 0 || static_cast<usz>(selected_index) >= items().size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (flow_widget_item* item = items().at(selected_index))
|
||||
if (selected_index >= 0 && static_cast<usz>(selected_index) < items().size())
|
||||
{
|
||||
item->setFocus();
|
||||
if (flow_widget_item* item = items().at(selected_index))
|
||||
{
|
||||
item->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
m_selected_index = selected_index;
|
||||
}
|
||||
|
||||
void flow_widget::mouseDoubleClickEvent(QMouseEvent* ev)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "flow_widget_item.h"
|
||||
#include "flow_layout.h"
|
||||
|
||||
#include <set>
|
||||
#include <QWidget>
|
||||
#include <QScrollArea>
|
||||
#include <QPaintEvent>
|
||||
|
|
@ -19,8 +20,10 @@ public:
|
|||
void add_widget(flow_widget_item* widget);
|
||||
void clear();
|
||||
|
||||
void set_multi_selection_enabled(bool enabled) { m_allow_multi_selection = enabled; }
|
||||
|
||||
std::vector<flow_widget_item*>& items() { return m_widgets; }
|
||||
flow_widget_item* selected_item() const;
|
||||
std::set<flow_widget_item*> selected_items() const;
|
||||
QScrollArea* scroll_area() const { return m_scroll_area; }
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
|
@ -33,7 +36,7 @@ private Q_SLOTS:
|
|||
void on_navigate(flow_navigation value);
|
||||
|
||||
protected:
|
||||
void select_item(flow_widget_item* item);
|
||||
void select_items(const std::set<flow_widget_item*>& selected_items, flow_widget_item* current_item = nullptr);
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
|
|
@ -41,8 +44,12 @@ private:
|
|||
flow_layout::position find_item(flow_widget_item* item);
|
||||
flow_layout::position find_next_item(flow_layout::position current_pos, flow_navigation value);
|
||||
|
||||
void update_selection(flow_widget_item* current_item);
|
||||
|
||||
flow_layout* m_flow_layout{};
|
||||
QScrollArea* m_scroll_area{};
|
||||
std::vector<flow_widget_item*> m_widgets;
|
||||
s64 m_selected_index = -1;
|
||||
std::set<s64> m_selected_items;
|
||||
s64 m_last_selected_item = -1;
|
||||
bool m_allow_multi_selection = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <QTimer>
|
||||
|
||||
LOG_CHANNEL(game_list_log, "GameList");
|
||||
LOG_CHANNEL(sys_log, "SYS");
|
||||
|
||||
extern atomic_t<bool> g_system_progress_canceled;
|
||||
|
||||
|
|
@ -340,7 +341,7 @@ void game_list_actions::ShowRemoveGameDialog(const std::vector<game_info>& games
|
|||
|
||||
if (content_info.is_single_selection) // Single selection
|
||||
{
|
||||
if (!RemoveContentList(games[0]->info.serial, true))
|
||||
if (!RemoveContentList(games[0]->info.serial))
|
||||
{
|
||||
QMessageBox::critical(m_game_list_frame, tr("Failure!"), caches->isChecked()
|
||||
? tr("Failed to remove %0 from drive!\nCaches and custom configs have been left intact.").arg(QString::fromStdString(games[0]->info.name))
|
||||
|
|
@ -351,7 +352,7 @@ void game_list_actions::ShowRemoveGameDialog(const std::vector<game_info>& games
|
|||
}
|
||||
else // Multi selection
|
||||
{
|
||||
BatchRemoveContentLists(games, true);
|
||||
BatchRemoveContentLists(games);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -363,6 +364,41 @@ void game_list_actions::ShowGameInfoDialog(const std::vector<game_info>& games)
|
|||
QMessageBox::information(m_game_list_frame, tr("Game Info"), GetContentInfo(games).info);
|
||||
}
|
||||
|
||||
void game_list_actions::ShowDiskUsageDialog()
|
||||
{
|
||||
if (m_disk_usage_future.isRunning()) // Still running the last request
|
||||
return;
|
||||
|
||||
// Disk usage calculation can take a while (in particular on non ssd/m.2 disks)
|
||||
// so run it on a concurrent thread avoiding to block the entire GUI
|
||||
m_disk_usage_future = QtConcurrent::run([this]()
|
||||
{
|
||||
const std::vector<std::pair<std::string, u64>> vfs_disk_usage = rpcs3::utils::get_vfs_disk_usage();
|
||||
const u64 cache_disk_usage = rpcs3::utils::get_cache_disk_usage();
|
||||
|
||||
QString text;
|
||||
u64 tot_data_size = 0;
|
||||
|
||||
for (const auto& [dev, data_size] : vfs_disk_usage)
|
||||
{
|
||||
text += tr("\n %0: %1").arg(QString::fromStdString(dev)).arg(gui::utils::format_byte_size(data_size));
|
||||
tot_data_size += data_size;
|
||||
}
|
||||
|
||||
if (!text.isEmpty())
|
||||
text = tr("\n VFS disk usage: %0%1").arg(gui::utils::format_byte_size(tot_data_size)).arg(text);
|
||||
|
||||
text += tr("\n Cache disk usage: %0").arg(gui::utils::format_byte_size(cache_disk_usage));
|
||||
|
||||
sys_log.success("%s", text);
|
||||
|
||||
Emu.CallFromMainThread([this, text]()
|
||||
{
|
||||
QMessageBox::information(m_game_list_frame, tr("Disk usage"), text);
|
||||
}, nullptr, false);
|
||||
});
|
||||
}
|
||||
|
||||
bool game_list_actions::IsGameRunning(const std::string& serial)
|
||||
{
|
||||
return !Emu.IsStopped(true) && (serial == Emu.GetTitleID() || (serial == "vsh.self" && Emu.IsVsh()));
|
||||
|
|
@ -1063,6 +1099,128 @@ void game_list_actions::BatchCreateCPUCaches(const std::vector<game_info>& games
|
|||
});
|
||||
}
|
||||
|
||||
void game_list_actions::BatchRemoveCustomConfigurations(const std::vector<game_info>& games, bool is_interactive)
|
||||
{
|
||||
if (is_interactive && QMessageBox::question(m_game_list_frame, tr("Confirm Removal"), tr("Remove custom configuration?")) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> serials;
|
||||
|
||||
for (const auto& game : (games.empty() ? m_game_list_frame->GetGameInfo() : games))
|
||||
{
|
||||
if (game->has_custom_config && !serials.count(game->info.serial))
|
||||
{
|
||||
serials.emplace(game->info.serial);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 total = ::size32(serials);
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
QMessageBox::information(m_game_list_frame, tr("Custom Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
progress_dialog* pdlg = new progress_dialog(tr("Custom Configuration Batch Removal"), tr("Removing all custom configurations"), tr("Cancel"), 0, total, false, m_game_list_frame);
|
||||
pdlg->setAutoClose(false);
|
||||
pdlg->setAutoReset(false);
|
||||
pdlg->open();
|
||||
|
||||
BatchActionBySerials(pdlg, serials, tr("%0/%1 custom configurations cleared"), [this](const std::string& serial)
|
||||
{
|
||||
return !serial.empty() && Emu.IsStopped(true) && RemoveCustomConfiguration(serial);
|
||||
},
|
||||
[](u32 removed, u32 total)
|
||||
{
|
||||
game_list_log.notice("Custom Configuration Batch Removal was canceled. %d/%d custom configurations cleared", removed, total);
|
||||
}, nullptr, true);
|
||||
}
|
||||
|
||||
void game_list_actions::BatchRemoveCustomPadConfigurations(const std::vector<game_info>& games, bool is_interactive)
|
||||
{
|
||||
if (is_interactive && QMessageBox::question(m_game_list_frame, tr("Confirm Removal"), tr("Remove custom gamepad configuration?")) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> serials;
|
||||
|
||||
for (const auto& game : (games.empty() ? m_game_list_frame->GetGameInfo() : games))
|
||||
{
|
||||
if (game->has_custom_pad_config && !serials.count(game->info.serial))
|
||||
{
|
||||
serials.emplace(game->info.serial);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 total = ::size32(serials);
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
QMessageBox::information(m_game_list_frame, tr("Custom Gamepad Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
progress_dialog* pdlg = new progress_dialog(tr("Custom Gamepad Configuration Batch Removal"), tr("Removing all custom gamepad configurations"), tr("Cancel"), 0, total, false, m_game_list_frame);
|
||||
pdlg->setAutoClose(false);
|
||||
pdlg->setAutoReset(false);
|
||||
pdlg->open();
|
||||
|
||||
BatchActionBySerials(pdlg, serials, tr("%0/%1 custom gamepad configurations cleared"), [this](const std::string& serial)
|
||||
{
|
||||
return !serial.empty() && Emu.IsStopped(true) && RemoveCustomPadConfiguration(serial);
|
||||
},
|
||||
[](u32 removed, u32 total)
|
||||
{
|
||||
game_list_log.notice("Custom Gamepad Configuration Batch Removal was canceled. %d/%d custom gamepad configurations cleared", removed, total);
|
||||
}, nullptr, true);
|
||||
}
|
||||
|
||||
void game_list_actions::BatchRemoveShaderCaches(const std::vector<game_info>& games, bool is_interactive)
|
||||
{
|
||||
if (!ValidateBatchRemoval("shader cache", is_interactive))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> serials;
|
||||
|
||||
if (games.empty())
|
||||
{
|
||||
serials.emplace("vsh.self");
|
||||
}
|
||||
|
||||
for (const auto& game : (games.empty() ? m_game_list_frame->GetGameInfo() : games))
|
||||
{
|
||||
serials.emplace(game->info.serial);
|
||||
}
|
||||
|
||||
const u32 total = ::size32(serials);
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
QMessageBox::information(m_game_list_frame, tr("Shader Cache Batch Removal"), tr("No files found"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
progress_dialog* pdlg = new progress_dialog(tr("Shader Cache Batch Removal"), tr("Removing all shader caches"), tr("Cancel"), 0, total, false, m_game_list_frame);
|
||||
pdlg->setAutoClose(false);
|
||||
pdlg->setAutoReset(false);
|
||||
pdlg->open();
|
||||
|
||||
BatchActionBySerials(pdlg, serials, tr("%0/%1 shader caches cleared"), [this](const std::string& serial)
|
||||
{
|
||||
return !serial.empty() && Emu.IsStopped(true) && RemoveShaderCache(serial);
|
||||
},
|
||||
[](u32 removed, u32 total)
|
||||
{
|
||||
game_list_log.notice("Shader Cache Batch Removal was canceled. %d/%d caches cleared", removed, total);
|
||||
}, nullptr, false);
|
||||
}
|
||||
|
||||
void game_list_actions::BatchRemovePPUCaches(const std::vector<game_info>& games, bool is_interactive)
|
||||
{
|
||||
if (!ValidateBatchRemoval("PPU cache", is_interactive))
|
||||
|
|
@ -1288,131 +1446,6 @@ void game_list_actions::BatchRemoveContentLists(const std::vector<game_info>& ga
|
|||
}, false);
|
||||
}
|
||||
|
||||
void game_list_actions::BatchRemoveCustomConfigurations(const std::vector<game_info>& games, bool is_interactive)
|
||||
{
|
||||
if (is_interactive && QMessageBox::question(m_game_list_frame, tr("Confirm Removal"), tr("Remove custom configuration?")) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> serials;
|
||||
|
||||
for (const auto& game : (games.empty() ? m_game_list_frame->GetGameInfo() : games))
|
||||
{
|
||||
if (game->has_custom_config && !serials.count(game->info.serial))
|
||||
{
|
||||
serials.emplace(game->info.serial);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 total = ::size32(serials);
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
QMessageBox::information(m_game_list_frame, tr("Custom Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
progress_dialog* pdlg = new progress_dialog(tr("Custom Configuration Batch Removal"), tr("Removing all custom configurations"), tr("Cancel"), 0, total, false, m_game_list_frame);
|
||||
pdlg->setAutoClose(false);
|
||||
pdlg->setAutoReset(false);
|
||||
pdlg->open();
|
||||
|
||||
BatchActionBySerials(pdlg, serials, tr("%0/%1 custom configurations cleared"),
|
||||
[this](const std::string& serial)
|
||||
{
|
||||
return !serial.empty() && Emu.IsStopped(true) && RemoveCustomConfiguration(serial);
|
||||
},
|
||||
[](u32 removed, u32 total)
|
||||
{
|
||||
game_list_log.notice("Custom Configuration Batch Removal was canceled. %d/%d custom configurations cleared", removed, total);
|
||||
}, nullptr, true);
|
||||
}
|
||||
|
||||
void game_list_actions::BatchRemoveCustomPadConfigurations(const std::vector<game_info>& games, bool is_interactive)
|
||||
{
|
||||
if (is_interactive && QMessageBox::question(m_game_list_frame, tr("Confirm Removal"), tr("Remove custom gamepad configuration?")) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> serials;
|
||||
|
||||
for (const auto& game : (games.empty() ? m_game_list_frame->GetGameInfo() : games))
|
||||
{
|
||||
if (game->has_custom_pad_config && !serials.count(game->info.serial))
|
||||
{
|
||||
serials.emplace(game->info.serial);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 total = ::size32(serials);
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
QMessageBox::information(m_game_list_frame, tr("Custom Gamepad Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
progress_dialog* pdlg = new progress_dialog(tr("Custom Gamepad Configuration Batch Removal"), tr("Removing all custom gamepad configurations"), tr("Cancel"), 0, total, false, m_game_list_frame);
|
||||
pdlg->setAutoClose(false);
|
||||
pdlg->setAutoReset(false);
|
||||
pdlg->open();
|
||||
|
||||
BatchActionBySerials(pdlg, serials, tr("%0/%1 custom gamepad configurations cleared"),
|
||||
[this](const std::string& serial)
|
||||
{
|
||||
return !serial.empty() && Emu.IsStopped(true) && RemoveCustomPadConfiguration(serial);
|
||||
},
|
||||
[](u32 removed, u32 total)
|
||||
{
|
||||
game_list_log.notice("Custom Gamepad Configuration Batch Removal was canceled. %d/%d custom gamepad configurations cleared", removed, total);
|
||||
}, nullptr, true);
|
||||
}
|
||||
|
||||
void game_list_actions::BatchRemoveShaderCaches(const std::vector<game_info>& games, bool is_interactive)
|
||||
{
|
||||
if (!ValidateBatchRemoval("shader cache", is_interactive))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> serials;
|
||||
|
||||
if (games.empty())
|
||||
{
|
||||
serials.emplace("vsh.self");
|
||||
}
|
||||
|
||||
for (const auto& game : (games.empty() ? m_game_list_frame->GetGameInfo() : games))
|
||||
{
|
||||
serials.emplace(game->info.serial);
|
||||
}
|
||||
|
||||
const u32 total = ::size32(serials);
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
QMessageBox::information(m_game_list_frame, tr("Shader Cache Batch Removal"), tr("No files found"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
progress_dialog* pdlg = new progress_dialog(tr("Shader Cache Batch Removal"), tr("Removing all shader caches"), tr("Cancel"), 0, total, false, m_game_list_frame);
|
||||
pdlg->setAutoClose(false);
|
||||
pdlg->setAutoReset(false);
|
||||
pdlg->open();
|
||||
|
||||
BatchActionBySerials(pdlg, serials, tr("%0/%1 shader caches cleared"),
|
||||
[this](const std::string& serial)
|
||||
{
|
||||
return !serial.empty() && Emu.IsStopped(true) && RemoveShaderCache(serial);
|
||||
},
|
||||
[](u32 removed, u32 total)
|
||||
{
|
||||
game_list_log.notice("Shader Cache Batch Removal was canceled. %d/%d caches cleared", removed, total);
|
||||
}, nullptr, false);
|
||||
}
|
||||
|
||||
void game_list_actions::CreateShortcuts(const std::vector<game_info>& games, const std::set<gui::utils::shortcut_location>& locations)
|
||||
{
|
||||
if (games.empty())
|
||||
|
|
@ -1501,7 +1534,7 @@ void game_list_actions::CreateShortcuts(const std::vector<game_info>& games, con
|
|||
#endif
|
||||
}
|
||||
|
||||
if (!gameid_token_value.empty() && gui::utils::create_shortcut(gameinfo->info.name, gameinfo->info.serial, target_cli_args, gameinfo->info.name, gameinfo->info.icon_path, target_icon_dir, location))
|
||||
if (!gameid_token_value.empty() && gui::utils::create_shortcut(gameinfo->info.name, gameinfo->icon_in_archive ? gameinfo->info.path : "", gameinfo->info.serial, target_cli_args, gameinfo->info.name, gameinfo->info.icon_path, target_icon_dir, location))
|
||||
{
|
||||
game_list_log.success("Created %s shortcut for %s", destination, QString::fromStdString(gameinfo->info.name).simplified());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "gui_game_info.h"
|
||||
#include "shortcut_utils.h"
|
||||
|
||||
#include <QFuture>
|
||||
#include <QObject>
|
||||
|
||||
class progress_dialog;
|
||||
|
|
@ -53,15 +54,7 @@ public:
|
|||
|
||||
void ShowRemoveGameDialog(const std::vector<game_info>& games);
|
||||
void ShowGameInfoDialog(const std::vector<game_info>& games);
|
||||
|
||||
void BatchCreateCPUCaches(const std::vector<game_info>& games = {}, bool is_fast_compilation = false, bool is_interactive = false);
|
||||
void BatchRemoveCustomConfigurations(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveCustomPadConfigurations(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveShaderCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemovePPUCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveSPUCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveHDD1Caches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveAllCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void ShowDiskUsageDialog();
|
||||
|
||||
// NOTES:
|
||||
// - SetContentList() MUST always be called to set the content's info to be removed by:
|
||||
|
|
@ -69,8 +62,6 @@ public:
|
|||
// - BatchRemoveContentLists()
|
||||
//
|
||||
void SetContentList(u16 content_types, const content_info& content_info);
|
||||
void BatchRemoveContentLists(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
|
||||
void ClearContentList(bool refresh = false);
|
||||
content_info GetContentInfo(const std::vector<game_info>& games);
|
||||
|
||||
|
|
@ -88,6 +79,16 @@ public:
|
|||
bool RemoveAllCaches(const std::string& serial, bool is_interactive = false);
|
||||
bool RemoveContentList(const std::string& serial, bool is_interactive = false);
|
||||
|
||||
void BatchCreateCPUCaches(const std::vector<game_info>& games = {}, bool is_fast_compilation = false, bool is_interactive = false);
|
||||
void BatchRemoveCustomConfigurations(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveCustomPadConfigurations(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveShaderCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemovePPUCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveSPUCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveHDD1Caches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveAllCaches(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
void BatchRemoveContentLists(const std::vector<game_info>& games = {}, bool is_interactive = false);
|
||||
|
||||
static bool RemoveContentPath(const std::string& path, const std::string& desc);
|
||||
static u32 RemoveContentPathList(const std::set<std::string>& path_list, const std::string& desc);
|
||||
static bool RemoveContentBySerial(const std::string& base_dir, const std::string& serial, const std::string& desc);
|
||||
|
|
@ -95,6 +96,7 @@ public:
|
|||
private:
|
||||
game_list_frame* m_game_list_frame = nullptr;
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
QFuture<void> m_disk_usage_future;
|
||||
|
||||
// NOTE:
|
||||
// m_content_info is used by:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "stdafx.h"
|
||||
#include "qt_utils.h"
|
||||
#include "game_list_base.h"
|
||||
|
||||
#include <QDir>
|
||||
|
|
@ -48,7 +49,7 @@ void game_list_base::IconLoadFunction(game_info game, qreal device_pixel_ratio,
|
|||
static std::unordered_set<std::string> warn_once_list;
|
||||
static shared_mutex s_mtx;
|
||||
|
||||
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(QString::fromStdString(game->info.icon_path))))
|
||||
if (game->icon.isNull() && !gui::utils::load_icon(game->icon, game->info.icon_path, game->icon_in_archive ? game->info.path : ""))
|
||||
{
|
||||
if (game_list_log.warning)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -303,13 +303,6 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
|||
QAction* remove_game = manage_game_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category));
|
||||
remove_game->setEnabled(!is_current_running_game);
|
||||
|
||||
// Game info
|
||||
QAction* game_info = manage_game_menu->addAction(tr("&Game Info"));
|
||||
connect(game_info, &QAction::triggered, this, [this, gameinfo]()
|
||||
{
|
||||
m_game_list_actions->ShowGameInfoDialog({gameinfo});
|
||||
});
|
||||
|
||||
// Custom Images menu
|
||||
QMenu* icon_menu = addMenu(tr("&Custom Images"));
|
||||
const std::array<QAction*, 3> custom_icon_actions =
|
||||
|
|
@ -457,8 +450,6 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
|||
icon_menu->setEnabled(false);
|
||||
}
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Open Folder menu
|
||||
QMenu* open_folder_menu = addMenu(tr("&Open Folder"));
|
||||
|
||||
|
|
@ -575,6 +566,22 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
|||
QAction* check_compat = addAction(tr("&Check Game Compatibility"));
|
||||
QAction* download_compat = addAction(tr("&Download Compatibility Database"));
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Disk usage
|
||||
QAction* disk_usage = addAction(tr("&Disk Usage"));
|
||||
connect(disk_usage, &QAction::triggered, this, [this]()
|
||||
{
|
||||
m_game_list_actions->ShowDiskUsageDialog();
|
||||
});
|
||||
|
||||
// Game info
|
||||
QAction* game_info = addAction(tr("&Game Info"));
|
||||
connect(game_info, &QAction::triggered, this, [this, gameinfo]()
|
||||
{
|
||||
m_game_list_actions->ShowGameInfoDialog({gameinfo});
|
||||
});
|
||||
|
||||
connect(boot, &QAction::triggered, m_game_list_frame, [this, gameinfo]()
|
||||
{
|
||||
sys_log.notice("Booting from gamelist per context menu...");
|
||||
|
|
@ -896,8 +903,25 @@ void game_list_context_menu::show_multi_selection_context_menu(const std::vector
|
|||
m_game_list_actions->ShowRemoveGameDialog(games);
|
||||
});
|
||||
|
||||
addSeparator();
|
||||
|
||||
QAction* download_compat = addAction(tr("&Download Compatibility Database"));
|
||||
connect(download_compat, &QAction::triggered, m_game_list_frame, [this]
|
||||
{
|
||||
ensure(m_game_list_frame->GetGameCompatibility())->RequestCompatibility(true);
|
||||
});
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Disk usage
|
||||
QAction* disk_usage = addAction(tr("&Disk Usage"));
|
||||
connect(disk_usage, &QAction::triggered, this, [this]()
|
||||
{
|
||||
m_game_list_actions->ShowDiskUsageDialog();
|
||||
});
|
||||
|
||||
// Game info
|
||||
QAction* game_info = manage_game_menu->addAction(tr("&Game Info"));
|
||||
QAction* game_info = addAction(tr("&Game Info"));
|
||||
connect(game_info, &QAction::triggered, this, [this, games]()
|
||||
{
|
||||
m_game_list_actions->ShowGameInfoDialog(games);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "Emu/vfs_config.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Loader/PSF.h"
|
||||
#include "Loader/ISO.h"
|
||||
#include "util/types.hpp"
|
||||
#include "Utilities/File.h"
|
||||
#include "util/sysinfo.hpp"
|
||||
|
|
@ -522,13 +523,26 @@ void game_list_frame::OnParsingFinished()
|
|||
|
||||
const auto add_game = [this, localized_title, localized_icon, localized_movie, dev_flash, cat_unknown_localized = localized.category.unknown.toStdString(), cat_unknown = cat::cat_unknown.toStdString(), game_icon_path, _hdd, play_hover_movies = m_play_hover_movies, show_custom_icons = m_show_custom_icons](const std::string& dir_or_elf)
|
||||
{
|
||||
std::unique_ptr<iso_archive> archive;
|
||||
if (is_file_iso(dir_or_elf))
|
||||
{
|
||||
archive = std::make_unique<iso_archive>(dir_or_elf);
|
||||
}
|
||||
|
||||
const auto file_exists = [&archive](const std::string& path)
|
||||
{
|
||||
return archive ? archive->is_file(path) : fs::is_file(path);
|
||||
};
|
||||
|
||||
gui_game_info game{};
|
||||
game.info.path = dir_or_elf;
|
||||
|
||||
const Localized thread_localized;
|
||||
|
||||
const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf);
|
||||
const psf::registry psf = psf::load_object(sfo_dir + "/PARAM.SFO");
|
||||
const std::string sfo_dir = archive ? "PS3_GAME" : rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf);
|
||||
const std::string sfo_path = sfo_dir + "/PARAM.SFO";
|
||||
|
||||
const psf::registry psf = archive ? archive->open_psf(sfo_path) : psf::load_object(sfo_path);
|
||||
const std::string_view title_id = psf::get_string(psf, "TITLE_ID", "");
|
||||
|
||||
if (title_id.empty())
|
||||
|
|
@ -596,7 +610,7 @@ void game_list_frame::OnParsingFinished()
|
|||
|
||||
if (game.info.icon_path.empty())
|
||||
{
|
||||
if (std::string icon_path = sfo_dir + "/" + localized_icon; fs::is_file(icon_path))
|
||||
if (std::string icon_path = sfo_dir + "/" + localized_icon; file_exists(icon_path))
|
||||
{
|
||||
game.info.icon_path = std::move(icon_path);
|
||||
}
|
||||
|
|
@ -604,19 +618,20 @@ void game_list_frame::OnParsingFinished()
|
|||
{
|
||||
game.info.icon_path = sfo_dir + "/ICON0.PNG";
|
||||
}
|
||||
game.icon_in_archive = archive && archive->exists(game.info.icon_path);
|
||||
}
|
||||
|
||||
if (std::string movie_path = game_icon_path + game.info.serial + "/hover.gif"; fs::is_file(movie_path))
|
||||
if (std::string movie_path = game_icon_path + game.info.serial + "/hover.gif"; file_exists(movie_path))
|
||||
{
|
||||
game.info.movie_path = std::move(movie_path);
|
||||
game.has_hover_gif = true;
|
||||
}
|
||||
else if (std::string movie_path = sfo_dir + "/" + localized_movie; fs::is_file(movie_path))
|
||||
else if (std::string movie_path = sfo_dir + "/" + localized_movie; file_exists(movie_path))
|
||||
{
|
||||
game.info.movie_path = std::move(movie_path);
|
||||
game.has_hover_pam = true;
|
||||
}
|
||||
else if (std::string movie_path = sfo_dir + "/ICON1.PAM"; fs::is_file(movie_path))
|
||||
else if (std::string movie_path = sfo_dir + "/ICON1.PAM"; file_exists(movie_path))
|
||||
{
|
||||
game.info.movie_path = std::move(movie_path);
|
||||
game.has_hover_pam = true;
|
||||
|
|
@ -741,6 +756,10 @@ void game_list_frame::OnParsingFinished()
|
|||
|
||||
add_disc_dir(entry.path, legit_paths);
|
||||
}
|
||||
else if (is_file_iso(entry.path))
|
||||
{
|
||||
push_path(entry.path, legit_paths);
|
||||
}
|
||||
else
|
||||
{
|
||||
game_list_log.trace("Invalid game path registered: %s", entry.path);
|
||||
|
|
@ -961,32 +980,31 @@ void game_list_frame::ShowContextMenu(const QPoint& pos)
|
|||
QPoint global_pos;
|
||||
std::vector<game_info> games;
|
||||
|
||||
// NOTE: Currently, only m_game_list supports rows multi selection!
|
||||
//
|
||||
// TODO: Add support to rows multi selection to m_game_grid
|
||||
|
||||
if (m_is_list_layout)
|
||||
{
|
||||
global_pos = m_game_list->viewport()->mapToGlobal(pos);
|
||||
|
||||
const auto item_list = m_game_list->selectedItems();
|
||||
game_info gameinfo;
|
||||
|
||||
for (const auto& item : item_list)
|
||||
for (const QTableWidgetItem* item : m_game_list->selectedItems())
|
||||
{
|
||||
if (item->column() != static_cast<int>(gui::game_list_columns::icon))
|
||||
if (!item || item->column() != static_cast<int>(gui::game_list_columns::icon))
|
||||
continue;
|
||||
|
||||
if (gameinfo = GetGameInfoFromItem(item); gameinfo)
|
||||
if (game_info gameinfo = GetGameInfoFromItem(item))
|
||||
games.push_back(gameinfo);
|
||||
}
|
||||
}
|
||||
else if (game_list_grid_item* item = static_cast<game_list_grid_item*>(m_game_grid->selected_item()))
|
||||
else if (m_game_grid)
|
||||
{
|
||||
global_pos = m_game_grid->mapToGlobal(pos);
|
||||
|
||||
if (game_info gameinfo = item->game(); gameinfo)
|
||||
games.push_back(gameinfo);
|
||||
for (const flow_widget_item* selected_item : m_game_grid->selected_items())
|
||||
{
|
||||
if (const game_list_grid_item* item = static_cast<const game_list_grid_item*>(selected_item))
|
||||
{
|
||||
if (game_info gameinfo = item->game())
|
||||
games.push_back(gameinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!games.empty())
|
||||
|
|
@ -1154,16 +1172,19 @@ bool game_list_frame::eventFilter(QObject *object, QEvent *event)
|
|||
|
||||
if (object == m_game_list)
|
||||
{
|
||||
QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), static_cast<int>(gui::game_list_columns::icon));
|
||||
const QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), static_cast<int>(gui::game_list_columns::icon));
|
||||
|
||||
if (!item || !item->isSelected())
|
||||
return false;
|
||||
|
||||
gameinfo = GetGameInfoFromItem(item);
|
||||
if (item && item->isSelected())
|
||||
{
|
||||
gameinfo = GetGameInfoFromItem(item);
|
||||
}
|
||||
}
|
||||
else if (game_list_grid_item* item = static_cast<game_list_grid_item*>(m_game_grid->selected_item()))
|
||||
else if (const auto items = m_game_grid->selected_items(); !items.empty())
|
||||
{
|
||||
gameinfo = item->game();
|
||||
if (const game_list_grid_item* item = static_cast<const game_list_grid_item*>(*items.begin()))
|
||||
{
|
||||
gameinfo = item->game();
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameinfo)
|
||||
|
|
@ -1279,11 +1300,14 @@ std::set<std::string> game_list_frame::CurrentSelectionPaths()
|
|||
}
|
||||
else if (m_game_grid)
|
||||
{
|
||||
if (const game_list_grid_item* item = static_cast<game_list_grid_item*>(m_game_grid->selected_item()))
|
||||
for (const flow_widget_item* selected_item : m_game_grid->selected_items())
|
||||
{
|
||||
if (const game_info& game = item->game())
|
||||
if (const game_list_grid_item* item = static_cast<const game_list_grid_item*>(selected_item))
|
||||
{
|
||||
selection.insert(game->info.path + game->info.icon_path);
|
||||
if (const game_info& game = item->game())
|
||||
{
|
||||
selection.insert(game->info.path + game->info.icon_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include "gui_settings.h"
|
||||
#include "qt_utils.h"
|
||||
|
||||
#include "Loader/ISO.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
game_list_grid::game_list_grid()
|
||||
|
|
@ -12,6 +14,8 @@ game_list_grid::game_list_grid()
|
|||
setObjectName("game_list_grid");
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
set_multi_selection_enabled(true);
|
||||
|
||||
m_icon_ready_callback = [this](const game_info& game, const movie_item_base* item)
|
||||
{
|
||||
Q_EMIT IconReady(game, item);
|
||||
|
|
@ -45,7 +49,7 @@ void game_list_grid::populate(
|
|||
{
|
||||
clear_list();
|
||||
|
||||
game_list_grid_item* selected_item = nullptr;
|
||||
std::set<flow_widget_item*> selected_items;
|
||||
|
||||
blockSignals(true);
|
||||
|
||||
|
|
@ -108,11 +112,16 @@ void game_list_grid::populate(
|
|||
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
|
||||
{
|
||||
item->set_video_path(game->info.movie_path);
|
||||
|
||||
if (!fs::exists(game->info.movie_path) && is_file_iso(game->info.path))
|
||||
{
|
||||
item->set_iso_path(game->info.path);
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_item_ids.contains(game->info.path + game->info.icon_path))
|
||||
{
|
||||
selected_item = item;
|
||||
selected_items.insert(item);
|
||||
}
|
||||
|
||||
add_widget(item);
|
||||
|
|
@ -125,7 +134,7 @@ void game_list_grid::populate(
|
|||
|
||||
QApplication::processEvents();
|
||||
|
||||
select_item(selected_item);
|
||||
select_items(selected_items);
|
||||
}
|
||||
|
||||
void game_list_grid::repaint_icons(std::vector<game_info>& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
#include "Emu/vfs_config.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#include "Loader/ISO.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QScrollBar>
|
||||
#include <QStringBuilder>
|
||||
|
|
@ -277,6 +279,13 @@ void game_list_table::populate(
|
|||
// Do not report size of apps inside /dev_flash (it does not make sense to do so)
|
||||
game->info.size_on_disk = 0;
|
||||
}
|
||||
else if (is_file_iso(game->info.path))
|
||||
{
|
||||
fs::stat_t iso_stat;
|
||||
fs::get_stat(game->info.path, iso_stat);
|
||||
|
||||
game->info.size_on_disk = iso_stat.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
game->info.size_on_disk = fs::get_dir_size(game->info.path, 1, cancel.get());
|
||||
|
|
@ -293,6 +302,11 @@ void game_list_table::populate(
|
|||
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
|
||||
{
|
||||
icon_item->set_video_path(game->info.movie_path);
|
||||
|
||||
if (!fs::exists(game->info.movie_path) && is_file_iso(game->info.path))
|
||||
{
|
||||
icon_item->set_iso_path(game->info.path);
|
||||
}
|
||||
}
|
||||
|
||||
icon_item->setData(Qt::UserRole, index, true);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "Emu/GameInfo.h"
|
||||
|
||||
#include <set>
|
||||
#include <QPixmap>
|
||||
|
||||
/* Having the icons associated with the game info simplifies logic internally */
|
||||
|
|
@ -20,6 +21,7 @@ struct gui_game_info
|
|||
bool has_custom_icon = false;
|
||||
bool has_hover_gif = false;
|
||||
bool has_hover_pam = false;
|
||||
bool icon_in_archive = false;
|
||||
movie_item_base* item = nullptr;
|
||||
|
||||
// Returns the visible version string in the game list
|
||||
|
|
|
|||
|
|
@ -3,15 +3,12 @@
|
|||
#include "gui_settings.h"
|
||||
#include "hex_validator.h"
|
||||
#include "memory_viewer_panel.h"
|
||||
#include "syntax_highlighter.h"
|
||||
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Utilities/lockless.h"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include <QtConcurrent>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QActionGroup>
|
||||
#include <QScrollBar>
|
||||
#include <QVBoxLayout>
|
||||
|
|
@ -21,8 +18,6 @@
|
|||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
LOG_CHANNEL(sys_log, "SYS");
|
||||
|
||||
extern fs::file g_tty;
|
||||
extern atomic_t<s64> g_tty_size;
|
||||
extern std::array<std::deque<std::string>, 16> g_tty_input;
|
||||
|
|
@ -167,32 +162,15 @@ log_frame::log_frame(std::shared_ptr<gui_settings> _gui_settings, QWidget* paren
|
|||
CreateAndConnectActions();
|
||||
LoadSettings();
|
||||
|
||||
if (m_ansi_tty)
|
||||
{
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document());
|
||||
}
|
||||
|
||||
m_timer = new QTimer(this);
|
||||
connect(m_timer, &QTimer::timeout, this, &log_frame::UpdateUI);
|
||||
}
|
||||
|
||||
void log_frame::show_disk_usage(const std::vector<std::pair<std::string, u64>>& vfs_disk_usage, u64 cache_disk_usage)
|
||||
{
|
||||
QString text;
|
||||
u64 tot_data_size = 0;
|
||||
|
||||
for (const auto& [dev, data_size] : vfs_disk_usage)
|
||||
{
|
||||
text += tr("\n %0: %1").arg(QString::fromStdString(dev)).arg(gui::utils::format_byte_size(data_size));
|
||||
tot_data_size += data_size;
|
||||
}
|
||||
|
||||
if (!text.isEmpty())
|
||||
{
|
||||
text = tr("\n VFS disk usage: %0%1").arg(gui::utils::format_byte_size(tot_data_size)).arg(text);
|
||||
}
|
||||
|
||||
text += tr("\n Cache disk usage: %0").arg(gui::utils::format_byte_size(cache_disk_usage));
|
||||
|
||||
sys_log.success("%s", text);
|
||||
QMessageBox::information(this, tr("Disk usage"), text);
|
||||
}
|
||||
|
||||
void log_frame::SetLogLevel(logs::level lev) const
|
||||
{
|
||||
switch (lev)
|
||||
|
|
@ -273,26 +251,6 @@ void log_frame::CreateAndConnectActions()
|
|||
m_tty->clear();
|
||||
});
|
||||
|
||||
m_show_disk_usage_act = new QAction(tr("Show Disk Usage"), this);
|
||||
connect(m_show_disk_usage_act, &QAction::triggered, [this]()
|
||||
{
|
||||
if (m_disk_usage_future.isRunning())
|
||||
{
|
||||
return; // Still running the last request
|
||||
}
|
||||
|
||||
m_disk_usage_future = QtConcurrent::run([this]()
|
||||
{
|
||||
const std::vector<std::pair<std::string, u64>> vfs_disk_usage = rpcs3::utils::get_vfs_disk_usage();
|
||||
const u64 cache_disk_usage = rpcs3::utils::get_cache_disk_usage();
|
||||
|
||||
Emu.CallFromMainThread([this, vfs_disk_usage, cache_disk_usage]()
|
||||
{
|
||||
show_disk_usage(vfs_disk_usage, cache_disk_usage);
|
||||
}, nullptr, false);
|
||||
});
|
||||
});
|
||||
|
||||
m_perform_goto_on_debugger = new QAction(tr("Go-To on Debugger"), this);
|
||||
connect(m_perform_goto_on_debugger, &QAction::triggered, [this]()
|
||||
{
|
||||
|
|
@ -336,6 +294,16 @@ void log_frame::CreateAndConnectActions()
|
|||
{
|
||||
m_gui_settings->SetValue(gui::l_ansi_code, checked);
|
||||
m_ansi_tty = checked;
|
||||
|
||||
if (m_ansi_tty && !m_tty_ansi_highlighter)
|
||||
{
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document());
|
||||
}
|
||||
else if (!m_ansi_tty && m_tty_ansi_highlighter)
|
||||
{
|
||||
m_tty_ansi_highlighter->deleteLater();
|
||||
m_tty_ansi_highlighter = nullptr;
|
||||
}
|
||||
});
|
||||
|
||||
m_tty_channel_acts = new QActionGroup(this);
|
||||
|
|
@ -418,8 +386,6 @@ void log_frame::CreateAndConnectActions()
|
|||
QMenu* menu = m_log->createStandardContextMenu();
|
||||
menu->addAction(m_clear_act);
|
||||
menu->addSeparator();
|
||||
menu->addAction(m_show_disk_usage_act);
|
||||
menu->addSeparator();
|
||||
menu->addAction(m_perform_goto_on_debugger);
|
||||
menu->addAction(m_perform_goto_thread_on_debugger);
|
||||
menu->addAction(m_perform_show_in_mem_viewer);
|
||||
|
|
@ -649,8 +615,22 @@ void log_frame::UpdateUI()
|
|||
buf_line.assign(std::string_view(m_tty_buf).substr(str_index, m_tty_buf.find_first_of('\n', str_index) - str_index));
|
||||
str_index += buf_line.size() + 1;
|
||||
|
||||
// Ignore control characters and greater/equal to 0x80
|
||||
buf_line.erase(std::remove_if(buf_line.begin(), buf_line.end(), [](s8 c) { return c <= 0x8 || c == 0x7F || (c >= 0xE && c <= 0x1F); }), buf_line.end());
|
||||
// If ANSI TTY is enabled, remove all control characters except for ESC (0x1B) for ANSI sequences
|
||||
if (m_ansi_tty)
|
||||
{
|
||||
buf_line.erase(std::remove_if(buf_line.begin(), buf_line.end(), [](s8 c)
|
||||
{
|
||||
return c <= 0x8 || c == 0x7F || (c >= 0xE && c <= 0x1F && c != 0x1B);
|
||||
}), buf_line.end());
|
||||
}
|
||||
// Otherwise, remove all control characters to keep the output clean
|
||||
else
|
||||
{
|
||||
buf_line.erase(std::remove_if(buf_line.begin(), buf_line.end(), [](s8 c)
|
||||
{
|
||||
return c <= 0x8 || c == 0x7F || (c >= 0xE && c <= 0x1F);
|
||||
}), buf_line.end());
|
||||
}
|
||||
|
||||
// save old scroll bar state
|
||||
QScrollBar* sb = m_tty->verticalScrollBar();
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QTabWidget>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QActionGroup>
|
||||
|
||||
class AnsiHighlighter;
|
||||
|
||||
class gui_settings;
|
||||
|
||||
class log_frame : public custom_dock_widget
|
||||
|
|
@ -39,7 +40,6 @@ protected:
|
|||
private Q_SLOTS:
|
||||
void UpdateUI();
|
||||
private:
|
||||
void show_disk_usage(const std::vector<std::pair<std::string, u64>>& vfs_disk_usage, u64 cache_disk_usage);
|
||||
void SetLogLevel(logs::level lev) const;
|
||||
void SetTTYLogging(bool val) const;
|
||||
|
||||
|
|
@ -50,7 +50,6 @@ private:
|
|||
std::unique_ptr<find_dialog> m_find_dialog;
|
||||
|
||||
QTimer* m_timer = nullptr;
|
||||
QFuture<void> m_disk_usage_future;
|
||||
|
||||
std::vector<QColor> m_color;
|
||||
QColor m_color_stack;
|
||||
|
|
@ -72,10 +71,10 @@ private:
|
|||
QPlainTextEdit* m_tty = nullptr;
|
||||
QLineEdit* m_tty_input = nullptr;
|
||||
int m_tty_channel = -1;
|
||||
AnsiHighlighter* m_tty_ansi_highlighter = nullptr;
|
||||
|
||||
QAction* m_clear_act = nullptr;
|
||||
QAction* m_clear_tty_act = nullptr;
|
||||
QAction* m_show_disk_usage_act = nullptr;
|
||||
QAction* m_perform_goto_on_debugger = nullptr;
|
||||
QAction* m_perform_goto_thread_on_debugger = nullptr;
|
||||
QAction* m_perform_show_in_mem_viewer = nullptr;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
#include "Loader/PUP.h"
|
||||
#include "Loader/TAR.h"
|
||||
#include "Loader/PSF.h"
|
||||
#include "Loader/ISO.h"
|
||||
#include "Loader/mself.hpp"
|
||||
|
||||
#include "Utilities/Thread.h"
|
||||
|
|
@ -542,8 +543,16 @@ void main_window::Boot(const std::string& path, const std::string& title_id, boo
|
|||
}
|
||||
else
|
||||
{
|
||||
std::string game_path = Emu.GetBoot();
|
||||
if (game_path.starts_with(iso_device::virtual_device_name))
|
||||
{
|
||||
const auto device = fs::get_virtual_device(iso_device::virtual_device_name + "/");
|
||||
const auto iso_dev = ensure(dynamic_cast<const iso_device*>(device.get()));
|
||||
game_path = iso_dev->get_loaded_iso();
|
||||
}
|
||||
|
||||
gui_log.success("Boot successful.");
|
||||
AddRecentAction(gui::Recent_Game(QString::fromStdString(Emu.GetBoot()), QString::fromStdString(Emu.GetTitleAndTitleID())), false);
|
||||
AddRecentAction(gui::Recent_Game(QString::fromStdString(game_path), QString::fromStdString(Emu.GetTitleAndTitleID())), false);
|
||||
}
|
||||
|
||||
if (refresh_list)
|
||||
|
|
@ -569,6 +578,7 @@ void main_window::BootElf()
|
|||
"SELF files (EBOOT.BIN *.self);;"
|
||||
"BOOT files (*BOOT.BIN);;"
|
||||
"BIN files (*.bin);;"
|
||||
"ISO files (*.iso);;"
|
||||
"All executable files (*.SAVESTAT.zst *.SAVESTAT.gz *.SAVESTAT *.sprx *.SPRX *.self *.SELF *.bin *.BIN *.prx *.PRX *.elf *.ELF *.o *.O);;"
|
||||
"All files (*.*)"),
|
||||
Q_NULLPTR, QFileDialog::DontResolveSymlinks);
|
||||
|
|
@ -690,6 +700,34 @@ void main_window::BootGame()
|
|||
Boot(dir_path.toStdString(), "", false, true);
|
||||
}
|
||||
|
||||
void main_window::BootISO()
|
||||
{
|
||||
bool stopped = false;
|
||||
|
||||
if (Emu.IsRunning())
|
||||
{
|
||||
Emu.Pause();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
const QString path_last_game = m_gui_settings->GetValue(gui::fd_boot_game).toString();
|
||||
const QString path = QFileDialog::getOpenFileName(this, tr("Select ISO"), path_last_game, tr("ISO files (*.iso);;All files (*.*)"));
|
||||
|
||||
if (path.isEmpty())
|
||||
{
|
||||
if (stopped)
|
||||
{
|
||||
Emu.Resume();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_gui_settings->SetValue(gui::fd_boot_game, QFileInfo(path).dir().path());
|
||||
|
||||
gui_log.notice("Booting from BootISO...");
|
||||
Boot(path.toStdString(), "", true, true);
|
||||
}
|
||||
|
||||
void main_window::BootVSH()
|
||||
{
|
||||
gui_log.notice("Booting from BootVSH...");
|
||||
|
|
@ -2469,6 +2507,7 @@ void main_window::CreateConnects()
|
|||
connect(ui->bootElfAct, &QAction::triggered, this, &main_window::BootElf);
|
||||
connect(ui->bootTestAct, &QAction::triggered, this, &main_window::BootTest);
|
||||
connect(ui->bootGameAct, &QAction::triggered, this, &main_window::BootGame);
|
||||
connect(ui->bootIsoAct, &QAction::triggered, this, &main_window::BootISO);
|
||||
connect(ui->bootVSHAct, &QAction::triggered, this, &main_window::BootVSH);
|
||||
connect(ui->actionopen_rsx_capture, &QAction::triggered, this, [this](){ BootRsxCapture(); });
|
||||
connect(ui->actionCreate_RSX_Capture, &QAction::triggered, this, []()
|
||||
|
|
@ -2521,6 +2560,22 @@ void main_window::CreateConnects()
|
|||
AddGamesFromDirs(std::move(paths));
|
||||
});
|
||||
|
||||
connect(ui->addIsoGamesAct, &QAction::triggered, this, [this]()
|
||||
{
|
||||
if (!m_gui_settings->GetBootConfirmation(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList paths = QFileDialog::getOpenFileNames(this, tr("Select ISO files to add"), QString::fromStdString(fs::get_config_dir()), tr("ISO files (*.iso);;All files (*.*)"));
|
||||
if (paths.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddGamesFromDirs(std::move(paths));
|
||||
});
|
||||
|
||||
connect(ui->bootRecentMenu, &QMenu::aboutToShow, this, [this]()
|
||||
{
|
||||
// Enable/Disable Recent Games List
|
||||
|
|
@ -3958,7 +4013,7 @@ main_window::drop_type main_window::IsValidFile(const QMimeData& md, QStringList
|
|||
const QString suffix_lo = info.suffix().toLower();
|
||||
|
||||
// check for directories first, only valid if all other paths led to directories until now.
|
||||
if (info.isDir())
|
||||
if (info.isDir() || is_file_iso(path.toStdString()))
|
||||
{
|
||||
if (type != drop_type::drop_dir && type != drop_type::drop_error)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ private Q_SLOTS:
|
|||
void BootElf();
|
||||
void BootTest();
|
||||
void BootGame();
|
||||
void BootISO();
|
||||
void BootVSH();
|
||||
void BootSavestate();
|
||||
void BootRsxCapture(std::string path = "");
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@
|
|||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
|
|
@ -211,6 +210,7 @@
|
|||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<addaction name="bootGameAct"/>
|
||||
<addaction name="bootIsoAct"/>
|
||||
<addaction name="bootVSHAct"/>
|
||||
<addaction name="bootElfMenu"/>
|
||||
<addaction name="bootSavestateAct"/>
|
||||
|
|
@ -218,6 +218,7 @@
|
|||
<addaction name="bootRecentMenu"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="addGamesAct"/>
|
||||
<addaction name="addIsoGamesAct"/>
|
||||
<addaction name="bootInstallPkgAct"/>
|
||||
<addaction name="bootInstallPupAct"/>
|
||||
<addaction name="separator"/>
|
||||
|
|
@ -242,17 +243,17 @@
|
|||
<property name="title">
|
||||
<string>Configuration</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuMice">
|
||||
<property name="title">
|
||||
<string>Mice</string>
|
||||
</property>
|
||||
<addaction name="actionBasic_Mouse"/>
|
||||
<addaction name="actionRaw_Mouse"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDevices">
|
||||
<property name="title">
|
||||
<string>Devices</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuMice">
|
||||
<property name="title">
|
||||
<string>Mice</string>
|
||||
</property>
|
||||
<addaction name="actionBasic_Mouse"/>
|
||||
<addaction name="actionRaw_Mouse"/>
|
||||
</widget>
|
||||
<addaction name="confPadsAct"/>
|
||||
<addaction name="menuMice"/>
|
||||
<addaction name="confCamerasAct"/>
|
||||
|
|
@ -296,9 +297,6 @@
|
|||
<property name="title">
|
||||
<string>Manage</string>
|
||||
</property>
|
||||
<addaction name="confVFSDialogAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionManage_Users"/>
|
||||
<widget class="QMenu" name="menuNetworkServices">
|
||||
<property name="title">
|
||||
<string>Network Services</string>
|
||||
|
|
@ -307,7 +305,6 @@
|
|||
<addaction name="confClansAct"/>
|
||||
<addaction name="confIPCAct"/>
|
||||
</widget>
|
||||
<addaction name="menuNetworkServices"/>
|
||||
<widget class="QMenu" name="menuPortals">
|
||||
<property name="title">
|
||||
<string>Portals and Gates</string>
|
||||
|
|
@ -317,6 +314,10 @@
|
|||
<addaction name="actionManage_Dimensions_ToyPad"/>
|
||||
<addaction name="actionManage_KamenRider_RideGate"/>
|
||||
</widget>
|
||||
<addaction name="confVFSDialogAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionManage_Users"/>
|
||||
<addaction name="menuNetworkServices"/>
|
||||
<addaction name="menuPortals"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionManage_RAP_Licenses"/>
|
||||
|
|
@ -1046,7 +1047,7 @@
|
|||
</action>
|
||||
<action name="toolbar_rpcn">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/Icons/rpcn.png</normaloff>:/Icons/rpcn.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
|
@ -1513,6 +1514,16 @@
|
|||
<string>Download Update</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="addIsoGamesAct">
|
||||
<property name="text">
|
||||
<string>Add ISO Games</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="bootIsoAct">
|
||||
<property name="text">
|
||||
<string>Boot ISO</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
|
|
|||
|
|
@ -482,7 +482,7 @@ void pad_settings_dialog::InitButtons()
|
|||
if ((!is_connected || !m_remap_timer.isActive()) && (
|
||||
is_connected != m_enable_buttons ||
|
||||
(is_connected && (
|
||||
!capabilities.has_pressure_sensitivity != m_enable_pressure_intensity_button ||
|
||||
capabilities.has_pressure_intensity_button != m_enable_pressure_intensity_button ||
|
||||
capabilities.has_rumble != m_enable_rumble ||
|
||||
capabilities.has_battery_led != m_enable_battery_led ||
|
||||
(capabilities.has_led || capabilities.has_mono_led) != m_enable_led ||
|
||||
|
|
@ -490,7 +490,7 @@ void pad_settings_dialog::InitButtons()
|
|||
{
|
||||
if (is_connected)
|
||||
{
|
||||
m_enable_pressure_intensity_button = !capabilities.has_pressure_sensitivity;
|
||||
m_enable_pressure_intensity_button = capabilities.has_pressure_intensity_button;
|
||||
m_enable_rumble = capabilities.has_rumble;
|
||||
m_enable_battery_led = capabilities.has_battery_led;
|
||||
m_enable_led = capabilities.has_led || capabilities.has_mono_led;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Utilities/File.h"
|
||||
#include "Loader/ISO.h"
|
||||
#include <cmath>
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
|
@ -392,27 +393,46 @@ namespace gui
|
|||
std::string icon_path = fs::get_config_dir() + "/Icons/game_icons/" + title_id + "/ICON0.PNG";
|
||||
bool found_file = fs::is_file(icon_path);
|
||||
|
||||
std::unique_ptr<QPixmap> pixmap;
|
||||
|
||||
if (!found_file)
|
||||
{
|
||||
// Get Icon for the gs_frame from path. this handles presumably all possible use cases
|
||||
const QString qpath = QString::fromStdString(path);
|
||||
const std::string path_list[] = { path, qpath.section("/", 0, -2, QString::SectionIncludeTrailingSep).toStdString(),
|
||||
qpath.section("/", 0, -3, QString::SectionIncludeTrailingSep).toStdString() };
|
||||
std::vector<std::string> path_list;
|
||||
|
||||
for (const std::string& pth : path_list)
|
||||
const bool is_archive = is_file_iso(path);
|
||||
if (is_archive)
|
||||
{
|
||||
if (!fs::is_dir(pth))
|
||||
icon_path = "PS3_GAME/ICON0.PNG";
|
||||
|
||||
QPixmap px;
|
||||
if (load_iso_icon(px, icon_path, path))
|
||||
{
|
||||
continue;
|
||||
found_file = true;
|
||||
pixmap = std::make_unique<QPixmap>(std::move(px));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString qpath = QString::fromStdString(path);
|
||||
path_list = { path, qpath.section("/", 0, -2, QString::SectionIncludeTrailingSep).toStdString(),
|
||||
qpath.section("/", 0, -3, QString::SectionIncludeTrailingSep).toStdString() };
|
||||
|
||||
const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(pth, title_id);
|
||||
icon_path = sfo_dir + "/ICON0.PNG";
|
||||
found_file = fs::is_file(icon_path);
|
||||
|
||||
if (found_file)
|
||||
for (const std::string& pth : path_list)
|
||||
{
|
||||
break;
|
||||
if (!fs::is_dir(pth))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(pth, title_id);
|
||||
icon_path = sfo_dir + "/ICON0.PNG";
|
||||
found_file = fs::is_file(icon_path);
|
||||
|
||||
if (found_file)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -420,7 +440,7 @@ namespace gui
|
|||
if (found_file)
|
||||
{
|
||||
// load the image from path. It will most likely be a rectangle
|
||||
const QImage source = QImage(QString::fromStdString(icon_path));
|
||||
const QImage source = pixmap ? pixmap->toImage() : QImage(QString::fromStdString(icon_path));
|
||||
const int edge_max = std::max(source.width(), source.height());
|
||||
|
||||
// create a new transparent image with square size and same format as source (maybe handle other formats than RGB32 as well?)
|
||||
|
|
@ -682,6 +702,36 @@ namespace gui
|
|||
return QString("%1 days ago %2").arg(current_date - exctrated_date).arg(date.toString(fmt_relative));
|
||||
}
|
||||
|
||||
bool load_iso_icon(QPixmap& icon, const std::string& icon_path, const std::string& archive_path)
|
||||
{
|
||||
if (icon_path.empty() || archive_path.empty()) return false;
|
||||
if (!is_file_iso(archive_path)) return false;
|
||||
|
||||
iso_archive archive(archive_path);
|
||||
if (!archive.exists(icon_path)) return false;
|
||||
|
||||
auto icon_file = archive.open(icon_path);
|
||||
const auto icon_size = icon_file.size();
|
||||
if (icon_size == 0) return false;
|
||||
|
||||
QByteArray data(icon_size, 0);
|
||||
icon_file.read(data.data(), icon_size);
|
||||
|
||||
return icon.loadFromData(data);
|
||||
}
|
||||
|
||||
bool load_icon(QPixmap& icon, const std::string& icon_path, const std::string& archive_path)
|
||||
{
|
||||
if (icon_path.empty()) return false;
|
||||
|
||||
if (archive_path.empty())
|
||||
{
|
||||
return icon.load(QString::fromStdString(icon_path));
|
||||
}
|
||||
|
||||
return load_iso_icon(icon, icon_path, archive_path);
|
||||
}
|
||||
|
||||
QString format_timestamp(s64 time, const QString& fmt)
|
||||
{
|
||||
return format_datetime(datetime(time), fmt);
|
||||
|
|
|
|||
|
|
@ -178,6 +178,12 @@ namespace gui
|
|||
return color_scheme() == Qt::ColorScheme::Dark;
|
||||
}
|
||||
|
||||
// Loads an icon from an (ISO) archive file.
|
||||
bool load_iso_icon(QPixmap& icon, const std::string& icon_path, const std::string& archive_path);
|
||||
|
||||
// Loads an icon (optionally from an (ISO) archive file).
|
||||
bool load_icon(QPixmap& icon, const std::string& icon_path, const std::string& archive_path);
|
||||
|
||||
template <typename T>
|
||||
void stop_future_watcher(QFutureWatcher<T>& watcher, bool cancel, std::shared_ptr<atomic_t<bool>> cancel_flag = nullptr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#include "Emu/System.h"
|
||||
#include "qt_video_source.h"
|
||||
|
||||
#include "Loader/ISO.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
qt_video_source::qt_video_source()
|
||||
|
|
@ -19,6 +21,11 @@ void qt_video_source::set_video_path(const std::string& video_path)
|
|||
m_video_path = QString::fromStdString(video_path);
|
||||
}
|
||||
|
||||
void qt_video_source::set_iso_path(const std::string& iso_path)
|
||||
{
|
||||
m_iso_path = iso_path;
|
||||
}
|
||||
|
||||
void qt_video_source::set_active(bool active)
|
||||
{
|
||||
if (m_active.exchange(active) == active) return;
|
||||
|
|
@ -55,7 +62,7 @@ void qt_video_source::init_movie()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!m_image_change_callback || m_video_path.isEmpty() || !QFile::exists(m_video_path))
|
||||
if (!m_image_change_callback || m_video_path.isEmpty() || (m_iso_path.empty() && !QFile::exists(m_video_path)))
|
||||
{
|
||||
m_video_path.clear();
|
||||
return;
|
||||
|
|
@ -65,8 +72,25 @@ void qt_video_source::init_movie()
|
|||
|
||||
if (lower.endsWith(".gif"))
|
||||
{
|
||||
m_movie = std::make_unique<QMovie>(m_video_path);
|
||||
m_video_path.clear();
|
||||
if (m_iso_path.empty())
|
||||
{
|
||||
m_movie = std::make_unique<QMovie>(m_video_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
iso_archive archive(m_iso_path);
|
||||
auto movie_file = archive.open(m_video_path.toStdString());
|
||||
const auto movie_size = movie_file.size();
|
||||
if (movie_size == 0) return;
|
||||
|
||||
m_video_data = QByteArray(movie_size, 0);
|
||||
movie_file.read(m_video_data.data(), movie_size);
|
||||
|
||||
m_video_buffer = std::make_unique<QBuffer>(&m_video_data);
|
||||
m_video_buffer->open(QIODevice::ReadOnly);
|
||||
m_movie = std::make_unique<QMovie>(m_video_buffer.get());
|
||||
|
||||
}
|
||||
|
||||
if (!m_movie->isValid())
|
||||
{
|
||||
|
|
@ -85,14 +109,31 @@ void qt_video_source::init_movie()
|
|||
if (lower.endsWith(".pam"))
|
||||
{
|
||||
// We can't set PAM files as source of the video player, so we have to feed them as raw data.
|
||||
QFile file(m_video_path);
|
||||
if (!file.open(QFile::OpenModeFlag::ReadOnly))
|
||||
if (m_iso_path.empty())
|
||||
{
|
||||
return;
|
||||
QFile file(m_video_path);
|
||||
if (!file.open(QFile::OpenModeFlag::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Decode the pam properly before pushing it to the player
|
||||
m_video_data = file.readAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
iso_archive archive(m_iso_path);
|
||||
auto movie_file = archive.open(m_video_path.toStdString());
|
||||
const auto movie_size = movie_file.size();
|
||||
if (movie_size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_video_data = QByteArray(movie_size, 0);
|
||||
movie_file.read(m_video_data.data(), movie_size);
|
||||
}
|
||||
|
||||
// TODO: Decode the pam properly before pushing it to the player
|
||||
m_video_data = file.readAll();
|
||||
if (m_video_data.isEmpty())
|
||||
{
|
||||
return;
|
||||
|
|
@ -148,6 +189,7 @@ void qt_video_source::stop_movie()
|
|||
if (m_movie)
|
||||
{
|
||||
m_movie->stop();
|
||||
m_movie.reset();
|
||||
}
|
||||
|
||||
m_video_sink.reset();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public:
|
|||
qt_video_source();
|
||||
virtual ~qt_video_source();
|
||||
|
||||
void set_iso_path(const std::string& iso_path);
|
||||
void set_video_path(const std::string& video_path) override;
|
||||
const QString& video_path() const { return m_video_path; }
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ protected:
|
|||
atomic_t<bool> m_has_new = false;
|
||||
|
||||
QString m_video_path;
|
||||
std::string m_iso_path; // path of the source archive
|
||||
QByteArray m_video_data{};
|
||||
QImage m_image{};
|
||||
std::vector<u8> m_image_path;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#include "gui_settings.h"
|
||||
#include "progress_dialog.h"
|
||||
|
||||
#include "Loader/ISO.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QScrollBar>
|
||||
|
|
@ -208,7 +210,7 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings>
|
|||
m_savestate_table->create_header_actions(m_savestate_column_acts,
|
||||
[this](int col) { return m_gui_settings->GetSavestateListColVisibility(static_cast<gui::savestate_list_columns>(col)); },
|
||||
[this](int col, bool visible) { m_gui_settings->SetSavestateListColVisibility(static_cast<gui::savestate_list_columns>(col), visible); });
|
||||
|
||||
|
||||
m_game_table->create_header_actions(m_game_column_acts,
|
||||
[this](int col) { return m_gui_settings->GetSavestateGamelistColVisibility(static_cast<gui::savestate_game_list_columns>(col)); },
|
||||
[this](int col, bool visible) { m_gui_settings->SetSavestateGamelistColVisibility(static_cast<gui::savestate_game_list_columns>(col), visible); });
|
||||
|
|
@ -416,9 +418,10 @@ void savestate_manager_dialog::ResizeGameIcons()
|
|||
{
|
||||
const qreal dpr = devicePixelRatioF();
|
||||
const int savestate_index = item->data(GameUserRole::GameIndex).toInt();
|
||||
const std::string icon_path = m_savestate_db[savestate_index]->game_icon_path;
|
||||
std::string game_icon_path = m_savestate_db[savestate_index]->game_icon_path;
|
||||
std::string game_archive_path = m_savestate_db[savestate_index]->archive_path;
|
||||
|
||||
item->set_icon_load_func([this, icon_path, savestate_index, cancel = item->icon_loading_aborted(), dpr](int index)
|
||||
item->set_icon_load_func([this, icon_path = std::move(game_icon_path), archive_path = std::move(game_archive_path), savestate_index, cancel = item->icon_loading_aborted(), dpr](int index)
|
||||
{
|
||||
if (cancel && cancel->load())
|
||||
{
|
||||
|
|
@ -432,7 +435,7 @@ void savestate_manager_dialog::ResizeGameIcons()
|
|||
if (!item->data(GameUserRole::GamePixmapLoaded).toBool())
|
||||
{
|
||||
// Load game icon
|
||||
if (!icon.load(QString::fromStdString(icon_path)))
|
||||
if (!gui::utils::load_icon(icon, icon_path, archive_path))
|
||||
{
|
||||
gui_log.warning("Could not load savestate game icon from path %s", icon_path);
|
||||
}
|
||||
|
|
@ -617,6 +620,11 @@ void savestate_manager_dialog::StartSavestateLoadThreads()
|
|||
{
|
||||
game_data_ptr->game_name = gameinfo->info.name;
|
||||
game_data_ptr->game_icon_path = gameinfo->info.icon_path;
|
||||
if (gameinfo->icon_in_archive)
|
||||
{
|
||||
game_data_ptr->archive_path = gameinfo->info.path;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ private:
|
|||
std::string game_name;
|
||||
std::string game_icon_path;
|
||||
std::string dir_path;
|
||||
std::string archive_path;
|
||||
};
|
||||
|
||||
bool LoadSavestateFolderToDB(std::unique_ptr<game_savestates_data>&& game_savestates);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "Emu/VFS.h"
|
||||
#include "Utilities/File.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Loader/ISO.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
|
|
@ -31,7 +32,7 @@ LOG_CHANNEL(sys_log, "SYS");
|
|||
|
||||
namespace gui::utils
|
||||
{
|
||||
bool create_square_shortcut_icon_file(const std::string& src_icon_path, const std::string& target_icon_dir, std::string& target_icon_path, const std::string& extension, int size)
|
||||
bool create_square_shortcut_icon_file(const std::string& path, const std::string& src_icon_path, const std::string& target_icon_dir, std::string& target_icon_path, const std::string& extension, int size)
|
||||
{
|
||||
if (src_icon_path.empty() || target_icon_dir.empty() || extension.empty())
|
||||
{
|
||||
|
|
@ -39,7 +40,15 @@ namespace gui::utils
|
|||
return false;
|
||||
}
|
||||
|
||||
QPixmap icon(QString::fromStdString(src_icon_path));
|
||||
const bool is_archive = is_file_iso(path);
|
||||
|
||||
QPixmap icon;
|
||||
if (!load_icon(icon, src_icon_path, is_archive ? path : ""))
|
||||
{
|
||||
sys_log.error("Failed to create shortcut. Failed to load %sicon: '%s'", is_archive ? "iso " : "", src_icon_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gui::utils::create_square_pixmap(icon, size))
|
||||
{
|
||||
sys_log.error("Failed to create shortcut. Icon empty.");
|
||||
|
|
@ -67,6 +76,7 @@ namespace gui::utils
|
|||
}
|
||||
|
||||
bool create_shortcut(const std::string& name,
|
||||
const std::string& path,
|
||||
[[maybe_unused]] const std::string& serial,
|
||||
[[maybe_unused]] const std::string& target_cli_args,
|
||||
[[maybe_unused]] const std::string& description,
|
||||
|
|
@ -189,7 +199,7 @@ namespace gui::utils
|
|||
if (!src_icon_path.empty() && !target_icon_dir.empty())
|
||||
{
|
||||
std::string target_icon_path;
|
||||
if (!create_square_shortcut_icon_file(src_icon_path, target_icon_dir, target_icon_path, "ico", 512))
|
||||
if (!create_square_shortcut_icon_file(path, src_icon_path, target_icon_dir, target_icon_path, "ico", 512))
|
||||
return cleanup(false, ".ico creation failed");
|
||||
|
||||
const std::wstring w_icon_path = utf8_to_wchar(target_icon_path);
|
||||
|
|
@ -301,7 +311,7 @@ namespace gui::utils
|
|||
if (!src_icon_path.empty())
|
||||
{
|
||||
std::string target_icon_path = resources_dir;
|
||||
if (!create_square_shortcut_icon_file(src_icon_path, resources_dir, target_icon_path, "icns", 512))
|
||||
if (!create_square_shortcut_icon_file(path, src_icon_path, resources_dir, target_icon_path, "icns", 512))
|
||||
{
|
||||
// Error is logged in create_square_shortcut_icon_file
|
||||
return false;
|
||||
|
|
@ -339,7 +349,7 @@ namespace gui::utils
|
|||
if (!src_icon_path.empty() && !target_icon_dir.empty())
|
||||
{
|
||||
std::string target_icon_path;
|
||||
if (!create_square_shortcut_icon_file(src_icon_path, target_icon_dir, target_icon_path, "png", 512))
|
||||
if (!create_square_shortcut_icon_file(path, src_icon_path, target_icon_dir, target_icon_path, "png", 512))
|
||||
{
|
||||
// Error is logged in create_square_shortcut_icon_file
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ namespace gui::utils
|
|||
};
|
||||
|
||||
bool create_shortcut(const std::string& name,
|
||||
const std::string& path,
|
||||
const std::string& serial,
|
||||
const std::string& target_cli_args,
|
||||
const std::string& description,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
#include "syntax_highlighter.h"
|
||||
#include "qt_utils.h"
|
||||
|
||||
Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)
|
||||
Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void Highlighter::addRule(const QString &pattern, const QBrush &brush)
|
||||
void Highlighter::addRule(const QString& pattern, const QBrush& brush)
|
||||
{
|
||||
HighlightingRule rule;
|
||||
rule.pattern = QRegularExpression(pattern);
|
||||
|
|
@ -13,14 +13,14 @@ void Highlighter::addRule(const QString &pattern, const QBrush &brush)
|
|||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
void Highlighter::highlightBlock(const QString &text)
|
||||
void Highlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
for (const HighlightingRule& rule : highlightingRules)
|
||||
{
|
||||
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
const QRegularExpressionMatch match = matchIterator.next();
|
||||
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ LogHighlighter::LogHighlighter(QTextDocument* parent) : Highlighter(parent)
|
|||
addRule("^·T.*$", gui::utils::get_label_color("log_level_trace", color, color));
|
||||
}
|
||||
|
||||
AsmHighlighter::AsmHighlighter(QTextDocument *parent) : Highlighter(parent)
|
||||
AsmHighlighter::AsmHighlighter(QTextDocument* parent) : Highlighter(parent)
|
||||
{
|
||||
addRule("^\\b[A-Z0-9]+\\b", Qt::darkBlue); // Instructions
|
||||
addRule("-?R\\d[^,;\\s]*", Qt::darkRed); // -R0.*
|
||||
|
|
@ -78,7 +78,7 @@ AsmHighlighter::AsmHighlighter(QTextDocument *parent) : Highlighter(parent)
|
|||
addRule("#[^\\n]*", Qt::darkGreen); // Single line comment
|
||||
}
|
||||
|
||||
GlslHighlighter::GlslHighlighter(QTextDocument *parent) : Highlighter(parent)
|
||||
GlslHighlighter::GlslHighlighter(QTextDocument* parent) : Highlighter(parent)
|
||||
{
|
||||
const QStringList keywordPatterns = QStringList()
|
||||
// Selection-Iteration-Jump Statements:
|
||||
|
|
@ -183,3 +183,100 @@ GlslHighlighter::GlslHighlighter(QTextDocument *parent) : Highlighter(parent)
|
|||
commentStartExpression = QRegularExpression("/\\*");
|
||||
commentEndExpression = QRegularExpression("\\*/");
|
||||
}
|
||||
|
||||
AnsiHighlighter::AnsiHighlighter(QTextDocument* parent) : Highlighter(parent)
|
||||
{
|
||||
m_escape_format.setForeground(Qt::darkGray);
|
||||
m_escape_format.setFontItalic(true);
|
||||
|
||||
m_foreground_color = gui::utils::get_foreground_color();
|
||||
}
|
||||
|
||||
void AnsiHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
QTextCharFormat current_format;
|
||||
current_format.setForeground(m_foreground_color);
|
||||
|
||||
int pos = 0;
|
||||
auto it = ansi_re.globalMatch(text);
|
||||
while (it.hasNext())
|
||||
{
|
||||
const auto match = it.next();
|
||||
const int start = match.capturedStart();
|
||||
const int length = match.capturedLength();
|
||||
|
||||
// Apply current format to the chunk before this escape sequence
|
||||
if (start > pos)
|
||||
{
|
||||
setFormat(pos, start - pos, current_format);
|
||||
}
|
||||
|
||||
// Highlight the escape sequence itself
|
||||
setFormat(start, length, m_escape_format);
|
||||
|
||||
// Parse SGR parameters and update currentFormat
|
||||
const QRegularExpressionMatch pm = param_re.match(match.captured());
|
||||
if (pm.hasMatch())
|
||||
{
|
||||
const QString params = pm.captured(1);
|
||||
if (params.isEmpty())
|
||||
{
|
||||
// empty or just \x1b[m = reset
|
||||
current_format = QTextCharFormat();
|
||||
current_format.setForeground(m_foreground_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
const QStringList codes = params.split(';', Qt::SkipEmptyParts);
|
||||
for (const QString& c : codes)
|
||||
{
|
||||
bool ok = false;
|
||||
const int code = c.toInt(&ok);
|
||||
if (!ok) continue;
|
||||
switch (code)
|
||||
{
|
||||
case 0:
|
||||
current_format = QTextCharFormat();
|
||||
current_format.setForeground(m_foreground_color);
|
||||
break;
|
||||
case 1:
|
||||
current_format.setFontWeight(QFont::Bold);
|
||||
break;
|
||||
case 3:
|
||||
current_format.setFontItalic(true);
|
||||
break;
|
||||
case 4:
|
||||
current_format.setFontUnderline(true);
|
||||
break;
|
||||
case 30: current_format.setForeground(Qt::black); break;
|
||||
case 31: current_format.setForeground(Qt::red); break;
|
||||
case 32: current_format.setForeground(Qt::darkGreen); break;
|
||||
case 33: current_format.setForeground(Qt::darkYellow); break;
|
||||
case 34: current_format.setForeground(Qt::darkBlue); break;
|
||||
case 35: current_format.setForeground(Qt::darkMagenta); break;
|
||||
case 36: current_format.setForeground(Qt::darkCyan); break;
|
||||
case 37: current_format.setForeground(Qt::lightGray); break;
|
||||
case 39: current_format.setForeground(m_foreground_color); break;
|
||||
case 90: current_format.setForeground(Qt::darkGray); break;
|
||||
case 91: current_format.setForeground(Qt::red); break;
|
||||
case 92: current_format.setForeground(Qt::green); break;
|
||||
case 93: current_format.setForeground(Qt::yellow); break;
|
||||
case 94: current_format.setForeground(Qt::blue); break;
|
||||
case 95: current_format.setForeground(Qt::magenta); break;
|
||||
case 96: current_format.setForeground(Qt::cyan); break;
|
||||
case 97: current_format.setForeground(Qt::white); break;
|
||||
// Background and extended colors not yet handled
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pos = start + length;
|
||||
}
|
||||
|
||||
// Apply remaining format
|
||||
if (pos < text.length())
|
||||
setFormat(pos, text.length() - pos, current_format);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QRegularExpression>
|
||||
#include <QBrush>
|
||||
|
||||
// Inspired by https://doc.qt.io/qt-5/qtwidgets-richtext-syntaxhighlighter-example.html
|
||||
|
||||
|
|
@ -10,11 +11,11 @@ class Highlighter : public QSyntaxHighlighter
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Highlighter(QTextDocument *parent = nullptr);
|
||||
explicit Highlighter(QTextDocument* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) override;
|
||||
void addRule(const QString &pattern, const QBrush &brush);
|
||||
void highlightBlock(const QString& text) override;
|
||||
void addRule(const QString& pattern, const QBrush& brush);
|
||||
|
||||
struct HighlightingRule
|
||||
{
|
||||
|
|
@ -42,7 +43,7 @@ class AsmHighlighter : public Highlighter
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AsmHighlighter(QTextDocument *parent = nullptr);
|
||||
explicit AsmHighlighter(QTextDocument* parent = nullptr);
|
||||
};
|
||||
|
||||
class GlslHighlighter : public Highlighter
|
||||
|
|
@ -50,5 +51,22 @@ class GlslHighlighter : public Highlighter
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GlslHighlighter(QTextDocument *parent = nullptr);
|
||||
explicit GlslHighlighter(QTextDocument* parent = nullptr);
|
||||
};
|
||||
|
||||
class AnsiHighlighter : public Highlighter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AnsiHighlighter(QTextDocument* parent = nullptr);
|
||||
|
||||
protected:
|
||||
const QRegularExpression ansi_re = QRegularExpression("\x1b\\[[0-9;]*m");
|
||||
const QRegularExpression param_re = QRegularExpression("\x1b\\[([0-9;]*)m");
|
||||
|
||||
QTextCharFormat m_escape_format;
|
||||
QColor m_foreground_color;
|
||||
|
||||
void highlightBlock(const QString& text) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -649,9 +649,9 @@ void trophy_manager_dialog::ResizeGameIcons()
|
|||
{
|
||||
const qreal dpr = devicePixelRatioF();
|
||||
const int trophy_index = item->data(GameUserRole::GameIndex).toInt();
|
||||
const QString icon_path = QString::fromStdString(m_trophies_db[trophy_index]->path);
|
||||
QString trophy_icon_path = QString::fromStdString(m_trophies_db[trophy_index]->path);
|
||||
|
||||
item->set_icon_load_func([this, icon_path, localized_icon, trophy_index, cancel = item->icon_loading_aborted(), dpr](int index)
|
||||
item->set_icon_load_func([this, icon_path = std::move(trophy_icon_path), localized_icon, trophy_index, cancel = item->icon_loading_aborted(), dpr](int index)
|
||||
{
|
||||
if (cancel && cancel->load())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -83,12 +83,12 @@ welcome_dialog::welcome_dialog(std::shared_ptr<gui_settings> gui_settings, bool
|
|||
{
|
||||
if (ui->create_desktop_shortcut->isChecked())
|
||||
{
|
||||
gui::utils::create_shortcut("RPCS3", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), gui::utils::shortcut_location::desktop);
|
||||
gui::utils::create_shortcut("RPCS3", "", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), gui::utils::shortcut_location::desktop);
|
||||
}
|
||||
|
||||
if (ui->create_applications_menu_shortcut->isChecked())
|
||||
{
|
||||
gui::utils::create_shortcut("RPCS3", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), gui::utils::shortcut_location::applications);
|
||||
gui::utils::create_shortcut("RPCS3", "", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), gui::utils::shortcut_location::applications);
|
||||
}
|
||||
|
||||
if (ui->use_dark_theme->isChecked() && ui->use_dark_theme->isEnabled()) // if checked and also on initial welcome dialog
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static" version="1.8.1.7" targetFramework="native" />
|
||||
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static" version="1.8.1.8" targetFramework="native" />
|
||||
</packages>
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" Condition="'$(GTestInstalled)' == 'true'">
|
||||
<!-- Use path instead of GTestPath variable to fix property pages -->
|
||||
<Import Project="$(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.targets" />
|
||||
<Import Project="$(GTestPath)" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
|
|
|||
|
|
@ -347,9 +347,22 @@ namespace utils
|
|||
{
|
||||
if (!codec) return false;
|
||||
|
||||
for (const AVSampleFormat* p = codec->sample_fmts; p && *p != AV_SAMPLE_FMT_NONE; p++)
|
||||
const void* sample_formats = nullptr;
|
||||
int num = 0;
|
||||
|
||||
if (const int err = avcodec_get_supported_config(nullptr, codec, AVCodecConfig::AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, &sample_formats, &num))
|
||||
{
|
||||
if (*p == sample_fmt)
|
||||
media_log.error("check_sample_fmt: avcodec_get_supported_config error: %d='%s'", err, av_error_to_string(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sample_formats)
|
||||
return true; // All supported
|
||||
|
||||
int i = 0;
|
||||
for (const AVSampleFormat* fmt = static_cast<const AVSampleFormat*>(sample_formats); fmt && *fmt != AV_SAMPLE_FMT_NONE && i < num; fmt++, i++)
|
||||
{
|
||||
if (*fmt == sample_fmt)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -360,18 +373,33 @@ namespace utils
|
|||
// just pick the highest supported samplerate
|
||||
static int select_sample_rate(const AVCodec* codec)
|
||||
{
|
||||
if (!codec || !codec->supported_samplerates)
|
||||
return 48000;
|
||||
constexpr int default_sample_rate = 48000;
|
||||
|
||||
int best_samplerate = 0;
|
||||
for (const int* samplerate = codec->supported_samplerates; samplerate && *samplerate != 0; samplerate++)
|
||||
if (!codec)
|
||||
return default_sample_rate;
|
||||
|
||||
const void* sample_rates = nullptr;
|
||||
int num = 0;
|
||||
|
||||
if (const int err = avcodec_get_supported_config(nullptr, codec, AVCodecConfig::AV_CODEC_CONFIG_SAMPLE_RATE, 0, &sample_rates, &num))
|
||||
{
|
||||
if (!best_samplerate || abs(48000 - *samplerate) < abs(48000 - best_samplerate))
|
||||
media_log.error("select_sample_rate: avcodec_get_supported_config error: %d='%s'", err, av_error_to_string(err));
|
||||
return default_sample_rate;
|
||||
}
|
||||
|
||||
if (!sample_rates)
|
||||
return default_sample_rate;
|
||||
|
||||
int i = 0;
|
||||
int best_sample_rate = 0;
|
||||
for (const int* sample_rate = static_cast<const int*>(sample_rates); sample_rate && *sample_rate != 0 && i < num; sample_rate++, i++)
|
||||
{
|
||||
if (!best_sample_rate || abs(default_sample_rate - *sample_rate) < abs(default_sample_rate - best_sample_rate))
|
||||
{
|
||||
best_samplerate = *samplerate;
|
||||
best_sample_rate = *sample_rate;
|
||||
}
|
||||
}
|
||||
return best_samplerate;
|
||||
return best_sample_rate;
|
||||
}
|
||||
|
||||
AVChannelLayout get_preferred_channel_layout(int channels)
|
||||
|
|
@ -397,12 +425,25 @@ namespace utils
|
|||
{
|
||||
if (!codec) return nullptr;
|
||||
|
||||
const void* ch_layouts = nullptr;
|
||||
int num = 0;
|
||||
|
||||
if (const int err = avcodec_get_supported_config(nullptr, codec, AVCodecConfig::AV_CODEC_CONFIG_CHANNEL_LAYOUT, 0, &ch_layouts, &num))
|
||||
{
|
||||
media_log.error("select_channel_layout: avcodec_get_supported_config error: %d='%s'", err, av_error_to_string(err));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ch_layouts)
|
||||
return nullptr;
|
||||
|
||||
const AVChannelLayout preferred_ch_layout = get_preferred_channel_layout(channels);
|
||||
const AVChannelLayout* found_ch_layout = nullptr;
|
||||
|
||||
for (const AVChannelLayout* ch_layout = codec->ch_layouts;
|
||||
ch_layout && memcmp(ch_layout, &empty_ch_layout, sizeof(AVChannelLayout)) != 0;
|
||||
ch_layout++)
|
||||
int i = 0;
|
||||
for (const AVChannelLayout* ch_layout = static_cast<const AVChannelLayout*>(ch_layouts);
|
||||
i < num && ch_layout && memcmp(ch_layout, &empty_ch_layout, sizeof(AVChannelLayout)) != 0;
|
||||
ch_layout++, i++)
|
||||
{
|
||||
media_log.notice("select_channel_layout: listing channel layout '%s' with %d channels", channel_layout_name(*ch_layout), ch_layout->nb_channels);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue