jdlo modifications
Some checks failed
Check Strings / check-strings (push) Has been cancelled
tx-src / sources (push) Has been cancelled

This commit is contained in:
2025-12-26 13:28:47 -03:00
parent cfae726289
commit b5c86787ab
7 changed files with 519 additions and 15 deletions

View File

@@ -17,8 +17,6 @@ add_library(core STATIC
constants.h
core.cpp
core.h
game_settings.cpp
game_settings.h
core_timing.cpp
core_timing.h
cpu_manager.cpp
@@ -45,7 +43,11 @@ add_library(core STATIC
device_memory.cpp
device_memory.h
device_memory_manager.h
device_memory.h
device_memory_manager.h
device_memory_manager.inc
internal_network/legacy_online.cpp
internal_network/legacy_online.h
file_sys/bis_factory.cpp
file_sys/bis_factory.h
file_sys/card_image.cpp

View File

@@ -49,6 +49,7 @@
#include "core/hle/service/services.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/internal_network/legacy_online.h"
#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -137,6 +138,13 @@ struct System::Impl {
kernel.SetMulticore(is_multicore);
cpu_manager.SetMulticore(is_multicore);
cpu_manager.SetAsyncGpu(is_async_gpu);
cpu_manager.SetMulticore(is_multicore);
cpu_manager.SetAsyncGpu(is_async_gpu);
if (!legacy_online) {
legacy_online = std::make_unique<Network::LegacyOnlineService>();
legacy_online->Start();
}
}
void ReinitializeIfNecessary(System& system) {
@@ -293,6 +301,48 @@ struct System::Impl {
return SystemResultStatus::Success;
}
void LoadOverrides(u64 programId) const {
std::string vendor = gpu_core->Renderer().GetDeviceVendor();
LOG_INFO(Core, "GPU Vendor: {}", vendor);
// Reset all per-game flags
Settings::values.use_squashed_iterated_blend = false;
// Insert PC overrides here
#ifdef ANDROID
// Example on how to set a setting based on the program ID and vendor
if (programId == 0x010028600EBDA000 && vendor == "Mali") { // Mario 3d World
// Settings::values.example = true;
}
// Example array of program IDs
const std::array<u64, 10> example_array = {
//0xprogramId
0x0004000000033400, // Game 1
0x0004000000033500 // Game 2
// And so on
};
for (auto id : example_array) {
if (programId == id) {
// Settings::values.example = true;
break;
}
}
#endif
// Ninja Gaiden Ragebound
constexpr u64 ngr = 0x0100781020710000ULL;
if (programId == ngr) {
LOG_INFO(Core, "Enabling game specifc override: use_squashed_iterated_blend");
Settings::values.use_squashed_iterated_blend = true;
}
}
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath,
Service::AM::FrontendAppletParameters& params) {
@@ -378,8 +428,7 @@ struct System::Impl {
LOG_ERROR(Core, "Failed to find program id for ROM");
}
GameSettings::LoadOverrides(program_id, gpu_core->Renderer());
LoadOverrides(program_id);
if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info;
game_info.name = name;
@@ -428,6 +477,11 @@ struct System::Impl {
stop_event = {};
Network::RestartSocketOperations();
if (legacy_online) {
legacy_online->Stop();
legacy_online.reset();
}
if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info{};
room_member->SendGameInfo(game_info);
@@ -517,6 +571,9 @@ struct System::Impl {
/// Network instance
Network::NetworkInstance network_instance;
/// Legacy Online Service
std::unique_ptr<Network::LegacyOnlineService> legacy_online;
/// Debugger
std::unique_ptr<Core::Debugger> debugger;

View File

@@ -11,6 +11,8 @@
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/hle/service/filesystem/filesystem.h"
namespace FileSys {
@@ -74,20 +76,97 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
u64 target_program_id = meta.program_id;
// CRITICAL FIX: If the game requests Cache/Temp with ProgramID 0 (generic),
// we MUST redirect it to the actual running TitleID, otherwise it looks in '.../0000000000000000'.
if ((meta.type == SaveDataType::Cache || meta.type == SaveDataType::Temporary) && target_program_id == 0) {
target_program_id = system.GetApplicationProcessProgramID();
LOG_INFO(Service_FS, "Redirecting generic Cache request (ID 0) to active TitleID: {:016X}", target_program_id);
}
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, target_program_id,
meta.user_id, meta.system_save_data_id);
auto out = dir->GetDirectoryRelative(save_directory);
// Fallback for Ryujinx-style cache paths: sdcard/Nintendo/save/{TitleID} (No /cache/ subdir)
// Also include Temporary, as games like Just Dance use Temporary storage for cache-like data (MapBaseCache),
// and Ryujinx stores this in the same simple 'save/{ID}' structure.
// Fallback logic removed. We now enforce standard paths.
// User instructions will direct them to the correct folder.
if (out == nullptr) {
LOG_WARNING(Service_FS, "Cache/Save path NOT FOUND: '{}'. Auto-create={}", save_directory, auto_create);
} else {
LOG_INFO(Service_FS, "Cache/Save path FOUND: '{}'", save_directory);
}
if (out == nullptr && (ShouldSaveDataBeAutomaticallyCreated(space, meta) && auto_create)) {
LOG_INFO(Service_FS, "Auto-creating save directory...");
return Create(space, meta);
}
if (out != nullptr) {
// Some emulators (Ryujinx) or even different firmware versions may rely on the commit
// directories /0 or /1 being present for cache or save data.
// We prioritizing /1 as it usually implies a newer commit if both exist,
// but /0 is what's commonly used by Ryujinx for cache.
// Ryujinx behavior: If 0 exists and 1 does not, copy 0 to 1.
auto dir_0 = out->GetSubdirectory("0");
auto dir_1 = out->GetSubdirectory("1");
if (dir_0) LOG_INFO(Service_FS, "Found subdirectory '0' in save path.");
if (dir_1) LOG_INFO(Service_FS, "Found subdirectory '1' in save path.");
if (dir_0 != nullptr && dir_1 == nullptr) {
LOG_INFO(Service_FS, "Ryujinx structure detected: '0' exists, '1' missing. Copying 0->1 for compatibility.");
dir_1 = out->CreateSubdirectory("1");
if (dir_1 != nullptr) {
// Copy contents from 0 to 1
VfsRawCopyD(dir_0, dir_1);
}
}
// Check for 'Addressables' and 'Addressables2' and delete 'json.cache' if present.
// This is a specific workaround for games (e.g. Just Dance) that freeze if they find old cache metadata.
// We force them to regenerate it.
const auto CleanCache = [](VirtualDir root) {
if (root == nullptr) return;
const char* subdirs[] = {"Addressables", "Addressables2"};
for (const char* subdir_name : subdirs) {
auto subdir = root->GetSubdirectory(subdir_name);
if (subdir != nullptr) {
if (subdir->DeleteFile("json.cache")) {
LOG_INFO(Service_FS, "Deleted stale 'json.cache' in '{}'", subdir_name);
}
}
}
};
if (dir_1 != nullptr) {
LOG_INFO(Service_FS, "Returning subdirectory '1' as Save Root.");
CleanCache(dir_1);
return dir_1;
}
if (dir_0 != nullptr) {
LOG_INFO(Service_FS, "Returning subdirectory '0' as Save Root.");
CleanCache(dir_0);
return dir_0;
}
LOG_INFO(Service_FS, "No '0' or '1' subdirectories found. Returning parent folder as Save Root.");
CleanCache(out);
}
return out;
}
VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
const auto path = GetSaveDataSpaceIdPath(space);
// Ensure the directory exists, otherwise FindAllSaves fails.
return GetOrCreateDirectoryRelative(dir, path);
// return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
}
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
@@ -96,12 +175,12 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
return "/system/";
case SaveDataSpaceId::User:
case SaveDataSpaceId::SdUser:
case SaveDataSpaceId::Temporary: // Map into User so we can find the save/ folder
return "/user/";
case SaveDataSpaceId::Temporary:
return "/temp/";
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
// ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
LOG_WARNING(Service_FS, "Unrecognized SaveDataSpaceId: {:02X}, defaulting to /user/", static_cast<u8>(space));
return "/user/";
}
}
@@ -137,8 +216,9 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
case SaveDataType::Temporary:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
// Unified Cache/Temporary Path: Always use save/cache/{TitleID}
// This simplifies user instructions and avoids UUID/Permission issues.
return fmt::format("{}save/cache/{:016X}", out, title_id);
case SaveDataType::Cache:
return fmt::format("{}save/cache/{:016X}", out, title_id);
default:

