CDVD: Remove exceptions (completely this time)

The actual reads are still missing error checking...
This commit is contained in:
Stenzek 2022-12-28 22:14:43 +10:00 committed by Connor McLaughlin
parent ab4592b8e9
commit 52266d7ac0
14 changed files with 423 additions and 408 deletions

View File

@ -1357,42 +1357,40 @@ std::string_view Achievements::GetELFNameForHash(const std::string& elf_path)
std::optional<std::vector<u8>> Achievements::ReadELFFromCurrentDisc(const std::string& elf_path)
{
// This CDVD stuff is super nasty and full of exceptions..
std::optional<std::vector<u8>> ret;
try
// ELF suffix hack. Taken from CDVD::loadElf
// MLB2k6 has an elf whose suffix is actually ;2
std::string filepath(elf_path);
const std::string::size_type semi_pos = filepath.rfind(';');
if (semi_pos != std::string::npos && std::string_view(filepath).substr(semi_pos) != ";1")
{
// ELF suffix hack. Taken from CDVD::loadElf
// MLB2k6 has an elf whose suffix is actually ;2
std::string filepath(elf_path);
const std::string::size_type semi_pos = filepath.rfind(';');
if (semi_pos != std::string::npos && std::string_view(filepath).substr(semi_pos) != ";1")
{
Console.Warning("(Achievements) Non-conforming version suffix (%s) detected and replaced.", elf_path.c_str());
filepath.erase(semi_pos);
filepath += ";1";
}
IsoFSCDVD isofs;
IsoFile file(isofs, filepath);
const u32 size = file.getLength();
ret = std::vector<u8>();
ret->resize(size);
if (size > 0)
{
const s32 bytes_read = file.read(ret->data(), static_cast<s32>(size));
if (bytes_read != static_cast<s32>(size))
{
Console.Error("(Achievements) Only read %d of %u bytes of ELF '%s'", bytes_read, size, elf_path.c_str());
ret.reset();
}
}
Console.Warning(fmt::format("(Achievements) Non-conforming version suffix ({}) detected and replaced.", elf_path));
filepath.erase(semi_pos);
filepath += ";1";
}
catch (...)
IsoFSCDVD isofs;
IsoFile file(isofs);
Error error;
if (!file.open(filepath, &error))
{
Console.Error("(Achievements) Caught exception while trying to read ELF '%s'.", elf_path.c_str());
ret.reset();
Console.Error(fmt::format("(Achievements) Failed to open ELF '{}' on disc: {}", elf_path, error.GetDescription()));
return ret;
}
const u32 size = file.getLength();
ret = std::vector<u8>();
ret->resize(size);
if (size > 0)
{
const s32 bytes_read = file.read(ret->data(), static_cast<s32>(size));
if (bytes_read != static_cast<s32>(size))
{
Console.Error(fmt::format("(Achievements) Only read {} of {} bytes of ELF '{}'", bytes_read, size, elf_path));
ret.reset();
}
}
return ret;

View File

@ -383,140 +383,118 @@ s32 cdvdWriteConfig(const u8* config)
return 0;
}
// Sets ElfCRC to the CRC of the game bound to the CDVD source.
std::unique_ptr<ElfObject> cdvdLoadElf(std::string filename, bool isPSXElf)
bool cdvdLoadElf(ElfObject* elfo, std::string elfpath, bool isPSXElf, Error* error)
{
if (StringUtil::StartsWith(filename, "host:"))
if (StringUtil::StartsWith(elfpath, "host:"))
{
std::string host_filename(filename.substr(5));
s64 host_size = FileSystem::GetPathFileSize(host_filename.c_str());
return std::make_unique<ElfObject>(std::move(host_filename), static_cast<u32>(std::max<s64>(host_size, 0)), isPSXElf);
std::string host_filename(elfpath.substr(5));
if (!elfo->OpenFile(host_filename, isPSXElf, error))
return false;
}
else
{
// Mimic PS2 behavior!
// Much trial-and-error with changing the ISOFS and BOOT2 contents of an image have shown that
// the PS2 BIOS performs the peculiar task of *ignoring* the version info from the parsed BOOT2
// filename *and* the ISOFS, when loading the game's ELF image. What this means is:
//
// 1. a valid PS2 ELF can have any version (ISOFS), and the version need not match the one in SYSTEM.CNF.
// 2. the version info on the file in the BOOT2 parameter of SYSTEM.CNF can be missing, 10 chars long,
// or anything else. Its all ignored.
// 3. Games loading their own files do *not* exhibit this behavior; likely due to using newer IOP modules
// or lower level filesystem APIs (fortunately that doesn't affect us).
//
// FIXME: Properly mimicing this behavior is troublesome since we need to add support for "ignoring"
// version information when doing file searches. I'll add this later. For now, assuming a ;1 should
// be sufficient (no known games have their ELF binary as anything but version ;1)
const std::string::size_type semi_pos = elfpath.rfind(';');
if (semi_pos != std::string::npos && std::string_view(elfpath).substr(semi_pos) != ";1")
{
Console.WriteLn(Color_Blue, "(LoadELF) Non-conforming version suffix (%s) detected and replaced.", elfpath.c_str());
elfpath.erase(semi_pos);
elfpath += ";1";
}
// Fix cdrom:path, the iso reader doesn't like it.
if (StringUtil::StartsWith(elfpath, "cdrom:") && elfpath[6] != '\\' && elfpath[6] != '/')
elfpath.insert(6, 1, '\\');
IsoFSCDVD isofs;
IsoFile file(isofs);
if (!file.open(elfpath, error) || !elfo->OpenIsoFile(elfpath, file, isPSXElf, error))
return false;
}
// Mimic PS2 behavior!
// Much trial-and-error with changing the ISOFS and BOOT2 contents of an image have shown that
// the PS2 BIOS performs the peculiar task of *ignoring* the version info from the parsed BOOT2
// filename *and* the ISOFS, when loading the game's ELF image. What this means is:
//
// 1. a valid PS2 ELF can have any version (ISOFS), and the version need not match the one in SYSTEM.CNF.
// 2. the version info on the file in the BOOT2 parameter of SYSTEM.CNF can be missing, 10 chars long,
// or anything else. Its all ignored.
// 3. Games loading their own files do *not* exhibit this behavior; likely due to using newer IOP modules
// or lower level filesystem APIs (fortunately that doesn't affect us).
//
// FIXME: Properly mimicing this behavior is troublesome since we need to add support for "ignoring"
// version information when doing file searches. I'll add this later. For now, assuming a ;1 should
// be sufficient (no known games have their ELF binary as anything but version ;1)
const std::string::size_type semi_pos = filename.rfind(';');
if (semi_pos != std::string::npos && std::string_view(filename).substr(semi_pos) != ";1")
{
Console.WriteLn(Color_Blue, "(LoadELF) Non-conforming version suffix (%s) detected and replaced.", filename.c_str());
filename.erase(semi_pos);
filename += ";1";
}
// Fix cdrom:path, the iso reader doesn't like it.
if (StringUtil::StartsWith(filename, "cdrom:") && filename[6] != '\\' && filename[6] != '/')
filename.insert(6, 1, '\\');
IsoFSCDVD isofs;
IsoFile file(isofs, filename);
return std::make_unique<ElfObject>(std::move(filename), file, isPSXElf);
return true;
}
u32 cdvdGetElfCRC(const std::string& path)
{
try
{
// Yay for write-after-read here. Isn't our ELF parser great....
const s64 host_size = FileSystem::GetPathFileSize(path.c_str());
if (host_size <= 0)
return 0;
std::unique_ptr<ElfObject> elfptr(std::make_unique<ElfObject>(path, static_cast<u32>(std::max<s64>(host_size, 0)), false));
elfptr->loadHeaders();
return elfptr->getCRC();
}
catch ([[maybe_unused]] Exception::FileNotFound& e)
{
ElfObject elfo;
if (!elfo.OpenFile(path, false, nullptr))
return 0;
}
return elfo.GetCRC();
}
// return value:
// 0 - Invalid or unknown disc.
// 1 - PS1 CD
// 2 - PS2 CD
static CDVDDiscType GetPS2ElfName(std::string* name, std::string* version)
{
CDVDDiscType retype = CDVDDiscType::Other;
name->clear();
version->clear();
try {
IsoFSCDVD isofs;
IsoFile file( isofs, "SYSTEM.CNF;1");
Error error;
IsoFSCDVD isofs;
IsoFile file(isofs);
if (!file.open("SYSTEM.CNF;1", &error))
{
Console.Error(fmt::format("(GetElfName) Failed to open SYSTEM.CNF: {}", error.GetDescription()));
return CDVDDiscType::Other;
}
int size = file.getLength();
if( size == 0 ) return CDVDDiscType::Other;
const int size = file.getLength();
if (size == 0)
return CDVDDiscType::Other;
while( !file.eof() )
{
const std::string line(file.readLine());
std::string_view key, value;
if (!StringUtil::ParseAssignmentString(line, &key, &value))
continue;
while (!file.eof())
{
const std::string line(file.readLine());
std::string_view key, value;
if (!StringUtil::ParseAssignmentString(line, &key, &value))
continue;
if( value.empty() && file.getLength() != file.getSeekPos() )
{ // Some games have a character on the last line of the file, don't print the error in those cases.
Console.Warning( "(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:" );
Console.Indent().WriteLn(line);
continue;
}
if( key == "BOOT2" )
{
Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PS2 Disc = %.*s",
static_cast<int>(value.size()), value.data());
*name = value;
retype = CDVDDiscType::PS2Disc;
}
else if( key == "BOOT" )
{
Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PSX/PSone Disc = %.*s",
static_cast<int>(value.size()), value.data());
*name = value;
retype = CDVDDiscType::PS1Disc;
}
else if( key == "VMODE" )
{
Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Disc region type = %.*s",
static_cast<int>(value.size()), value.data());
}
else if( key == "VER" )
{
Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Software version = %.*s",
static_cast<int>(value.size()), value.data());
*version = value;
}
if (value.empty() && file.getLength() != file.getSeekPos())
{ // Some games have a character on the last line of the file, don't print the error in those cases.
Console.Warning("(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:");
Console.Indent().WriteLn(line);
continue;
}
if( retype == CDVDDiscType::Other )
if (key == "BOOT2")
{
Console.Error("(GetElfName) Disc image is *not* a PlayStation or PS2 game!");
return CDVDDiscType::Other;
Console.WriteLn(Color_StrongBlue, fmt::format("(SYSTEM.CNF) Detected PS2 Disc = {}", value));
*name = value;
retype = CDVDDiscType::PS2Disc;
}
else if (key == "BOOT")
{
Console.WriteLn(Color_StrongBlue, fmt::format("(SYSTEM.CNF) Detected PSX/PSone Disc = {}", value));
*name = value;
retype = CDVDDiscType::PS1Disc;
}
else if (key == "VMODE")
{
Console.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Disc region type = {}", value));
}
else if (key == "VER")
{
Console.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Software version = {}", value));
*version = value;
}
}
catch( Exception::FileNotFound& )
{
//Console.Warning(ex.FormatDiagnosticMessage());
return CDVDDiscType::Other; // no SYSTEM.CNF, not a PS1/PS2 disc.
}
catch (Exception::BadStream& ex)
{
Console.Error(ex.FormatDiagnosticMessage());
return CDVDDiscType::Other; // ISO error
}
if (retype == CDVDDiscType::Other)
Console.Error("(GetElfName) Disc image is *not* a PlayStation or PS2 game!");
return retype;
}
@ -586,21 +564,13 @@ void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::st
if (disc_type == CDVDDiscType::PS2Disc || disc_type == CDVDDiscType::PS1Disc)
{
try
{
const bool isPSXElf = (disc_type == CDVDDiscType::PS1Disc);
std::unique_ptr<ElfObject> elfptr(cdvdLoadElf(elfpath, isPSXElf));
elfptr->loadHeaders();
crc = elfptr->getCRC();
}
catch ([[maybe_unused]] Exception::FileNotFound& e)
{
Console.Error(fmt::format("Failed to load ELF info for {}", elfpath));
}
catch (Exception::BadStream& ex)
{
Console.Error(ex.FormatDiagnosticMessage());
}
Error error;
ElfObject elfo;
const bool isPSXElf = (disc_type == CDVDDiscType::PS1Disc);
if (!cdvdLoadElf(&elfo, elfpath, isPSXElf, &error))
Console.Error(fmt::format("Failed to load ELF info for {}: {}", elfpath, error.GetDescription()));
else
crc = elfo.GetCRC();
}
*out_crc = crc;

