System: Fix triple popup on missing BIOS

This commit is contained in:
Stenzek 2024-05-12 23:58:36 +10:00
parent be920acf38
commit 318fd0b0fd
No known key found for this signature in database
3 changed files with 75 additions and 82 deletions

View file

@ -1,16 +1,20 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "bios.h"
#include "cpu_disasm.h"
#include "host.h"
#include "settings.h"
#include "common/assert.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/md5_digest.h"
#include "common/path.h"
#include "cpu_disasm.h"
#include "host.h"
#include "settings.h"
#include <cerrno>
Log_SetChannel(BIOS);
static constexpr BIOS::Hash MakeHashFromString(const char str[])
@ -164,13 +168,13 @@ BIOS::Hash BIOS::GetImageHash(const BIOS::Image& image)
return hash;
}
std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename)
std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename, Error* error)
{
Image ret(BIOS_SIZE);
auto fp = FileSystem::OpenManagedCFile(filename, "rb");
auto fp = FileSystem::OpenManagedCFile(filename, "rb", error);
if (!fp)
{
Log_ErrorPrintf("Failed to open BIOS image '%s', errno=%d", filename, errno);
Error::AddPrefixFmt(error, "Failed to open BIOS '{}': ", Path::GetFileName(filename));
return std::nullopt;
}
@ -180,14 +184,15 @@ std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename)
if (size != BIOS_SIZE && size != BIOS_SIZE_PS2 && size != BIOS_SIZE_PS3)
{
Log_ErrorPrintf("BIOS image '%s' size mismatch, expecting either %u or %u or %u bytes but got %u bytes", filename,
BIOS_SIZE, BIOS_SIZE_PS2, BIOS_SIZE_PS3, size);
Error::SetStringFmt(error, "BIOS image '{}' size mismatch, expecting either {} or {} bytes but got {} bytes",
Path::GetFileName(filename), static_cast<unsigned>(BIOS_SIZE),
static_cast<unsigned>(BIOS_SIZE_PS2), size);
return std::nullopt;
}
if (std::fread(ret.data(), 1, ret.size(), fp.get()) != ret.size())
{
Log_ErrorPrintf("Failed to read BIOS image '%s'", filename);
Error::SetErrno(error, TinyString::from_format("Failed to read BIOS '{}': ", Path::GetFileName(filename)), errno);
return std::nullopt;
}
@ -326,7 +331,7 @@ DiscRegion BIOS::GetPSExeDiscRegion(const PSEXEHeader& header)
return DiscRegion::Other;
}
std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region, Error* error)
{
std::string bios_name;
switch (region)
@ -345,31 +350,37 @@ std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
break;
}
std::optional<Image> image;
if (bios_name.empty())
{
// auto-detect
return FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str());
image = FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str(), error);
}
// try the configured path
std::optional<Image> image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str());
if (!image.has_value())
else
{
Host::ReportFormattedErrorAsync("Error", TRANSLATE("HostInterface", "Failed to load configured BIOS file '%s'"),
bios_name.c_str());
return std::nullopt;
// try the configured path
image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str(), error);
}
const ImageInfo* ii = GetInfoForImage(image.value());
if (!ii || !IsValidBIOSForRegion(region, ii->region))
Log_WarningPrintf("BIOS '%s' does not match region. This may cause issues.", bios_name.c_str());
// verify region
if (image.has_value())
{
const ImageInfo* ii = GetInfoForImage(image.value());
if (!ii || !IsValidBIOSForRegion(region, ii->region))
{
Log_WarningFmt("BIOS region {} does not match requested region {}. This may cause issues.",
ii ? Settings::GetConsoleRegionName(ii->region) : "UNKNOWN",
Settings::GetConsoleRegionName(region));
}
}
return image;
}
std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory)
std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory, Error* error)
{
Log_InfoPrintf("Searching for a %s BIOS in '%s'...", Settings::GetConsoleRegionDisplayName(region), directory);
Log_InfoFmt("Searching for a {} BIOS in '{}'...", Settings::GetConsoleRegionName(region), directory);
FileSystem::FindResultsArray results;
FileSystem::FindFiles(
@ -383,20 +394,21 @@ std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion regi
{
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
{
Log_WarningPrintf("Skipping '%s': incorrect size", fd.FileName.c_str());
Log_WarningFmt("Skipping '{}': incorrect size", fd.FileName.c_str());
continue;
}
std::string full_path(Path::Combine(directory, fd.FileName));
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str(), nullptr);
if (!found_image)
continue;
const ImageInfo* ii = GetInfoForImage(found_image.value());
if (ii && IsValidBIOSForRegion(region, ii->region))
{
Log_InfoPrintf("Using BIOS '%s': %s", fd.FileName.c_str(), ii->description);
return found_image;
Log_InfoFmt("Using BIOS '{}': {}", fd.FileName.c_str(), ii->description);
fallback_image = std::move(found_image);
return fallback_image;
}
// don't let an unknown bios take precedence over a known one
@ -410,53 +422,24 @@ std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion regi
if (!fallback_image.has_value())
{
Host::ReportFormattedErrorAsync("Error", TRANSLATE("HostInterface", "No BIOS image found for %s region"),
Settings::GetConsoleRegionDisplayName(region));
return std::nullopt;
Error::SetStringFmt(error, TRANSLATE_FS("System", "No BIOS image found for {} region."),
Settings::GetConsoleRegionName(region));
return fallback_image;
}
if (!fallback_info)
{
Log_WarningPrintf("Using unknown BIOS '%s'. This may crash.", fallback_path.c_str());
Log_WarningFmt("Using unknown BIOS '{}'. This may crash.", Path::GetFileName(fallback_path));
}
else
{
Log_WarningPrintf("Falling back to possibly-incompatible image '%s': %s", fallback_path.c_str(),
fallback_info->description);
Log_WarningFmt("Falling back to possibly-incompatible image '{}': {}", Path::GetFileName(fallback_path),
fallback_info->description);
}
return fallback_image;
}
std::string BIOS::FindBIOSPathWithHash(const char* directory, const Hash& hash)
{
FileSystem::FindResultsArray files;
FileSystem::FindFiles(directory, "*",
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &files);
std::string ret;
for (FILESYSTEM_FIND_DATA& fd : files)
{
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
continue;
std::string full_path(Path::Combine(directory, fd.FileName));
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
if (!found_image)
continue;
const BIOS::Hash found_hash = GetImageHash(found_image.value());
if (found_hash == hash)
{
ret = std::move(full_path);
break;
}
}
return ret;
}
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImagesInDirectory(const char* directory)
{
std::vector<std::pair<std::string, const ImageInfo*>> results;
@ -471,7 +454,7 @@ std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImages
continue;
std::string full_path(Path::Combine(directory, fd.FileName));
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str(), nullptr);
if (!found_image)
continue;
@ -484,5 +467,5 @@ std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImages
bool BIOS::HasAnyBIOSImages()
{
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str()).has_value();
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str(), nullptr).has_value();
}