View File

@@ -686,6 +686,154 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
using EdenPath = Common::FS::EdenPath;
const auto sdmc_dir_path = Common::FS::GetEdenPath(EdenPath::SDMCDir);
const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
// If the NAND user save location doesn't exist but the SDMC contains
// Nintendo/save (common portable save structure), create a host-side
// symlink so the emulator will see those saves under the expected NAND path.
// This helps users who placed saves under `<eden>/user/sdmc/Nintendo/save/...`.
try {
const auto nand_fs_path = Common::FS::GetEdenPath(EdenPath::NANDDir);
const auto sdmc_fs_path = sdmc_dir_path;
const auto nand_user_save_path = nand_fs_path / "user" / "save";
const auto sdmc_nintendo_save_path = sdmc_fs_path / "Nintendo" / "save";
std::error_code ec;
if (!std::filesystem::exists(nand_user_save_path) &&
std::filesystem::exists(sdmc_nintendo_save_path)) {
std::filesystem::create_directory_symlink(sdmc_nintendo_save_path, nand_user_save_path, ec);
if (ec) {
LOG_WARNING(Service_FS, "Could not create symlink {} -> {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_save_path), ec.message());
// Fallback: copy the SDMC saves into the NAND save folder so the emulator can see them
try {
if (!std::filesystem::exists(nand_user_save_path)) {
std::filesystem::create_directories(nand_user_save_path);
}
std::filesystem::copy(sdmc_nintendo_save_path, nand_user_save_path,
std::filesystem::copy_options::recursive |
std::filesystem::copy_options::skip_existing,
ec);
if (ec) {
LOG_WARNING(Service_FS, "Failed to copy SDMC saves to {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path), ec.message());
} else {
LOG_INFO(Service_FS, "Copied SDMC saves to NAND save path: {} -> {}",
Common::FS::PathToUTF8String(sdmc_nintendo_save_path),
Common::FS::PathToUTF8String(nand_user_save_path));
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while copying SDMC saves: {}", ex.what());
}
} else {
LOG_INFO(Service_FS, "Linked NAND save path to SDMC saves: {} -> {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_save_path));
}
}
// Also support official-style SD cache directory name used by some Switch setups.
// If users placed saves or cache under `<eden>/user/sdmc/Nintendo/SD_Cache.0000`,
// expose it to the NAND user save path as well so the emulator can find them.
const auto sdmc_nintendo_cache_path = sdmc_fs_path / "Nintendo" / "SD_Cache.0000";
if (!std::filesystem::exists(nand_user_save_path) &&
std::filesystem::exists(sdmc_nintendo_cache_path)) {
std::error_code ec2;
std::filesystem::create_directory_symlink(sdmc_nintendo_cache_path, nand_user_save_path,
ec2);
if (ec2) {
LOG_WARNING(Service_FS,
"Could not create symlink {} -> {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_cache_path), ec2.message());
// Fallback: copy the SDMC cache into the NAND save folder so the emulator can see them
try {
if (!std::filesystem::exists(nand_user_save_path)) {
std::filesystem::create_directories(nand_user_save_path);
}
std::filesystem::copy(sdmc_nintendo_cache_path, nand_user_save_path,
std::filesystem::copy_options::recursive |
std::filesystem::copy_options::skip_existing,
ec2);
if (ec2) {
LOG_WARNING(Service_FS, "Failed to copy SDMC cache to {}: {}",
Common::FS::PathToUTF8String(nand_user_save_path), ec2.message());
} else {
LOG_INFO(Service_FS, "Copied SDMC cache to NAND save path: {} -> {}",
Common::FS::PathToUTF8String(sdmc_nintendo_cache_path),
Common::FS::PathToUTF8String(nand_user_save_path));
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while copying SDMC cache: {}", ex.what());
}
} else {
LOG_INFO(Service_FS, "Linked NAND save path to SDMC cache: {} -> {}",
Common::FS::PathToUTF8String(nand_user_save_path),
Common::FS::PathToUTF8String(sdmc_nintendo_cache_path));
}
}
// If the NAND save folder already exists, ensure individual entries from
// SDMC (both `Nintendo/save` and `Nintendo/SD_Cache.0000`) are visible
// inside it: create per-entry symlinks (with copy fallback) for missing
// title/account directories so saves placed in SDMC are reachable.
auto try_merge_sdmc_entries = [&](const std::filesystem::path& sdmc_src) {
try {
if (!std::filesystem::exists(sdmc_src))
return;
std::error_code ec;
if (!std::filesystem::exists(nand_user_save_path))
std::filesystem::create_directories(nand_user_save_path, ec);
for (auto& ent : std::filesystem::directory_iterator(sdmc_src)) {
const auto name = ent.path().filename();
const auto target = nand_user_save_path / name;
if (std::filesystem::exists(target))
continue;
std::error_code ec2;
std::filesystem::create_directory_symlink(ent.path(), target, ec2);
if (ec2) {
LOG_WARNING(Service_FS, "Could not create symlink {} -> {}: {}",
Common::FS::PathToUTF8String(target),
Common::FS::PathToUTF8String(ent.path()), ec2.message());
try {
std::filesystem::copy(ent.path(), target,
std::filesystem::copy_options::recursive |
std::filesystem::copy_options::skip_existing,
ec2);
if (ec2) {
LOG_WARNING(Service_FS, "Failed to copy {} -> {}: {}",
Common::FS::PathToUTF8String(ent.path()),
Common::FS::PathToUTF8String(target), ec2.message());
} else {
LOG_INFO(Service_FS, "Copied SDMC entry to NAND: {} -> {}",
Common::FS::PathToUTF8String(ent.path()),
Common::FS::PathToUTF8String(target));
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while copying SDMC entry: {}",
ex.what());
}
} else {
LOG_INFO(Service_FS, "Linked NAND entry to SDMC: {} -> {}",
Common::FS::PathToUTF8String(target),
Common::FS::PathToUTF8String(ent.path()));
}
}
} catch (const std::exception& ex) {
LOG_WARNING(Service_FS, "Exception while merging SDMC entries: {}", ex.what());
}
};
try_merge_sdmc_entries(sdmc_nintendo_save_path);
try_merge_sdmc_entries(sdmc_nintendo_cache_path);
} catch (const std::exception& e) {
LOG_WARNING(Service_FS, "Exception while linking SDMC saves: {}", e.what());
}
const auto rw_mode = FileSys::OpenMode::ReadWrite;
auto nand_directory =

View File

@@ -0,0 +1,164 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/internal_network/legacy_online.h"
#include <cstring>
#include <iostream>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include "common/logging/log.h"
namespace Network {
LegacyOnlineService::LegacyOnlineService() = default;
LegacyOnlineService::~LegacyOnlineService() {
Stop();
}
void LegacyOnlineService::Start() {
if (is_running) {
return;
}
is_running = true;
worker_thread = std::thread(&LegacyOnlineService::ServerLoop, this);
}
void LegacyOnlineService::Stop() {
if (!is_running) {
return;
}
is_running = false;
// Close socket to wake up the thread if it's blocked on recvfrom
if (socket_fd != ~0ULL) {
#ifdef _WIN32
closesocket(static_cast<SOCKET>(socket_fd));
#else
close(static_cast<int>(socket_fd));
#endif
socket_fd = ~0ULL;
}
if (worker_thread.joinable()) {
worker_thread.join();
}
}
void LegacyOnlineService::ServerLoop() {
LOG_INFO(Network, "Starting Legacy Online UDP Server on port {}", PORT);
#ifdef _WIN32
WSADATA wsa_data;
if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
LOG_ERROR(Network, "WSAStartup failed");
is_running = false;
return;
}
#endif
auto s = socket(AF_INET, SOCK_DGRAM, 0);
#ifdef _WIN32
if (s == INVALID_SOCKET) {
#else
if (s == -1) {
#endif
LOG_ERROR(Network, "Failed to create socket");
is_running = false;
return;
}
socket_fd = static_cast<uintptr_t>(s);
int opt = 1;
#ifdef _WIN32
setsockopt(static_cast<SOCKET>(socket_fd), SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
#else
setsockopt(static_cast<int>(socket_fd), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
int res = -1;
#ifdef _WIN32
res = bind(static_cast<SOCKET>(socket_fd), (sockaddr*)&server_addr, sizeof(server_addr));
#else
res = bind(static_cast<int>(socket_fd), (sockaddr*)&server_addr, sizeof(server_addr));
#endif
if (res < 0) {
LOG_ERROR(Network, "Failed to bind to port {}", PORT);
#ifdef _WIN32
closesocket(static_cast<SOCKET>(socket_fd));
#else
close(static_cast<int>(socket_fd));
#endif
socket_fd = ~0ULL;
is_running = false;
return;
}
LOG_INFO(Network, "Legacy Online Server waiting for messages...");
// Set a timeout for recvfrom so check is_running periodically if not closed via socket
// Alternatively, closing the socket (as done in Stop) will cause recvfrom to return error
char buffer[2048];
while (is_running) {
sockaddr_in client_addr{};
#ifdef _WIN32
int client_len = sizeof(client_addr);
#else
socklen_t client_len = sizeof(client_addr);
#endif
int len = -1;
#ifdef _WIN32
len = recvfrom(static_cast<SOCKET>(socket_fd), buffer, sizeof(buffer), 0, (sockaddr*)&client_addr, &client_len);
#else
len = recvfrom(static_cast<int>(socket_fd), buffer, sizeof(buffer), 0, (sockaddr*)&client_addr, &client_len);
#endif
if (!is_running) break;
if (len > 0) {
// Send ACK
const char* ack_msg = "ACK";
#ifdef _WIN32
sendto(static_cast<SOCKET>(socket_fd), ack_msg, static_cast<int>(strlen(ack_msg)), 0, (sockaddr*)&client_addr, client_len);
#else
sendto(static_cast<int>(socket_fd), ack_msg, strlen(ack_msg), 0, (sockaddr*)&client_addr, client_len);
#endif
} else {
// Error or closed
// If we closed the socket in Stop(), this will likely trigger.
break;
}
}
#ifdef _WIN32
if (socket_fd != ~0ULL) closesocket(static_cast<SOCKET>(socket_fd));
WSACleanup();
#else
if (socket_fd != ~0ULL) close(static_cast<int>(socket_fd));
#endif
socket_fd = ~0ULL;
LOG_INFO(Network, "Legacy Online Server stopped");
}
} // namespace Network

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <cstdint>
#include <memory>
#include <thread>
namespace Network {
class LegacyOnlineService {
public:
LegacyOnlineService();
~LegacyOnlineService();
void Start();
void Stop();
private:
void ServerLoop();
std::atomic_bool is_running{false};
std::thread worker_thread;
uintptr_t socket_fd{~0ULL}; // ~0ULL is approx -1 equivalent for unsigned
static constexpr int PORT = 6000;
};
} // namespace Network

View File

@@ -107,9 +107,19 @@ void Vic::Execute() {
auto output_height{config.output_surface_config.out_surface_height + 1};
output_surface.resize_destructive(output_width * output_height);
// Initialize the surface with the appropriate black pixel
Pixel black_pixel{};
if (config.output_surface_config.out_pixel_format == VideoPixelFormat::Y8__V8U8_N420) {
// Y=0, U=512, V=512 (10-bit), A=0
black_pixel = {0, 512, 512, 0};
} else {
// R=0, G=0, B=0, A=0
black_pixel = {0, 0, 0, 0};
}
std::fill(output_surface.begin(), output_surface.end(), black_pixel);
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Off) [[unlikely]] {
// Fill the frame with black, as otherwise they can have random data and be very glitchy.
std::fill(output_surface.begin(), output_surface.end(), Pixel{});
} else {
for (size_t i = 0; i < config.slot_structs.size(); i++) {
auto& slot_config{config.slot_structs[i]};
@@ -122,7 +132,18 @@ void Vic::Execute() {
nvdec_id = frame_queue.VicFindNvdecFdFromOffset(luma_offset);
}
if (auto frame = frame_queue.GetFrame(nvdec_id, luma_offset); frame) {
auto frame = frame_queue.GetFrame(nvdec_id, luma_offset);
if (!frame) {
// We might've failed to find the frame, or the nvdec id is stale/wrong.
// Try to find the nvdec id again.
const s32 new_id = frame_queue.VicFindNvdecFdFromOffset(luma_offset);
if (new_id != -1) {
nvdec_id = new_id;
frame = frame_queue.GetFrame(nvdec_id, luma_offset);
}
}
if (frame) {
if (frame.get()) {
switch (frame->GetPixelFormat()) {
case AV_PIX_FMT_YUV420P:
@@ -170,6 +191,7 @@ void Vic::ReadProgressiveY8__V8U8_N420(const SlotStruct& slot, std::span<const P
}
slot_surface.resize_destructive(out_luma_width * out_luma_height);
std::fill(slot_surface.begin(), slot_surface.end(), Pixel{0, 512, 512, 0});
const auto in_luma_width{(std::min)(frame->GetWidth(), s32(out_luma_width))};
const auto in_luma_height{(std::min)(frame->GetHeight(), s32(out_luma_height))};
@@ -219,6 +241,7 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span<const Pl
const auto out_luma_stride{out_luma_width};
slot_surface.resize_destructive(out_luma_width * out_luma_height);
std::fill(slot_surface.begin(), slot_surface.end(), Pixel{0, 512, 512, 0});
const auto in_luma_width{(std::min)(frame->GetWidth(), s32(out_luma_width))};
[[maybe_unused]] const auto in_luma_height{