View File

@ -21,6 +21,7 @@
#include <string>
#include <string_view>
class Error;
class ElfObject;
#define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
@ -189,7 +190,7 @@ extern void cdvdWrite(u8 key, u8 rt);
extern void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
CDVDDiscType* out_disc_type);
extern u32 cdvdGetElfCRC(const std::string& path);
extern std::unique_ptr<ElfObject> cdvdLoadElf(std::string filename, bool isPSXElf);
extern bool cdvdLoadElf(ElfObject* elfo, std::string filename, bool isPSXElf, Error* error);
extern s32 cdvdCtrlTrayOpen();
extern s32 cdvdCtrlTrayClose();

View File

@ -73,14 +73,11 @@ static void CheckNullCDVD()
static int CheckDiskTypeFS(int baseType)
{
IsoFSCDVD isofs;
try
IsoDirectory rootdir(isofs);
if (rootdir.OpenRootDirectory())
{
IsoDirectory rootdir(isofs);
try
if (IsoFile file(isofs); file.open(rootdir, "SYSTEM.CNF;1"))
{
IsoFile file(rootdir, "SYSTEM.CNF;1");
const int size = file.getLength();
const std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size + 1);
file.read(buffer.get(), size);
@ -97,40 +94,16 @@ static int CheckDiskTypeFS(int baseType)
return (baseType == CDVD_TYPE_DETCTCD) ? CDVD_TYPE_PS2CD : CDVD_TYPE_PS2DVD;
}
catch (Exception::FileNotFound&)
{
}
// PS2 Linux disc 2, doesn't have a System.CNF or a normal ELF
try
{
IsoFile file(rootdir, "P2L_0100.02;1");
if (rootdir.Exists("P2L_0100.02;1"))
return CDVD_TYPE_PS2DVD;
}
catch (Exception::FileNotFound&)
{
}
try
{
IsoFile file(rootdir, "PSX.EXE;1");
if (rootdir.Exists("PSX.EXE;1"))
return CDVD_TYPE_PSCD;
}
catch (Exception::FileNotFound&)
{
}
try
{
IsoFile file(rootdir, "VIDEO_TS/VIDEO_TS.IFO;1");
if (rootdir.Exists("VIDEO_TS/VIDEO_TS.IFO;1"))
return CDVD_TYPE_DVDV;
}
catch (Exception::FileNotFound&)
{
}
}
catch (Exception::FileNotFound&)
{
}
#ifdef PCSX2_DEVBUILD

