diff options
| -rw-r--r-- | .ci/scripts/common/pre-upload.sh | 2 | ||||
| -rwxr-xr-x | .ci/scripts/linux/docker.sh | 15 | ||||
| -rwxr-xr-x | .ci/scripts/linux/upload.sh | 5 | ||||
| -rw-r--r-- | externals/nx_tzdb/CMakeLists.txt | 2 | ||||
| m--------- | externals/nx_tzdb/tzdb_to_nx | 0 | ||||
| -rw-r--r-- | src/common/common_funcs.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 16 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_a.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_manager.cpp | 86 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_manager.h | 9 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_types.h | 14 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_u.cpp | 74 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_texture_cache.cpp | 14 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_texture_cache.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_texture_cache.cpp | 44 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_texture_cache.h | 2 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 1 | ||||
| -rw-r--r-- | src/yuzu/util/util.cpp | 81 |
19 files changed, 271 insertions, 106 deletions
diff --git a/.ci/scripts/common/pre-upload.sh b/.ci/scripts/common/pre-upload.sh index 705362a3c..3583f9840 100644 --- a/.ci/scripts/common/pre-upload.sh +++ b/.ci/scripts/common/pre-upload.sh | |||
| @@ -5,6 +5,6 @@ | |||
| 5 | 5 | ||
| 6 | GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" | 6 | GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" |
| 7 | GITREV="`git show -s --format='%h'`" | 7 | GITREV="`git show -s --format='%h'`" |
| 8 | ARTIFACTS_DIR="artifacts" | 8 | ARTIFACTS_DIR="$PWD/artifacts" |
| 9 | 9 | ||
| 10 | mkdir -p "${ARTIFACTS_DIR}/" | 10 | mkdir -p "${ARTIFACTS_DIR}/" |
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index e5d83d4b9..a16577b27 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh | |||
| @@ -11,7 +11,7 @@ ccache -s | |||
| 11 | mkdir build || true && cd build | 11 | mkdir build || true && cd build |
| 12 | cmake .. \ | 12 | cmake .. \ |
| 13 | -DBoost_USE_STATIC_LIBS=ON \ | 13 | -DBoost_USE_STATIC_LIBS=ON \ |
| 14 | -DCMAKE_BUILD_TYPE=Release \ | 14 | -DCMAKE_BUILD_TYPE=RelWithDebInfo \ |
| 15 | -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ | 15 | -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ |
| 16 | -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \ | 16 | -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \ |
| 17 | -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \ | 17 | -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \ |
| @@ -31,6 +31,19 @@ ccache -s | |||
| 31 | 31 | ||
| 32 | ctest -VV -C Release | 32 | ctest -VV -C Release |
| 33 | 33 | ||
| 34 | # Separate debug symbols from specified executables | ||
| 35 | for EXE in yuzu; do | ||
| 36 | EXE_PATH="bin/$EXE" | ||
| 37 | # Copy debug symbols out | ||
| 38 | objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug | ||
| 39 | # Add debug link and strip debug symbols | ||
| 40 | objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out | ||
| 41 | # Overwrite original with stripped copy | ||
| 42 | mv $EXE_PATH.out $EXE_PATH | ||
| 43 | done | ||
| 44 | # Strip debug symbols from all executables | ||
| 45 | find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';' | ||
| 46 | |||
| 34 | DESTDIR="$PWD/AppDir" ninja install | 47 | DESTDIR="$PWD/AppDir" ninja install |
| 35 | rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester | 48 | rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester |
| 36 | 49 | ||
diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh index e0f336427..fbb2d9c1b 100755 --- a/.ci/scripts/linux/upload.sh +++ b/.ci/scripts/linux/upload.sh | |||
| @@ -59,4 +59,9 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; | |||
| 59 | cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage" | 59 | cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage" |
| 60 | fi | 60 | fi |
| 61 | 61 | ||
| 62 | # Copy debug symbols to artifacts | ||
| 63 | cd build/bin | ||
| 64 | tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug | ||
| 65 | cd - | ||
| 66 | |||
| 62 | . .ci/scripts/common/post-upload.sh | 67 | . .ci/scripts/common/post-upload.sh |
diff --git a/externals/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt index 593786250..0fad24642 100644 --- a/externals/nx_tzdb/CMakeLists.txt +++ b/externals/nx_tzdb/CMakeLists.txt | |||
| @@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID) | |||
| 27 | set(CAN_BUILD_NX_TZDB false) | 27 | set(CAN_BUILD_NX_TZDB false) |
| 28 | endif() | 28 | endif() |
| 29 | 29 | ||
| 30 | set(NX_TZDB_VERSION "220816") | 30 | set(NX_TZDB_VERSION "221202") |
| 31 | set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip") | 31 | set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip") |
| 32 | 32 | ||
| 33 | set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") | 33 | set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") |
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx | |||
| Subproject 212afa2394a74226dcf1b7996a570aae17debb6 | Subproject 0d17dd066d91f954a4c89d46dcb067eead6b1e4 | ||
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 0dad9338a..47d028d48 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -39,8 +39,12 @@ | |||
| 39 | #define Crash() exit(1) | 39 | #define Crash() exit(1) |
| 40 | #endif | 40 | #endif |
| 41 | 41 | ||
| 42 | #define LTO_NOINLINE __attribute__((noinline)) | ||
| 43 | |||
| 42 | #else // _MSC_VER | 44 | #else // _MSC_VER |
| 43 | 45 | ||
| 46 | #define LTO_NOINLINE | ||
| 47 | |||
| 44 | // Locale Cross-Compatibility | 48 | // Locale Cross-Compatibility |
| 45 | #define locale_t _locale_t | 49 | #define locale_t _locale_t |
| 46 | 50 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index a1134b7e2..cb025c3d6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -373,7 +373,7 @@ struct KernelCore::Impl { | |||
| 373 | static inline thread_local u8 host_thread_id = UINT8_MAX; | 373 | static inline thread_local u8 host_thread_id = UINT8_MAX; |
| 374 | 374 | ||
| 375 | /// Sets the host thread ID for the caller. | 375 | /// Sets the host thread ID for the caller. |
| 376 | u32 SetHostThreadId(std::size_t core_id) { | 376 | LTO_NOINLINE u32 SetHostThreadId(std::size_t core_id) { |
| 377 | // This should only be called during core init. | 377 | // This should only be called during core init. |
| 378 | ASSERT(host_thread_id == UINT8_MAX); | 378 | ASSERT(host_thread_id == UINT8_MAX); |
| 379 | 379 | ||
| @@ -384,13 +384,13 @@ struct KernelCore::Impl { | |||
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | /// Gets the host thread ID for the caller | 386 | /// Gets the host thread ID for the caller |
| 387 | u32 GetHostThreadId() const { | 387 | LTO_NOINLINE u32 GetHostThreadId() const { |
| 388 | return host_thread_id; | 388 | return host_thread_id; |
| 389 | } | 389 | } |
| 390 | 390 | ||
| 391 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time | 391 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time |
| 392 | KThread* GetHostDummyThread(KThread* existing_thread) { | 392 | LTO_NOINLINE KThread* GetHostDummyThread(KThread* existing_thread) { |
| 393 | const auto initialize{[](KThread* thread) { | 393 | const auto initialize{[](KThread* thread) LTO_NOINLINE { |
| 394 | ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); | 394 | ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); |
| 395 | return thread; | 395 | return thread; |
| 396 | }}; | 396 | }}; |
| @@ -424,11 +424,11 @@ struct KernelCore::Impl { | |||
| 424 | 424 | ||
| 425 | static inline thread_local bool is_phantom_mode_for_singlecore{false}; | 425 | static inline thread_local bool is_phantom_mode_for_singlecore{false}; |
| 426 | 426 | ||
| 427 | bool IsPhantomModeForSingleCore() const { | 427 | LTO_NOINLINE bool IsPhantomModeForSingleCore() const { |
| 428 | return is_phantom_mode_for_singlecore; | 428 | return is_phantom_mode_for_singlecore; |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | void SetIsPhantomModeForSingleCore(bool value) { | 431 | LTO_NOINLINE void SetIsPhantomModeForSingleCore(bool value) { |
| 432 | ASSERT(!is_multicore); | 432 | ASSERT(!is_multicore); |
| 433 | is_phantom_mode_for_singlecore = value; | 433 | is_phantom_mode_for_singlecore = value; |
| 434 | } | 434 | } |
| @@ -439,14 +439,14 @@ struct KernelCore::Impl { | |||
| 439 | 439 | ||
| 440 | static inline thread_local KThread* current_thread{nullptr}; | 440 | static inline thread_local KThread* current_thread{nullptr}; |
| 441 | 441 | ||
| 442 | KThread* GetCurrentEmuThread() { | 442 | LTO_NOINLINE KThread* GetCurrentEmuThread() { |
| 443 | if (!current_thread) { | 443 | if (!current_thread) { |
| 444 | current_thread = GetHostDummyThread(nullptr); | 444 | current_thread = GetHostDummyThread(nullptr); |
| 445 | } | 445 | } |
| 446 | return current_thread; | 446 | return current_thread; |
| 447 | } | 447 | } |
| 448 | 448 | ||
| 449 | void SetCurrentEmuThread(KThread* thread) { | 449 | LTO_NOINLINE void SetCurrentEmuThread(KThread* thread) { |
| 450 | current_thread = thread; | 450 | current_thread = thread; |
| 451 | } | 451 | } |
| 452 | 452 | ||
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 286f9fd10..31dd98140 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp | |||
| @@ -16,7 +16,7 @@ namespace Service::Capture { | |||
| 16 | 16 | ||
| 17 | void LoopProcess(Core::System& system) { | 17 | void LoopProcess(Core::System& system) { |
| 18 | auto server_manager = std::make_unique<ServerManager>(system); | 18 | auto server_manager = std::make_unique<ServerManager>(system); |
| 19 | auto album_manager = std::make_shared<AlbumManager>(); | 19 | auto album_manager = std::make_shared<AlbumManager>(system); |
| 20 | 20 | ||
| 21 | server_manager->RegisterNamedService( | 21 | server_manager->RegisterNamedService( |
| 22 | "caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager)); | 22 | "caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager)); |
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index e22f72bf6..9925720a3 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp | |||
| @@ -128,9 +128,9 @@ void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) { | |||
| 128 | ctx.WriteBuffer(entries); | 128 | ctx.WriteBuffer(entries); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | IPC::ResponseBuilder rb{ctx, 3}; | 131 | IPC::ResponseBuilder rb{ctx, 4}; |
| 132 | rb.Push(result); | 132 | rb.Push(result); |
| 133 | rb.Push(entries.size()); | 133 | rb.Push<u64>(entries.size()); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { | 136 | void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 2df6a930a..2b4e3f076 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp | |||
| @@ -8,12 +8,15 @@ | |||
| 8 | #include "common/fs/file.h" | 8 | #include "common/fs/file.h" |
| 9 | #include "common/fs/path_util.h" | 9 | #include "common/fs/path_util.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/core.h" | ||
| 11 | #include "core/hle/service/caps/caps_manager.h" | 12 | #include "core/hle/service/caps/caps_manager.h" |
| 12 | #include "core/hle/service/caps/caps_result.h" | 13 | #include "core/hle/service/caps/caps_result.h" |
| 14 | #include "core/hle/service/time/time_manager.h" | ||
| 15 | #include "core/hle/service/time/time_zone_content_manager.h" | ||
| 13 | 16 | ||
| 14 | namespace Service::Capture { | 17 | namespace Service::Capture { |
| 15 | 18 | ||
| 16 | AlbumManager::AlbumManager() {} | 19 | AlbumManager::AlbumManager(Core::System& system_) : system{system_} {} |
| 17 | 20 | ||
| 18 | AlbumManager::~AlbumManager() = default; | 21 | AlbumManager::~AlbumManager() = default; |
| 19 | 22 | ||
| @@ -83,6 +86,34 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu | |||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, | 88 | Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, |
| 89 | ContentType contex_type, s64 start_posix_time, | ||
| 90 | s64 end_posix_time, u64 aruid) const { | ||
| 91 | if (!is_mounted) { | ||
| 92 | return ResultIsNotMounted; | ||
| 93 | } | ||
| 94 | |||
| 95 | std::vector<ApplicationAlbumEntry> album_entries; | ||
| 96 | const auto start_date = ConvertToAlbumDateTime(start_posix_time); | ||
| 97 | const auto end_date = ConvertToAlbumDateTime(end_posix_time); | ||
| 98 | const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid); | ||
| 99 | |||
| 100 | if (result.IsError()) { | ||
| 101 | return result; | ||
| 102 | } | ||
| 103 | |||
| 104 | for (const auto& album_entry : album_entries) { | ||
| 105 | ApplicationAlbumFileEntry entry{ | ||
| 106 | .entry = album_entry, | ||
| 107 | .datetime = album_entry.datetime, | ||
| 108 | .unknown = {}, | ||
| 109 | }; | ||
| 110 | out_entries.push_back(entry); | ||
| 111 | } | ||
| 112 | |||
| 113 | return ResultSuccess; | ||
| 114 | } | ||
| 115 | |||
| 116 | Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, | ||
| 86 | ContentType contex_type, AlbumFileDateTime start_date, | 117 | ContentType contex_type, AlbumFileDateTime start_date, |
| 87 | AlbumFileDateTime end_date, u64 aruid) const { | 118 | AlbumFileDateTime end_date, u64 aruid) const { |
| 88 | if (!is_mounted) { | 119 | if (!is_mounted) { |
| @@ -93,31 +124,25 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou | |||
| 93 | if (file_id.type != contex_type) { | 124 | if (file_id.type != contex_type) { |
| 94 | continue; | 125 | continue; |
| 95 | } | 126 | } |
| 96 | |||
| 97 | if (file_id.date > start_date) { | 127 | if (file_id.date > start_date) { |
| 98 | continue; | 128 | continue; |
| 99 | } | 129 | } |
| 100 | |||
| 101 | if (file_id.date < end_date) { | 130 | if (file_id.date < end_date) { |
| 102 | continue; | 131 | continue; |
| 103 | } | 132 | } |
| 104 | |||
| 105 | if (out_entries.size() >= SdAlbumFileLimit) { | 133 | if (out_entries.size() >= SdAlbumFileLimit) { |
| 106 | break; | 134 | break; |
| 107 | } | 135 | } |
| 108 | 136 | ||
| 109 | const auto entry_size = Common::FS::GetSize(path); | 137 | const auto entry_size = Common::FS::GetSize(path); |
| 110 | ApplicationAlbumFileEntry entry{.entry = | 138 | ApplicationAlbumEntry entry{ |
| 111 | { | 139 | .size = entry_size, |
| 112 | .size = entry_size, | 140 | .hash{}, |
| 113 | .hash{}, | 141 | .datetime = file_id.date, |
| 114 | .datetime = file_id.date, | 142 | .storage = file_id.storage, |
| 115 | .storage = file_id.storage, | 143 | .content = contex_type, |
| 116 | .content = contex_type, | 144 | .unknown = 1, |
| 117 | .unknown = 1, | 145 | }; |
| 118 | }, | ||
| 119 | .datetime = file_id.date, | ||
| 120 | .unknown = {}}; | ||
| 121 | out_entries.push_back(entry); | 146 | out_entries.push_back(entry); |
| 122 | } | 147 | } |
| 123 | 148 | ||
| @@ -274,12 +299,12 @@ Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem: | |||
| 274 | .application_id = static_cast<u64>(std::stoll(application, 0, 16)), | 299 | .application_id = static_cast<u64>(std::stoll(application, 0, 16)), |
| 275 | .date = | 300 | .date = |
| 276 | { | 301 | { |
| 277 | .year = static_cast<u16>(std::stoi(year)), | 302 | .year = static_cast<s16>(std::stoi(year)), |
| 278 | .month = static_cast<u8>(std::stoi(month)), | 303 | .month = static_cast<s8>(std::stoi(month)), |
| 279 | .day = static_cast<u8>(std::stoi(day)), | 304 | .day = static_cast<s8>(std::stoi(day)), |
| 280 | .hour = static_cast<u8>(std::stoi(hour)), | 305 | .hour = static_cast<s8>(std::stoi(hour)), |
| 281 | .minute = static_cast<u8>(std::stoi(minute)), | 306 | .minute = static_cast<s8>(std::stoi(minute)), |
| 282 | .second = static_cast<u8>(std::stoi(second)), | 307 | .second = static_cast<s8>(std::stoi(second)), |
| 283 | .unique_id = 0, | 308 | .unique_id = 0, |
| 284 | }, | 309 | }, |
| 285 | .storage = AlbumStorage::Sd, | 310 | .storage = AlbumStorage::Sd, |
| @@ -339,4 +364,23 @@ Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::p | |||
| 339 | 364 | ||
| 340 | return ResultSuccess; | 365 | return ResultSuccess; |
| 341 | } | 366 | } |
| 367 | |||
| 368 | AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { | ||
| 369 | Time::TimeZone::CalendarInfo calendar_date{}; | ||
| 370 | const auto& time_zone_manager = | ||
| 371 | system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); | ||
| 372 | |||
| 373 | time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); | ||
| 374 | |||
| 375 | return { | ||
| 376 | .year = calendar_date.time.year, | ||
| 377 | .month = calendar_date.time.month, | ||
| 378 | .day = calendar_date.time.day, | ||
| 379 | .hour = calendar_date.time.hour, | ||
| 380 | .minute = calendar_date.time.minute, | ||
| 381 | .second = calendar_date.time.second, | ||
| 382 | .unique_id = 0, | ||
| 383 | }; | ||
| 384 | } | ||
| 385 | |||
| 342 | } // namespace Service::Capture | 386 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h index 8337c655c..f65eb12c1 100644 --- a/src/core/hle/service/caps/caps_manager.h +++ b/src/core/hle/service/caps/caps_manager.h | |||
| @@ -37,7 +37,7 @@ namespace Service::Capture { | |||
| 37 | 37 | ||
| 38 | class AlbumManager { | 38 | class AlbumManager { |
| 39 | public: | 39 | public: |
| 40 | explicit AlbumManager(); | 40 | explicit AlbumManager(Core::System& system_); |
| 41 | ~AlbumManager(); | 41 | ~AlbumManager(); |
| 42 | 42 | ||
| 43 | Result DeleteAlbumFile(const AlbumFileId& file_id); | 43 | Result DeleteAlbumFile(const AlbumFileId& file_id); |
| @@ -45,6 +45,9 @@ public: | |||
| 45 | Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, | 45 | Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, |
| 46 | u8 flags) const; | 46 | u8 flags) const; |
| 47 | Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, | 47 | Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, |
| 48 | ContentType contex_type, s64 start_posix_time, s64 end_posix_time, | ||
| 49 | u64 aruid) const; | ||
| 50 | Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, | ||
| 48 | ContentType contex_type, AlbumFileDateTime start_date, | 51 | ContentType contex_type, AlbumFileDateTime start_date, |
| 49 | AlbumFileDateTime end_date, u64 aruid) const; | 52 | AlbumFileDateTime end_date, u64 aruid) const; |
| 50 | Result GetAutoSavingStorage(bool& out_is_autosaving) const; | 53 | Result GetAutoSavingStorage(bool& out_is_autosaving) const; |
| @@ -65,8 +68,12 @@ private: | |||
| 65 | Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width, | 68 | Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width, |
| 66 | int height, ScreenShotDecoderFlag flag) const; | 69 | int height, ScreenShotDecoderFlag flag) const; |
| 67 | 70 | ||
| 71 | AlbumFileDateTime ConvertToAlbumDateTime(u64 posix_time) const; | ||
| 72 | |||
| 68 | bool is_mounted{}; | 73 | bool is_mounted{}; |
| 69 | std::unordered_map<AlbumFileId, std::filesystem::path> album_files; | 74 | std::unordered_map<AlbumFileId, std::filesystem::path> album_files; |
| 75 | |||
| 76 | Core::System& system; | ||
| 70 | }; | 77 | }; |
| 71 | 78 | ||
| 72 | } // namespace Service::Capture | 79 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_types.h b/src/core/hle/service/caps/caps_types.h index bf6061273..7fd357954 100644 --- a/src/core/hle/service/caps/caps_types.h +++ b/src/core/hle/service/caps/caps_types.h | |||
| @@ -41,13 +41,13 @@ enum class ScreenShotDecoderFlag : u64 { | |||
| 41 | 41 | ||
| 42 | // This is nn::capsrv::AlbumFileDateTime | 42 | // This is nn::capsrv::AlbumFileDateTime |
| 43 | struct AlbumFileDateTime { | 43 | struct AlbumFileDateTime { |
| 44 | u16 year{}; | 44 | s16 year{}; |
| 45 | u8 month{}; | 45 | s8 month{}; |
| 46 | u8 day{}; | 46 | s8 day{}; |
| 47 | u8 hour{}; | 47 | s8 hour{}; |
| 48 | u8 minute{}; | 48 | s8 minute{}; |
| 49 | u8 second{}; | 49 | s8 second{}; |
| 50 | u8 unique_id{}; | 50 | s8 unique_id{}; |
| 51 | 51 | ||
| 52 | friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default; | 52 | friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default; |
| 53 | friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) { | 53 | friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) { |
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 260f25490..b6b33fb2f 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp | |||
| @@ -50,22 +50,35 @@ void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { | |||
| 50 | 50 | ||
| 51 | void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) { | 51 | void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) { |
| 52 | IPC::RequestParser rp{ctx}; | 52 | IPC::RequestParser rp{ctx}; |
| 53 | const auto pid{rp.Pop<s32>()}; | 53 | struct Parameters { |
| 54 | const auto content_type{rp.PopEnum<ContentType>()}; | 54 | ContentType content_type; |
| 55 | const auto start_posix_time{rp.Pop<s64>()}; | 55 | INSERT_PADDING_BYTES(7); |
| 56 | const auto end_posix_time{rp.Pop<s64>()}; | 56 | s64 start_posix_time; |
| 57 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 57 | s64 end_posix_time; |
| 58 | u64 applet_resource_user_id; | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 61 | |||
| 62 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 58 | 63 | ||
| 59 | LOG_WARNING(Service_Capture, | 64 | LOG_WARNING(Service_Capture, |
| 60 | "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " | 65 | "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, " |
| 61 | "end_posix_time={}, applet_resource_user_id={}", | 66 | "applet_resource_user_id={}", |
| 62 | pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id); | 67 | parameters.content_type, parameters.start_posix_time, parameters.end_posix_time, |
| 68 | parameters.applet_resource_user_id); | ||
| 63 | 69 | ||
| 64 | // TODO: Translate posix to DateTime | 70 | Result result = ResultSuccess; |
| 71 | |||
| 72 | if (result.IsSuccess()) { | ||
| 73 | result = manager->IsAlbumMounted(AlbumStorage::Sd); | ||
| 74 | } | ||
| 65 | 75 | ||
| 66 | std::vector<ApplicationAlbumFileEntry> entries; | 76 | std::vector<ApplicationAlbumFileEntry> entries; |
| 67 | const Result result = | 77 | if (result.IsSuccess()) { |
| 68 | manager->GetAlbumFileList(entries, content_type, {}, {}, applet_resource_user_id); | 78 | result = manager->GetAlbumFileList(entries, parameters.content_type, |
| 79 | parameters.start_posix_time, parameters.end_posix_time, | ||
| 80 | parameters.applet_resource_user_id); | ||
| 81 | } | ||
| 69 | 82 | ||
| 70 | if (!entries.empty()) { | 83 | if (!entries.empty()) { |
| 71 | ctx.WriteBuffer(entries); | 84 | ctx.WriteBuffer(entries); |
| @@ -78,19 +91,38 @@ void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestCo | |||
| 78 | 91 | ||
| 79 | void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { | 92 | void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { |
| 80 | IPC::RequestParser rp{ctx}; | 93 | IPC::RequestParser rp{ctx}; |
| 81 | const auto pid{rp.Pop<s32>()}; | 94 | struct Parameters { |
| 82 | const auto content_type{rp.PopEnum<ContentType>()}; | 95 | ContentType content_type; |
| 83 | const auto start_date_time{rp.PopRaw<AlbumFileDateTime>()}; | 96 | INSERT_PADDING_BYTES(1); |
| 84 | const auto end_date_time{rp.PopRaw<AlbumFileDateTime>()}; | 97 | AlbumFileDateTime start_date_time; |
| 85 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 98 | AlbumFileDateTime end_date_time; |
| 99 | INSERT_PADDING_BYTES(6); | ||
| 100 | u64 applet_resource_user_id; | ||
| 101 | }; | ||
| 102 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 103 | |||
| 104 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 86 | 105 | ||
| 87 | LOG_WARNING(Service_Capture, | 106 | LOG_WARNING(Service_Capture, |
| 88 | "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, | 107 | "(STUBBED) called. content_type={}, start_date={}/{}/{}, " |
| 89 | content_type, applet_resource_user_id); | 108 | "end_date={}/{}/{}, applet_resource_user_id={}", |
| 109 | parameters.content_type, parameters.start_date_time.year, | ||
| 110 | parameters.start_date_time.month, parameters.start_date_time.day, | ||
| 111 | parameters.end_date_time.year, parameters.end_date_time.month, | ||
| 112 | parameters.end_date_time.day, parameters.applet_resource_user_id); | ||
| 90 | 113 | ||
| 91 | std::vector<ApplicationAlbumFileEntry> entries; | 114 | Result result = ResultSuccess; |
| 92 | const Result result = manager->GetAlbumFileList(entries, content_type, start_date_time, | 115 | |
| 93 | end_date_time, applet_resource_user_id); | 116 | if (result.IsSuccess()) { |
| 117 | result = manager->IsAlbumMounted(AlbumStorage::Sd); | ||
| 118 | } | ||
| 119 | |||
| 120 | std::vector<ApplicationAlbumEntry> entries; | ||
| 121 | if (result.IsSuccess()) { | ||
| 122 | result = | ||
| 123 | manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time, | ||
| 124 | parameters.end_date_time, parameters.applet_resource_user_id); | ||
| 125 | } | ||
| 94 | 126 | ||
| 95 | if (!entries.empty()) { | 127 | if (!entries.empty()) { |
| 96 | ctx.WriteBuffer(entries); | 128 | ctx.WriteBuffer(entries); |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 9cafd2983..512eef575 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -1048,6 +1048,10 @@ void Image::Scale(bool up_scale) { | |||
| 1048 | } | 1048 | } |
| 1049 | 1049 | ||
| 1050 | bool Image::ScaleUp(bool ignore) { | 1050 | bool Image::ScaleUp(bool ignore) { |
| 1051 | const auto& resolution = runtime->resolution; | ||
| 1052 | if (!resolution.active) { | ||
| 1053 | return false; | ||
| 1054 | } | ||
| 1051 | if (True(flags & ImageFlagBits::Rescaled)) { | 1055 | if (True(flags & ImageFlagBits::Rescaled)) { |
| 1052 | return false; | 1056 | return false; |
| 1053 | } | 1057 | } |
| @@ -1060,9 +1064,6 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1060 | return false; | 1064 | return false; |
| 1061 | } | 1065 | } |
| 1062 | flags |= ImageFlagBits::Rescaled; | 1066 | flags |= ImageFlagBits::Rescaled; |
| 1063 | if (!runtime->resolution.active) { | ||
| 1064 | return false; | ||
| 1065 | } | ||
| 1066 | has_scaled = true; | 1067 | has_scaled = true; |
| 1067 | if (ignore) { | 1068 | if (ignore) { |
| 1068 | current_texture = upscaled_backup.handle; | 1069 | current_texture = upscaled_backup.handle; |
| @@ -1073,13 +1074,14 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1073 | } | 1074 | } |
| 1074 | 1075 | ||
| 1075 | bool Image::ScaleDown(bool ignore) { | 1076 | bool Image::ScaleDown(bool ignore) { |
| 1076 | if (False(flags & ImageFlagBits::Rescaled)) { | 1077 | const auto& resolution = runtime->resolution; |
| 1078 | if (!resolution.active) { | ||
| 1077 | return false; | 1079 | return false; |
| 1078 | } | 1080 | } |
| 1079 | flags &= ~ImageFlagBits::Rescaled; | 1081 | if (False(flags & ImageFlagBits::Rescaled)) { |
| 1080 | if (!runtime->resolution.active) { | ||
| 1081 | return false; | 1082 | return false; |
| 1082 | } | 1083 | } |
| 1084 | flags &= ~ImageFlagBits::Rescaled; | ||
| 1083 | if (ignore) { | 1085 | if (ignore) { |
| 1084 | current_texture = texture.handle; | 1086 | current_texture = texture.handle; |
| 1085 | return true; | 1087 | return true; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 3676eaaa9..e71b87e99 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -118,6 +118,8 @@ public: | |||
| 118 | 118 | ||
| 119 | void InsertUploadMemoryBarrier(); | 119 | void InsertUploadMemoryBarrier(); |
| 120 | 120 | ||
| 121 | void TransitionImageLayout(Image& image) {} | ||
| 122 | |||
| 121 | FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; | 123 | FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; |
| 122 | 124 | ||
| 123 | bool HasNativeBgr() const noexcept { | 125 | bool HasNativeBgr() const noexcept { |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 00ab47268..93773a69f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -1530,15 +1530,15 @@ bool Image::IsRescaled() const noexcept { | |||
| 1530 | } | 1530 | } |
| 1531 | 1531 | ||
| 1532 | bool Image::ScaleUp(bool ignore) { | 1532 | bool Image::ScaleUp(bool ignore) { |
| 1533 | const auto& resolution = runtime->resolution; | ||
| 1534 | if (!resolution.active) { | ||
| 1535 | return false; | ||
| 1536 | } | ||
| 1533 | if (True(flags & ImageFlagBits::Rescaled)) { | 1537 | if (True(flags & ImageFlagBits::Rescaled)) { |
| 1534 | return false; | 1538 | return false; |
| 1535 | } | 1539 | } |
| 1536 | ASSERT(info.type != ImageType::Linear); | 1540 | ASSERT(info.type != ImageType::Linear); |
| 1537 | flags |= ImageFlagBits::Rescaled; | 1541 | flags |= ImageFlagBits::Rescaled; |
| 1538 | const auto& resolution = runtime->resolution; | ||
| 1539 | if (!resolution.active) { | ||
| 1540 | return false; | ||
| 1541 | } | ||
| 1542 | has_scaled = true; | 1542 | has_scaled = true; |
| 1543 | if (!scaled_image) { | 1543 | if (!scaled_image) { |
| 1544 | const bool is_2d = info.type == ImageType::e2D; | 1544 | const bool is_2d = info.type == ImageType::e2D; |
| @@ -1567,15 +1567,15 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1567 | } | 1567 | } |
| 1568 | 1568 | ||
| 1569 | bool Image::ScaleDown(bool ignore) { | 1569 | bool Image::ScaleDown(bool ignore) { |
| 1570 | const auto& resolution = runtime->resolution; | ||
| 1571 | if (!resolution.active) { | ||
| 1572 | return false; | ||
| 1573 | } | ||
| 1570 | if (False(flags & ImageFlagBits::Rescaled)) { | 1574 | if (False(flags & ImageFlagBits::Rescaled)) { |
| 1571 | return false; | 1575 | return false; |
| 1572 | } | 1576 | } |
| 1573 | ASSERT(info.type != ImageType::Linear); | 1577 | ASSERT(info.type != ImageType::Linear); |
| 1574 | flags &= ~ImageFlagBits::Rescaled; | 1578 | flags &= ~ImageFlagBits::Rescaled; |
| 1575 | const auto& resolution = runtime->resolution; | ||
| 1576 | if (!resolution.active) { | ||
| 1577 | return false; | ||
| 1578 | } | ||
| 1579 | current_image = *original_image; | 1579 | current_image = *original_image; |
| 1580 | if (ignore) { | 1580 | if (ignore) { |
| 1581 | return true; | 1581 | return true; |
| @@ -2013,4 +2013,32 @@ void TextureCacheRuntime::AccelerateImageUpload( | |||
| 2013 | ASSERT(false); | 2013 | ASSERT(false); |
| 2014 | } | 2014 | } |
| 2015 | 2015 | ||
| 2016 | void TextureCacheRuntime::TransitionImageLayout(Image& image) { | ||
| 2017 | if (!image.ExchangeInitialization()) { | ||
| 2018 | VkImageMemoryBarrier barrier{ | ||
| 2019 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 2020 | .pNext = nullptr, | ||
| 2021 | .srcAccessMask = VK_ACCESS_NONE, | ||
| 2022 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 2023 | .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||
| 2024 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 2025 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 2026 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 2027 | .image = image.Handle(), | ||
| 2028 | .subresourceRange{ | ||
| 2029 | .aspectMask = image.AspectMask(), | ||
| 2030 | .baseMipLevel = 0, | ||
| 2031 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 2032 | .baseArrayLayer = 0, | ||
| 2033 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 2034 | }, | ||
| 2035 | }; | ||
| 2036 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 2037 | scheduler.Record([barrier = barrier](vk::CommandBuffer cmdbuf) { | ||
| 2038 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 2039 | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier); | ||
| 2040 | }); | ||
| 2041 | } | ||
| 2042 | } | ||
| 2043 | |||
| 2016 | } // namespace Vulkan | 2044 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index d6c5a15cc..7a0807709 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -92,6 +92,8 @@ public: | |||
| 92 | 92 | ||
| 93 | void InsertUploadMemoryBarrier() {} | 93 | void InsertUploadMemoryBarrier() {} |
| 94 | 94 | ||
| 95 | void TransitionImageLayout(Image& image); | ||
| 96 | |||
| 95 | bool HasBrokenTextureViewFormats() const noexcept { | 97 | bool HasBrokenTextureViewFormats() const noexcept { |
| 96 | // No known Vulkan driver has broken image views | 98 | // No known Vulkan driver has broken image views |
| 97 | return false; | 99 | return false; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 1bdb0def5..d575c57ca 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -1016,6 +1016,7 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) { | |||
| 1016 | 1016 | ||
| 1017 | if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) { | 1017 | if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) { |
| 1018 | LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented"); | 1018 | LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented"); |
| 1019 | runtime.TransitionImageLayout(image); | ||
| 1019 | return; | 1020 | return; |
| 1020 | } | 1021 | } |
| 1021 | if (True(image.flags & ImageFlagBits::AsynchronousDecode)) { | 1022 | if (True(image.flags & ImageFlagBits::AsynchronousDecode)) { |
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 61cf00176..f2854c8ec 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp | |||
| @@ -63,25 +63,15 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { | |||
| 63 | }; | 63 | }; |
| 64 | #pragma pack(pop) | 64 | #pragma pack(pop) |
| 65 | 65 | ||
| 66 | QImage source_image = image.convertToFormat(QImage::Format_RGB32); | 66 | const QImage source_image = image.convertToFormat(QImage::Format_RGB32); |
| 67 | constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16}; | ||
| 67 | constexpr int bytes_per_pixel = 4; | 68 | constexpr int bytes_per_pixel = 4; |
| 68 | const int image_size = source_image.width() * source_image.height() * bytes_per_pixel; | 69 | |
| 69 | 70 | const IconDir icon_dir{ | |
| 70 | BITMAPINFOHEADER info_header{}; | 71 | .id_reserved = 0, |
| 71 | info_header.biSize = sizeof(BITMAPINFOHEADER), info_header.biWidth = source_image.width(), | 72 | .id_type = 1, |
| 72 | info_header.biHeight = source_image.height() * 2, info_header.biPlanes = 1, | 73 | .id_count = static_cast<WORD>(scale_sizes.size()), |
| 73 | info_header.biBitCount = bytes_per_pixel * 8, info_header.biCompression = BI_RGB; | 74 | }; |
| 74 | |||
| 75 | const IconDir icon_dir{.id_reserved = 0, .id_type = 1, .id_count = 1}; | ||
| 76 | const IconDirEntry icon_entry{.width = static_cast<BYTE>(source_image.width()), | ||
| 77 | .height = static_cast<BYTE>(source_image.height() * 2), | ||
| 78 | .color_count = 0, | ||
| 79 | .reserved = 0, | ||
| 80 | .planes = 1, | ||
| 81 | .bit_count = bytes_per_pixel * 8, | ||
| 82 | .bytes_in_res = | ||
| 83 | static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), | ||
| 84 | .image_offset = sizeof(IconDir) + sizeof(IconDirEntry)}; | ||
| 85 | 75 | ||
| 86 | Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, | 76 | Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, |
| 87 | Common::FS::FileType::BinaryFile); | 77 | Common::FS::FileType::BinaryFile); |
| @@ -92,20 +82,55 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { | |||
| 92 | if (!icon_file.Write(icon_dir)) { | 82 | if (!icon_file.Write(icon_dir)) { |
| 93 | return false; | 83 | return false; |
| 94 | } | 84 | } |
| 95 | if (!icon_file.Write(icon_entry)) { | 85 | |
| 96 | return false; | 86 | std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size()); |
| 97 | } | 87 | for (std::size_t i = 0; i < scale_sizes.size(); i++) { |
| 98 | if (!icon_file.Write(info_header)) { | 88 | const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel; |
| 99 | return false; | 89 | const IconDirEntry icon_entry{ |
| 90 | .width = static_cast<BYTE>(scale_sizes[i]), | ||
| 91 | .height = static_cast<BYTE>(scale_sizes[i]), | ||
| 92 | .color_count = 0, | ||
| 93 | .reserved = 0, | ||
| 94 | .planes = 1, | ||
| 95 | .bit_count = bytes_per_pixel * 8, | ||
| 96 | .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), | ||
| 97 | .image_offset = static_cast<DWORD>(image_offset), | ||
| 98 | }; | ||
| 99 | image_offset += icon_entry.bytes_in_res; | ||
| 100 | if (!icon_file.Write(icon_entry)) { | ||
| 101 | return false; | ||
| 102 | } | ||
| 100 | } | 103 | } |
| 101 | 104 | ||
| 102 | for (int y = 0; y < image.height(); y++) { | 105 | for (std::size_t i = 0; i < scale_sizes.size(); i++) { |
| 103 | const auto* line = source_image.scanLine(source_image.height() - 1 - y); | 106 | const QImage scaled_image = source_image.scaled( |
| 104 | std::vector<u8> line_data(source_image.width() * bytes_per_pixel); | 107 | scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
| 105 | std::memcpy(line_data.data(), line, line_data.size()); | 108 | const BITMAPINFOHEADER info_header{ |
| 106 | if (!icon_file.Write(line_data)) { | 109 | .biSize = sizeof(BITMAPINFOHEADER), |
| 110 | .biWidth = scaled_image.width(), | ||
| 111 | .biHeight = scaled_image.height() * 2, | ||
| 112 | .biPlanes = 1, | ||
| 113 | .biBitCount = bytes_per_pixel * 8, | ||
| 114 | .biCompression = BI_RGB, | ||
| 115 | .biSizeImage{}, | ||
| 116 | .biXPelsPerMeter{}, | ||
| 117 | .biYPelsPerMeter{}, | ||
| 118 | .biClrUsed{}, | ||
| 119 | .biClrImportant{}, | ||
| 120 | }; | ||
| 121 | |||
| 122 | if (!icon_file.Write(info_header)) { | ||
| 107 | return false; | 123 | return false; |
| 108 | } | 124 | } |
| 125 | |||
| 126 | for (int y = 0; y < scaled_image.height(); y++) { | ||
| 127 | const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y); | ||
| 128 | std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel); | ||
| 129 | std::memcpy(line_data.data(), line, line_data.size()); | ||
| 130 | if (!icon_file.Write(line_data)) { | ||
| 131 | return false; | ||
| 132 | } | ||
| 133 | } | ||
| 109 | } | 134 | } |
| 110 | icon_file.Close(); | 135 | icon_file.Close(); |
| 111 | 136 | ||