View file

@ -1,13 +1,17 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>.
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>.
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "types.h"
#include <optional>
#include <string>
#include <string_view>
#include <vector>
class Error;
namespace BIOS {
enum : u32
{
@ -58,7 +62,7 @@ struct PSEXEHeader
static_assert(sizeof(PSEXEHeader) == 0x800);
#pragma pack(pop)
std::optional<Image> LoadImageFromFile(const char* filename);
std::optional<Image> LoadImageFromFile(const char* filename, Error* error);
Hash GetImageHash(const Image& image);
const ImageInfo* GetInfoForImage(const Image& image);
@ -74,14 +78,11 @@ bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size);
DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header);
/// Loads the BIOS image for the specified region.
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region, Error* error);
/// Searches for a BIOS image for the specified region in the specified directory. If no match is found, the first
/// BIOS image within 512KB and 4MB will be used.
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory);
/// Returns a BIOS image which matches the specified hash.
std::string FindBIOSPathWithHash(const char* directory, const BIOS::Hash& hash);
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory, Error* error);
/// Returns a list of filenames and descriptions for BIOS images in a directory.
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);

View file

@ -102,7 +102,7 @@ static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirec
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
std::vector<u8>* out_executable_data);
static bool LoadBIOS(const std::string& override_bios_path);
static bool LoadBIOS(const std::string& override_bios_path, Error* error);
static void InternalReset();
static void ClearRunningGame();
static void DestroySystem();
@ -1465,7 +1465,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
}
// Load BIOS image.
if (!LoadBIOS(parameters.override_bios))
if (!LoadBIOS(parameters.override_bios, error))
{
s_state = State::Shutdown;
ClearRunningGame();
@ -2251,20 +2251,29 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
return !sw.HasError();
}
bool System::LoadBIOS(const std::string& override_bios_path)
bool System::LoadBIOS(const std::string& override_bios_path, Error* error)
{
std::optional<BIOS::Image> bios_image(
override_bios_path.empty() ? BIOS::GetBIOSImage(s_region) : FileSystem::ReadBinaryFile(override_bios_path.c_str()));
if (!bios_image.has_value())
std::optional<BIOS::Image> bios_image;
if (!override_bios_path.empty())
{
Host::ReportFormattedErrorAsync("Error", TRANSLATE("System", "Failed to load %s BIOS."),
Settings::GetConsoleRegionName(s_region));
return false;
bios_image = FileSystem::ReadBinaryFile(override_bios_path.c_str(), error);
if (!bios_image.has_value())
{
Error::AddPrefixFmt(error, TRANSLATE_FS("System", "Failed to load {} BIOS."),
Settings::GetConsoleRegionName(s_region));
return false;
}
}
else
{
bios_image = BIOS::GetBIOSImage(s_region, error);
if (!bios_image.has_value())
return false;
}
if (bios_image->size() != static_cast<u32>(Bus::BIOS_SIZE))
{
Host::ReportFormattedErrorAsync("Error", TRANSLATE("System", "Incorrect BIOS image size"));
Error::SetStringView(error, TRANSLATE_SV("System", "Incorrect BIOS image size"));
return false;
}