View File

@ -16,26 +16,31 @@
#pragma once
#include "common/Pcsx2Defs.h"
#include <optional>
#include <string_view>
#include <vector>
class Error;
enum IsoFS_Type
{
FStype_ISO9660 = 1,
FStype_Joliet = 2,
};
class IsoDirectory
class IsoDirectory final
{
public:
SectorSource& internalReader;
std::vector<IsoFileDescriptor> files;
IsoFS_Type m_fstype;
IsoFS_Type m_fstype = FStype_ISO9660;
public:
IsoDirectory(SectorSource& r);
IsoDirectory(SectorSource& r, const IsoFileDescriptor& directoryEntry);
virtual ~IsoDirectory() = default;
~IsoDirectory();
bool OpenRootDirectory(Error* error = nullptr);
bool Open(const IsoFileDescriptor& directoryEntry, Error* error = nullptr);
std::string FStype_ToString() const;
SectorSource& GetReader() const { return internalReader; }
@ -46,12 +51,10 @@ public:
u32 GetFileSize(const std::string_view& filePath) const;
IsoFileDescriptor FindFile(const std::string_view& filePath) const;
std::optional<IsoFileDescriptor> FindFile(const std::string_view& filePath) const;
protected:
const IsoFileDescriptor& GetEntry(const std::string_view& fileName) const;
const IsoFileDescriptor& GetEntry(int index) const;
const IsoFileDescriptor& GetEntry(size_t index) const;
void Init(const IsoFileDescriptor& directoryEntry);
int GetIndexOf(const std::string_view& fileName) const;
};

