Add integrity check to PSN Content, DLC and Game Update files (#18669)
This commit is contained in:
committed by
GitHub
parent
5252f47a33
commit
23b414ac84
@@ -128,7 +128,7 @@ target_sources(rpcs3_emu PRIVATE
|
|||||||
../Loader/TAR.cpp
|
../Loader/TAR.cpp
|
||||||
../Loader/ISO.cpp
|
../Loader/ISO.cpp
|
||||||
../Loader/iso_cache.cpp
|
../Loader/iso_cache.cpp
|
||||||
../Loader/iso_validation.cpp
|
../Loader/content_validation.cpp
|
||||||
../Loader/TROPUSR.cpp
|
../Loader/TROPUSR.cpp
|
||||||
../Loader/TRP.cpp
|
../Loader/TRP.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -256,6 +256,36 @@ namespace rpcs3::utils
|
|||||||
return get_data_dir() + "redump/";
|
return get_data_dir() + "redump/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_psn_content_db_path()
|
||||||
|
{
|
||||||
|
return fs::get_config_dir(true) + "psn_content.dat";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_psn_content_db_download_url()
|
||||||
|
{
|
||||||
|
return "https://api.rpcs3.net/nointro/content/?api=v1";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_psn_dlc_db_path()
|
||||||
|
{
|
||||||
|
return fs::get_config_dir(true) + "psn_dlc.dat";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_psn_dlc_db_download_url()
|
||||||
|
{
|
||||||
|
return "https://api.rpcs3.net/nointro/dlc/?api=v1";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_psn_update_db_path()
|
||||||
|
{
|
||||||
|
return fs::get_config_dir(true) + "psn_update.dat";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_psn_update_db_download_url()
|
||||||
|
{
|
||||||
|
return "https://api.rpcs3.net/nointro/updates/?api=v1";
|
||||||
|
}
|
||||||
|
|
||||||
std::string get_data_dir()
|
std::string get_data_dir()
|
||||||
{
|
{
|
||||||
return fs::get_config_dir() + "data/";
|
return fs::get_config_dir() + "data/";
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ namespace rpcs3::utils
|
|||||||
std::string get_redump_db_path();
|
std::string get_redump_db_path();
|
||||||
std::string get_redump_db_download_url();
|
std::string get_redump_db_download_url();
|
||||||
std::string get_redump_key_dir();
|
std::string get_redump_key_dir();
|
||||||
|
std::string get_psn_content_db_path();
|
||||||
|
std::string get_psn_content_db_download_url();
|
||||||
|
std::string get_psn_dlc_db_path();
|
||||||
|
std::string get_psn_dlc_db_download_url();
|
||||||
|
std::string get_psn_update_db_path();
|
||||||
|
std::string get_psn_update_db_download_url();
|
||||||
|
|
||||||
std::string get_data_dir();
|
std::string get_data_dir();
|
||||||
std::string get_icons_dir();
|
std::string get_icons_dir();
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "content_validation.h"
|
||||||
|
#include "ISO.h"
|
||||||
|
|
||||||
|
#include "Emu/system_utils.hpp"
|
||||||
|
#include "Utilities/File.h"
|
||||||
|
#include "Utilities/rXml.h"
|
||||||
|
#include "Crypto/md5.h"
|
||||||
|
#include "Crypto/utils.h"
|
||||||
|
|
||||||
|
LOG_CHANNEL(sys_log, "VALIDATION");
|
||||||
|
|
||||||
|
content_integrity_status content_validation::check_integrity(content_file_type file_type, const std::string& hash, std::string* game_name)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Check for Redump db
|
||||||
|
//
|
||||||
|
|
||||||
|
std::string db_path;
|
||||||
|
|
||||||
|
switch (file_type)
|
||||||
|
{
|
||||||
|
case content_file_type::ISO:
|
||||||
|
db_path = rpcs3::utils::get_redump_db_path();
|
||||||
|
break;
|
||||||
|
case content_file_type::PSN_CONTENT:
|
||||||
|
db_path = rpcs3::utils::get_psn_content_db_path();
|
||||||
|
break;
|
||||||
|
case content_file_type::PSN_DLC:
|
||||||
|
db_path = rpcs3::utils::get_psn_dlc_db_path();
|
||||||
|
break;
|
||||||
|
case content_file_type::PSN_UPDATE:
|
||||||
|
db_path = rpcs3::utils::get_psn_update_db_path();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::file db_file(db_path);
|
||||||
|
|
||||||
|
// If no db file exists
|
||||||
|
if (!db_file)
|
||||||
|
{
|
||||||
|
// An empty hash is used to simply test the presence (without any logging) of the Redump db
|
||||||
|
if (!hash.empty())
|
||||||
|
{
|
||||||
|
sys_log.error("check_integrity: Failed to open file: %s", db_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return content_integrity_status::ERROR_OPENING_DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash.empty())
|
||||||
|
{
|
||||||
|
return content_integrity_status::NO_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
rXmlDocument db;
|
||||||
|
|
||||||
|
if (!db.Read(db_file.to_string()))
|
||||||
|
{
|
||||||
|
sys_log.error("check_integrity: Failed to process file: %s", db_path);
|
||||||
|
return content_integrity_status::ERROR_PARSING_DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file and work with the data loaded into the "db" document
|
||||||
|
db_file.close();
|
||||||
|
|
||||||
|
std::shared_ptr<rXmlNode> db_base = db.GetRoot();
|
||||||
|
|
||||||
|
if (!db_base)
|
||||||
|
{
|
||||||
|
sys_log.error("check_integrity: Failed to get 'root' node on file: %s", db_path);
|
||||||
|
return content_integrity_status::ERROR_PARSING_DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db_base = db_base->GetChild(std::string_view("datafile")); !db_base)
|
||||||
|
{
|
||||||
|
sys_log.error("check_integrity: Failed to get 'datafile' node on file: %s", db_path);
|
||||||
|
return content_integrity_status::ERROR_PARSING_DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check for a match on Redump db
|
||||||
|
//
|
||||||
|
|
||||||
|
for (auto node = db_base->GetChildren(); node; node = node->GetNext())
|
||||||
|
{
|
||||||
|
if (node->GetName() == "game")
|
||||||
|
{
|
||||||
|
for (auto child = node->GetChildren(); child; child = child->GetNext())
|
||||||
|
{
|
||||||
|
// If a match is found, fill in "game_desc" (if requested) and return FOUND_MATCH
|
||||||
|
if (child->GetName() == "rom" && hash == child->GetAttribute(std::string_view("md5")))
|
||||||
|
{
|
||||||
|
if (game_name)
|
||||||
|
{
|
||||||
|
*game_name = node->GetAttribute(std::string_view("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return content_integrity_status::FOUND_MATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No match found
|
||||||
|
return content_integrity_status::NO_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool content_validation::init_hash(const std::string& path)
|
||||||
|
{
|
||||||
|
std::string new_path = path;
|
||||||
|
|
||||||
|
fs::get_optical_raw_device(path, &new_path);
|
||||||
|
|
||||||
|
iso_file file(new_path);
|
||||||
|
|
||||||
|
// If no file exists
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
sys_log.error("init_hash: Failed to open file: %s", new_path);
|
||||||
|
m_status = content_hash_status::ABORTED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_path = new_path;
|
||||||
|
m_name = new_path.find_last_of(fs::delim) != umax ? new_path.substr(new_path.find_last_of(fs::delim) + 1) : new_path;
|
||||||
|
m_size = file.size();
|
||||||
|
m_bytes_read = 0;
|
||||||
|
m_status = content_hash_status::INITIALIZED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
content_hash_status content_validation::calculate_hash(std::string& hash)
|
||||||
|
{
|
||||||
|
if (m_status != content_hash_status::INITIALIZED)
|
||||||
|
{
|
||||||
|
sys_log.error("calculate_hash: MD5 hash calculation already performed: %s", m_path);
|
||||||
|
m_status = content_hash_status::ABORTED;
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
iso_file file(m_path);
|
||||||
|
|
||||||
|
// If no file exists
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
sys_log.error("calculate_hash: Failed to open file: %s", m_path);
|
||||||
|
m_status = content_hash_status::ABORTED;
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 block_size = 4096;
|
||||||
|
std::array<u8, block_size> buf;
|
||||||
|
u64 bytes_read;
|
||||||
|
mbedtls_md5_context md5_ctx;
|
||||||
|
unsigned char md5_hash[16];
|
||||||
|
|
||||||
|
mbedtls_md5_starts_ret(&md5_ctx);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bytes_read = file.read(buf.data(), block_size);
|
||||||
|
mbedtls_md5_update_ret(&md5_ctx, buf.data(), bytes_read);
|
||||||
|
|
||||||
|
m_bytes_read += bytes_read;
|
||||||
|
} while (bytes_read == block_size && m_status != content_hash_status::ABORTED);
|
||||||
|
|
||||||
|
if (m_status == content_hash_status::ABORTED)
|
||||||
|
{
|
||||||
|
sys_log.warning("calculate_hash: MD5 hash calculation aborted by user: %s", m_path);
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mbedtls_md5_finish_ret(&md5_ctx, md5_hash) != 0)
|
||||||
|
{
|
||||||
|
sys_log.error("calculate_hash: Failed to calculate MD5 hash on file: %s", m_path);
|
||||||
|
m_status = content_hash_status::ABORTED;
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the MD5 hash to hex string
|
||||||
|
bytes_to_hex(hash, md5_hash, 16);
|
||||||
|
|
||||||
|
m_status = content_hash_status::COMPLETED;
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/types.hpp"
|
||||||
|
|
||||||
|
// Enum identifying the content file type
|
||||||
|
enum class content_file_type
|
||||||
|
{
|
||||||
|
ISO,
|
||||||
|
PSN_CONTENT,
|
||||||
|
PSN_DLC,
|
||||||
|
PSN_UPDATE
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enum returned by calculating hash
|
||||||
|
enum class content_hash_status
|
||||||
|
{
|
||||||
|
INITIALIZED,
|
||||||
|
COMPLETED,
|
||||||
|
ABORTED
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enum returned by checking integrity
|
||||||
|
enum class content_integrity_status
|
||||||
|
{
|
||||||
|
NO_MATCH,
|
||||||
|
FOUND_MATCH,
|
||||||
|
ERROR_OPENING_DB,
|
||||||
|
ERROR_PARSING_DB
|
||||||
|
};
|
||||||
|
|
||||||
|
// Content validation class
|
||||||
|
class content_validation
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string m_path;
|
||||||
|
std::string m_name;
|
||||||
|
u64 m_size = 0;
|
||||||
|
u64 m_bytes_read = 0;
|
||||||
|
u16 m_count = 0; // Set only by set_count()
|
||||||
|
content_hash_status m_status = content_hash_status::INITIALIZED;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static content_integrity_status check_integrity(content_file_type file_type, const std::string& hash, std::string* game_name = nullptr);
|
||||||
|
|
||||||
|
const std::string& get_path() const { return m_path; }
|
||||||
|
const std::string& get_name() const { return m_name; }
|
||||||
|
u64 get_size() const { return m_size; }
|
||||||
|
u64 get_bytes_read() const { return m_bytes_read; }
|
||||||
|
u16 get_count() const { return m_count; }
|
||||||
|
content_hash_status get_status() const { return m_status; }
|
||||||
|
|
||||||
|
void set_count(u16 count) { m_count = count; }
|
||||||
|
void abort_hash() { m_status = content_hash_status::ABORTED; }
|
||||||
|
|
||||||
|
bool init_hash(const std::string& path);
|
||||||
|
content_hash_status calculate_hash(std::string& hash);
|
||||||
|
};
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
#include "stdafx.h"
|
|
||||||
|
|
||||||
#include "iso_validation.h"
|
|
||||||
#include "ISO.h"
|
|
||||||
|
|
||||||
#include "Emu/system_utils.hpp"
|
|
||||||
#include "Utilities/File.h"
|
|
||||||
#include "Utilities/rXml.h"
|
|
||||||
#include "Crypto/md5.h"
|
|
||||||
#include "Crypto/utils.h"
|
|
||||||
|
|
||||||
LOG_CHANNEL(iso_log, "ISO");
|
|
||||||
|
|
||||||
iso_integrity_status iso_file_validation::check_integrity(const std::string& hash, std::string* game_name)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Check for Redump db
|
|
||||||
//
|
|
||||||
|
|
||||||
const std::string db_path = rpcs3::utils::get_redump_db_path();
|
|
||||||
fs::file db_file(db_path);
|
|
||||||
|
|
||||||
// If no db file exists
|
|
||||||
if (!db_file)
|
|
||||||
{
|
|
||||||
// An empty hash is used to simply test the presence (without any logging) of the Redump db
|
|
||||||
if (!hash.empty())
|
|
||||||
{
|
|
||||||
iso_log.error("check_integrity: Failed to open file: %s", db_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return iso_integrity_status::ERROR_OPENING_DB;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hash.empty())
|
|
||||||
{
|
|
||||||
return iso_integrity_status::NO_MATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
rXmlDocument db;
|
|
||||||
|
|
||||||
if (!db.Read(db_file.to_string()))
|
|
||||||
{
|
|
||||||
iso_log.error("check_integrity: Failed to process file: %s", db_path);
|
|
||||||
return iso_integrity_status::ERROR_PARSING_DB;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<rXmlNode> db_base = db.GetRoot();
|
|
||||||
|
|
||||||
if (!db_base)
|
|
||||||
{
|
|
||||||
iso_log.error("check_integrity: Failed to get 'root' node on file: %s", db_path);
|
|
||||||
return iso_integrity_status::ERROR_PARSING_DB;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (db_base = db_base->GetChild(std::string_view("datafile")); !db_base)
|
|
||||||
{
|
|
||||||
iso_log.error("check_integrity: Failed to get 'datafile' node on file: %s", db_path);
|
|
||||||
return iso_integrity_status::ERROR_PARSING_DB;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check for a match on Redump db
|
|
||||||
//
|
|
||||||
|
|
||||||
for (auto node = db_base->GetChildren(); node; node = node->GetNext())
|
|
||||||
{
|
|
||||||
if (node->GetName() == "game")
|
|
||||||
{
|
|
||||||
if (const auto child = node->GetChild(std::string_view("rom")))
|
|
||||||
{
|
|
||||||
// If a match is found, fill in "game_desc" (if requested) and return FOUND_MATCH
|
|
||||||
if (hash == child->GetAttribute(std::string_view("md5")))
|
|
||||||
{
|
|
||||||
if (game_name)
|
|
||||||
{
|
|
||||||
*game_name = node->GetAttribute(std::string_view("name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return iso_integrity_status::FOUND_MATCH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No match found
|
|
||||||
return iso_integrity_status::NO_MATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool iso_file_validation::init_hash(const std::string& path)
|
|
||||||
{
|
|
||||||
fs::file iso_file(path);
|
|
||||||
|
|
||||||
// If no ISO file exists
|
|
||||||
if (!iso_file)
|
|
||||||
{
|
|
||||||
iso_log.error("init_hash: Failed to open file: %s", path);
|
|
||||||
m_status = iso_hash_status::ABORTED;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_path = path;
|
|
||||||
m_size = iso_file.size();
|
|
||||||
m_bytes_read = 0;
|
|
||||||
m_status = iso_hash_status::INITIALIZED;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
iso_hash_status iso_file_validation::calculate_hash(std::string& hash)
|
|
||||||
{
|
|
||||||
if (m_status != iso_hash_status::INITIALIZED)
|
|
||||||
{
|
|
||||||
iso_log.error("calculate_hash: MD5 hash calculation already performed: %s", m_path);
|
|
||||||
m_status = iso_hash_status::ABORTED;
|
|
||||||
return m_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::file iso_file(m_path);
|
|
||||||
|
|
||||||
// If no ISO file exists
|
|
||||||
if (!iso_file)
|
|
||||||
{
|
|
||||||
iso_log.error("calculate_hash: Failed to open file: %s", m_path);
|
|
||||||
m_status = iso_hash_status::ABORTED;
|
|
||||||
return m_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u64 block_size = ISO_SECTOR_SIZE * 2;
|
|
||||||
std::array<u8, block_size> buf;
|
|
||||||
u64 bytes_read;
|
|
||||||
mbedtls_md5_context md5_ctx;
|
|
||||||
unsigned char md5_hash[16];
|
|
||||||
|
|
||||||
mbedtls_md5_starts_ret(&md5_ctx);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
bytes_read = iso_file.read(buf.data(), block_size);
|
|
||||||
mbedtls_md5_update_ret(&md5_ctx, buf.data(), bytes_read);
|
|
||||||
|
|
||||||
m_bytes_read += bytes_read;
|
|
||||||
} while (bytes_read == block_size && m_status != iso_hash_status::ABORTED);
|
|
||||||
|
|
||||||
if (m_status == iso_hash_status::ABORTED)
|
|
||||||
{
|
|
||||||
iso_log.warning("calculate_hash: MD5 hash calculation aborted by user: %s", m_path);
|
|
||||||
return m_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mbedtls_md5_finish_ret(&md5_ctx, md5_hash) != 0)
|
|
||||||
{
|
|
||||||
iso_log.error("calculate_hash: Failed to calculate MD5 hash on file: %s", m_path);
|
|
||||||
m_status = iso_hash_status::ABORTED;
|
|
||||||
return m_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the MD5 hash to hex string
|
|
||||||
bytes_to_hex(hash, md5_hash, 16);
|
|
||||||
|
|
||||||
m_status = iso_hash_status::COMPLETED;
|
|
||||||
return m_status;
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "util/types.hpp"
|
|
||||||
|
|
||||||
// Enum returned by calculating hash
|
|
||||||
enum class iso_hash_status
|
|
||||||
{
|
|
||||||
INITIALIZED,
|
|
||||||
COMPLETED,
|
|
||||||
ABORTED
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enum returned by checking integrity
|
|
||||||
enum class iso_integrity_status
|
|
||||||
{
|
|
||||||
NO_MATCH,
|
|
||||||
FOUND_MATCH,
|
|
||||||
ERROR_OPENING_DB,
|
|
||||||
ERROR_PARSING_DB
|
|
||||||
};
|
|
||||||
|
|
||||||
// ISO file validation class
|
|
||||||
class iso_file_validation
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string m_path;
|
|
||||||
u64 m_size = 0;
|
|
||||||
u64 m_bytes_read = 0;
|
|
||||||
iso_hash_status m_status = iso_hash_status::INITIALIZED;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static iso_integrity_status check_integrity(const std::string& hash, std::string* game_name = nullptr);
|
|
||||||
|
|
||||||
const std::string& get_path() const { return m_path; }
|
|
||||||
u64 get_size() const { return m_size; }
|
|
||||||
u64 get_bytes_read() const { return m_bytes_read; }
|
|
||||||
iso_hash_status get_status() const { return m_status; }
|
|
||||||
void abort_hash() { m_status = iso_hash_status::ABORTED; }
|
|
||||||
|
|
||||||
bool init_hash(const std::string& path);
|
|
||||||
iso_hash_status calculate_hash(std::string& hash);
|
|
||||||
};
|
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Emu\vfs_config.cpp" />
|
<ClCompile Include="Emu\vfs_config.cpp" />
|
||||||
<ClCompile Include="Loader\disc.cpp" />
|
<ClCompile Include="Loader\disc.cpp" />
|
||||||
<ClCompile Include="Loader\iso_validation.cpp" />
|
<ClCompile Include="Loader\content_validation.cpp" />
|
||||||
<ClCompile Include="util\emu_utils.cpp" />
|
<ClCompile Include="util\emu_utils.cpp" />
|
||||||
<ClCompile Include="util\serialization_ext.cpp">
|
<ClCompile Include="util\serialization_ext.cpp">
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
@@ -782,8 +782,8 @@
|
|||||||
<ClInclude Include="Emu\system_config_types.h" />
|
<ClInclude Include="Emu\system_config_types.h" />
|
||||||
<ClInclude Include="Emu\vfs_config.h" />
|
<ClInclude Include="Emu\vfs_config.h" />
|
||||||
<ClInclude Include="Loader\disc.h" />
|
<ClInclude Include="Loader\disc.h" />
|
||||||
|
<ClInclude Include="Loader\content_validation.h" />
|
||||||
<ClInclude Include="Loader\iso_cache.h" />
|
<ClInclude Include="Loader\iso_cache.h" />
|
||||||
<ClInclude Include="Loader\iso_validation.h" />
|
|
||||||
<ClInclude Include="Loader\mself.hpp" />
|
<ClInclude Include="Loader\mself.hpp" />
|
||||||
<ClInclude Include="util\atomic.hpp" />
|
<ClInclude Include="util\atomic.hpp" />
|
||||||
<ClInclude Include="util\bit_set.hpp" />
|
<ClInclude Include="util\bit_set.hpp" />
|
||||||
@@ -1067,6 +1067,7 @@
|
|||||||
<ClInclude Include="Loader\PUP.h" />
|
<ClInclude Include="Loader\PUP.h" />
|
||||||
<ClInclude Include="Loader\TAR.h" />
|
<ClInclude Include="Loader\TAR.h" />
|
||||||
<ClInclude Include="Loader\ISO.h" />
|
<ClInclude Include="Loader\ISO.h" />
|
||||||
|
<ClInclude Include="Loader\iso_cache.h" />
|
||||||
<ClInclude Include="Loader\TROPUSR.h" />
|
<ClInclude Include="Loader\TROPUSR.h" />
|
||||||
<ClInclude Include="Loader\TRP.h" />
|
<ClInclude Include="Loader\TRP.h" />
|
||||||
<ClInclude Include="rpcs3_version.h" />
|
<ClInclude Include="rpcs3_version.h" />
|
||||||
|
|||||||
@@ -1414,6 +1414,9 @@
|
|||||||
<ClCompile Include="Loader\ISO.cpp">
|
<ClCompile Include="Loader\ISO.cpp">
|
||||||
<Filter>Loader</Filter>
|
<Filter>Loader</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Loader\iso_cache.cpp">
|
||||||
|
<Filter>Loader</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="Emu\RSX\Overlays\overlay_audio.cpp">
|
<ClCompile Include="Emu\RSX\Overlays\overlay_audio.cpp">
|
||||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -1432,7 +1435,7 @@
|
|||||||
<ClCompile Include="Emu\RSX\Overlays\overlay_select.cpp">
|
<ClCompile Include="Emu\RSX\Overlays\overlay_select.cpp">
|
||||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Loader\iso_validation.cpp">
|
<ClCompile Include="Loader\content_validation.cpp">
|
||||||
<Filter>Loader</Filter>
|
<Filter>Loader</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\Utilities\stereo_config.cpp">
|
<ClCompile Include="..\Utilities\stereo_config.cpp">
|
||||||
@@ -2887,6 +2890,9 @@
|
|||||||
<ClInclude Include="Loader\ISO.h">
|
<ClInclude Include="Loader\ISO.h">
|
||||||
<Filter>Loader</Filter>
|
<Filter>Loader</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Loader\iso_cache.h">
|
||||||
|
<Filter>Loader</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="Emu\RSX\Overlays\overlay_audio.h">
|
<ClInclude Include="Emu\RSX\Overlays\overlay_audio.h">
|
||||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -2911,7 +2917,7 @@
|
|||||||
<ClInclude Include="Emu\RSX\Common\aligned_malloc.hpp">
|
<ClInclude Include="Emu\RSX\Common\aligned_malloc.hpp">
|
||||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="Loader\iso_validation.h">
|
<ClInclude Include="Loader\content_validation.h">
|
||||||
<Filter>Loader</Filter>
|
<Filter>Loader</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\Utilities\stereo_config.h">
|
<ClInclude Include="..\Utilities\stereo_config.h">
|
||||||
|
|||||||
+6
-6
@@ -344,7 +344,7 @@
|
|||||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_ipc_settings_dialog.cpp">
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_ipc_settings_dialog.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_iso_integrity.cpp">
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_content_integrity.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_kamen_rider_dialog.cpp">
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_kamen_rider_dialog.cpp">
|
||||||
@@ -656,7 +656,7 @@
|
|||||||
<ClCompile Include="QTGeneratedFiles\Release\moc_ipc_settings_dialog.cpp">
|
<ClCompile Include="QTGeneratedFiles\Release\moc_ipc_settings_dialog.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Release\moc_iso_integrity.cpp">
|
<ClCompile Include="QTGeneratedFiles\Release\moc_content_integrity.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Release\moc_kamen_rider_dialog.cpp">
|
<ClCompile Include="QTGeneratedFiles\Release\moc_kamen_rider_dialog.cpp">
|
||||||
@@ -929,7 +929,7 @@
|
|||||||
<ClCompile Include="rpcs3qt\config_database.cpp" />
|
<ClCompile Include="rpcs3qt\config_database.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\game_compatibility.cpp" />
|
<ClCompile Include="rpcs3qt\game_compatibility.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\game_list_grid.cpp" />
|
<ClCompile Include="rpcs3qt\game_list_grid.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\iso_integrity.cpp" />
|
<ClCompile Include="rpcs3qt\content_integrity.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\progress_dialog.cpp" />
|
<ClCompile Include="rpcs3qt\progress_dialog.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\qt_utils.cpp" />
|
<ClCompile Include="rpcs3qt\qt_utils.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\syntax_highlighter.cpp" />
|
<ClCompile Include="rpcs3qt\syntax_highlighter.cpp" />
|
||||||
@@ -1924,13 +1924,13 @@
|
|||||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
<CustomBuild Include="rpcs3qt\iso_integrity.h">
|
<CustomBuild Include="rpcs3qt\content_integrity.h">
|
||||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing iso_integrity.h...</Message>
|
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing content_integrity.h...</Message>
|
||||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing iso_integrity.h...</Message>
|
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing content_integrity.h...</Message>
|
||||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
|
|||||||
@@ -1302,13 +1302,13 @@
|
|||||||
<ClCompile Include="rpcs3qt\emu_settings_type.cpp">
|
<ClCompile Include="rpcs3qt\emu_settings_type.cpp">
|
||||||
<Filter>Gui\settings</Filter>
|
<Filter>Gui\settings</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_iso_integrity.cpp">
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_content_integrity.cpp">
|
||||||
<Filter>Generated Files\Debug</Filter>
|
<Filter>Generated Files\Debug</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Release\moc_iso_integrity.cpp">
|
<ClCompile Include="QTGeneratedFiles\Release\moc_content_integrity.cpp">
|
||||||
<Filter>Generated Files\Release</Filter>
|
<Filter>Generated Files\Release</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="rpcs3qt\iso_integrity.cpp">
|
<ClCompile Include="rpcs3qt\content_integrity.cpp">
|
||||||
<Filter>Gui\game list</Filter>
|
<Filter>Gui\game list</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="rpcs3qt\anaglyph_settings_dialog.cpp">
|
<ClCompile Include="rpcs3qt\anaglyph_settings_dialog.cpp">
|
||||||
@@ -2083,7 +2083,7 @@
|
|||||||
</Text>
|
</Text>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<CustomBuild Include="rpcs3qt\iso_integrity.h">
|
<CustomBuild Include="rpcs3qt\content_integrity.h">
|
||||||
<Filter>Gui\game list</Filter>
|
<Filter>Gui\game list</Filter>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ add_library(rpcs3_ui STATIC
|
|||||||
config_adapter.cpp
|
config_adapter.cpp
|
||||||
config_checker.cpp
|
config_checker.cpp
|
||||||
config_database.cpp
|
config_database.cpp
|
||||||
|
content_integrity.cpp
|
||||||
curl_handle.cpp
|
curl_handle.cpp
|
||||||
custom_dialog.cpp
|
custom_dialog.cpp
|
||||||
custom_table_widget_item.cpp
|
custom_table_widget_item.cpp
|
||||||
@@ -51,7 +52,6 @@ add_library(rpcs3_ui STATIC
|
|||||||
input_dialog.cpp
|
input_dialog.cpp
|
||||||
instruction_editor_dialog.cpp
|
instruction_editor_dialog.cpp
|
||||||
ipc_settings_dialog.cpp
|
ipc_settings_dialog.cpp
|
||||||
iso_integrity.cpp
|
|
||||||
kamen_rider_dialog.cpp
|
kamen_rider_dialog.cpp
|
||||||
kernel_explorer.cpp
|
kernel_explorer.cpp
|
||||||
localized.cpp
|
localized.cpp
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
#include "content_integrity.h"
|
||||||
|
#include "gui_settings.h"
|
||||||
|
|
||||||
|
#include "Emu/system_utils.hpp"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
LOG_CHANNEL(sys_log, "INTEGRITY");
|
||||||
|
|
||||||
|
content_integrity::content_integrity(QWidget* parent, content_file_type file_type)
|
||||||
|
: QObject(parent), m_file_type(file_type)
|
||||||
|
{
|
||||||
|
switch (m_file_type)
|
||||||
|
{
|
||||||
|
case content_file_type::ISO:
|
||||||
|
m_file_path = QString::fromStdString(rpcs3::utils::get_redump_db_path());
|
||||||
|
m_url = rpcs3::utils::get_redump_db_download_url();
|
||||||
|
m_data_prefix = "redump";
|
||||||
|
break;
|
||||||
|
case content_file_type::PSN_CONTENT:
|
||||||
|
m_file_path = QString::fromStdString(rpcs3::utils::get_psn_content_db_path());
|
||||||
|
m_url = rpcs3::utils::get_psn_content_db_download_url();
|
||||||
|
m_data_prefix = "nointro";
|
||||||
|
break;
|
||||||
|
case content_file_type::PSN_DLC:
|
||||||
|
m_file_path = QString::fromStdString(rpcs3::utils::get_psn_dlc_db_path());
|
||||||
|
m_url = rpcs3::utils::get_psn_dlc_db_download_url();
|
||||||
|
m_data_prefix = "nointro";
|
||||||
|
break;
|
||||||
|
case content_file_type::PSN_UPDATE:
|
||||||
|
m_file_path = QString::fromStdString(rpcs3::utils::get_psn_update_db_path());
|
||||||
|
m_url = rpcs3::utils::get_psn_update_db_download_url();
|
||||||
|
m_data_prefix = "nointro";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_downloader = new downloader(parent);
|
||||||
|
|
||||||
|
connect(m_downloader, &downloader::signal_download_finished, this, &content_integrity::handle_download_finished);
|
||||||
|
connect(m_downloader, &downloader::signal_download_canceled, this, &content_integrity::handle_download_canceled);
|
||||||
|
connect(m_downloader, &downloader::signal_download_error, this, &content_integrity::handle_download_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void content_integrity::download()
|
||||||
|
{
|
||||||
|
sys_log.notice("Starting database download from: %s", m_url);
|
||||||
|
|
||||||
|
m_downloader->start(m_url, true, true, true, tr("Downloading database"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void content_integrity::handle_download_finished(const QByteArray& content)
|
||||||
|
{
|
||||||
|
sys_log.notice("Database download finished");
|
||||||
|
|
||||||
|
// Write database to file
|
||||||
|
if (QByteArray data = read_json(content, true); !data.isEmpty())
|
||||||
|
{
|
||||||
|
QFile file(m_file_path);
|
||||||
|
|
||||||
|
if (file.exists())
|
||||||
|
{
|
||||||
|
sys_log.notice("Database file found: %s", m_file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::WriteOnly))
|
||||||
|
{
|
||||||
|
sys_log.error("Failed to write database to file: %s", m_file_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(data);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
sys_log.success("Database written to file: %s", m_file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void content_integrity::handle_download_canceled()
|
||||||
|
{
|
||||||
|
sys_log.notice("Database download canceled");
|
||||||
|
}
|
||||||
|
|
||||||
|
void content_integrity::handle_download_error(const QString& error)
|
||||||
|
{
|
||||||
|
sys_log.error("", error.toStdString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray content_integrity::read_json(const QByteArray& data, bool after_download)
|
||||||
|
{
|
||||||
|
QJsonParseError error{};
|
||||||
|
const QJsonDocument json_document = QJsonDocument::fromJson(data, &error);
|
||||||
|
|
||||||
|
if (!json_document.isObject())
|
||||||
|
{
|
||||||
|
sys_log.error("Integrity database error - Invalid JSON: '%s'", error.errorString());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject json_data = json_document.object();
|
||||||
|
const int return_code = json_data["return_code"].toInt(-255);
|
||||||
|
|
||||||
|
if (return_code < 0)
|
||||||
|
{
|
||||||
|
if (after_download)
|
||||||
|
{
|
||||||
|
std::string error_message;
|
||||||
|
|
||||||
|
switch (return_code)
|
||||||
|
{
|
||||||
|
case -1: error_message = "Server Error - Internal Error"; break;
|
||||||
|
case -2: error_message = "Server Error - Maintenance Mode"; break;
|
||||||
|
case -255: error_message = "Server Error - Return code not found"; break;
|
||||||
|
default: error_message = "Server Error - Unknown Error"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_log.error("%s: return code %d", error_message, return_code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sys_log.error("Integrity database error - Invalid: return code %d", return_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json_data[m_data_prefix.c_str()].isString())
|
||||||
|
{
|
||||||
|
sys_log.error("Integrity database error - Unusable string");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray().fromStdString(json_data[m_data_prefix.c_str()].toString().toStdString());
|
||||||
|
};
|
||||||
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
#include "downloader.h"
|
#include "downloader.h"
|
||||||
|
|
||||||
class iso_integrity : public QObject
|
#include "Loader/content_validation.h"
|
||||||
|
|
||||||
|
class content_integrity : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Handles download for the ISO integrity database
|
// Handles download for the content integrity database
|
||||||
iso_integrity(QWidget* parent);
|
content_integrity(QWidget* parent, content_file_type file_type);
|
||||||
|
|
||||||
// Downloads and writes the database to file
|
// Downloads and writes the database to file
|
||||||
void download();
|
void download();
|
||||||
@@ -21,6 +23,9 @@ private Q_SLOTS:
|
|||||||
private:
|
private:
|
||||||
QByteArray read_json(const QByteArray& data, bool after_download);
|
QByteArray read_json(const QByteArray& data, bool after_download);
|
||||||
|
|
||||||
QString m_filepath;
|
content_file_type m_file_type;
|
||||||
|
QString m_file_path;
|
||||||
|
std::string m_url;
|
||||||
|
std::string m_data_prefix;
|
||||||
downloader* m_downloader = nullptr;
|
downloader* m_downloader = nullptr;
|
||||||
};
|
};
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
|
#include <QFileDialog>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@@ -367,67 +368,167 @@ void game_list_actions::ShowGameInfoDialog(const std::vector<game_info>& games)
|
|||||||
QMessageBox::information(m_game_list_frame, tr("Game Info"), GetContentInfo(games).info);
|
QMessageBox::information(m_game_list_frame, tr("Game Info"), GetContentInfo(games).info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_list_actions::ShowGameIntegrityDialog(const game_info& game)
|
void game_list_actions::ShowGameIntegrityDialog(content_file_type file_type, const std::string& game_path)
|
||||||
{
|
{
|
||||||
if (m_game_integrity_future.isRunning()) // Still running the last request
|
if (m_game_integrity_future.isRunning()) // Still running the last request
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Initialize the validator (set also file size etc.)
|
QStringList path_list;
|
||||||
m_iso_validator->init_hash(game->info.path);
|
|
||||||
|
switch (file_type)
|
||||||
|
{
|
||||||
|
case content_file_type::ISO:
|
||||||
|
path_list.push_back(QString::fromStdString(game_path));
|
||||||
|
break;
|
||||||
|
default: // Auto-detect the file type
|
||||||
|
const QString path_last_pkg = m_gui_settings->GetValue(gui::fd_install_pkg).toString();
|
||||||
|
|
||||||
|
path_list = QFileDialog::getOpenFileNames(nullptr, tr("Select package or rap file to check"),
|
||||||
|
path_last_pkg, tr("All relevant (*.pkg *.PKG *.rap *.RAP *.edat *.EDAT);;Package files (*.pkg *.PKG);;Rap files (*.rap *.RAP);;Edat files (*.edat *.EDAT);;All files (*.*)"));
|
||||||
|
|
||||||
|
if (path_list.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the progress bar thread how many hash will be checked
|
||||||
|
m_game_validator->set_count(path_list.size());
|
||||||
|
|
||||||
// Game integrity check can take a while (in particular on non ssd/m.2 disks)
|
// Game integrity check can take a while (in particular on non ssd/m.2 disks)
|
||||||
// so run it on a concurrent thread avoiding to block the entire GUI
|
// so run it on a concurrent thread avoiding to block the entire GUI
|
||||||
m_game_integrity_future = QtConcurrent::run([this]()
|
m_game_integrity_future = QtConcurrent::run([this, type = file_type, path_list]()
|
||||||
{
|
{
|
||||||
thread_base::set_name("Game Integrity");
|
thread_base::set_name("Game Integrity");
|
||||||
|
|
||||||
QString text;
|
content_file_type file_type = type;
|
||||||
std::string hash, game_name;
|
QString text_result;
|
||||||
bool info_dialog = false;
|
std::string db_id, hash, game_name;
|
||||||
|
bool info_dialog = true;
|
||||||
|
|
||||||
if (m_iso_validator->calculate_hash(hash) != iso_hash_status::COMPLETED)
|
for (int i = 0; i < path_list.size(); i++)
|
||||||
{
|
{
|
||||||
text = "Hash calculation failed!\n\nIntegrity check aborted";
|
bool use_fallback_db = false; // Set to "true" only for ".rap" and ".edat"
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
text = "Integrity check completed!\n\n";
|
|
||||||
|
|
||||||
switch (m_iso_validator->check_integrity(hash, &game_name))
|
if (file_type == content_file_type::ISO)
|
||||||
{
|
{
|
||||||
case iso_integrity_status::NO_MATCH:
|
db_id = "REDUMP";
|
||||||
text += tr("Game check NOT PASSED\n\nNo match found on DB or game corrupted:\n - Hash: %0")
|
|
||||||
.arg(QString::fromStdString(hash));
|
|
||||||
break;
|
|
||||||
case iso_integrity_status::FOUND_MATCH:
|
|
||||||
text += tr("Game check PASSED\n\nMatch found on DB:\n - Game: %0\n - Hash: %1")
|
|
||||||
.arg(QString::fromStdString(game_name))
|
|
||||||
.arg(QString::fromStdString(hash));
|
|
||||||
|
|
||||||
info_dialog = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text += tr("Error parsing DB");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
else if (path_list[i].endsWith(".rap", Qt::CaseInsensitive) || path_list[i].endsWith(".edat", Qt::CaseInsensitive))
|
||||||
|
|
||||||
Emu.CallFromMainThread([this, text, info_dialog]()
|
|
||||||
{
|
|
||||||
if (info_dialog)
|
|
||||||
{
|
{
|
||||||
sys_log.success("%s", text.toStdString());
|
// NOTE: This is the default type for any ".rap" and ".edat" due to it's not possible to detect the type by file parsing.
|
||||||
QMessageBox::information(m_game_list_frame, tr("Game Integrity"), text);
|
// If no match for ".rap" or ".edat" will be found on default "PSN Content" DB, we will try on "PSN DLC" DB
|
||||||
|
file_type = content_file_type::PSN_CONTENT;
|
||||||
|
db_id = "PSN CONTENT";
|
||||||
|
use_fallback_db = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sys_log.error("%s", text.toStdString());
|
const compat::package_info info = game_compatibility::GetPkgInfo(path_list[i], m_game_list_frame->GetGameCompatibility());
|
||||||
QMessageBox::critical(m_game_list_frame, tr("Game Integrity"), text);
|
|
||||||
|
switch (info.type)
|
||||||
|
{
|
||||||
|
case compat::package_type::update:
|
||||||
|
file_type = content_file_type::PSN_UPDATE;
|
||||||
|
db_id = "PSN UPDATE";
|
||||||
|
break;
|
||||||
|
case compat::package_type::dlc:
|
||||||
|
file_type = content_file_type::PSN_DLC;
|
||||||
|
db_id = "PSN DLC";
|
||||||
|
break;
|
||||||
|
case compat::package_type::other:
|
||||||
|
file_type = content_file_type::PSN_CONTENT;
|
||||||
|
db_id = "PSN CONTENT";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the validator (set also file size etc.)
|
||||||
|
m_game_validator->init_hash(path_list[i].toStdString());
|
||||||
|
|
||||||
|
if (m_game_validator->calculate_hash(hash) == content_hash_status::COMPLETED)
|
||||||
|
{
|
||||||
|
content_integrity_status integrity_status = m_game_validator->check_integrity(file_type, hash, &game_name);
|
||||||
|
|
||||||
|
// If no match for ".rap" or ".edat" is found on default "PSN Content" DB, try on "PSN DLC" DB
|
||||||
|
if (integrity_status == content_integrity_status::NO_MATCH && use_fallback_db)
|
||||||
|
{
|
||||||
|
db_id += " -> PSN DLC";
|
||||||
|
integrity_status = m_game_validator->check_integrity(content_file_type::PSN_DLC, hash, &game_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (integrity_status)
|
||||||
|
{
|
||||||
|
case content_integrity_status::NO_MATCH:
|
||||||
|
text_result += tr("Game check NOT PASSED\n\nNo match found on '%0' DB or game corrupted:\n - File: %1\n - Hash: %2")
|
||||||
|
.arg(QString::fromStdString(db_id))
|
||||||
|
.arg(QString::fromStdString(m_game_validator->get_name()))
|
||||||
|
.arg(QString::fromStdString(hash));
|
||||||
|
|
||||||
|
info_dialog = false;
|
||||||
|
break;
|
||||||
|
case content_integrity_status::FOUND_MATCH:
|
||||||
|
text_result += tr("Game check PASSED\n\nMatch found on '%0' DB:\n - File: %1\n - Hash: %2\n - Game: %3")
|
||||||
|
.arg(QString::fromStdString(db_id))
|
||||||
|
.arg(QString::fromStdString(m_game_validator->get_name()))
|
||||||
|
.arg(QString::fromStdString(hash))
|
||||||
|
.arg(QString::fromStdString(game_name));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text_result += tr("Error parsing '%0' DB or DB not existing:\n - File: %1\n - Hash: %2")
|
||||||
|
.arg(QString::fromStdString(db_id))
|
||||||
|
.arg(QString::fromStdString(m_game_validator->get_name()))
|
||||||
|
.arg(QString::fromStdString(hash));
|
||||||
|
|
||||||
|
info_dialog = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < path_list.size() - 1) // If it's not the last processed entry, add empty lines as separator
|
||||||
|
{
|
||||||
|
text_result += "\n\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_game_validator->get_status() == content_hash_status::ABORTED)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text_dialog;
|
||||||
|
|
||||||
|
if (m_game_validator->get_status() == content_hash_status::ABORTED)
|
||||||
|
{
|
||||||
|
text_dialog = tr("Hash calculation failed!\n\nIntegrity check aborted");
|
||||||
|
info_dialog = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text_dialog = tr("Integrity check completed!\n\n%0").arg(text_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the progress bar thread to terminate
|
||||||
|
m_game_validator->set_count(0);
|
||||||
|
|
||||||
|
Emu.CallFromMainThread([this, text_dialog, info_dialog]()
|
||||||
|
{
|
||||||
|
if (info_dialog)
|
||||||
|
{
|
||||||
|
sys_log.success("%s", text_dialog.toStdString());
|
||||||
|
QMessageBox::information(m_game_list_frame, tr("Game Integrity"), text_dialog);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sys_log.error("%s", text_dialog.toStdString());
|
||||||
|
QMessageBox::critical(m_game_list_frame, tr("Game Integrity"), text_dialog);
|
||||||
}
|
}
|
||||||
}, nullptr, false);
|
}, nullptr, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
progress_dialog* pdlg = new progress_dialog(tr("ISO File Hash Calculation"), tr("Calculating hash"), tr("Cancel"),
|
progress_dialog* pdlg = new progress_dialog(tr("File Hash Calculation"), "", tr("Cancel"),
|
||||||
0, 100, false, m_game_list_frame);
|
0, 100, false, m_game_list_frame);
|
||||||
|
|
||||||
pdlg->setAutoClose(false);
|
pdlg->setAutoClose(false);
|
||||||
@@ -436,21 +537,22 @@ void game_list_actions::ShowGameIntegrityDialog(const game_info& game)
|
|||||||
|
|
||||||
connect(pdlg, &progress_dialog::canceled, m_game_list_frame, [this]()
|
connect(pdlg, &progress_dialog::canceled, m_game_list_frame, [this]()
|
||||||
{
|
{
|
||||||
m_iso_validator->abort_hash();
|
m_game_validator->abort_hash();
|
||||||
});
|
});
|
||||||
|
|
||||||
QTimer* update_timer = new QTimer(m_game_list_frame);
|
QTimer* update_timer = new QTimer(m_game_list_frame);
|
||||||
|
|
||||||
connect(update_timer, &QTimer::timeout, m_game_list_frame, [this, pdlg, update_timer]()
|
connect(update_timer, &QTimer::timeout, m_game_list_frame, [this, pdlg, update_timer]()
|
||||||
{
|
{
|
||||||
if (m_iso_validator->get_status() == iso_hash_status::INITIALIZED)
|
if (m_game_validator->get_count())
|
||||||
{
|
{
|
||||||
// Set progress in range 0-100
|
// Set progress in range 0-100
|
||||||
const int progress = m_iso_validator->get_size() ?
|
const int progress = m_game_validator->get_size() ?
|
||||||
(static_cast<float>(m_iso_validator->get_bytes_read()) / m_iso_validator->get_size()) * 100 :
|
(static_cast<float>(m_game_validator->get_bytes_read()) / m_game_validator->get_size()) * 100 :
|
||||||
0;
|
0;
|
||||||
|
|
||||||
pdlg->setValue(progress);
|
pdlg->setValue(progress);
|
||||||
|
pdlg->setLabelText(tr("Calculating hash: %0").arg(m_game_validator->get_name()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "gui_game_info.h"
|
#include "gui_game_info.h"
|
||||||
#include "shortcut_utils.h"
|
#include "shortcut_utils.h"
|
||||||
#include "Loader/iso_validation.h"
|
#include "Loader/content_validation.h"
|
||||||
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@@ -55,7 +55,7 @@ public:
|
|||||||
|
|
||||||
void ShowRemoveGameDialog(const std::vector<game_info>& games);
|
void ShowRemoveGameDialog(const std::vector<game_info>& games);
|
||||||
void ShowGameInfoDialog(const std::vector<game_info>& games);
|
void ShowGameInfoDialog(const std::vector<game_info>& games);
|
||||||
void ShowGameIntegrityDialog(const game_info& game);
|
void ShowGameIntegrityDialog(content_file_type file_type, const std::string& game_path);
|
||||||
void ShowDiskUsageDialog();
|
void ShowDiskUsageDialog();
|
||||||
|
|
||||||
// NOTES:
|
// NOTES:
|
||||||
@@ -100,7 +100,7 @@ private:
|
|||||||
std::shared_ptr<gui_settings> m_gui_settings;
|
std::shared_ptr<gui_settings> m_gui_settings;
|
||||||
QFuture<void> m_disk_usage_future;
|
QFuture<void> m_disk_usage_future;
|
||||||
QFuture<void> m_game_integrity_future;
|
QFuture<void> m_game_integrity_future;
|
||||||
std::shared_ptr<iso_file_validation> m_iso_validator = std::make_shared<iso_file_validation>();
|
std::shared_ptr<content_validation> m_game_validator = std::make_shared<content_validation>();
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// m_content_info is used by:
|
// m_content_info is used by:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include "Utilities/File.h"
|
#include "Utilities/File.h"
|
||||||
#include "Emu/system_utils.hpp"
|
#include "Emu/system_utils.hpp"
|
||||||
#include "Loader/ISO.h"
|
#include "Loader/ISO.h"
|
||||||
#include "Loader/iso_validation.h"
|
#include "Loader/content_validation.h"
|
||||||
|
|
||||||
#include "QApplication"
|
#include "QApplication"
|
||||||
#include "QClipboard"
|
#include "QClipboard"
|
||||||
@@ -339,7 +339,7 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
|||||||
manage_game_menu->addSeparator();
|
manage_game_menu->addSeparator();
|
||||||
|
|
||||||
// Remove game
|
// Remove game
|
||||||
QAction* remove_game = manage_game_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category));
|
QAction* remove_game = manage_game_menu->addAction(tr("&Remove %0").arg(gameinfo->localized_category));
|
||||||
remove_game->setEnabled(!is_current_running_game);
|
remove_game->setEnabled(!is_current_running_game);
|
||||||
|
|
||||||
// Custom Images menu
|
// Custom Images menu
|
||||||
@@ -602,31 +602,31 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
|||||||
|
|
||||||
addSeparator();
|
addSeparator();
|
||||||
|
|
||||||
// Check integrity
|
// Check disc game integrity
|
||||||
if (QString::fromStdString(current_game.category) == cat::cat_disc_game)
|
if (QString::fromStdString(current_game.category) == cat::cat_disc_game)
|
||||||
{
|
{
|
||||||
|
const bool raw_archive = is_iso_file(current_game.path);
|
||||||
const iso_type_status iso_type = iso_file_decryption::check_type(current_game.path);
|
const iso_type_status iso_type = iso_file_decryption::check_type(current_game.path);
|
||||||
|
|
||||||
// If it's an ISO file (e.g. even a decrypted ISO), always provide the entry on the context menu but disable
|
// If it's an ISO file (e.g. even a decrypted ISO), always provide the entry on the context menu but disable
|
||||||
// it if the ISO does not support integrity check (e.g. non Redump ISO) or no integrity DB is found.
|
// it if the ISO does not support integrity check (e.g. non Redump ISO) or no integrity DB is found.
|
||||||
// That is to highlight a Redump ISO from a non Redump ISO
|
// That is to highlight a Redump ISO from a non Redump ISO
|
||||||
if (iso_type != iso_type_status::NOT_ISO)
|
if (raw_archive || iso_type != iso_type_status::NOT_ISO)
|
||||||
{
|
{
|
||||||
const iso_integrity_status iso_integrity = iso_file_validation::check_integrity("");
|
QAction* check_iso_integrity = addAction(tr("&Check ISO Integrity"));
|
||||||
|
|
||||||
QAction* check_integrity = addAction(tr("&Check ISO Integrity"));
|
|
||||||
|
|
||||||
// If it's a Redump ISO and the integrity DB exists
|
// If it's a Redump ISO and the integrity DB exists
|
||||||
if (iso_type == iso_type_status::REDUMP_ISO && iso_integrity != iso_integrity_status::ERROR_OPENING_DB)
|
if ((raw_archive || iso_type == iso_type_status::REDUMP_ISO) &&
|
||||||
|
content_validation::check_integrity(content_file_type::ISO, "") != content_integrity_status::ERROR_OPENING_DB)
|
||||||
{
|
{
|
||||||
connect(check_integrity, &QAction::triggered, this, [this, gameinfo]()
|
connect(check_iso_integrity, &QAction::triggered, this, [this, gameinfo]()
|
||||||
{
|
{
|
||||||
m_game_list_actions->ShowGameIntegrityDialog(gameinfo);
|
m_game_list_actions->ShowGameIntegrityDialog(content_file_type::ISO, gameinfo->info.path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
check_integrity->setEnabled(false);
|
check_iso_integrity->setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,10 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
|||||||
m_game_list->installEventFilter(this);
|
m_game_list->installEventFilter(this);
|
||||||
m_game_list->verticalScrollBar()->installEventFilter(this);
|
m_game_list->verticalScrollBar()->installEventFilter(this);
|
||||||
|
|
||||||
m_iso_integrity = new iso_integrity(this);
|
m_iso_integrity = new content_integrity(this, content_file_type::ISO);
|
||||||
|
m_psn_content_integrity = new content_integrity(this, content_file_type::PSN_CONTENT);
|
||||||
|
m_psn_dlc_integrity = new content_integrity(this, content_file_type::PSN_DLC);
|
||||||
|
m_psn_update_integrity = new content_integrity(this, content_file_type::PSN_UPDATE);
|
||||||
m_game_compat = new game_compatibility(this);
|
m_game_compat = new game_compatibility(this);
|
||||||
m_config_db = new config_database(this);
|
m_config_db = new config_database(this);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "game_list.h"
|
#include "game_list.h"
|
||||||
#include "game_list_actions.h"
|
#include "game_list_actions.h"
|
||||||
#include "custom_dock_widget.h"
|
#include "custom_dock_widget.h"
|
||||||
#include "iso_integrity.h"
|
#include "content_integrity.h"
|
||||||
|
|
||||||
#include "Utilities/lockless.h"
|
#include "Utilities/lockless.h"
|
||||||
#include "Utilities/mutex.h"
|
#include "Utilities/mutex.h"
|
||||||
@@ -56,7 +56,10 @@ public:
|
|||||||
|
|
||||||
void SetShowHidden(bool show);
|
void SetShowHidden(bool show);
|
||||||
|
|
||||||
iso_integrity* GetIsoIntegrity() const { return m_iso_integrity; }
|
content_integrity* GetIsoIntegrity() const { return m_iso_integrity; }
|
||||||
|
content_integrity* GetPsnContentIntegrity() const { return m_psn_content_integrity; }
|
||||||
|
content_integrity* GetPsnDlcIntegrity() const { return m_psn_dlc_integrity; }
|
||||||
|
content_integrity* GetPsnUpdateIntegrity() const { return m_psn_update_integrity; }
|
||||||
game_compatibility* GetGameCompatibility() const { return ensure(m_game_compat); }
|
game_compatibility* GetGameCompatibility() const { return ensure(m_game_compat); }
|
||||||
config_database* GetConfigDatabase() const { return ensure(m_config_db); }
|
config_database* GetConfigDatabase() const { return ensure(m_config_db); }
|
||||||
const std::vector<game_info>& GetGameInfo() const { return m_game_data; }
|
const std::vector<game_info>& GetGameInfo() const { return m_game_data; }
|
||||||
@@ -156,7 +159,10 @@ private:
|
|||||||
|
|
||||||
// Game List
|
// Game List
|
||||||
game_list_table* m_game_list = nullptr;
|
game_list_table* m_game_list = nullptr;
|
||||||
iso_integrity* m_iso_integrity = nullptr;
|
content_integrity* m_iso_integrity = nullptr;
|
||||||
|
content_integrity* m_psn_content_integrity = nullptr;
|
||||||
|
content_integrity* m_psn_dlc_integrity = nullptr;
|
||||||
|
content_integrity* m_psn_update_integrity = nullptr;
|
||||||
game_compatibility* m_game_compat = nullptr;
|
game_compatibility* m_game_compat = nullptr;
|
||||||
config_database* m_config_db = nullptr;
|
config_database* m_config_db = nullptr;
|
||||||
progress_dialog* m_progress_dialog = nullptr;
|
progress_dialog* m_progress_dialog = nullptr;
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
#include "iso_integrity.h"
|
|
||||||
#include "gui_settings.h"
|
|
||||||
|
|
||||||
#include "Emu/system_utils.hpp"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
LOG_CHANNEL(iso_log, "ISO");
|
|
||||||
|
|
||||||
iso_integrity::iso_integrity(QWidget* parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
m_filepath = QString::fromStdString(rpcs3::utils::get_redump_db_path());
|
|
||||||
m_downloader = new downloader(parent);
|
|
||||||
|
|
||||||
connect(m_downloader, &downloader::signal_download_finished, this, &iso_integrity::handle_download_finished);
|
|
||||||
connect(m_downloader, &downloader::signal_download_canceled, this, &iso_integrity::handle_download_canceled);
|
|
||||||
connect(m_downloader, &downloader::signal_download_error, this, &iso_integrity::handle_download_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_integrity::download()
|
|
||||||
{
|
|
||||||
const std::string url = rpcs3::utils::get_redump_db_download_url();
|
|
||||||
|
|
||||||
iso_log.notice("Starting database download from: %s", url);
|
|
||||||
|
|
||||||
m_downloader->start(url, true, true, true, tr("Downloading database"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_integrity::handle_download_finished(const QByteArray& content)
|
|
||||||
{
|
|
||||||
iso_log.notice("Database download finished");
|
|
||||||
|
|
||||||
// Write database to file
|
|
||||||
if (QByteArray data = read_json(content, true); !data.isEmpty())
|
|
||||||
{
|
|
||||||
QFile file(m_filepath);
|
|
||||||
|
|
||||||
if (file.exists())
|
|
||||||
{
|
|
||||||
iso_log.notice("Database file found: %s", m_filepath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
iso_log.error("Failed to write database to file: %s", m_filepath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write(data);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
iso_log.success("Database written to file: %s", m_filepath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_integrity::handle_download_canceled()
|
|
||||||
{
|
|
||||||
iso_log.notice("Database download canceled");
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_integrity::handle_download_error(const QString& error)
|
|
||||||
{
|
|
||||||
iso_log.error("", error.toStdString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray iso_integrity::read_json(const QByteArray& data, bool after_download)
|
|
||||||
{
|
|
||||||
QJsonParseError error{};
|
|
||||||
const QJsonDocument json_document = QJsonDocument::fromJson(data, &error);
|
|
||||||
|
|
||||||
if (!json_document.isObject())
|
|
||||||
{
|
|
||||||
iso_log.error("ISO Integrity database error - Invalid JSON: '%s'", error.errorString());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const QJsonObject json_data = json_document.object();
|
|
||||||
const int return_code = json_data["return_code"].toInt(-255);
|
|
||||||
|
|
||||||
if (return_code < 0)
|
|
||||||
{
|
|
||||||
if (after_download)
|
|
||||||
{
|
|
||||||
std::string error_message;
|
|
||||||
|
|
||||||
switch (return_code)
|
|
||||||
{
|
|
||||||
case -1: error_message = "Server Error - Internal Error"; break;
|
|
||||||
case -2: error_message = "Server Error - Maintenance Mode"; break;
|
|
||||||
case -255: error_message = "Server Error - Return code not found"; break;
|
|
||||||
default: error_message = "Server Error - Unknown Error"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
iso_log.error("%s: return code %d", error_message, return_code);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iso_log.error("ISO Integrity database error - Invalid: return code %d", return_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!json_data["redump"].isString())
|
|
||||||
{
|
|
||||||
iso_log.error("ISO Integrity database error - Unusable Redump string");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return QByteArray().fromStdString(json_data["redump"].toString().toStdString());
|
|
||||||
};
|
|
||||||
@@ -3544,6 +3544,9 @@ void main_window::CreateConnects()
|
|||||||
connect(ui->downloadIntegrityDbAct, &QAction::triggered, this, [this]()
|
connect(ui->downloadIntegrityDbAct, &QAction::triggered, this, [this]()
|
||||||
{
|
{
|
||||||
ensure(m_game_list_frame->GetIsoIntegrity())->download();
|
ensure(m_game_list_frame->GetIsoIntegrity())->download();
|
||||||
|
ensure(m_game_list_frame->GetPsnContentIntegrity())->download();
|
||||||
|
ensure(m_game_list_frame->GetPsnDlcIntegrity())->download();
|
||||||
|
ensure(m_game_list_frame->GetPsnUpdateIntegrity())->download();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->downloadCompatDbAct, &QAction::triggered, this, [this]()
|
connect(ui->downloadCompatDbAct, &QAction::triggered, this, [this]()
|
||||||
@@ -3556,6 +3559,14 @@ void main_window::CreateConnects()
|
|||||||
m_game_list_frame->GetConfigDatabase()->request_config_database(true);
|
m_game_list_frame->GetConfigDatabase()->request_config_database(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Check integrity for the content categories based on .PKG, .RAP and .EDAT (e.g. HDD game, DLC, Update)
|
||||||
|
connect(ui->checkPackageIntegrityAct, &QAction::triggered, this, [this]()
|
||||||
|
{
|
||||||
|
// File type different than ISO as passed here (PSN_CONTENT) will be properly detected in
|
||||||
|
// ShowGameIntegrityDialog() based on the selected package file
|
||||||
|
m_game_list_frame->actions()->ShowGameIntegrityDialog(content_file_type::PSN_CONTENT, "");
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->welcomeAct, &QAction::triggered, this, [this]()
|
connect(ui->welcomeAct, &QAction::triggered, this, [this]()
|
||||||
{
|
{
|
||||||
welcome_dialog* welcome = new welcome_dialog(m_gui_settings, true, this);
|
welcome_dialog* welcome = new welcome_dialog(m_gui_settings, true, this);
|
||||||
|
|||||||
@@ -323,6 +323,12 @@
|
|||||||
<addaction name="downloadCompatDbAct"/>
|
<addaction name="downloadCompatDbAct"/>
|
||||||
<addaction name="downloadConfigDbAct"/>
|
<addaction name="downloadConfigDbAct"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuPackages">
|
||||||
|
<property name="title">
|
||||||
|
<string>Packages/Raps/Edats</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="checkPackageIntegrityAct"/>
|
||||||
|
</widget>
|
||||||
<addaction name="confVFSDialogAct"/>
|
<addaction name="confVFSDialogAct"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionManage_Users"/>
|
<addaction name="actionManage_Users"/>
|
||||||
@@ -337,6 +343,7 @@
|
|||||||
<addaction name="actionManage_Cheats"/>
|
<addaction name="actionManage_Cheats"/>
|
||||||
<addaction name="actionManage_Game_Patches"/>
|
<addaction name="actionManage_Game_Patches"/>
|
||||||
<addaction name="menuDatabases"/>
|
<addaction name="menuDatabases"/>
|
||||||
|
<addaction name="menuPackages"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionManage_Screenshots"/>
|
<addaction name="actionManage_Screenshots"/>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -874,6 +881,11 @@
|
|||||||
<string>Download Config Database</string>
|
<string>Download Config Database</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="checkPackageIntegrityAct">
|
||||||
|
<property name="text">
|
||||||
|
<string>Check Integrity</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="welcomeAct">
|
<action name="welcomeAct">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>View Welcome Dialog</string>
|
<string>View Welcome Dialog</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user