mirror of https://github.com/PCSX2/pcsx2.git
CDVD: Rewrite ISO parser
This commit is contained in:
parent
51aeaeb508
commit
db1e1bcc1f
|
@ -15,6 +15,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "Pcsx2Types.h"
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
|
@ -340,4 +341,15 @@ namespace StringUtil
|
|||
/// Converts unsigned 128-bit data to string.
|
||||
std::string U128ToString(const u128& u);
|
||||
std::string& AppendU128ToString(const u128& u, std::string& s);
|
||||
|
||||
template <typename ContainerType>
|
||||
static inline bool ContainsSubString(const ContainerType& haystack, const std::string_view& needle)
|
||||
{
|
||||
using ValueType = typename ContainerType::value_type;
|
||||
if (needle.empty())
|
||||
return std::empty(haystack);
|
||||
|
||||
return std::search(std::begin(haystack), std::end(haystack), reinterpret_cast<const ValueType*>(needle.data()),
|
||||
reinterpret_cast<const ValueType*>(needle.data() + needle.length())) != std::end(haystack);
|
||||
}
|
||||
} // namespace StringUtil
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
#include "Achievements.h"
|
||||
#include "CDVD/CDVD.h"
|
||||
#include "CDVD/IsoFS/IsoFS.h"
|
||||
#include "CDVD/IsoFS/IsoFSCDVD.h"
|
||||
#include "Elfheader.h"
|
||||
#include "Host.h"
|
||||
#include "ImGui/FullscreenUI.h"
|
||||
|
@ -37,6 +35,7 @@
|
|||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/General.h"
|
||||
#include "common/HTTPDownloader.h"
|
||||
#include "common/MD5Digest.h"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 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-
|
||||
|
@ -14,6 +14,17 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "CDVD/CDVD.h"
|
||||
#include "CDVD/Ps1CD.h"
|
||||
#include "CDVD/CDVD_internal.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
#include "CDVD/IsoFileFormats.h"
|
||||
#include "GS.h"
|
||||
#include "Elfheader.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
#include "Host.h"
|
||||
#include "R3000A.h"
|
||||
#include "Common.h"
|
||||
#include "IopHw.h"
|
||||
|
@ -21,25 +32,15 @@
|
|||
#include "VMManager.h"
|
||||
#include "Sio.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Threading.h"
|
||||
|
||||
#include "Ps1CD.h"
|
||||
#include "CDVD.h"
|
||||
#include "CDVD_internal.h"
|
||||
#include "IsoFileFormats.h"
|
||||
|
||||
#include "GS.h" // for gsVideoMode
|
||||
#include "Elfheader.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
#include "Host.h"
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
|
||||
cdvdStruct cdvd;
|
||||
|
||||
|
@ -383,6 +384,44 @@ s32 cdvdWriteConfig(const u8* config)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool cdvdUncheckedLoadDiscElf(ElfObject* elfo, IsoReader& isor, const std::string_view& elfpath, bool isPSXElf, Error* error)
|
||||
{
|
||||
// Strip out cdrom: prefix, and any leading slashes.
|
||||
size_t start_pos = (elfpath[5] == '0') ? 7 : 6;
|
||||
while (start_pos < elfpath.size() && (elfpath[start_pos] == '\\' || elfpath[start_pos] == '/'))
|
||||
start_pos++;
|
||||
|
||||
// Strip out any version information. Some games use ;2 (MLB2k6), others put multiple versions in
|
||||
// (Syphon Filter Omega Strain). The PS2 BIOS appears to ignore the suffix entirely, so we'll do
|
||||
// the same, and hope that no games actually have multiple ELFs with different versions.
|
||||
// Previous notes:
|
||||
// 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).
|
||||
//
|
||||
size_t length = elfpath.length() - start_pos;
|
||||
const size_t semi_pos = elfpath.find(';', start_pos);
|
||||
if (semi_pos != std::string::npos)
|
||||
length = semi_pos - start_pos;
|
||||
|
||||
std::string iso_filename(elfpath.substr(start_pos, length));
|
||||
DevCon.WriteLn(fmt::format("cvdLoadElf(): '{}' -> '{}' in ISO.", elfpath, iso_filename));
|
||||
if (iso_filename.empty())
|
||||
{
|
||||
Error::SetString(error, "ISO filename is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return elfo->OpenIsoFile(std::move(iso_filename), isor, isPSXElf, error);
|
||||
}
|
||||
|
||||
bool cdvdLoadElf(ElfObject* elfo, const std::string_view& elfpath, bool isPSXElf, Error* error)
|
||||
{
|
||||
if (StringUtil::StartsWith(elfpath, "host:"))
|
||||
|
@ -392,52 +431,25 @@ bool cdvdLoadElf(ElfObject* elfo, const std::string_view& elfpath, bool isPSXElf
|
|||
}
|
||||
else if (StringUtil::StartsWith(elfpath, "cdrom:") || StringUtil::StartsWith(elfpath, "cdrom0:"))
|
||||
{
|
||||
// Strip out cdrom: prefix, and any leading slashes.
|
||||
size_t start_pos = (elfpath[5] == '0') ? 7 : 6;
|
||||
while (start_pos < elfpath.size() && (elfpath[start_pos] == '\\' || elfpath[start_pos] == '/'))
|
||||
start_pos++;
|
||||
|
||||
// Strip out any version information. Some games use ;2 (MLB2k6), others put multiple versions in
|
||||
// (Syphon Filter Omega Strain). The PS2 BIOS appears to ignore the suffix entirely, so we'll do
|
||||
// the same, and hope that no games actually have multiple ELFs with different versions.
|
||||
// Previous notes:
|
||||
// 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).
|
||||
//
|
||||
size_t length = elfpath.length() - start_pos;
|
||||
const size_t semi_pos = elfpath.find(';', start_pos);
|
||||
if (semi_pos != std::string::npos)
|
||||
length = semi_pos - start_pos;
|
||||
|
||||
std::string iso_filename(elfpath.substr(start_pos, length));
|
||||
DevCon.WriteLn(fmt::format("cvdLoadElf(): '{}' -> '{}' in ISO.", elfpath, iso_filename));
|
||||
if (iso_filename.empty())
|
||||
{
|
||||
Error::SetString(error, "ISO filename is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file(isofs);
|
||||
if (!file.open(iso_filename, error))
|
||||
IsoReader isor;
|
||||
if (!isor.Open(error))
|
||||
return false;
|
||||
|
||||
return elfo->OpenIsoFile(std::move(iso_filename), file, isPSXElf, error);
|
||||
return cdvdLoadDiscElf(elfo, isor, elfpath, isPSXElf, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error(fmt::format("cdvdLoadElf(): Unknown device in ELF path '{}'", elfpath));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
bool cdvdLoadDiscElf(ElfObject* elfo, IsoReader& isor, const std::string_view& elfpath, bool isPSXElf, Error* error)
|
||||
{
|
||||
if (!StringUtil::StartsWith(elfpath, "cdrom:") && !StringUtil::StartsWith(elfpath, "cdrom0:"))
|
||||
return false;
|
||||
|
||||
return cdvdUncheckedLoadDiscElf(elfo, isor, elfpath, isPSXElf, error);
|
||||
}
|
||||
|
||||
u32 cdvdGetElfCRC(const std::string& path)
|
||||
|
@ -449,65 +461,57 @@ u32 cdvdGetElfCRC(const std::string& path)
|
|||
return elfo.GetCRC();
|
||||
}
|
||||
|
||||
static CDVDDiscType GetPS2ElfName(std::string* name, std::string* version)
|
||||
static CDVDDiscType GetPS2ElfName(IsoReader& isor, std::string* name, std::string* version, Error* error)
|
||||
{
|
||||
CDVDDiscType retype = CDVDDiscType::Other;
|
||||
name->clear();
|
||||
version->clear();
|
||||
|
||||
Error error;
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file(isofs);
|
||||
if (!file.open("SYSTEM.CNF", &error))
|
||||
{
|
||||
Console.Error(fmt::format("(GetElfName) Failed to open SYSTEM.CNF: {}", error.GetDescription()));
|
||||
return CDVDDiscType::Other;
|
||||
}
|
||||
|
||||
const int size = file.getLength();
|
||||
if (size == 0)
|
||||
std::vector<u8> data;
|
||||
if (!isor.ReadFile("SYSTEM.CNF", &data, error))
|
||||
return CDVDDiscType::Other;
|
||||
|
||||
while (!file.eof())
|
||||
const std::vector<std::string_view> lines =
|
||||
StringUtil::SplitString(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()), '\n');
|
||||
for (size_t lineno = 0; lineno < lines.size(); lineno++)
|
||||
{
|
||||
const std::string line(file.readLine());
|
||||
const std::string_view line = StringUtil::StripWhitespace(lines[lineno]);
|
||||
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.
|
||||
// Some games have a character on the last line of the file, don't print the error in those cases.
|
||||
if (value.empty() && (lineno == (lines.size() - 1)))
|
||||
{
|
||||
Console.Warning("(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:");
|
||||
Console.Indent().WriteLn(line);
|
||||
Console.Indent().WriteLn(std::string(line));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == "BOOT2")
|
||||
{
|
||||
Console.WriteLn(Color_StrongBlue, fmt::format("(SYSTEM.CNF) Detected PS2 Disc = {}", value));
|
||||
DevCon.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));
|
||||
DevCon.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));
|
||||
DevCon.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));
|
||||
DevCon.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Software version = {}", value));
|
||||
*version = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (retype == CDVDDiscType::Other)
|
||||
Console.Error("(GetElfName) Disc image is *not* a PlayStation or PS2 game!");
|
||||
|
||||
Error::SetString(error, "Disc image is *not* a PlayStation or PS2 game");
|
||||
return retype;
|
||||
}
|
||||
|
||||
|
@ -566,8 +570,13 @@ static std::string ExecutablePathToSerial(const std::string& path)
|
|||
void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
|
||||
CDVDDiscType* out_disc_type)
|
||||
{
|
||||
Error error;
|
||||
IsoReader isor;
|
||||
|
||||
std::string elfpath, version;
|
||||
const CDVDDiscType disc_type = GetPS2ElfName(&elfpath, &version);
|
||||
CDVDDiscType disc_type = CDVDDiscType::Other;
|
||||
if (!isor.Open(&error) || (disc_type = GetPS2ElfName(isor, &elfpath, &version, &error)) == CDVDDiscType::Other)
|
||||
Console.Error(fmt::format("Failed to get ELF name: {}", error.GetDescription()));
|
||||
|
||||
// Don't bother parsing it if we don't need the CRC.
|
||||
if (out_crc)
|
||||
|
@ -576,10 +585,9 @@ void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::st
|
|||
|
||||
if (disc_type == CDVDDiscType::PS2Disc || disc_type == CDVDDiscType::PS1Disc)
|
||||
{
|
||||
Error error;
|
||||
ElfObject elfo;
|
||||
const bool isPSXElf = (disc_type == CDVDDiscType::PS1Disc);
|
||||
if (!cdvdLoadElf(&elfo, elfpath, isPSXElf, &error))
|
||||
if (!cdvdLoadDiscElf(&elfo, isor, elfpath, isPSXElf, &error))
|
||||
Console.Error(fmt::format("Failed to load ELF info for {}: {}", elfpath, error.GetDescription()));
|
||||
else
|
||||
crc = elfo.GetCRC();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
class Error;
|
||||
class ElfObject;
|
||||
class IsoReader;
|
||||
|
||||
#define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
|
||||
#define itob(i) ((i) / 10 * 16 + (i) % 10) /* u_char to BCD */
|
||||
|
@ -190,7 +191,8 @@ 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 bool cdvdLoadElf(ElfObject* elfo, const std::string_view& filename, bool isPSXElf, Error* error);
|
||||
extern bool cdvdLoadElf(ElfObject* elfo, const std::string_view& elfpath, bool isPSXElf, Error* error);
|
||||
extern bool cdvdLoadDiscElf(ElfObject* elfo, IsoReader& isor, const std::string_view& elfpath, bool isPSXElf, Error* error);
|
||||
|
||||
extern s32 cdvdCtrlTrayOpen();
|
||||
extern s32 cdvdCtrlTrayClose();
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
/*
|
||||
Interrupts - values are flag bits.
|
||||
|
||||
|
|
|
@ -16,29 +16,29 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#define ENABLE_TIMESTAMPS
|
||||
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include "IsoFS/IsoFS.h"
|
||||
#include "IsoFS/IsoFSCDVD.h"
|
||||
#include "IsoFileFormats.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
#include "CDVD/IsoFileFormats.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "Config.h"
|
||||
#include "Host.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
|
||||
CDVD_API* CDVD = NULL;
|
||||
#include "common/Assertions.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <time.h>
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#define ENABLE_TIMESTAMPS
|
||||
|
||||
CDVD_API* CDVD = nullptr;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// diskTypeCached
|
||||
|
@ -71,37 +71,35 @@ static void CheckNullCDVD()
|
|||
//
|
||||
static int CheckDiskTypeFS(int baseType)
|
||||
{
|
||||
IsoFSCDVD isofs;
|
||||
IsoDirectory rootdir(isofs);
|
||||
if (rootdir.OpenRootDirectory())
|
||||
IsoReader isor;
|
||||
if (isor.Open())
|
||||
{
|
||||
if (IsoFile file(isofs); file.open(rootdir, "SYSTEM.CNF"))
|
||||
std::vector<u8> data;
|
||||
if (isor.ReadFile("SYSTEM.CNF", &data))
|
||||
{
|
||||
const int size = file.getLength();
|
||||
const std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size + 1);
|
||||
file.read(buffer.get(), size);
|
||||
buffer[size] = '\0';
|
||||
|
||||
char* pos = strstr(buffer.get(), "BOOT2");
|
||||
if (pos == NULL)
|
||||
if (StringUtil::ContainsSubString(data, "BOOT2"))
|
||||
{
|
||||
pos = strstr(buffer.get(), "BOOT");
|
||||
if (pos == NULL)
|
||||
return CDVD_TYPE_ILLEGAL;
|
||||
// PS2 DVD/CD.
|
||||
return (baseType == CDVD_TYPE_DETCTCD) ? CDVD_TYPE_PS2CD : CDVD_TYPE_PS2DVD;
|
||||
}
|
||||
|
||||
if (StringUtil::ContainsSubString(data, "BOOT"))
|
||||
{
|
||||
// PSX CD.
|
||||
return CDVD_TYPE_PSCD;
|
||||
}
|
||||
|
||||
return (baseType == CDVD_TYPE_DETCTCD) ? CDVD_TYPE_PS2CD : CDVD_TYPE_PS2DVD;
|
||||
return CDVD_TYPE_ILLEGAL;
|
||||
}
|
||||
|
||||
// PS2 Linux disc 2, doesn't have a System.CNF or a normal ELF
|
||||
if (rootdir.Exists("P2L_0100.02"))
|
||||
if (isor.FileExists("P2L_0100.02"))
|
||||
return CDVD_TYPE_PS2DVD;
|
||||
|
||||
if (rootdir.Exists("PSX.EXE"))
|
||||
if (isor.FileExists("PSX.EXE"))
|
||||
return CDVD_TYPE_PSCD;
|
||||
|
||||
if (rootdir.Exists("VIDEO_TS/VIDEO_TS.IFO"))
|
||||
if (isor.FileExists("VIDEO_TS/VIDEO_TS.IFO"))
|
||||
return CDVD_TYPE_DVDV;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 final
|
||||
{
|
||||
public:
|
||||
SectorSource& internalReader;
|
||||
std::vector<IsoFileDescriptor> files;
|
||||
IsoFS_Type m_fstype = FStype_ISO9660;
|
||||
|
||||
public:
|
||||
IsoDirectory(SectorSource& r);
|
||||
~IsoDirectory();
|
||||
|
||||
bool OpenRootDirectory(Error* error = nullptr);
|
||||
bool Open(const IsoFileDescriptor& directoryEntry, Error* error = nullptr);
|
||||
|
||||
std::string FStype_ToString() const;
|
||||
SectorSource& GetReader() const { return internalReader; }
|
||||
|
||||
bool Exists(const std::string_view& filePath) const;
|
||||
bool IsFile(const std::string_view& filePath) const;
|
||||
bool IsDir(const std::string_view& filePath) const;
|
||||
|
||||
u32 GetFileSize(const std::string_view& filePath) const;
|
||||
|
||||
std::optional<IsoFileDescriptor> FindFile(const std::string_view& filePath) const;
|
||||
|
||||
protected:
|
||||
const IsoFileDescriptor& GetEntry(size_t index) const;
|
||||
|
||||
int GetIndexOf(const std::string_view& fileName) const;
|
||||
};
|
|
@ -1,310 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "IsoFS.h"
|
||||
#include "IsoFile.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// IsoDirectory
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//u8 filesystemType; // 0x01 = ISO9660, 0x02 = Joliet, 0xFF = NULL
|
||||
//u8 volID[5]; // "CD001"
|
||||
|
||||
|
||||
std::string IsoDirectory::FStype_ToString() const
|
||||
{
|
||||
switch (m_fstype)
|
||||
{
|
||||
case FStype_ISO9660:
|
||||
return "ISO9660";
|
||||
case FStype_Joliet:
|
||||
return "Joliet";
|
||||
}
|
||||
|
||||
return StringUtil::StdStringFromFormat("Unrecognized Code (0x%x)", m_fstype);
|
||||
}
|
||||
|
||||
// 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;
|
||||
bool done = false;
|
||||
uint i = 16;
|
||||
|
||||
m_fstype = FStype_ISO9660;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
u8 sector[2048];
|
||||
// If this fails, we're not reading an iso, or it's bad.
|
||||
if (!internalReader.readSector(sector, i))
|
||||
break;
|
||||
|
||||
if (memcmp(§or[1], "CD001", 5) == 0)
|
||||
{
|
||||
switch (sector[0])
|
||||
{
|
||||
case 0:
|
||||
DevCon.WriteLn(Color_Green, "(IsoFS) Block 0x%x: Boot partition info.", i);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
DevCon.WriteLn("(IsoFS) Block 0x%x: Primary partition info.", i);
|
||||
rootDirEntry.Load(sector + 156, 38);
|
||||
isValid = true;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Probably means Joliet (long filenames support), which PCSX2 doesn't care about.
|
||||
DevCon.WriteLn(Color_Green, "(IsoFS) Block 0x%x: Extended partition info.", i);
|
||||
m_fstype = FStype_Joliet;
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
// Null terminator. End of partition information.
|
||||
done = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error("(IsoFS) Unknown partition type ID=%d, encountered at block 0x%x", sector[0], i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sector[9] = 0;
|
||||
Console.Error("(IsoFS) Invalid partition descriptor encountered at block 0x%x: '%s'", i, §or[1]);
|
||||
break; // if no valid root partition was found, an exception will be thrown below.
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
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());
|
||||
return Open(rootDirEntry);
|
||||
}
|
||||
|
||||
// Used to load a specific directory from a file descriptor
|
||||
bool IsoDirectory::Open(const IsoFileDescriptor& directoryEntry, Error* error /* = nullptr */)
|
||||
{
|
||||
// parse directory sector
|
||||
IsoFile dataStream(internalReader, directoryEntry);
|
||||
|
||||
files.clear();
|
||||
|
||||
u32 remainingSize = directoryEntry.size;
|
||||
|
||||
u8 b[257];
|
||||
|
||||
while (remainingSize >= 4) // hm hack :P
|
||||
{
|
||||
b[0] = dataStream.read<u8>();
|
||||
|
||||
if (b[0] == 0)
|
||||
{
|
||||
break; // or continue?
|
||||
}
|
||||
|
||||
remainingSize -= b[0];
|
||||
|
||||
if (dataStream.read(b + 1, static_cast<s32>(b[0] - 1)) != static_cast<s32>(b[0] - 1))
|
||||
break;
|
||||
|
||||
files.emplace_back(b, b[0]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const IsoFileDescriptor& IsoDirectory::GetEntry(size_t index) const
|
||||
{
|
||||
return files[index];
|
||||
}
|
||||
|
||||
int IsoDirectory::GetIndexOf(const std::string_view& fileName) const
|
||||
{
|
||||
for (unsigned int i = 0; i < files.size(); i++)
|
||||
{
|
||||
if (files[i].name == fileName)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::optional<IsoFileDescriptor> IsoDirectory::FindFile(const std::string_view& filePath) const
|
||||
{
|
||||
if (filePath.empty())
|
||||
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));
|
||||
const IsoDirectory* dir = this;
|
||||
IsoDirectory subdir(internalReader);
|
||||
|
||||
for (size_t index = 0; index < (parts.size() - 1); index++)
|
||||
{
|
||||
const int subdir_index = dir->GetIndexOf(parts[index]);
|
||||
if (subdir_index < 0)
|
||||
return std::nullopt;
|
||||
|
||||
const IsoFileDescriptor& subdir_entry = GetEntry(static_cast<size_t>(subdir_index));
|
||||
if (subdir_entry.IsFile() || !subdir.Open(subdir_entry, nullptr))
|
||||
return std::nullopt;
|
||||
|
||||
dir = &subdir;
|
||||
}
|
||||
|
||||
const int file_index = dir->GetIndexOf(parts.back());
|
||||
if (file_index < 0)
|
||||
return std::nullopt;
|
||||
|
||||
return dir->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;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
{
|
||||
if (filePath.empty())
|
||||
return 0;
|
||||
|
||||
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
|
||||
if (fd.has_value())
|
||||
return 0;
|
||||
|
||||
return fd->size;
|
||||
}
|
||||
|
||||
IsoFileDescriptor::IsoFileDescriptor()
|
||||
: lba(0)
|
||||
, size(0)
|
||||
, flags(0)
|
||||
{
|
||||
memset(&date, 0, sizeof(date));
|
||||
}
|
||||
|
||||
IsoFileDescriptor::IsoFileDescriptor(const u8* data, int length)
|
||||
{
|
||||
Load(data, length);
|
||||
}
|
||||
|
||||
void IsoFileDescriptor::Load(const u8* data, int length)
|
||||
{
|
||||
lba = (u32&)data[2];
|
||||
size = (u32&)data[10];
|
||||
|
||||
date.year = data[18] + 1900;
|
||||
date.month = data[19];
|
||||
date.day = data[20];
|
||||
date.hour = data[21];
|
||||
date.minute = data[22];
|
||||
date.second = data[23];
|
||||
date.gmtOffset = data[24];
|
||||
|
||||
flags = data[25];
|
||||
|
||||
int file_name_length = data[32];
|
||||
|
||||
if (file_name_length == 1)
|
||||
{
|
||||
u8 c = data[33];
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
name = ".";
|
||||
break;
|
||||
case 1:
|
||||
name = "..";
|
||||
break;
|
||||
default:
|
||||
name = static_cast<char>(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const u8* fnsrc = data + 33;
|
||||
|
||||
// Strip any version information like the PS2 BIOS does.
|
||||
int length_without_version = 0;
|
||||
for (; length_without_version < file_name_length; length_without_version++)
|
||||
{
|
||||
if (fnsrc[length_without_version] == ';' || fnsrc[length_without_version] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
name.assign(reinterpret_cast<const char*>(fnsrc), length_without_version);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class IsoFile;
|
||||
class IsoDirectory;
|
||||
struct IsoFileDescriptor;
|
||||
|
||||
#include "SectorSource.h"
|
||||
#include "IsoFileDescriptor.h"
|
||||
#include "IsoDirectory.h"
|
||||
#include "IsoFile.h"
|
|
@ -1,37 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "IsoFSCDVD.h"
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
|
||||
IsoFSCDVD::IsoFSCDVD()
|
||||
{
|
||||
}
|
||||
|
||||
bool IsoFSCDVD::readSector(unsigned char* buffer, int lba)
|
||||
{
|
||||
return DoCDVDreadSector(buffer, lba, CDVD_MODE_2048) >= 0;
|
||||
}
|
||||
|
||||
int IsoFSCDVD::getNumSectors()
|
||||
{
|
||||
cdvdTD td;
|
||||
CDVD->getTD(0, &td);
|
||||
|
||||
return td.lsn;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SectorSource.h"
|
||||
|
||||
class IsoFSCDVD final : public SectorSource
|
||||
{
|
||||
public:
|
||||
IsoFSCDVD();
|
||||
virtual ~IsoFSCDVD() = default;
|
||||
|
||||
virtual bool readSector(unsigned char* buffer, int lba);
|
||||
|
||||
virtual int getNumSectors();
|
||||
};
|
|
@ -1,262 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Error.h"
|
||||
|
||||
#include "IsoFS.h"
|
||||
#include "IsoFile.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
IsoFile::IsoFile(SectorSource& reader)
|
||||
: internalReader(reader)
|
||||
{
|
||||
}
|
||||
|
||||
IsoFile::~IsoFile() = default;
|
||||
|
||||
IsoFile::IsoFile(SectorSource& reader, const IsoFileDescriptor& fileEntry)
|
||||
: internalReader(reader)
|
||||
, fileEntry(fileEntry)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
void IsoFile::Init()
|
||||
{
|
||||
//pxAssertDev( fileEntry.IsFile(), "IsoFile Error: Filename points to a directory." );
|
||||
|
||||
currentSectorNumber = fileEntry.lba;
|
||||
currentOffset = 0;
|
||||
sectorOffset = 0;
|
||||
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;
|
||||
|
||||
int oldSectorNumber = currentSectorNumber;
|
||||
int newSectorNumber = fileEntry.lba + (int)(endOffset / sectorLength);
|
||||
|
||||
if (oldSectorNumber != newSectorNumber)
|
||||
{
|
||||
internalReader.readSector(currentSector, newSectorNumber);
|
||||
}
|
||||
|
||||
currentOffset = endOffset;
|
||||
currentSectorNumber = newSectorNumber;
|
||||
sectorOffset = (int)(currentOffset % sectorLength);
|
||||
|
||||
return currentOffset;
|
||||
}
|
||||
|
||||
// Returns the new offset in the file. Out-of-bounds seeks are automatically truncated at 0
|
||||
// and fileLength.
|
||||
u32 IsoFile::seek(s64 offset, int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case SEEK_SET:
|
||||
pxAssertDev(offset >= 0 && offset <= (s64)ULONG_MAX, "Invalid seek position from start.");
|
||||
return seek(offset);
|
||||
|
||||
case SEEK_CUR:
|
||||
// truncate negative values to zero, and positive values to 4gb
|
||||
return seek(std::min(std::max<s64>(0, (s64)currentOffset + offset), (s64)ULONG_MAX));
|
||||
|
||||
case SEEK_END:
|
||||
// truncate negative values to zero, and positive values to 4gb
|
||||
return seek(std::min(std::max<s64>(0, (s64)fileEntry.size + offset), (s64)ULONG_MAX));
|
||||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
|
||||
return 0; // unreachable
|
||||
}
|
||||
|
||||
void IsoFile::reset()
|
||||
{
|
||||
seek(0);
|
||||
}
|
||||
|
||||
// Returns the number of bytes actually skipped.
|
||||
s32 IsoFile::skip(s32 n)
|
||||
{
|
||||
s32 oldOffset = currentOffset;
|
||||
|
||||
if (n < 0)
|
||||
return 0;
|
||||
|
||||
seek(currentOffset + n);
|
||||
|
||||
return currentOffset - oldOffset;
|
||||
}
|
||||
|
||||
u32 IsoFile::getSeekPos() const
|
||||
{
|
||||
return currentOffset;
|
||||
}
|
||||
|
||||
bool IsoFile::eof() const
|
||||
{
|
||||
return (currentOffset == maxOffset);
|
||||
}
|
||||
|
||||
// loads the current sector index into the CurrentSector buffer.
|
||||
void IsoFile::makeDataAvailable()
|
||||
{
|
||||
if (sectorOffset >= sectorLength)
|
||||
{
|
||||
currentSectorNumber++;
|
||||
internalReader.readSector(currentSector, currentSectorNumber);
|
||||
sectorOffset -= sectorLength;
|
||||
}
|
||||
}
|
||||
|
||||
u8 IsoFile::readByte()
|
||||
{
|
||||
if (currentOffset >= maxOffset)
|
||||
return 0;
|
||||
|
||||
makeDataAvailable();
|
||||
|
||||
currentOffset++;
|
||||
|
||||
return currentSector[sectorOffset++];
|
||||
}
|
||||
|
||||
// Reads data from a single sector at a time. Reads cannot cross sector boundaries.
|
||||
int IsoFile::internalRead(void* dest, int off, int len)
|
||||
{
|
||||
if (len > 0)
|
||||
{
|
||||
size_t slen = len;
|
||||
if (slen > (maxOffset - currentOffset))
|
||||
{
|
||||
slen = (int)(maxOffset - currentOffset);
|
||||
}
|
||||
|
||||
memcpy((u8*)dest + off, currentSector + sectorOffset, slen);
|
||||
|
||||
sectorOffset += slen;
|
||||
currentOffset += slen;
|
||||
return slen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns the number of bytes actually read.
|
||||
s32 IsoFile::read(void* dest, s32 len)
|
||||
{
|
||||
pxAssert(dest != NULL);
|
||||
pxAssert(len >= 0); // should we silent-fail on negative length reads? prolly not...
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
int off = 0;
|
||||
|
||||
int totalLength = 0;
|
||||
|
||||
int firstSector = internalRead(dest, off, std::min(len, sectorLength - sectorOffset));
|
||||
off += firstSector;
|
||||
len -= firstSector;
|
||||
totalLength += firstSector;
|
||||
|
||||
// Read whole sectors
|
||||
while ((len >= sectorLength) && (currentOffset < maxOffset))
|
||||
{
|
||||
makeDataAvailable();
|
||||
int n = internalRead(dest, off, sectorLength);
|
||||
off += n;
|
||||
len -= n;
|
||||
totalLength += n;
|
||||
}
|
||||
|
||||
// Read remaining, if any
|
||||
if (len > 0)
|
||||
{
|
||||
makeDataAvailable();
|
||||
int lastSector = internalRead(dest, off, len);
|
||||
totalLength += lastSector;
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
// Reads data until it reaches a newline character (either \n, \r, or ASCII-Z). The caller is
|
||||
// responsible for handling files with DOS-style newlines (CR/LF pairs), if needed. The resulting
|
||||
// string has no newlines.
|
||||
//
|
||||
// Read data is unformatted 8 bit / Ascii. If the source file is known to be UTF8, use the fromUTF8()
|
||||
// conversion helper provided by PCSX2 utility classes.
|
||||
//
|
||||
std::string IsoFile::readLine()
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(512);
|
||||
|
||||
while (!eof())
|
||||
{
|
||||
u8 c = read<u8>();
|
||||
|
||||
if ((c == '\n') || (c == '\r') || (c == 0))
|
||||
break;
|
||||
|
||||
s += c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
u32 IsoFile::getLength()
|
||||
{
|
||||
return maxOffset;
|
||||
}
|
||||
|
||||
const IsoFileDescriptor& IsoFile::getEntry()
|
||||
{
|
||||
return fileEntry;
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IsoFileDescriptor.h"
|
||||
#include "SectorSource.h"
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <string_view>
|
||||
|
||||
class Error;
|
||||
|
||||
class IsoFile final
|
||||
{
|
||||
public:
|
||||
static const int sectorLength = 2048;
|
||||
|
||||
protected:
|
||||
SectorSource& internalReader;
|
||||
IsoFileDescriptor fileEntry = {};
|
||||
|
||||
u32 currentOffset = 0;
|
||||
u32 maxOffset = 0;
|
||||
|
||||
int currentSectorNumber = 0;
|
||||
int sectorOffset = 0;
|
||||
u8 currentSector[sectorLength];
|
||||
|
||||
public:
|
||||
IsoFile(SectorSource& reader);
|
||||
IsoFile(SectorSource& reader, const IsoFileDescriptor& fileEntry);
|
||||
~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);
|
||||
void reset();
|
||||
|
||||
s32 skip(s32 n);
|
||||
u32 getSeekPos() const;
|
||||
u32 getLength();
|
||||
bool eof() const;
|
||||
|
||||
const IsoFileDescriptor& getEntry();
|
||||
|
||||
u8 readByte();
|
||||
s32 read(void* dest, s32 len);
|
||||
std::string readLine();
|
||||
|
||||
// Tool to read a specific value type, including structs.
|
||||
template <class T>
|
||||
T read()
|
||||
{
|
||||
if (sizeof(T) == 1)
|
||||
return (T)readByte();
|
||||
else
|
||||
{
|
||||
T t;
|
||||
read((u8*)&t, sizeof(t));
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void makeDataAvailable();
|
||||
int internalRead(void* dest, int off, int len);
|
||||
void Init();
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <string>
|
||||
|
||||
struct IsoFileDescriptor
|
||||
{
|
||||
struct FileDate // not 1:1 with iso9660 date struct
|
||||
{
|
||||
s32 year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
u8 gmtOffset; // Offset from Greenwich Mean Time in number of 15 min intervals from -48 (West) to + 52 (East)
|
||||
|
||||
} date;
|
||||
|
||||
u32 lba;
|
||||
u32 size;
|
||||
int flags;
|
||||
std::string name;
|
||||
|
||||
IsoFileDescriptor();
|
||||
IsoFileDescriptor(const u8* data, int length);
|
||||
|
||||
void Load(const u8* data, int length);
|
||||
|
||||
bool IsFile() const { return !(flags & 2); }
|
||||
bool IsDir() const { return !IsFile(); }
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class SectorSource
|
||||
{
|
||||
public:
|
||||
virtual int getNumSectors() = 0;
|
||||
virtual bool readSector(unsigned char* buffer, int lba) = 0;
|
||||
virtual ~SectorSource() = default;
|
||||
};
|
|
@ -0,0 +1,315 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
IsoReader::IsoReader() = default;
|
||||
|
||||
IsoReader::~IsoReader() = default;
|
||||
|
||||
bool IsoReader::Open(Error* error)
|
||||
{
|
||||
if (!ReadPVD(error))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsoReader::ReadSector(u8* buf, u32 lsn, Error* error)
|
||||
{
|
||||
if (DoCDVDreadSector(buf, lsn, CDVD_MODE_2048) != 0)
|
||||
{
|
||||
Error::SetString(error, fmt::format("Failed to read sector LSN #{}", lsn));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsoReader::ReadPVD(Error* error)
|
||||
{
|
||||
// volume descriptor start at sector 16
|
||||
static constexpr u32 START_SECTOR = 16;
|
||||
|
||||
// try only a maximum of 256 volume descriptors
|
||||
for (u32 i = 0; i < 256; i++)
|
||||
{
|
||||
u8 buffer[SECTOR_SIZE];
|
||||
if (!ReadSector(buffer, START_SECTOR + i, error))
|
||||
return false;
|
||||
|
||||
const ISOVolumeDescriptorHeader* header = reinterpret_cast<ISOVolumeDescriptorHeader*>(buffer);
|
||||
if (std::memcmp(header->standard_identifier, "CD001", 5) != 0)
|
||||
continue;
|
||||
else if (header->type_code != 1)
|
||||
continue;
|
||||
else if (header->type_code == 255)
|
||||
break;
|
||||
|
||||
std::memcpy(&m_pvd, buffer, sizeof(ISOPrimaryVolumeDescriptor));
|
||||
DevCon.WriteLn(fmt::format("ISOReader: PVD found at index {}", i));
|
||||
return true;
|
||||
}
|
||||
|
||||
Error::SetString(error, "Failed to find the Primary Volume Descriptor.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<IsoReader::ISODirectoryEntry> IsoReader::LocateFile(const std::string_view& path, Error* error)
|
||||
{
|
||||
const ISODirectoryEntry* root_de = reinterpret_cast<const ISODirectoryEntry*>(m_pvd.root_directory_entry);
|
||||
if (path.empty() || path == "/" || path == "\\")
|
||||
{
|
||||
// locating the root directory
|
||||
return *root_de;
|
||||
}
|
||||
|
||||
// start at the root directory
|
||||
u8 sector_buffer[SECTOR_SIZE];
|
||||
return LocateFile(path, sector_buffer, root_de->location_le, root_de->length_le, error);
|
||||
}
|
||||
|
||||
std::string_view IsoReader::GetDirectoryEntryFileName(const u8* sector, u32 de_sector_offset)
|
||||
{
|
||||
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(sector + de_sector_offset);
|
||||
if ((sizeof(ISODirectoryEntry) + de->filename_length) > de->entry_length ||
|
||||
(sizeof(ISODirectoryEntry) + de->filename_length + de_sector_offset) > SECTOR_SIZE)
|
||||
{
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
const char* str = reinterpret_cast<const char*>(sector + de_sector_offset + sizeof(ISODirectoryEntry));
|
||||
if (de->filename_length == 1)
|
||||
{
|
||||
if (str[0] == '\0')
|
||||
return ".";
|
||||
else if (str[0] == '\1')
|
||||
return "..";
|
||||
}
|
||||
|
||||
// Strip any version information like the PS2 BIOS does.
|
||||
u32 length_without_version = 0;
|
||||
for (; length_without_version < de->filename_length; length_without_version++)
|
||||
{
|
||||
if (str[length_without_version] == ';' || str[length_without_version] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return std::string_view(str, length_without_version);
|
||||
}
|
||||
|
||||
std::optional<IsoReader::ISODirectoryEntry> IsoReader::LocateFile(
|
||||
const std::string_view& path, u8* sector_buffer, u32 directory_record_lba, u32 directory_record_size, Error* error)
|
||||
{
|
||||
if (directory_record_size == 0)
|
||||
{
|
||||
Error::SetString(error, fmt::format("Directory entry record size 0 while looking for '{}'", path));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// strip any leading slashes
|
||||
size_t path_component_start = 0;
|
||||
while (path_component_start < path.length() &&
|
||||
(path[path_component_start] == '/' || path[path_component_start] == '\\'))
|
||||
{
|
||||
path_component_start++;
|
||||
}
|
||||
|
||||
size_t path_component_length = 0;
|
||||
while ((path_component_start + path_component_length) < path.length() &&
|
||||
path[path_component_start + path_component_length] != '/' &&
|
||||
path[path_component_start + path_component_length] != '\\')
|
||||
{
|
||||
path_component_length++;
|
||||
}
|
||||
|
||||
const std::string_view path_component = path.substr(path_component_start, path_component_length);
|
||||
if (path_component.empty())
|
||||
{
|
||||
Error::SetString(error, fmt::format("Empty path component in {}", path));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// start reading directory entries
|
||||
const u32 num_sectors = (directory_record_size + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
|
||||
for (u32 i = 0; i < num_sectors; i++)
|
||||
{
|
||||
if (!ReadSector(sector_buffer, directory_record_lba + i, error))
|
||||
return std::nullopt;
|
||||
|
||||
u32 sector_offset = 0;
|
||||
while ((sector_offset + sizeof(ISODirectoryEntry)) < SECTOR_SIZE)
|
||||
{
|
||||
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(§or_buffer[sector_offset]);
|
||||
if (de->entry_length < sizeof(ISODirectoryEntry))
|
||||
break;
|
||||
|
||||
const std::string_view de_filename = GetDirectoryEntryFileName(sector_buffer, sector_offset);
|
||||
sector_offset += de->entry_length;
|
||||
|
||||
// Empty file would be pretty strange..
|
||||
if (de_filename.empty() || de_filename == "." || de_filename == "..")
|
||||
continue;
|
||||
|
||||
if (!StringUtil::compareNoCase(de_filename, path_component))
|
||||
continue;
|
||||
|
||||
// found it. is this the file we're looking for?
|
||||
if ((path_component_start + path_component_length) == path.length())
|
||||
return *de;
|
||||
|
||||
// if it is a directory, recurse into it
|
||||
if (de->flags & ISODirectoryEntryFlag_Directory)
|
||||
{
|
||||
return LocateFile(path.substr(path_component_start + path_component_length), sector_buffer,
|
||||
de->location_le, de->length_le, error);
|
||||
}
|
||||
|
||||
// we're looking for a directory but got a file
|
||||
Error::SetString(error, fmt::format("Looking for directory '{}' but got file", path_component));
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Error::SetString(error, fmt::format("Path component '{}' not found", path_component));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::string> IsoReader::GetFilesInDirectory(const std::string_view& path, Error* error)
|
||||
{
|
||||
std::string base_path(path);
|
||||
u32 directory_record_lsn;
|
||||
u32 directory_record_length;
|
||||
if (base_path.empty())
|
||||
{
|
||||
// root directory
|
||||
const ISODirectoryEntry* root_de = reinterpret_cast<const ISODirectoryEntry*>(m_pvd.root_directory_entry);
|
||||
directory_record_lsn = root_de->location_le;
|
||||
directory_record_length = root_de->length_le;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto directory_de = LocateFile(base_path, error);
|
||||
if (!directory_de.has_value())
|
||||
return {};
|
||||
|
||||
if ((directory_de->flags & ISODirectoryEntryFlag_Directory) == 0)
|
||||
{
|
||||
Error::SetString(error, fmt::format("Path '{}' is not a directory, can't list", path));
|
||||
return {};
|
||||
}
|
||||
|
||||
directory_record_lsn = directory_de->location_le;
|
||||
directory_record_length = directory_de->length_le;
|
||||
|
||||
if (base_path[base_path.size() - 1] != '/')
|
||||
base_path += '/';
|
||||
}
|
||||
|
||||
// start reading directory entries
|
||||
const u32 num_sectors = (directory_record_length + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
|
||||
std::vector<std::string> files;
|
||||
u8 sector_buffer[SECTOR_SIZE];
|
||||
for (u32 i = 0; i < num_sectors; i++)
|
||||
{
|
||||
if (!ReadSector(sector_buffer, directory_record_lsn + i, error))
|
||||
break;
|
||||
|
||||
u32 sector_offset = 0;
|
||||
while ((sector_offset + sizeof(ISODirectoryEntry)) < SECTOR_SIZE)
|
||||
{
|
||||
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(§or_buffer[sector_offset]);
|
||||
if (de->entry_length < sizeof(ISODirectoryEntry))
|
||||
break;
|
||||
|
||||
const std::string_view de_filename = GetDirectoryEntryFileName(sector_buffer, sector_offset);
|
||||
sector_offset += de->entry_length;
|
||||
|
||||
// Empty file would be pretty strange..
|
||||
if (de_filename.empty() || de_filename == "." || de_filename == "..")
|
||||
continue;
|
||||
|
||||
files.push_back(fmt::format("{}/{}", base_path, de_filename));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
bool IsoReader::FileExists(const std::string_view& path, Error* error)
|
||||
{
|
||||
auto de = LocateFile(path, error);
|
||||
if (!de)
|
||||
return false;
|
||||
|
||||
return (de->flags & ISODirectoryEntryFlag_Directory) == 0;
|
||||
}
|
||||
|
||||
bool IsoReader::DirectoryExists(const std::string_view& path, Error* error)
|
||||
{
|
||||
auto de = LocateFile(path, error);
|
||||
if (!de)
|
||||
return false;
|
||||
|
||||
return (de->flags & ISODirectoryEntryFlag_Directory) == ISODirectoryEntryFlag_Directory;
|
||||
}
|
||||
|
||||
bool IsoReader::ReadFile(const std::string_view& path, std::vector<u8>* data, Error* error)
|
||||
{
|
||||
auto de = LocateFile(path, error);
|
||||
if (!de)
|
||||
return false;
|
||||
|
||||
return ReadFile(de.value(), data, error);
|
||||
}
|
||||
|
||||
bool IsoReader::ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, Error* error /*= nullptr*/)
|
||||
{
|
||||
if (de.flags & ISODirectoryEntryFlag_Directory)
|
||||
{
|
||||
Error::SetString(error, "File is a directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (de.length_le == 0)
|
||||
{
|
||||
data->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(sizeof(size_t) == sizeof(u64));
|
||||
const u32 num_sectors = (de.length_le + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
|
||||
data->resize(num_sectors * static_cast<u64>(SECTOR_SIZE));
|
||||
for (u32 i = 0, lsn = de.location_le; i < num_sectors; i++, lsn++)
|
||||
{
|
||||
if (!ReadSector(data->data() + (i * SECTOR_SIZE), lsn, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Might not be sector aligned, so reduce it back.
|
||||
data->resize(de.length_le);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
|
||||
class IsoReader
|
||||
{
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
SECTOR_SIZE = 2048
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ISOVolumeDescriptorHeader
|
||||
{
|
||||
u8 type_code;
|
||||
char standard_identifier[5];
|
||||
u8 version;
|
||||
};
|
||||
static_assert(sizeof(ISOVolumeDescriptorHeader) == 7);
|
||||
|
||||
struct ISOBootRecord
|
||||
{
|
||||
ISOVolumeDescriptorHeader header;
|
||||
char boot_system_identifier[32];
|
||||
char boot_identifier[32];
|
||||
u8 data[1977];
|
||||
};
|
||||
static_assert(sizeof(ISOBootRecord) == 2048);
|
||||
|
||||
struct ISOPVDDateTime
|
||||
{
|
||||
char year[4];
|
||||
char month[2];
|
||||
char day[2];
|
||||
char hour[2];
|
||||
char minute[2];
|
||||
char second[2];
|
||||
char milliseconds[2];
|
||||
s8 gmt_offset;
|
||||
};
|
||||
static_assert(sizeof(ISOPVDDateTime) == 17);
|
||||
|
||||
struct ISOPrimaryVolumeDescriptor
|
||||
{
|
||||
ISOVolumeDescriptorHeader header;
|
||||
u8 unused;
|
||||
char system_identifier[32];
|
||||
char volume_identifier[32];
|
||||
char unused2[8];
|
||||
u32 total_sectors_le;
|
||||
u32 total_sectors_be;
|
||||
char unused3[32];
|
||||
u16 volume_set_size_le;
|
||||
u16 volume_set_size_be;
|
||||
u16 volume_sequence_number_le;
|
||||
u16 volume_sequence_number_be;
|
||||
u16 block_size_le;
|
||||
u16 block_size_be;
|
||||
u32 path_table_size_le;
|
||||
u32 path_table_size_be;
|
||||
u32 path_table_location_le;
|
||||
u32 optional_path_table_location_le;
|
||||
u32 path_table_location_be;
|
||||
u32 optional_path_table_location_be;
|
||||
u8 root_directory_entry[34];
|
||||
char volume_set_identifier[128];
|
||||
char publisher_identifier[128];
|
||||
char data_preparer_identifier[128];
|
||||
char application_identifier[128];
|
||||
char copyright_file_identifier[38];
|
||||
char abstract_file_identifier[36];
|
||||
char bibliographic_file_identifier[37];
|
||||
ISOPVDDateTime volume_creation_time;
|
||||
ISOPVDDateTime volume_modification_time;
|
||||
ISOPVDDateTime volume_expiration_time;
|
||||
ISOPVDDateTime volume_effective_time;
|
||||
u8 structure_version;
|
||||
u8 unused4;
|
||||
u8 application_used[512];
|
||||
u8 reserved[653];
|
||||
};
|
||||
static_assert(sizeof(ISOPrimaryVolumeDescriptor) == 2048);
|
||||
|
||||
struct ISODirectoryEntryDateTime
|
||||
{
|
||||
u8 years_since_1900;
|
||||
u8 month;
|
||||
u8 day;
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
s8 gmt_offset;
|
||||
};
|
||||
|
||||
enum ISODirectoryEntryFlags : u8
|
||||
{
|
||||
ISODirectoryEntryFlag_Hidden = (1 << 0),
|
||||
ISODirectoryEntryFlag_Directory = (1 << 1),
|
||||
ISODirectoryEntryFlag_AssociatedFile = (1 << 2),
|
||||
ISODirectoryEntryFlag_ExtendedAttributePresent = (1 << 3),
|
||||
ISODirectoryEntryFlag_OwnerGroupPermissions = (1 << 4),
|
||||
ISODirectoryEntryFlag_MoreExtents = (1 << 7),
|
||||
};
|
||||
|
||||
struct ISODirectoryEntry
|
||||
{
|
||||
u8 entry_length;
|
||||
u8 extended_attribute_length;
|
||||
u32 location_le;
|
||||
u32 location_be;
|
||||
u32 length_le;
|
||||
u32 length_be;
|
||||
ISODirectoryEntryDateTime recoding_time;
|
||||
ISODirectoryEntryFlags flags;
|
||||
u8 interleaved_unit_size;
|
||||
u8 interleaved_gap_size;
|
||||
u16 sequence_le;
|
||||
u16 sequence_be;
|
||||
u8 filename_length;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
IsoReader();
|
||||
~IsoReader();
|
||||
|
||||
const ISOPrimaryVolumeDescriptor& GetPVD() const { return m_pvd; }
|
||||
|
||||
// TODO: Eventually we'll want to pass a handle to the currently-open file here.
|
||||
// ... once I have the energy to make CDVD not depend on a global object.
|
||||
bool Open(Error* error = nullptr);
|
||||
|
||||
std::vector<std::string> GetFilesInDirectory(const std::string_view& path, Error* error = nullptr);
|
||||
|
||||
std::optional<ISODirectoryEntry> LocateFile(const std::string_view& path, Error* error);
|
||||
|
||||
bool FileExists(const std::string_view& path, Error* error = nullptr);
|
||||
bool DirectoryExists(const std::string_view& path, Error* error = nullptr);
|
||||
bool ReadFile(const std::string_view& path, std::vector<u8>* data, Error* error = nullptr);
|
||||
bool ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, Error* error = nullptr);
|
||||
|
||||
private:
|
||||
static std::string_view GetDirectoryEntryFileName(const u8* sector, u32 de_sector_offset);
|
||||
|
||||
bool ReadSector(u8* buf, u32 lsn, Error* error);
|
||||
bool ReadPVD(Error* error);
|
||||
|
||||
std::optional<ISODirectoryEntry> LocateFile(const std::string_view& path, u8* sector_buffer,
|
||||
u32 directory_record_lba, u32 directory_record_size, Error* error);
|
||||
|
||||
ISOPrimaryVolumeDescriptor m_pvd = {};
|
||||
};
|
|
@ -247,6 +247,7 @@ set(pcsx2CDVDSources
|
|||
CDVD/CDVDisoReader.cpp
|
||||
CDVD/CDVDdiscThread.cpp
|
||||
CDVD/InputIsoFile.cpp
|
||||
CDVD/IsoReader.cpp
|
||||
CDVD/OutputIsoFile.cpp
|
||||
CDVD/ChunksCache.cpp
|
||||
CDVD/CompressedFileReader.cpp
|
||||
|
@ -254,9 +255,6 @@ set(pcsx2CDVDSources
|
|||
CDVD/CsoFileReader.cpp
|
||||
CDVD/GzippedFileReader.cpp
|
||||
CDVD/ThreadedFileReader.cpp
|
||||
CDVD/IsoFS/IsoFile.cpp
|
||||
CDVD/IsoFS/IsoFSCDVD.cpp
|
||||
CDVD/IsoFS/IsoFS.cpp
|
||||
)
|
||||
|
||||
# CDVD headers
|
||||
|
@ -273,12 +271,7 @@ set(pcsx2CDVDHeaders
|
|||
CDVD/GzippedFileReader.h
|
||||
CDVD/ThreadedFileReader.h
|
||||
CDVD/IsoFileFormats.h
|
||||
CDVD/IsoFS/IsoDirectory.h
|
||||
CDVD/IsoFS/IsoFileDescriptor.h
|
||||
CDVD/IsoFS/IsoFile.h
|
||||
CDVD/IsoFS/IsoFSCDVD.h
|
||||
CDVD/IsoFS/IsoFS.h
|
||||
CDVD/IsoFS/SectorSource.h
|
||||
CDVD/IsoReader.h
|
||||
CDVD/zlib_indexed.h
|
||||
)
|
||||
|
||||
|
|
|
@ -14,14 +14,16 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include "Elfheader.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "GS.h" // for sending game crc to mtgs
|
||||
#include "Elfheader.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PSXEXEHeader
|
||||
{
|
||||
|
@ -47,20 +49,17 @@ ElfObject::ElfObject() = default;
|
|||
|
||||
ElfObject::~ElfObject() = default;
|
||||
|
||||
bool ElfObject::OpenIsoFile(std::string srcfile, IsoFile& isofile, bool isPSXElf_, Error* error)
|
||||
bool ElfObject::OpenIsoFile(std::string srcfile, IsoReader& isor, bool isPSXElf_, Error* error)
|
||||
{
|
||||
const u32 length = isofile.getLength();
|
||||
if (!CheckElfSize(length, error))
|
||||
const auto de = isor.LocateFile(srcfile, error);
|
||||
if (!de)
|
||||
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");
|
||||
if (!CheckElfSize(de->length_le, error))
|
||||
return false;
|
||||
|
||||
if (!isor.ReadFile(de.value(), &data, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
filename = std::move(srcfile);
|
||||
isPSXElf = isPSXElf_;
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/Error.h"
|
||||
#include "CDVD/IsoFS/IsoFSCDVD.h"
|
||||
#include "CDVD/IsoFS/IsoFS.h"
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
class IsoReader;
|
||||
|
||||
struct ELF_HEADER {
|
||||
u8 e_ident[16]; //0x7f,"ELF" (ELF file identifier)
|
||||
u16 e_type; //ELF type: 0=NONE, 1=REL, 2=EXEC, 3=SHARED, 4=CORE
|
||||
|
@ -130,7 +130,7 @@ public:
|
|||
__fi u32 GetSize() const { return static_cast<u32>(data.size()); }
|
||||
|
||||
bool OpenFile(std::string srcfile, bool isPSXElf_, Error* error);
|
||||
bool OpenIsoFile(std::string srcfile, IsoFile& isofile, bool isPSXElf_, Error* error);
|
||||
bool OpenIsoFile(std::string srcfile, IsoReader& isor, bool isPSXElf_, Error* error);
|
||||
|
||||
void LoadHeaders();
|
||||
|
||||
|
|
|
@ -949,9 +949,11 @@ void VMManager::UpdateELFInfo(std::string elf_path)
|
|||
{
|
||||
Error error;
|
||||
ElfObject elfo;
|
||||
if (!cdvdLoadElf(&elfo, elf_path, false, &error))
|
||||
if (elf_path.empty() || !cdvdLoadElf(&elfo, elf_path, false, &error))
|
||||
{
|
||||
Console.Error(fmt::format("Failed to read ELF being loaded: {}: {}", elf_path, error.GetDescription()));
|
||||
if (!elf_path.empty())
|
||||
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;
|
||||
|
@ -1039,7 +1041,6 @@ bool VMManager::AutoDetectSource(const std::string& filename)
|
|||
{
|
||||
// make sure we're not fast booting when we have no filename
|
||||
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
|
||||
s_fast_boot_requested = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1157,6 +1158,7 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
|||
// ELFs must be fast booted, and GS dumps are never fast booted.
|
||||
s_fast_boot_requested =
|
||||
(boot_params.fast_boot.value_or(static_cast<bool>(EmuConfig.EnableFastBoot)) || !s_elf_override.empty()) &&
|
||||
(CDVDsys_GetSourceType() != CDVD_SourceType::NoDisc || !s_elf_override.empty()) &&
|
||||
!GSDumpReplayer::IsReplayingDump();
|
||||
|
||||
if (!s_elf_override.empty())
|
||||
|
@ -1169,7 +1171,6 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
|||
}
|
||||
|
||||
Hle_SetElfPath(s_elf_override.c_str());
|
||||
s_fast_boot_requested = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
<ClCompile Include="CDVD\CompressedFileReader.cpp" />
|
||||
<ClCompile Include="CDVD\CsoFileReader.cpp" />
|
||||
<ClCompile Include="CDVD\GzippedFileReader.cpp" />
|
||||
<ClCompile Include="CDVD\IsoReader.cpp" />
|
||||
<ClCompile Include="CDVD\OutputIsoFile.cpp" />
|
||||
<ClCompile Include="CDVD\ThreadedFileReader.cpp" />
|
||||
<ClCompile Include="CDVD\Linux\DriveUtility.cpp">
|
||||
|
@ -466,9 +467,6 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="x86\ix86-32\iCore-32.cpp" />
|
||||
<ClCompile Include="x86\iCore.cpp" />
|
||||
<ClCompile Include="CDVD\IsoFS\IsoFile.cpp" />
|
||||
<ClCompile Include="CDVD\IsoFS\IsoFS.cpp" />
|
||||
<ClCompile Include="CDVD\IsoFS\IsoFSCDVD.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Achievements.h" />
|
||||
|
@ -480,6 +478,7 @@
|
|||
<ClInclude Include="CDVD\CsoFileReader.h" />
|
||||
<ClInclude Include="CDVD\ChdFileReader.h" />
|
||||
<ClInclude Include="CDVD\GzippedFileReader.h" />
|
||||
<ClInclude Include="CDVD\IsoReader.h" />
|
||||
<ClInclude Include="CDVD\ThreadedFileReader.h" />
|
||||
<ClInclude Include="CDVD\zlib_indexed.h" />
|
||||
<ClInclude Include="DebugTools\Breakpoints.h" />
|
||||
|
@ -802,12 +801,6 @@
|
|||
<ClInclude Include="ps2\BiosTools.h" />
|
||||
<ClInclude Include="MemoryTypes.h" />
|
||||
<ClInclude Include="x86\iCore.h" />
|
||||
<ClInclude Include="CDVD\IsoFS\IsoDirectory.h" />
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFile.h" />
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFileDescriptor.h" />
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFS.h" />
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFSCDVD.h" />
|
||||
<ClInclude Include="CDVD\IsoFS\SectorSource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\fmt\fmt.vcxproj">
|
||||
|
|
|
@ -680,15 +680,6 @@
|
|||
<ClCompile Include="x86\iCore.cpp">
|
||||
<Filter>System\Ps2\iCore</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CDVD\IsoFS\IsoFile.cpp">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CDVD\IsoFS\IsoFS.cpp">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CDVD\IsoFS\IsoFSCDVD.cpp">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GameDatabase.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1406,6 +1397,7 @@
|
|||
<ClCompile Include="ImGui\ImGuiFullscreen.cpp">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CDVD\IsoReader.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Patch.h">
|
||||
|
@ -1636,24 +1628,6 @@
|
|||
<ClInclude Include="x86\iCore.h">
|
||||
<Filter>System\Ps2\iCore</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDVD\IsoFS\IsoDirectory.h">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFile.h">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFileDescriptor.h">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFS.h">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDVD\IsoFS\IsoFSCDVD.h">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDVD\IsoFS\SectorSource.h">
|
||||
<Filter>System\IsoFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GameDatabase.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
|
@ -2354,6 +2328,7 @@
|
|||
<ClInclude Include="ImGui\ImGuiFullscreen.h">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDVD\IsoReader.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuildStep Include="rdebug\deci2.h">
|
||||
|
|
Loading…
Reference in New Issue