View File

@ -20,7 +20,7 @@
#include "IsoFile.h"
#include "common/Assertions.h"
#include "common/Exceptions.h"
#include "common/Error.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/StringUtil.h"
@ -51,6 +51,12 @@ std::string IsoDirectory::FStype_ToString() const
// Used to load the Root directory from an image
IsoDirectory::IsoDirectory(SectorSource& r)
: internalReader(r)
{
}
IsoDirectory::~IsoDirectory() = default;
bool IsoDirectory::OpenRootDirectory(Error* error /* = nullptr */)
{
IsoFileDescriptor rootDirEntry;
bool isValid = false;
@ -107,22 +113,17 @@ IsoDirectory::IsoDirectory(SectorSource& r)
}
if (!isValid)
throw Exception::FileNotFound("IsoFileSystem") // FIXME: Should report the name of the ISO here...
.SetDiagMsg("IsoFS could not find the root directory on the ISO image.");
{
Error::SetString(error, "IsoFS could not find the root directory on the ISO image.");
return false;
}
DevCon.WriteLn("(IsoFS) Filesystem is %s", FStype_ToString().c_str());
Init(rootDirEntry);
return Open(rootDirEntry);
}
// Used to load a specific directory from a file descriptor
IsoDirectory::IsoDirectory(SectorSource& r, const IsoFileDescriptor& directoryEntry)
: internalReader(r)
{
m_fstype = FStype_ISO9660;
Init(directoryEntry);
}
void IsoDirectory::Init(const IsoFileDescriptor& directoryEntry)
bool IsoDirectory::Open(const IsoFileDescriptor& directoryEntry, Error* error /* = nullptr */)
{
// parse directory sector
IsoFile dataStream(internalReader, directoryEntry);
@ -163,9 +164,10 @@ void IsoDirectory::Init(const IsoFileDescriptor& directoryEntry)
}
b[0] = 0;
return true;
}
const IsoFileDescriptor& IsoDirectory::GetEntry(int index) const
const IsoFileDescriptor& IsoDirectory::GetEntry(size_t index) const
{
return files[index];
}
@ -178,25 +180,19 @@ int IsoDirectory::GetIndexOf(const std::string_view& fileName) const
return i;
}
throw Exception::FileNotFound(std::string(fileName));
return -1;
}
const IsoFileDescriptor& IsoDirectory::GetEntry(const std::string_view& fileName) const
{
return GetEntry(GetIndexOf(fileName));
}
IsoFileDescriptor IsoDirectory::FindFile(const std::string_view& filePath) const
std::optional<IsoFileDescriptor> IsoDirectory::FindFile(const std::string_view& filePath) const
{
if (filePath.empty())
throw Exception::FileNotFound();
return std::nullopt;
// DOS-style parser should work fine for ISO 9660 path names. Only practical difference
// is case sensitivity, and that won't matter for path splitting.
std::vector<std::string_view> parts(Path::SplitWindowsPath(filePath));
IsoFileDescriptor info;
const IsoDirectory* dir = this;
std::unique_ptr<IsoDirectory> deleteme;
IsoDirectory subdir(internalReader);
// walk through path ("." and ".." entries are in the directories themselves, so even if the
// path included . and/or .., it still works)
@ -206,35 +202,67 @@ IsoFileDescriptor IsoDirectory::FindFile(const std::string_view& filePath) const
for (size_t index = has_device ? 1 : 0; index < (parts.size() - 1); index++)
{
info = dir->GetEntry(parts[index]);
if (info.IsFile())
throw Exception::FileNotFound(std::string(filePath));
const int subdir_index = GetIndexOf(parts[index]);
if (subdir_index < 0)
return std::nullopt;
deleteme.reset(new IsoDirectory(internalReader, info));
dir = deleteme.get();
const IsoFileDescriptor& subdir_entry = GetEntry(static_cast<size_t>(index));
if (subdir_entry.IsFile() || !subdir.Open(subdir_entry, nullptr))
return std::nullopt;
dir = &subdir;
}
info = dir->GetEntry(parts.back());
return info;
const int file_index = dir->GetIndexOf(parts.back());
if (file_index < 0)
return std::nullopt;
return GetEntry(static_cast<size_t>(file_index));
}
bool IsoDirectory::Exists(const std::string_view& filePath) const
{
if (filePath.empty())
return false;
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
return fd.has_value();
}
bool IsoDirectory::IsFile(const std::string_view& filePath) const
{
if (filePath.empty())
return false;
return (FindFile(filePath).flags & 2) != 2;
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
if (fd.has_value())
return false;
return ((fd->flags & 2) != 2);
}
bool IsoDirectory::IsDir(const std::string_view& filePath) const
{
if (filePath.empty())
return false;
return (FindFile(filePath).flags & 2) == 2;
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
if (fd.has_value())
return false;
return ((fd->flags & 2) == 2);
}
u32 IsoDirectory::GetFileSize(const std::string_view& filePath) const
{
return FindFile(filePath).size;
if (filePath.empty())
return 0;
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
if (fd.has_value())
return 0;
return fd->size;
}
IsoFileDescriptor::IsoFileDescriptor()

