mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b89020082 | ||
|
|
a7b07eb53f | ||
|
|
58d13dac34 | ||
|
|
5a8921dd22 | ||
|
|
f964dfaa5e | ||
|
|
17274eb397 | ||
|
|
2f0b00a7a1 | ||
|
|
260380abec |
@@ -22,7 +22,7 @@ jobs:
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
title: "PAD: Update to latest controller database"
|
||||
commit-message: "PAD: Update to latest controller database."
|
||||
commit-message: "[ci skip] PAD: Update to latest controller database."
|
||||
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
|
||||
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
|
||||
body: "Weekly automatic update of SDL Controller DB."
|
||||
|
||||
8
3rdparty/libchdr/include/libchdr/chd.h
vendored
8
3rdparty/libchdr/include/libchdr/chd.h
vendored
@@ -290,8 +290,7 @@ enum _chd_error
|
||||
CHDERR_INVALID_STATE,
|
||||
CHDERR_OPERATION_PENDING,
|
||||
CHDERR_NO_ASYNC_OPERATION,
|
||||
CHDERR_UNSUPPORTED_FORMAT,
|
||||
CHDERR_CANCELLED,
|
||||
CHDERR_UNSUPPORTED_FORMAT
|
||||
};
|
||||
typedef enum _chd_error chd_error;
|
||||
|
||||
@@ -383,7 +382,6 @@ CHD_EXPORT chd_error chd_open(const char *filename, int mode, chd_file *parent,
|
||||
|
||||
/* precache underlying file */
|
||||
CHD_EXPORT chd_error chd_precache(chd_file *chd);
|
||||
CHD_EXPORT chd_error chd_precache_progress(chd_file* chd, bool(*progress)(size_t pos, size_t total, void* param), void* param);
|
||||
|
||||
/* close a CHD file */
|
||||
CHD_EXPORT void chd_close(chd_file *chd);
|
||||
@@ -391,9 +389,6 @@ CHD_EXPORT void chd_close(chd_file *chd);
|
||||
/* return the associated core_file */
|
||||
CHD_EXPORT core_file *chd_core_file(chd_file *chd);
|
||||
|
||||
/* return the overall size of a CHD, and any of its parents */
|
||||
CHD_EXPORT UINT64 chd_get_compressed_size(chd_file* chd);
|
||||
|
||||
/* return an error string for the given CHD error */
|
||||
CHD_EXPORT const char *chd_error_string(chd_error err);
|
||||
|
||||
@@ -407,7 +402,6 @@ CHD_EXPORT const chd_header *chd_get_header(chd_file *chd);
|
||||
CHD_EXPORT chd_error chd_read_header_core_file(core_file *file, chd_header *header);
|
||||
CHD_EXPORT chd_error chd_read_header_file(FILE *file, chd_header *header);
|
||||
CHD_EXPORT chd_error chd_read_header(const char *filename, chd_header *header);
|
||||
CHD_EXPORT bool chd_is_matching_parent(const chd_header* header, const chd_header* parent_header);
|
||||
|
||||
|
||||
|
||||
|
||||
91
3rdparty/libchdr/src/libchdr_chd.c
vendored
91
3rdparty/libchdr/src/libchdr_chd.c
vendored
@@ -1953,56 +1953,28 @@ cleanup:
|
||||
|
||||
CHD_EXPORT chd_error chd_precache(chd_file* chd)
|
||||
{
|
||||
return chd_precache_progress(chd, NULL, NULL);
|
||||
}
|
||||
INT64 count;
|
||||
UINT64 size;
|
||||
|
||||
CHD_EXPORT chd_error chd_precache_progress(chd_file* chd, bool(*progress)(size_t pos, size_t total, void* param), void* param)
|
||||
{
|
||||
#define PRECACHE_CHUNK_SIZE 16 * 1024 * 1024
|
||||
|
||||
if (chd->file_cache == NULL)
|
||||
{
|
||||
const UINT64 size = core_fsize(chd->file);
|
||||
if ((INT64)size <= 0)
|
||||
return CHDERR_INVALID_DATA;
|
||||
|
||||
if (size > SIZE_MAX)
|
||||
if (chd->file_cache == NULL)
|
||||
{
|
||||
size = core_fsize(chd->file);
|
||||
if ((INT64)size <= 0)
|
||||
return CHDERR_INVALID_DATA;
|
||||
chd->file_cache = malloc(size);
|
||||
if (chd->file_cache == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
|
||||
chd->file_cache = malloc(size);
|
||||
if (chd->file_cache == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
core_fseek(chd->file, 0, SEEK_SET);
|
||||
|
||||
UINT64 done = 0;
|
||||
while (done < size)
|
||||
core_fseek(chd->file, 0, SEEK_SET);
|
||||
count = core_fread(chd->file, chd->file_cache, size);
|
||||
if (count != size)
|
||||
{
|
||||
UINT64 req_count = size - done;
|
||||
if (req_count > PRECACHE_CHUNK_SIZE)
|
||||
req_count = PRECACHE_CHUNK_SIZE;
|
||||
|
||||
size_t count = core_fread(chd->file, chd->file_cache + (size_t)done, (size_t)req_count);
|
||||
if (count != (size_t)req_count)
|
||||
{
|
||||
free(chd->file_cache);
|
||||
chd->file_cache = NULL;
|
||||
return CHDERR_READ_ERROR;
|
||||
}
|
||||
|
||||
done += req_count;
|
||||
if (progress != NULL)
|
||||
{
|
||||
if (!progress(done, size, param))
|
||||
{
|
||||
free(chd->file_cache);
|
||||
chd->file_cache = NULL;
|
||||
return CHDERR_CANCELLED;
|
||||
}
|
||||
}
|
||||
free(chd->file_cache);
|
||||
chd->file_cache = NULL;
|
||||
return CHDERR_READ_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CHDERR_NONE;
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
@@ -2169,14 +2141,6 @@ CHD_EXPORT core_file *chd_core_file(chd_file *chd)
|
||||
return chd->file;
|
||||
}
|
||||
|
||||
CHD_EXPORT UINT64 chd_get_compressed_size(chd_file *chd)
|
||||
{
|
||||
UINT64 size = chd->file->fsize(chd->file);
|
||||
if (chd->parent)
|
||||
size += chd_get_compressed_size(chd->parent);
|
||||
return size;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
chd_error_string - return an error string for
|
||||
the given CHD error
|
||||
@@ -2281,27 +2245,6 @@ CHD_EXPORT chd_error chd_read_header(const char *filename, chd_header *header)
|
||||
return err;
|
||||
}
|
||||
|
||||
CHD_EXPORT bool chd_is_matching_parent(const chd_header* header, const chd_header* parent_header)
|
||||
{
|
||||
/* check MD5 if it isn't empty */
|
||||
if (memcmp(nullmd5, header->parentmd5, sizeof(header->parentmd5)) != 0 &&
|
||||
memcmp(nullmd5, parent_header->md5, sizeof(parent_header->md5)) != 0 &&
|
||||
memcmp(parent_header->md5, header->parentmd5, sizeof(header->parentmd5)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check SHA1 if it isn't empty */
|
||||
if (memcmp(nullsha1, header->parentsha1, sizeof(header->parentsha1)) != 0 &&
|
||||
memcmp(nullsha1, parent_header->sha1, sizeof(parent_header->sha1)) != 0 &&
|
||||
memcmp(parent_header->sha1, header->parentsha1, sizeof(header->parentsha1)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
CORE DATA READ/WRITE
|
||||
***************************************************************************/
|
||||
|
||||
@@ -1230,6 +1230,14 @@ size_t FileSystem::ReadFileWithProgress(std::FILE* fp, void* dst, size_t length,
|
||||
{
|
||||
progress->SetProgressRange(100);
|
||||
|
||||
return FileSystem::ReadFileWithPartialProgress(fp, dst, length, progress, 0, 100, error, chunk_size);
|
||||
}
|
||||
|
||||
size_t FileSystem::ReadFileWithPartialProgress(std::FILE* fp, void* dst, size_t length,
|
||||
ProgressCallback* progress, int startPercent, int endPercent, Error* error, size_t chunk_size)
|
||||
{
|
||||
const int deltaPercent = endPercent - startPercent;
|
||||
|
||||
size_t done = 0;
|
||||
while (done < length)
|
||||
{
|
||||
@@ -1243,7 +1251,7 @@ size_t FileSystem::ReadFileWithProgress(std::FILE* fp, void* dst, size_t length,
|
||||
break;
|
||||
}
|
||||
|
||||
progress->SetProgressValue((done * 100) / length);
|
||||
progress->SetProgressValue(startPercent + (done * deltaPercent) / length);
|
||||
done += read_size;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,8 @@ namespace FileSystem
|
||||
bool WriteStringToFile(const char* filename, const std::string_view sv);
|
||||
size_t ReadFileWithProgress(std::FILE* fp, void* dst, size_t length, ProgressCallback* progress,
|
||||
Error* error = nullptr, size_t chunk_size = 16 * 1024 * 1024);
|
||||
size_t ReadFileWithPartialProgress(std::FILE* fp, void* dst, size_t length, ProgressCallback* progress,
|
||||
int startPercent, int endPercent, Error* error = nullptr, size_t chunk_size = 16 * 1024 * 1024);
|
||||
|
||||
/// creates a directory in the local filesystem
|
||||
/// if the directory already exists, the return value will be true.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ static std::vector<std::pair<std::string, chd_header>> s_chd_hash_cache; // <fil
|
||||
static std::recursive_mutex s_chd_hash_cache_mutex;
|
||||
|
||||
// Provides an implementation of core_file which allows us to control if the underlying FILE handle is freed.
|
||||
// Additionally, this class allows greater control and feedback while precaching CHD files.
|
||||
// The lifetime of ChdCoreFileWrapper will be equal to that of the relevant chd_file,
|
||||
// ChdCoreFileWrapper will also get destroyed if chd_open_core_file fails.
|
||||
class ChdCoreFileWrapper
|
||||
@@ -31,10 +32,15 @@ private:
|
||||
core_file m_core;
|
||||
std::FILE* m_file;
|
||||
bool m_free_file = false;
|
||||
ChdCoreFileWrapper* m_parent = nullptr;
|
||||
std::unique_ptr<u8[]> m_file_cache;
|
||||
s64 m_file_cache_size;
|
||||
s64 m_file_cache_pos;
|
||||
|
||||
public:
|
||||
ChdCoreFileWrapper(std::FILE* file)
|
||||
ChdCoreFileWrapper(std::FILE* file, ChdCoreFileWrapper* parent)
|
||||
: m_file{file}
|
||||
, m_parent{parent}
|
||||
{
|
||||
m_core.argp = this;
|
||||
m_core.fsize = FSize;
|
||||
@@ -45,7 +51,7 @@ public:
|
||||
|
||||
~ChdCoreFileWrapper()
|
||||
{
|
||||
if (m_free_file)
|
||||
if (m_free_file && m_file)
|
||||
std::fclose(m_file);
|
||||
}
|
||||
|
||||
@@ -64,15 +70,100 @@ public:
|
||||
m_free_file = isOwner;
|
||||
}
|
||||
|
||||
s64 GetPrecacheSize()
|
||||
{
|
||||
const s64 size = static_cast<size_t>(FileSystem::FSize64(m_file));
|
||||
if (m_parent != nullptr)
|
||||
return m_parent->GetPrecacheSize() + size;
|
||||
else
|
||||
return size;
|
||||
}
|
||||
|
||||
bool Precache(ProgressCallback* progress, Error* error)
|
||||
{
|
||||
progress->SetProgressRange(100);
|
||||
|
||||
const s64 size = GetPrecacheSize();
|
||||
return PrecacheInternal(progress, error, 0, size);
|
||||
}
|
||||
|
||||
private:
|
||||
bool PrecacheInternal(ProgressCallback* progress, Error* error, s64 startSize, s64 finalSize)
|
||||
{
|
||||
m_file_cache_size = FileSystem::FSize64(m_file);
|
||||
if (m_file_cache_size <= 0)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to determine file size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the current file position.
|
||||
m_file_cache_pos = FileSystem::FTell64(m_file);
|
||||
if (m_file_cache_pos <= 0)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to determine file position.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file_cache = std::make_unique_for_overwrite<u8[]>(m_file_cache_size);
|
||||
if (FileSystem::FSeek64(m_file, 0, SEEK_SET) != 0 ||
|
||||
FileSystem::ReadFileWithPartialProgress(
|
||||
m_file, m_file_cache.get(), m_file_cache_size, progress,
|
||||
(startSize * 100) / finalSize,
|
||||
((startSize + m_file_cache_size) * 100) / finalSize,
|
||||
error) != static_cast<size_t>(m_file_cache_size))
|
||||
{
|
||||
m_file_cache.reset();
|
||||
// Precache failed, continue using file
|
||||
// Restore file position incase it's used for subsequent reads
|
||||
FileSystem::FSeek64(m_file, m_file_cache_pos, SEEK_SET);
|
||||
Error::SetStringView(error, "Failed to read part of the file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
startSize += m_file_cache_size;
|
||||
|
||||
if (m_parent)
|
||||
{
|
||||
if (!m_parent->PrecacheInternal(progress, error, startSize, finalSize))
|
||||
{
|
||||
// Precache failed, continue using file
|
||||
// Restore file position incase it's used for subsequent reads
|
||||
FileSystem::FSeek64(m_file, m_file_cache_pos, SEEK_SET);
|
||||
m_file_cache.reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_free_file)
|
||||
std::fclose(m_file);
|
||||
m_file = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u64 FSize(core_file* file)
|
||||
{
|
||||
return static_cast<u64>(FileSystem::FSize64(FromCoreFile(file)->m_file));
|
||||
ChdCoreFileWrapper* fileWrapper = FromCoreFile(file);
|
||||
if (fileWrapper->m_file_cache)
|
||||
return fileWrapper->m_file_cache_size;
|
||||
else
|
||||
return static_cast<u64>(FileSystem::FSize64(fileWrapper->m_file));
|
||||
}
|
||||
|
||||
static size_t FRead(void* buffer, size_t elmSize, size_t elmCount, core_file* file)
|
||||
{
|
||||
return std::fread(buffer, elmSize, elmCount, FromCoreFile(file)->m_file);
|
||||
ChdCoreFileWrapper* fileWrapper = FromCoreFile(file);
|
||||
if (fileWrapper->m_file_cache)
|
||||
{
|
||||
// While currently libchdr only uses an elmCount of 1, we can't guarantee that will always be the case.
|
||||
elmCount = std::min<size_t>(elmCount, std::max<s64>(fileWrapper->m_file_cache_size - fileWrapper->m_file_cache_pos, 0) / elmSize);
|
||||
const size_t size = elmSize * elmCount;
|
||||
std::memcpy(buffer, &fileWrapper->m_file_cache[fileWrapper->m_file_cache_pos], size);
|
||||
return elmCount;
|
||||
}
|
||||
else
|
||||
return std::fread(buffer, elmSize, elmCount, fileWrapper->m_file);
|
||||
}
|
||||
|
||||
static int FClose(core_file* file)
|
||||
@@ -84,7 +175,28 @@ private:
|
||||
|
||||
static int FSeek(core_file* file, int64_t offset, int whence)
|
||||
{
|
||||
return FileSystem::FSeek64(FromCoreFile(file)->m_file, offset, whence);
|
||||
ChdCoreFileWrapper* fileWrapper = FromCoreFile(file);
|
||||
if (fileWrapper->m_file_cache)
|
||||
{
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
fileWrapper->m_file_cache_pos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
fileWrapper->m_file_cache_pos += offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
fileWrapper->m_file_cache_pos = fileWrapper->m_file_cache_size + offset;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return FileSystem::FSeek64(fileWrapper->m_file, offset, whence);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,10 +207,34 @@ ChdFileReader::~ChdFileReader()
|
||||
pxAssert(!ChdFile);
|
||||
}
|
||||
|
||||
static bool IsHeaderParentCHD(const chd_header& header, const chd_header& parent_header)
|
||||
{
|
||||
static const u8 nullmd5[CHD_MD5_BYTES]{};
|
||||
static const u8 nullsha1[CHD_SHA1_BYTES]{};
|
||||
|
||||
// Check MD5 if it isn't empty.
|
||||
if (std::memcmp(nullmd5, header.parentmd5, CHD_MD5_BYTES) != 0 &&
|
||||
std::memcmp(nullmd5, parent_header.md5, CHD_MD5_BYTES) != 0 &&
|
||||
std::memcmp(parent_header.md5, header.parentmd5, CHD_MD5_BYTES) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check SHA1 if it isn't empty.
|
||||
if (std::memcmp(nullsha1, header.parentsha1, CHD_SHA1_BYTES) != 0 &&
|
||||
std::memcmp(nullsha1, parent_header.sha1, CHD_SHA1_BYTES) != 0 &&
|
||||
std::memcmp(parent_header.sha1, header.parentsha1, CHD_SHA1_BYTES) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePtr fp, Error* error, u32 recursion_level)
|
||||
{
|
||||
chd_file* chd;
|
||||
ChdCoreFileWrapper* core_wrapper = new ChdCoreFileWrapper(fp.get());
|
||||
ChdCoreFileWrapper* core_wrapper = new ChdCoreFileWrapper(fp.get(), nullptr);
|
||||
// libchdr will take ownership of core_wrapper, and will close/free it on failure.
|
||||
chd_error err = chd_open_core_file(core_wrapper->GetCoreFile(), CHD_OPEN_READ, nullptr, &chd);
|
||||
if (err == CHDERR_NONE)
|
||||
@@ -144,14 +280,14 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
|
||||
if (!StringUtil::compareNoCase(parent_dir, Path::GetDirectory(it->first)))
|
||||
continue;
|
||||
|
||||
if (!chd_is_matching_parent(&header, &it->second))
|
||||
if (!IsHeaderParentCHD(header, it->second))
|
||||
continue;
|
||||
|
||||
// Re-check the header, it might have changed since we last opened.
|
||||
chd_header parent_header;
|
||||
auto parent_fp = FileSystem::OpenManagedSharedCFile(it->first.c_str(), "rb", FileSystem::FileShareMode::DenyWrite);
|
||||
if (parent_fp && chd_read_header_file(parent_fp.get(), &parent_header) == CHDERR_NONE &&
|
||||
chd_is_matching_parent(&header, &parent_header))
|
||||
IsHeaderParentCHD(header, parent_header))
|
||||
{
|
||||
// Need to take a copy of the string, because the parent might add to the list and invalidate the iterator.
|
||||
const std::string filename_to_open = it->first;
|
||||
@@ -192,7 +328,7 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
|
||||
else
|
||||
s_chd_hash_cache.emplace_back(fd.FileName, parent_header);
|
||||
|
||||
if (!chd_is_matching_parent(&header, &parent_header))
|
||||
if (!IsHeaderParentCHD(header, parent_header))
|
||||
continue;
|
||||
|
||||
// Match! Open this one.
|
||||
@@ -212,7 +348,7 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
|
||||
}
|
||||
|
||||
// Our last core file wrapper got freed, so make a new one.
|
||||
core_wrapper = new ChdCoreFileWrapper(fp.get());
|
||||
core_wrapper = new ChdCoreFileWrapper(fp.get(), ChdCoreFileWrapper::FromCoreFile(chd_core_file(parent_chd)));
|
||||
// Now try re-opening with the parent.
|
||||
err = chd_open_core_file(core_wrapper->GetCoreFile(), CHD_OPEN_READ, parent_chd, &chd);
|
||||
if (err != CHDERR_NONE)
|
||||
@@ -266,28 +402,11 @@ bool ChdFileReader::Open2(std::string filename, Error* error)
|
||||
|
||||
bool ChdFileReader::Precache2(ProgressCallback* progress, Error* error)
|
||||
{
|
||||
if (!CheckAvailableMemoryForPrecaching(chd_get_compressed_size(ChdFile), error))
|
||||
ChdCoreFileWrapper* fileWrapper = ChdCoreFileWrapper::FromCoreFile(chd_core_file(ChdFile));
|
||||
if (!CheckAvailableMemoryForPrecaching(fileWrapper->GetPrecacheSize(), error))
|
||||
return false;
|
||||
|
||||
progress->SetProgressRange(100);
|
||||
|
||||
const auto callback = [](size_t pos, size_t total, void* param) -> bool {
|
||||
ProgressCallback* progress = static_cast<ProgressCallback*>(param);
|
||||
const u32 percent = static_cast<u32>((pos * 100) / total);
|
||||
progress->SetProgressValue(std::min<u32>(percent, 100));
|
||||
return !progress->IsCancelled();
|
||||
};
|
||||
|
||||
const chd_error cerror = chd_precache_progress(ChdFile, callback, progress);
|
||||
if (cerror != CHDERR_NONE)
|
||||
{
|
||||
if (cerror != CHDERR_CANCELLED)
|
||||
Error::SetStringView(error, "Failed to read part of the file.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return fileWrapper->Precache(progress, error);
|
||||
}
|
||||
|
||||
ThreadedFileReader::Chunk ChdFileReader::ChunkForOffset(u64 offset)
|
||||
|
||||
@@ -7050,10 +7050,6 @@ TRANSLATE_NOOP("FullscreenUI", "Increases or decreases the virtual picture size
|
||||
TRANSLATE_NOOP("FullscreenUI", "Crop");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Crops the image, while respecting aspect ratio.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "%dpx");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enable Widescreen Patches");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables loading widescreen patches from pnach files.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enable No-Interlacing Patches");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables loading no-interlacing patches from pnach files.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bilinear Upscaling");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Smooths out the image when upscaling the console to the screen.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Integer Upscaling");
|
||||
|
||||
@@ -852,8 +852,16 @@ namespace R3000A
|
||||
|
||||
v0 = file->read(buf.get(), count);
|
||||
|
||||
for (s32 i = 0; i < (s32)v0; i++)
|
||||
iopMemWrite8(data + i, buf[i]);
|
||||
[[likely]]
|
||||
if (v0 >= 0 && iopMemSafeWriteBytes(data, buf.get(), v0))
|
||||
{
|
||||
psxCpu->Clear(data, (v0 + 3) / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (s32 i = 0; i < static_cast<s32>(v0); i++)
|
||||
iopMemWrite8(data + i, buf[i]);
|
||||
}
|
||||
|
||||
pc = ra;
|
||||
return 1;
|
||||
@@ -899,8 +907,12 @@ namespace R3000A
|
||||
{
|
||||
auto buf = std::make_unique<char[]>(count);
|
||||
|
||||
for (u32 i = 0; i < count; i++)
|
||||
buf[i] = iopMemRead8(data + i);
|
||||
[[unlikely]]
|
||||
if (!iopMemSafeReadBytes(data, buf.get(), count))
|
||||
{
|
||||
for (u32 i = 0; i < count; i++)
|
||||
buf[i] = iopMemRead8(data + i);
|
||||
}
|
||||
|
||||
v0 = file->write(buf.get(), count);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user