2024-05-25 05:45:17 +00:00
|
|
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
2024-09-01 12:08:31 +00:00
|
|
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
2022-12-04 11:03:45 +00:00
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
#include "cd_image.h"
|
2023-11-18 06:21:51 +00:00
|
|
|
|
2022-07-08 12:43:38 +00:00
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/file_system.h"
|
|
|
|
#include "common/log.h"
|
2024-05-25 05:45:17 +00:00
|
|
|
#include "common/path.h"
|
2023-11-18 06:21:51 +00:00
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cerrno>
|
|
|
|
#include <map>
|
|
|
|
#include <unordered_map>
|
2023-11-18 06:21:51 +00:00
|
|
|
|
2024-10-31 04:22:41 +00:00
|
|
|
LOG_CHANNEL(CDImage);
|
2021-06-01 11:42:50 +00:00
|
|
|
|
2023-11-18 06:21:51 +00:00
|
|
|
namespace {
|
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
enum : u32
|
|
|
|
{
|
|
|
|
DESC_SIZE = 50,
|
|
|
|
BLOCKCHECK_SIZE = 1024
|
|
|
|
};
|
|
|
|
|
|
|
|
class CDImagePPF : public CDImage
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CDImagePPF();
|
|
|
|
~CDImagePPF() override;
|
|
|
|
|
|
|
|
bool Open(const char* filename, std::unique_ptr<CDImage> parent_image);
|
|
|
|
|
|
|
|
bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override;
|
2024-10-25 05:24:43 +00:00
|
|
|
bool HasSubchannelData() const override;
|
2023-12-20 13:40:24 +00:00
|
|
|
s64 GetSizeOnDisk() const override;
|
2021-06-01 11:42:50 +00:00
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
std::string GetMetadata(std::string_view type) const override;
|
|
|
|
std::string GetSubImageMetadata(u32 index, std::string_view type) const override;
|
2021-06-01 11:42:50 +00:00
|
|
|
|
2022-07-23 03:00:45 +00:00
|
|
|
PrecacheResult Precache(ProgressCallback* progress = ProgressCallback::NullProgressCallback) override;
|
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
protected:
|
|
|
|
bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool ReadV1Patch(std::FILE* fp);
|
|
|
|
bool ReadV2Patch(std::FILE* fp);
|
|
|
|
bool ReadV3Patch(std::FILE* fp);
|
|
|
|
u32 ReadFileIDDiz(std::FILE* fp, u32 version);
|
|
|
|
|
|
|
|
bool AddPatch(u64 offset, const u8* patch, u32 patch_size);
|
|
|
|
|
|
|
|
std::unique_ptr<CDImage> m_parent_image;
|
|
|
|
std::vector<u8> m_replacement_data;
|
|
|
|
std::unordered_map<u32, u32> m_replacement_map;
|
2023-12-20 13:40:24 +00:00
|
|
|
s64 m_patch_size = 0;
|
2021-06-01 11:42:50 +00:00
|
|
|
u32 m_replacement_offset = 0;
|
|
|
|
};
|
|
|
|
|
2023-11-18 06:21:51 +00:00
|
|
|
} // namespace
|
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
CDImagePPF::CDImagePPF() = default;
|
|
|
|
|
|
|
|
CDImagePPF::~CDImagePPF() = default;
|
|
|
|
|
|
|
|
bool CDImagePPF::Open(const char* filename, std::unique_ptr<CDImage> parent_image)
|
|
|
|
{
|
2024-05-17 04:42:37 +00:00
|
|
|
auto fp = FileSystem::OpenManagedSharedCFile(filename, "rb", FileSystem::FileShareMode::DenyWrite);
|
2021-06-01 11:42:50 +00:00
|
|
|
if (!fp)
|
|
|
|
{
|
2024-05-25 05:45:17 +00:00
|
|
|
ERROR_LOG("Failed to open '{}'", Path::GetFileName(filename));
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-20 13:40:24 +00:00
|
|
|
m_patch_size = FileSystem::FSize64(fp.get());
|
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
u32 magic;
|
|
|
|
if (std::fread(&magic, sizeof(magic), 1, fp.get()) != 1)
|
|
|
|
{
|
2024-05-25 05:45:17 +00:00
|
|
|
ERROR_LOG("Failed to read magic from '{}'", Path::GetFileName(filename));
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// work out the offset from the start of the parent image which we need to patch
|
|
|
|
// i.e. the two second implicit pregap on data sectors
|
|
|
|
if (parent_image->GetTrack(1).mode != TrackMode::Audio)
|
|
|
|
m_replacement_offset = parent_image->GetIndex(1).start_lba_on_disc;
|
|
|
|
|
|
|
|
// copy all the stuff from the parent image
|
2024-10-25 05:24:43 +00:00
|
|
|
m_filename = parent_image->GetPath();
|
2021-06-01 11:42:50 +00:00
|
|
|
m_tracks = parent_image->GetTracks();
|
|
|
|
m_indices = parent_image->GetIndices();
|
|
|
|
m_parent_image = std::move(parent_image);
|
|
|
|
|
2021-06-03 13:03:06 +00:00
|
|
|
if (magic == 0x33465050) // PPF3
|
2021-06-01 11:42:50 +00:00
|
|
|
return ReadV3Patch(fp.get());
|
2021-06-03 13:03:06 +00:00
|
|
|
else if (magic == 0x32465050) // PPF2
|
2021-06-01 11:42:50 +00:00
|
|
|
return ReadV2Patch(fp.get());
|
2021-06-03 13:03:06 +00:00
|
|
|
else if (magic == 0x31465050) // PPF1
|
2021-06-01 11:42:50 +00:00
|
|
|
return ReadV1Patch(fp.get());
|
|
|
|
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Unknown PPF magic {:08X}", magic);
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 CDImagePPF::ReadFileIDDiz(std::FILE* fp, u32 version)
|
|
|
|
{
|
|
|
|
const int lenidx = (version == 2) ? 4 : 2;
|
|
|
|
|
|
|
|
u32 magic;
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fseek(fp, -(lenidx + 4), SEEK_END) != 0 || std::fread(&magic, sizeof(magic), 1, fp) != 1) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
WARNING_LOG("Failed to read diz magic");
|
2021-06-01 11:42:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-06-03 13:03:06 +00:00
|
|
|
if (magic != 0x5A49442E) // .DIZ
|
2021-06-01 11:42:50 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
u32 dlen = 0;
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fseek(fp, -lenidx, SEEK_END) != 0 || std::fread(&dlen, lenidx, 1, fp) != 1) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
WARNING_LOG("Failed to read diz length");
|
2021-06-01 11:42:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-05-23 10:20:16 +00:00
|
|
|
if (dlen > static_cast<u32>(std::ftell(fp))) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
WARNING_LOG("diz length out of range");
|
2021-06-01 11:42:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string fdiz;
|
|
|
|
fdiz.resize(dlen);
|
|
|
|
if (std::fseek(fp, -(lenidx + 16 + static_cast<int>(dlen)), SEEK_END) != 0 ||
|
2024-05-23 10:20:16 +00:00
|
|
|
std::fread(fdiz.data(), 1, dlen, fp) != dlen) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
WARNING_LOG("Failed to read fdiz");
|
2021-06-01 11:42:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-05-25 05:45:17 +00:00
|
|
|
INFO_LOG("File_Id.diz: {}", fdiz);
|
2021-06-01 11:42:50 +00:00
|
|
|
return dlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDImagePPF::ReadV1Patch(std::FILE* fp)
|
|
|
|
{
|
|
|
|
char desc[DESC_SIZE + 1] = {};
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fseek(fp, 6, SEEK_SET) != 0 || std::fread(desc, sizeof(char), DESC_SIZE, fp) != DESC_SIZE) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read description");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 filelen;
|
|
|
|
if (std::fseek(fp, 0, SEEK_END) != 0 || (filelen = static_cast<u32>(std::ftell(fp))) == 0 || filelen < 56)
|
2024-05-23 10:20:16 +00:00
|
|
|
[[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Invalid ppf file");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 count = filelen - 56;
|
|
|
|
if (count <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (std::fseek(fp, 56, SEEK_SET) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::vector<u8> temp;
|
|
|
|
while (count > 0)
|
|
|
|
{
|
|
|
|
u32 offset;
|
|
|
|
u8 chunk_size;
|
|
|
|
if (std::fread(&offset, sizeof(offset), 1, fp) != 1 || std::fread(&chunk_size, sizeof(chunk_size), 1, fp) != 1)
|
2024-05-23 10:20:16 +00:00
|
|
|
[[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Incomplete ppf");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp.resize(chunk_size);
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fread(temp.data(), 1, chunk_size, fp) != chunk_size) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read patch data");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-05-23 10:20:16 +00:00
|
|
|
if (!AddPatch(offset, temp.data(), chunk_size)) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
count -= sizeof(offset) + sizeof(chunk_size) + chunk_size;
|
|
|
|
}
|
|
|
|
|
2024-05-23 10:55:28 +00:00
|
|
|
INFO_LOG("Loaded {} replacement sectors from version 1 PPF", m_replacement_map.size());
|
2021-06-01 11:42:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDImagePPF::ReadV2Patch(std::FILE* fp)
|
|
|
|
{
|
|
|
|
char desc[DESC_SIZE + 1] = {};
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fseek(fp, 6, SEEK_SET) != 0 || std::fread(desc, sizeof(char), DESC_SIZE, fp) != DESC_SIZE) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read description");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-05-25 05:45:17 +00:00
|
|
|
INFO_LOG("Patch description: {}", desc);
|
2021-06-01 11:42:50 +00:00
|
|
|
|
|
|
|
const u32 idlen = ReadFileIDDiz(fp, 2);
|
|
|
|
|
|
|
|
u32 origlen;
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fseek(fp, 56, SEEK_SET) != 0 || std::fread(&origlen, sizeof(origlen), 1, fp) != 1) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read size");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<u8> temp;
|
|
|
|
temp.resize(BLOCKCHECK_SIZE);
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fread(temp.data(), 1, BLOCKCHECK_SIZE, fp) != BLOCKCHECK_SIZE) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read blockcheck data");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do blockcheck
|
|
|
|
{
|
|
|
|
u32 blockcheck_src_sector = 16 + m_replacement_offset;
|
|
|
|
u32 blockcheck_src_offset = 32;
|
|
|
|
|
|
|
|
std::vector<u8> src_sector(RAW_SECTOR_SIZE);
|
|
|
|
if (m_parent_image->Seek(blockcheck_src_sector) && m_parent_image->ReadRawSector(src_sector.data(), nullptr))
|
|
|
|
{
|
|
|
|
if (std::memcmp(&src_sector[blockcheck_src_offset], temp.data(), BLOCKCHECK_SIZE) != 0)
|
2024-05-23 10:55:28 +00:00
|
|
|
WARNING_LOG("Blockcheck failed. The patch may not apply correctly.");
|
2021-06-01 11:42:50 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
WARNING_LOG("Failed to read blockcheck sector {}", blockcheck_src_sector);
|
2021-06-01 11:42:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 filelen;
|
|
|
|
if (std::fseek(fp, 0, SEEK_END) != 0 || (filelen = static_cast<u32>(std::ftell(fp))) == 0 || filelen < 1084)
|
2024-05-23 10:20:16 +00:00
|
|
|
[[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Invalid ppf file");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 count = filelen - 1084;
|
|
|
|
if (idlen > 0)
|
|
|
|
count -= (idlen + 38);
|
|
|
|
|
|
|
|
if (count <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (std::fseek(fp, 1084, SEEK_SET) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
while (count > 0)
|
|
|
|
{
|
|
|
|
u32 offset;
|
|
|
|
u8 chunk_size;
|
|
|
|
if (std::fread(&offset, sizeof(offset), 1, fp) != 1 || std::fread(&chunk_size, sizeof(chunk_size), 1, fp) != 1)
|
2024-05-23 10:20:16 +00:00
|
|
|
[[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Incomplete ppf");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp.resize(chunk_size);
|
2024-05-23 10:20:16 +00:00
|
|
|
if (std::fread(temp.data(), 1, chunk_size, fp) != chunk_size) [[unlikely]]
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read patch data");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AddPatch(offset, temp.data(), chunk_size))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
count -= sizeof(offset) + sizeof(chunk_size) + chunk_size;
|
|
|
|
}
|
|
|
|
|
2024-05-23 10:55:28 +00:00
|
|
|
INFO_LOG("Loaded {} replacement sectors from version 2 PPF", m_replacement_map.size());
|
2021-06-01 11:42:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
|
|
|
{
|
|
|
|
char desc[DESC_SIZE + 1] = {};
|
|
|
|
if (std::fseek(fp, 6, SEEK_SET) != 0 || std::fread(desc, sizeof(char), DESC_SIZE, fp) != DESC_SIZE)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read description");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-05-23 10:55:28 +00:00
|
|
|
INFO_LOG("Patch description: {}", desc);
|
2021-06-01 11:42:50 +00:00
|
|
|
|
|
|
|
u32 idlen = ReadFileIDDiz(fp, 3);
|
|
|
|
|
|
|
|
u8 image_type;
|
|
|
|
u8 block_check;
|
|
|
|
u8 undo;
|
|
|
|
if (std::fseek(fp, 56, SEEK_SET) != 0 || std::fread(&image_type, sizeof(image_type), 1, fp) != 1 ||
|
|
|
|
std::fread(&block_check, sizeof(block_check), 1, fp) != 1 || std::fread(&undo, sizeof(undo), 1, fp) != 1)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read headers");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Blockcheck
|
|
|
|
|
|
|
|
std::fseek(fp, 0, SEEK_END);
|
|
|
|
u32 count = static_cast<u32>(std::ftell(fp));
|
|
|
|
|
|
|
|
u32 seekpos = (block_check) ? 1084 : 60;
|
|
|
|
if (seekpos >= count)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("File is too short");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
count -= seekpos;
|
|
|
|
if (idlen > 0)
|
|
|
|
{
|
|
|
|
const u32 extralen = idlen + 18 + 16 + 2;
|
|
|
|
if (count < extralen)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("File is too short (diz)");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
count -= extralen;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::fseek(fp, seekpos, SEEK_SET) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::vector<u8> temp;
|
|
|
|
|
|
|
|
while (count > 0)
|
|
|
|
{
|
|
|
|
u64 offset;
|
|
|
|
u8 chunk_size;
|
|
|
|
if (std::fread(&offset, sizeof(offset), 1, fp) != 1 || std::fread(&chunk_size, sizeof(chunk_size), 1, fp) != 1)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Incomplete ppf");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp.resize(chunk_size);
|
|
|
|
if (std::fread(temp.data(), 1, chunk_size, fp) != chunk_size)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read patch data");
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AddPatch(offset, temp.data(), chunk_size))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
count -= sizeof(offset) + sizeof(chunk_size) + chunk_size;
|
|
|
|
}
|
|
|
|
|
2024-05-23 10:55:28 +00:00
|
|
|
INFO_LOG("Loaded {} replacement sectors from version 3 PPF", m_replacement_map.size());
|
2021-06-01 11:42:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDImagePPF::AddPatch(u64 offset, const u8* patch, u32 patch_size)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
DEBUG_LOG("Starting applying patch of {} bytes at at offset {}", patch_size, offset);
|
2021-06-01 11:42:50 +00:00
|
|
|
|
|
|
|
while (patch_size > 0)
|
|
|
|
{
|
|
|
|
const u32 sector_index = Truncate32(offset / RAW_SECTOR_SIZE) + m_replacement_offset;
|
|
|
|
const u32 sector_offset = Truncate32(offset % RAW_SECTOR_SIZE);
|
|
|
|
if (sector_index >= m_parent_image->GetLBACount())
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Sector {} in patch is out of range", sector_index);
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const u32 bytes_to_patch = std::min(patch_size, RAW_SECTOR_SIZE - sector_offset);
|
|
|
|
|
|
|
|
auto iter = m_replacement_map.find(sector_index);
|
|
|
|
if (iter == m_replacement_map.end())
|
|
|
|
{
|
|
|
|
const u32 replacement_buffer_start = static_cast<u32>(m_replacement_data.size());
|
|
|
|
m_replacement_data.resize(m_replacement_data.size() + RAW_SECTOR_SIZE);
|
|
|
|
if (!m_parent_image->Seek(sector_index) ||
|
|
|
|
!m_parent_image->ReadRawSector(&m_replacement_data[replacement_buffer_start], nullptr))
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to read sector {} from parent image", sector_index);
|
2021-06-01 11:42:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
iter = m_replacement_map.emplace(sector_index, replacement_buffer_start).first;
|
|
|
|
}
|
|
|
|
|
|
|
|
// patch it!
|
2024-05-23 10:55:28 +00:00
|
|
|
DEBUG_LOG(" Patching {} bytes at sector {} offset {}", bytes_to_patch, sector_index, sector_offset);
|
2021-06-01 11:42:50 +00:00
|
|
|
std::memcpy(&m_replacement_data[iter->second + sector_offset], patch, bytes_to_patch);
|
|
|
|
offset += bytes_to_patch;
|
|
|
|
patch += bytes_to_patch;
|
|
|
|
patch_size -= bytes_to_patch;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDImagePPF::ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index)
|
|
|
|
{
|
|
|
|
return m_parent_image->ReadSubChannelQ(subq, index, lba_in_index);
|
|
|
|
}
|
|
|
|
|
2024-10-25 05:24:43 +00:00
|
|
|
bool CDImagePPF::HasSubchannelData() const
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
2024-10-25 05:24:43 +00:00
|
|
|
return m_parent_image->HasSubchannelData();
|
2021-06-01 11:42:50 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
std::string CDImagePPF::GetMetadata(std::string_view type) const
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
|
|
|
return m_parent_image->GetMetadata(type);
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
std::string CDImagePPF::GetSubImageMetadata(u32 index, std::string_view type) const
|
2021-06-01 11:42:50 +00:00
|
|
|
{
|
|
|
|
// We only support a single sub-image for patched games.
|
|
|
|
std::string ret;
|
|
|
|
if (index == 0)
|
|
|
|
ret = m_parent_image->GetSubImageMetadata(index, type);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-07-23 03:00:45 +00:00
|
|
|
CDImage::PrecacheResult CDImagePPF::Precache(ProgressCallback* progress /*= ProgressCallback::NullProgressCallback*/)
|
|
|
|
{
|
|
|
|
return m_parent_image->Precache(progress);
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
bool CDImagePPF::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index)
|
|
|
|
{
|
|
|
|
DebugAssert(index.file_index == 0);
|
|
|
|
|
|
|
|
const u32 sector_number = index.start_lba_on_disc + lba_in_index;
|
|
|
|
const auto it = m_replacement_map.find(sector_number);
|
|
|
|
if (it == m_replacement_map.end())
|
|
|
|
return m_parent_image->ReadSectorFromIndex(buffer, index, lba_in_index);
|
|
|
|
|
|
|
|
std::memcpy(buffer, &m_replacement_data[it->second], RAW_SECTOR_SIZE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-12-20 13:40:24 +00:00
|
|
|
s64 CDImagePPF::GetSizeOnDisk() const
|
|
|
|
{
|
|
|
|
return m_patch_size + m_parent_image->GetSizeOnDisk();
|
|
|
|
}
|
|
|
|
|
2021-06-01 11:42:50 +00:00
|
|
|
std::unique_ptr<CDImage>
|
|
|
|
CDImage::OverlayPPFPatch(const char* filename, std::unique_ptr<CDImage> parent_image,
|
|
|
|
ProgressCallback* progress /* = ProgressCallback::NullProgressCallback */)
|
|
|
|
{
|
2021-06-05 08:12:36 +00:00
|
|
|
std::unique_ptr<CDImagePPF> ppf_image = std::make_unique<CDImagePPF>();
|
|
|
|
if (!ppf_image->Open(filename, std::move(parent_image)))
|
2021-06-01 11:42:50 +00:00
|
|
|
return {};
|
|
|
|
|
2021-06-05 08:12:36 +00:00
|
|
|
return ppf_image;
|
2021-06-01 11:42:50 +00:00
|
|
|
}
|