View File

@ -17,7 +17,7 @@
class IsoFile;
class IsoDirectory;
struct ISoFileDescriptor;
struct IsoFileDescriptor;
#include "SectorSource.h"
#include "IsoFileDescriptor.h"

View File

@ -19,7 +19,7 @@
#include "SectorSource.h"
class IsoFSCDVD : public SectorSource
class IsoFSCDVD final : public SectorSource
{
public:
IsoFSCDVD();

View File

@ -16,26 +16,19 @@
#include "PrecompiledHeader.h"
#include "common/Assertions.h"
#include "common/Exceptions.h"
#include "common/Error.h"
#include "IsoFS.h"
#include "IsoFile.h"
#include <cstdio>
IsoFile::IsoFile(SectorSource& reader, const std::string_view& filename)
IsoFile::IsoFile(SectorSource& reader)
: internalReader(reader)
, fileEntry(IsoDirectory(reader).FindFile(filename))
{
Init();
}
IsoFile::IsoFile(const IsoDirectory& dir, const std::string_view& filename)
: internalReader(dir.GetReader())
, fileEntry(dir.FindFile(filename))
{
Init();
}
IsoFile::~IsoFile() = default;
IsoFile::IsoFile(SectorSource& reader, const IsoFileDescriptor& fileEntry)
: internalReader(reader)
@ -51,12 +44,35 @@ void IsoFile::Init()
currentSectorNumber = fileEntry.lba;
currentOffset = 0;
sectorOffset = 0;
maxOffset = std::max<u32>(0, fileEntry.size);
maxOffset = fileEntry.size;
if (maxOffset > 0)
internalReader.readSector(currentSector, currentSectorNumber);
}
bool IsoFile::open(const IsoDirectory& dir, const std::string_view& filename, Error* error /*= nullptr*/)
{
const std::optional<IsoFileDescriptor> fd(dir.FindFile(filename));
if (!fd.has_value())
{
Error::SetString(error, fmt::format("Failed to find file '{}'", filename));
return false;
}
fileEntry = std::move(fd.value());
Init();
return true;
}
bool IsoFile::open(const std::string_view& filename, Error* error /*= nullptr*/)
{
IsoDirectory dir(internalReader);
if (!dir.OpenRootDirectory(error))
return false;
return open(dir, filename, error);
}
u32 IsoFile::seek(u32 absoffset)
{
u32 endOffset = absoffset;
@ -142,7 +158,7 @@ void IsoFile::makeDataAvailable()
u8 IsoFile::readByte()
{
if (currentOffset >= maxOffset)
throw Exception::EndOfStream();
return 0;
makeDataAvailable();

View File

@ -21,27 +21,31 @@
#include "common/Pcsx2Defs.h"
#include <string_view>
class IsoFile
class Error;
class IsoFile final
{
public:
static const int sectorLength = 2048;
protected:
SectorSource& internalReader;
IsoFileDescriptor fileEntry;
IsoFileDescriptor fileEntry = {};
u32 currentOffset;
u32 maxOffset;
u32 currentOffset = 0;
u32 maxOffset = 0;
int currentSectorNumber;
int currentSectorNumber = 0;
int sectorOffset = 0;
u8 currentSector[sectorLength];
int sectorOffset;
public:
IsoFile(const IsoDirectory& dir, const std::string_view& filename);
IsoFile(SectorSource& reader, const std::string_view& filename);
IsoFile(SectorSource& reader);
IsoFile(SectorSource& reader, const IsoFileDescriptor& fileEntry);
virtual ~IsoFile() = default;
~IsoFile();
bool open(const IsoDirectory& dir, const std::string_view& filename, Error* error = nullptr);
bool open(const std::string_view& filename, Error* error = nullptr);
u32 seek(u32 absoffset);
u32 seek(s64 offset, int mode);

View File

@ -43,50 +43,79 @@ struct PSXEXEHeader
static_assert(sizeof(PSXEXEHeader) == 0x800);
#pragma pack(pop)
// All of ElfObjects functions.
ElfObject::ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf_)
: data(isofile.getLength(), "ELF headers")
, header(*(ELF_HEADER*)data.GetPtr())
, filename(std::move(srcfile))
, isPSXElf(isPSXElf_)
ElfObject::ElfObject() = default;
ElfObject::~ElfObject() = default;
bool ElfObject::OpenIsoFile(std::string srcfile, IsoFile& isofile, bool isPSXElf_, Error* error)
{
checkElfSize(data.GetSizeInBytes());
readIso(isofile);
initElfHeaders();
const u32 length = isofile.getLength();
if (!CheckElfSize(length, error))
return false;
data.resize(length);
const s32 rsize = isofile.read(data.data(), static_cast<s32>(length));
if (rsize < static_cast<s32>(length))
{
Error::SetString(error, "Failed to read ELF from ISO");
return false;
}
filename = std::move(srcfile);
isPSXElf = isPSXElf_;
InitElfHeaders();
return true;
}
ElfObject::ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf_)
: data(hdrsize, "ELF headers")
, header(*(ELF_HEADER*)data.GetPtr())
, filename(std::move(srcfile))
, isPSXElf(isPSXElf_)
bool ElfObject::OpenFile(std::string srcfile, bool isPSXElf_, Error* error)
{
checkElfSize(data.GetSizeInBytes());
readFile();
initElfHeaders();
auto fp = FileSystem::OpenManagedCFile(srcfile.c_str(), "rb", error);
FILESYSTEM_STAT_DATA sd;
if (!fp || !FileSystem::StatFile(fp.get(), &sd))
{
Error::SetString(error, fmt::format("Failed to read ELF from '{}'", srcfile));
return false;
}
if (!isPSXElf_ && !CheckElfSize(sd.Size, error))
return false;
data.resize(static_cast<size_t>(sd.Size));
if (std::fread(data.data(), data.size(), 1, fp.get()) != 1)
{
Error::SetString(error, fmt::format("Failed to read ELF from '{}'", srcfile));
return false;
}
filename = std::move(srcfile);
isPSXElf = isPSXElf_;
InitElfHeaders();
return true;
}
void ElfObject::initElfHeaders()
void ElfObject::InitElfHeaders()
{
if (isPSXElf)
return;
DevCon.WriteLn("Initializing Elf: %d bytes", data.GetSizeInBytes());
DevCon.WriteLn("Initializing Elf: %zu bytes", data.size());
const ELF_HEADER& header = GetHeader();
if (header.e_phnum > 0)
{
if ((header.e_phoff + sizeof(ELF_PHR)) <= static_cast<u32>(data.GetSizeInBytes()))
if ((header.e_phoff + sizeof(ELF_PHR)) <= data.size())
proghead = reinterpret_cast<ELF_PHR*>(&data[header.e_phoff]);
else
Console.Error("(ELF) Program header offset %u is larger than file size %u", header.e_phoff, data.GetSizeInBytes());
Console.Error("(ELF) Program header offset %u is larger than file size %zu", header.e_phoff, data.size());
}
if (header.e_shnum > 0)
{
if ((header.e_shoff + sizeof(ELF_SHR)) <= static_cast<u32>(data.GetSizeInBytes()))
if ((header.e_shoff + sizeof(ELF_SHR)) <= data.size())
secthead = reinterpret_cast<ELF_SHR*>(&data[header.e_shoff]);
else
Console.Error("(ELF) Section header offset %u is larger than file size %u", header.e_shoff, data.GetSizeInBytes());
Console.Error("(ELF) Section header offset %u is larger than file size %zu", header.e_shoff, data.size());
}
if ((header.e_shnum > 0) && (header.e_shentsize != sizeof(ELF_SHR)))
@ -147,49 +176,61 @@ void ElfObject::initElfHeaders()
//applyPatches();
}
bool ElfObject::hasValidPSXHeader()
bool ElfObject::HasValidPSXHeader() const
{
if (data.GetSizeInBytes() < static_cast<s64>(sizeof(PSXEXEHeader)))
if (data.size() < sizeof(PSXEXEHeader))
return false;
const PSXEXEHeader* header = reinterpret_cast<const PSXEXEHeader*>(data.GetPtr());
const PSXEXEHeader* header = reinterpret_cast<const PSXEXEHeader*>(data.data());
static constexpr char expected_id[] = {'P', 'S', '-', 'X', ' ', 'E', 'X', 'E'};
if (std::memcmp(header->id, expected_id, sizeof(expected_id)) != 0)
return false;
if (static_cast<s64>(header->file_size + sizeof(PSXEXEHeader)) > data.GetSizeInBytes())
if ((header->file_size + sizeof(PSXEXEHeader)) > data.size())
{
Console.Warning("Incorrect file size in PS-EXE header: %u bytes should not be greater than %u bytes",
header->file_size, static_cast<unsigned>(data.GetSizeInBytes() - sizeof(PSXEXEHeader)));
header->file_size, static_cast<unsigned>(data.size() - sizeof(PSXEXEHeader)));
}
return true;
}
bool ElfObject::hasProgramHeaders() { return (proghead != NULL); }
bool ElfObject::hasSectionHeaders() { return (secthead != NULL); }
bool ElfObject::hasHeaders() { return (hasProgramHeaders() && hasSectionHeaders()); }
bool ElfObject::HasProgramHeaders() const
{
return (proghead != nullptr);
}
u32 ElfObject::getEntryPoint()
bool ElfObject::HasSectionHeaders() const
{
return (secthead != nullptr);
}
bool ElfObject::HasHeaders() const
{
return (HasProgramHeaders() && HasSectionHeaders());
}
u32 ElfObject::GetEntryPoint() const
{
if (isPSXElf)
{
if (hasValidPSXHeader())
return reinterpret_cast<const PSXEXEHeader*>(data.GetPtr())->initial_pc;
if (HasValidPSXHeader())
return reinterpret_cast<const PSXEXEHeader*>(data.data())->initial_pc;
else
return 0xFFFFFFFFu;
}
else
{
return header.e_entry;
return GetHeader().e_entry;
}
}
std::pair<u32,u32> ElfObject::getTextRange()
std::pair<u32,u32> ElfObject::GetTextRange() const
{
if (!isPSXElf && hasProgramHeaders())
if (!isPSXElf && HasProgramHeaders())
{
const ELF_HEADER& header = GetHeader();
for (int i = 0; i < header.e_phnum; i++)
{
const u32 start = proghead[i].p_vaddr;
@ -203,62 +244,36 @@ std::pair<u32,u32> ElfObject::getTextRange()
return std::make_pair(0,0);
}
void ElfObject::readIso(IsoFile& file)
bool ElfObject::CheckElfSize(s64 size, Error* error)
{
int rsize = file.read(data.GetPtr(), data.GetSizeInBytes());
if (rsize < data.GetSizeInBytes()) throw Exception::EndOfStream(filename);
if (size > 0xfffffff)
Error::SetString(error, "Illegal ELF file size over 2GB!");
else if (size == -1)
Error::SetString(error, "ELF file does not exist!");
else if (size <= static_cast<s64>(sizeof(ELF_HEADER)))
Error::SetString(error, "Unexpected end of ELF file.");
else
return true;
return false;
}
void ElfObject::readFile()
{
int rsize = 0;
FILE *f = FileSystem::OpenCFile( filename.c_str(), "rb");
if (f == NULL) throw Exception::FileNotFound(filename);
fseek(f, 0, SEEK_SET);
rsize = fread(data.GetPtr(), 1, data.GetSizeInBytes(), f);
fclose( f );
if (rsize < data.GetSizeInBytes()) throw Exception::EndOfStream(filename);
}
static std::string GetMsg_InvalidELF()
{
return
"Cannot load ELF binary image. The file may be corrupt or incomplete."
"\n\n"
"If loading from an ISO image, this error may be caused by an unsupported ISO image type or a bug in PCSX2 ISO image support.";
}
void ElfObject::checkElfSize(s64 elfsize)
{
const char* diagMsg = NULL;
if (elfsize > 0xfffffff) diagMsg = "Illegal ELF file size over 2GB!";
else if (elfsize == -1) diagMsg = "ELF file does not exist!";
else if (elfsize == 0) diagMsg = "Unexpected end of ELF file.";
if (diagMsg)
throw Exception::BadStream(filename)
.SetDiagMsg(diagMsg)
.SetUserMsg(GetMsg_InvalidELF());
}
u32 ElfObject::getCRC()
u32 ElfObject::GetCRC() const
{
u32 CRC = 0;
const u32* srcdata = (u32*)data.GetPtr();
for(u32 i=data.GetSizeInBytes()/4; i; --i, ++srcdata)
const u32* srcdata = reinterpret_cast<const u32*>(data.data());
for (u32 i = static_cast<u32>(data.size()) / 4; i; --i, ++srcdata)
CRC ^= *srcdata;
return CRC;
}
void ElfObject::loadProgramHeaders()
void ElfObject::LoadProgramHeaders()
{
if (proghead == NULL) return;
const ELF_HEADER& header = GetHeader();
for( int i = 0 ; i < header.e_phnum ; i++ )
{
ELF_LOG( "Elf32 Program Header" );
@ -289,11 +304,17 @@ void ElfObject::loadProgramHeaders()
}
}
void ElfObject::loadSectionHeaders()
void ElfObject::LoadSectionHeaders()
{
if (secthead == NULL || header.e_shoff > (u32)data.GetLength()) return;
const ELF_HEADER& header = GetHeader();
if (!secthead || header.e_shoff > data.size())
return;
const u8* sections_names = data.GetPtr( secthead[ (header.e_shstrndx == 0xffff ? 0 : header.e_shstrndx) ].sh_offset );
// This function scares me a lot. There's a lot of potential for buffer overreads.
// All the accesses should be wrapped in bounds checked read() calls.
const u32 section_names_offset = secthead[(header.e_shstrndx == 0xffff ? 0 : header.e_shstrndx)].sh_offset;
const u8* sections_names = data.data() + section_names_offset;
int i_st = -1, i_dt = -1;
@ -343,28 +364,33 @@ void ElfObject::loadSectionHeaders()
if ((i_st >= 0) && (i_dt >= 0))
{
const char * SymNames;
Elf32_Sym * eS;
const char* SymNames;
Elf32_Sym* eS;
SymNames = (char*)data.GetPtr(secthead[i_dt].sh_offset);
eS = (Elf32_Sym*)data.GetPtr(secthead[i_st].sh_offset);
Console.WriteLn("found %d symbols", secthead[i_st].sh_size / sizeof(Elf32_Sym));
if (secthead[i_dt].sh_offset < data.size() &&
secthead[i_st].sh_offset < data.size())
{
SymNames = (char*)(data.data() + secthead[i_dt].sh_offset);
eS = (Elf32_Sym*)(data.data() + secthead[i_st].sh_offset);
Console.WriteLn("found %d symbols", secthead[i_st].sh_size / sizeof(Elf32_Sym));
R5900SymbolMap.Clear();
for(uint i = 1; i < (secthead[i_st].sh_size / sizeof(Elf32_Sym)); i++) {
if ((eS[i].st_value != 0) && (ELF32_ST_TYPE(eS[i].st_info) == 2))
R5900SymbolMap.Clear();
for (uint i = 1; i < (secthead[i_st].sh_size / sizeof(Elf32_Sym)); i++)
{
R5900SymbolMap.AddLabel(&SymNames[eS[i].st_name],eS[i].st_value);
if ((eS[i].st_value != 0) && (ELF32_ST_TYPE(eS[i].st_info) == 2))
{
R5900SymbolMap.AddLabel(&SymNames[eS[i].st_name], eS[i].st_value);
}
}
}
}
}
void ElfObject::loadHeaders()
void ElfObject::LoadHeaders()
{
if (isPSXElf)
return;
loadProgramHeaders();
loadSectionHeaders();
LoadProgramHeaders();
LoadSectionHeaders();
}

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -15,10 +15,10 @@
#pragma once
#include "common/SafeArray.h"
#include "common/SafeArray.inl"
#include "common/Error.h"
#include "CDVD/IsoFS/IsoFSCDVD.h"
#include "CDVD/IsoFS/IsoFS.h"
#include <vector>
struct ELF_HEADER {
u8 e_ident[16]; //0x7f,"ELF" (ELF file identifier)
@ -118,39 +118,43 @@ struct Elf32_Rel {
u32 r_info;
};
class ElfObject
class ElfObject final
{
private:
SafeArray<u8> data;
ELF_HEADER& header;
ELF_PHR* proghead = nullptr;
ELF_SHR* secthead = nullptr;
std::string filename;
bool isPSXElf;
public:
ElfObject();
ElfObject(const ElfObject&) = delete;
~ElfObject();
void initElfHeaders();
bool hasValidPSXHeader();
void readIso(IsoFile& file);
void readFile();
void checkElfSize(s64 elfsize);
__fi const ELF_HEADER& GetHeader() const { return *reinterpret_cast<const ELF_HEADER*>(data.data()); }
__fi u32 GetSize() const { return static_cast<u32>(data.size()); }
public:
ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf_);
ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf_);
bool OpenFile(std::string srcfile, bool isPSXElf_, Error* error);
bool OpenIsoFile(std::string srcfile, IsoFile& isofile, bool isPSXElf_, Error* error);
bool IsPSXElf() const { return isPSXElf; }
void LoadHeaders();
void loadProgramHeaders();
void loadSectionHeaders();
void loadHeaders();
bool HasProgramHeaders() const;
bool HasSectionHeaders() const;
bool HasHeaders() const;
bool hasProgramHeaders();
bool hasSectionHeaders();
bool hasHeaders();
std::pair<u32, u32> GetTextRange() const;
u32 GetEntryPoint() const;
u32 GetCRC() const;
std::pair<u32,u32> getTextRange();
u32 getEntryPoint();
u32 getCRC();
private:
std::vector<u8> data;
ELF_PHR* proghead = nullptr;
ELF_SHR* secthead = nullptr;
std::string filename;
bool isPSXElf;
bool CheckElfSize(s64 size, Error* error);
void InitElfHeaders();
void LoadProgramHeaders();
void LoadSectionHeaders();
bool HasValidPSXHeader() const;
};
//-------------------

View File

@ -182,16 +182,8 @@ bool GameList::GetIsoSerialAndCRC(const std::string& path, s32* disc_type, std::
bool GameList::GetElfListEntry(const std::string& path, GameList::Entry* entry)
{
const s64 file_size = FileSystem::GetPathFileSize(path.c_str());
if (file_size <= 0)
return false;
try
{
ElfObject eo(path, static_cast<uint>(file_size), false);
entry->crc = eo.getCRC();
}
catch (...)
ElfObject eo;
if (!eo.OpenFile(path, false, nullptr))
{
Console.Error("Failed to parse ELF '%s'", path.c_str());
return false;
@ -201,9 +193,10 @@ bool GameList::GetElfListEntry(const std::string& path, GameList::Entry* entry)
entry->serial.clear();
entry->title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
entry->region = Region::Other;
entry->total_size = static_cast<u64>(file_size);
entry->type = EntryType::ELF;
entry->compatibility_rating = CompatibilityRating::Unknown;
entry->crc = eo.GetCRC();
entry->total_size = eo.GetSize();
std::string disc_path(VMManager::GetDiscOverrideFromGameSettings(path));
if (!disc_path.empty())

View File

@ -589,7 +589,12 @@ std::string VMManager::GetGameSettingsPath(const std::string_view& game_serial,
std::string VMManager::GetDiscOverrideFromGameSettings(const std::string& elf_path)
{
std::string iso_path;
if (const u32 crc = cdvdGetElfCRC(elf_path); crc != 0)
ElfObject elfo;
if (!elfo.OpenFile(elf_path, false, nullptr))
return iso_path;
const u32 crc = elfo.GetCRC();
if (crc != 0)
{
INISettingsInterface si(GetGameSettingsPath(std::string_view(), crc));
if (si.Load())
@ -941,29 +946,23 @@ void VMManager::HandleELFChange(bool verbose_patches_if_changed)
void VMManager::UpdateELFInfo(std::string elf_path)
{
try
Error error;
ElfObject elfo;
if (!cdvdLoadElf(&elfo, elf_path, false, &error))
{
std::unique_ptr<ElfObject> elfptr = cdvdLoadElf(elf_path, false);
elfptr->loadHeaders();
s_current_crc = elfptr->getCRC();
s_elf_entry_point = elfptr->getEntryPoint();
s_elf_text_range = elfptr->getTextRange();
s_elf_path = std::move(elf_path);
Console.Error(fmt::format("Failed to read ELF being loaded: {}: {}", elf_path, error.GetDescription()));
s_elf_path = {};
s_elf_text_range = {};
s_elf_entry_point = 0xFFFFFFFFu;
s_current_crc = 0;
return;
}
catch ([[maybe_unused]] Exception::FileNotFound& e)
{
}
catch (Exception::BadStream& ex)
{
Console.Error(ex.FormatDiagnosticMessage());
}
Console.Error(fmt::format("Failed to read ELF being loaded: {}", elf_path));
s_elf_path = {};
s_elf_text_range = {};
s_elf_entry_point = 0xFFFFFFFFu;
s_current_crc = 0;
elfo.LoadHeaders();
s_current_crc = elfo.GetCRC();
s_elf_entry_point = elfo.GetEntryPoint();
s_elf_text_range = elfo.GetTextRange();
s_elf_path = std::move(elf_path);
}
void VMManager::ClearELFInfo()