mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
System: Fix triple popup on missing BIOS
This commit is contained in:
parent
be920acf38
commit
318fd0b0fd
|
@ -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)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "bios.h"
|
#include "bios.h"
|
||||||
|
#include "cpu_disasm.h"
|
||||||
|
#include "host.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/md5_digest.h"
|
#include "common/md5_digest.h"
|
||||||
#include "common/path.h"
|
#include "common/path.h"
|
||||||
#include "cpu_disasm.h"
|
|
||||||
#include "host.h"
|
|
||||||
#include "settings.h"
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
Log_SetChannel(BIOS);
|
Log_SetChannel(BIOS);
|
||||||
|
|
||||||
static constexpr BIOS::Hash MakeHashFromString(const char str[])
|
static constexpr BIOS::Hash MakeHashFromString(const char str[])
|
||||||
|
@ -164,13 +168,13 @@ BIOS::Hash BIOS::GetImageHash(const BIOS::Image& image)
|
||||||
return hash;
|
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);
|
Image ret(BIOS_SIZE);
|
||||||
auto fp = FileSystem::OpenManagedCFile(filename, "rb");
|
auto fp = FileSystem::OpenManagedCFile(filename, "rb", error);
|
||||||
if (!fp)
|
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;
|
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)
|
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,
|
Error::SetStringFmt(error, "BIOS image '{}' size mismatch, expecting either {} or {} bytes but got {} bytes",
|
||||||
BIOS_SIZE, BIOS_SIZE_PS2, BIOS_SIZE_PS3, size);
|
Path::GetFileName(filename), static_cast<unsigned>(BIOS_SIZE),
|
||||||
|
static_cast<unsigned>(BIOS_SIZE_PS2), size);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::fread(ret.data(), 1, ret.size(), fp.get()) != ret.size())
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +331,7 @@ DiscRegion BIOS::GetPSExeDiscRegion(const PSEXEHeader& header)
|
||||||
return DiscRegion::Other;
|
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;
|
std::string bios_name;
|
||||||
switch (region)
|
switch (region)
|
||||||
|
@ -345,31 +350,37 @@ std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Image> image;
|
||||||
|
|
||||||
if (bios_name.empty())
|
if (bios_name.empty())
|
||||||
{
|
{
|
||||||
// auto-detect
|
// auto-detect
|
||||||
return FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str());
|
image = FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str(), error);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// try the configured path
|
|
||||||
std::optional<Image> image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str());
|
|
||||||
if (!image.has_value())
|
|
||||||
{
|
{
|
||||||
Host::ReportFormattedErrorAsync("Error", TRANSLATE("HostInterface", "Failed to load configured BIOS file '%s'"),
|
// try the configured path
|
||||||
bios_name.c_str());
|
image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str(), error);
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify region
|
||||||
|
if (image.has_value())
|
||||||
|
{
|
||||||
const ImageInfo* ii = GetInfoForImage(image.value());
|
const ImageInfo* ii = GetInfoForImage(image.value());
|
||||||
if (!ii || !IsValidBIOSForRegion(region, ii->region))
|
if (!ii || !IsValidBIOSForRegion(region, ii->region))
|
||||||
Log_WarningPrintf("BIOS '%s' does not match region. This may cause issues.", bios_name.c_str());
|
{
|
||||||
|
Log_WarningFmt("BIOS region {} does not match requested region {}. This may cause issues.",
|
||||||
|
ii ? Settings::GetConsoleRegionName(ii->region) : "UNKNOWN",
|
||||||
|
Settings::GetConsoleRegionName(region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return image;
|
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::FindResultsArray results;
|
||||||
FileSystem::FindFiles(
|
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)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string full_path(Path::Combine(directory, fd.FileName));
|
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)
|
if (!found_image)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ImageInfo* ii = GetInfoForImage(found_image.value());
|
const ImageInfo* ii = GetInfoForImage(found_image.value());
|
||||||
if (ii && IsValidBIOSForRegion(region, ii->region))
|
if (ii && IsValidBIOSForRegion(region, ii->region))
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("Using BIOS '%s': %s", fd.FileName.c_str(), ii->description);
|
Log_InfoFmt("Using BIOS '{}': {}", fd.FileName.c_str(), ii->description);
|
||||||
return found_image;
|
fallback_image = std::move(found_image);
|
||||||
|
return fallback_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't let an unknown bios take precedence over a known one
|
// 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())
|
if (!fallback_image.has_value())
|
||||||
{
|
{
|
||||||
Host::ReportFormattedErrorAsync("Error", TRANSLATE("HostInterface", "No BIOS image found for %s region"),
|
Error::SetStringFmt(error, TRANSLATE_FS("System", "No BIOS image found for {} region."),
|
||||||
Settings::GetConsoleRegionDisplayName(region));
|
Settings::GetConsoleRegionName(region));
|
||||||
return std::nullopt;
|
return fallback_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fallback_info)
|
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
|
else
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("Falling back to possibly-incompatible image '%s': %s", fallback_path.c_str(),
|
Log_WarningFmt("Falling back to possibly-incompatible image '{}': {}", Path::GetFileName(fallback_path),
|
||||||
fallback_info->description);
|
fallback_info->description);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fallback_image;
|
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 BIOS::ImageInfo*>> BIOS::FindBIOSImagesInDirectory(const char* directory)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::string, const ImageInfo*>> results;
|
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;
|
continue;
|
||||||
|
|
||||||
std::string full_path(Path::Combine(directory, fd.FileName));
|
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)
|
if (!found_image)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -484,5 +467,5 @@ std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImages
|
||||||
|
|
||||||
bool BIOS::HasAnyBIOSImages()
|
bool BIOS::HasAnyBIOSImages()
|
||||||
{
|
{
|
||||||
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str()).has_value();
|
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str(), nullptr).has_value();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
namespace BIOS {
|
namespace BIOS {
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
|
@ -58,7 +62,7 @@ struct PSEXEHeader
|
||||||
static_assert(sizeof(PSEXEHeader) == 0x800);
|
static_assert(sizeof(PSEXEHeader) == 0x800);
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
std::optional<Image> LoadImageFromFile(const char* filename);
|
std::optional<Image> LoadImageFromFile(const char* filename, Error* error);
|
||||||
Hash GetImageHash(const Image& image);
|
Hash GetImageHash(const Image& image);
|
||||||
|
|
||||||
const ImageInfo* GetInfoForImage(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);
|
DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header);
|
||||||
|
|
||||||
/// Loads the BIOS image for the specified region.
|
/// 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
|
/// 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.
|
/// BIOS image within 512KB and 4MB will be used.
|
||||||
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory);
|
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory, Error* error);
|
||||||
|
|
||||||
/// Returns a BIOS image which matches the specified hash.
|
|
||||||
std::string FindBIOSPathWithHash(const char* directory, const BIOS::Hash& hash);
|
|
||||||
|
|
||||||
/// Returns a list of filenames and descriptions for BIOS images in a directory.
|
/// 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);
|
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);
|
||||||
|
|
|
@ -102,7 +102,7 @@ static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirec
|
||||||
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
|
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
|
||||||
std::vector<u8>* out_executable_data);
|
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 InternalReset();
|
||||||
static void ClearRunningGame();
|
static void ClearRunningGame();
|
||||||
static void DestroySystem();
|
static void DestroySystem();
|
||||||
|
@ -1465,7 +1465,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load BIOS image.
|
// Load BIOS image.
|
||||||
if (!LoadBIOS(parameters.override_bios))
|
if (!LoadBIOS(parameters.override_bios, error))
|
||||||
{
|
{
|
||||||
s_state = State::Shutdown;
|
s_state = State::Shutdown;
|
||||||
ClearRunningGame();
|
ClearRunningGame();
|
||||||
|
@ -2251,20 +2251,29 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
|
||||||
return !sw.HasError();
|
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(
|
std::optional<BIOS::Image> bios_image;
|
||||||
override_bios_path.empty() ? BIOS::GetBIOSImage(s_region) : FileSystem::ReadBinaryFile(override_bios_path.c_str()));
|
if (!override_bios_path.empty())
|
||||||
|
{
|
||||||
|
bios_image = FileSystem::ReadBinaryFile(override_bios_path.c_str(), error);
|
||||||
if (!bios_image.has_value())
|
if (!bios_image.has_value())
|
||||||
{
|
{
|
||||||
Host::ReportFormattedErrorAsync("Error", TRANSLATE("System", "Failed to load %s BIOS."),
|
Error::AddPrefixFmt(error, TRANSLATE_FS("System", "Failed to load {} BIOS."),
|
||||||
Settings::GetConsoleRegionName(s_region));
|
Settings::GetConsoleRegionName(s_region));
|
||||||
return false;
|
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))
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue