rpcsx/rpcs3/Loader/TRP.cpp

207 lines
3.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "stdafx.h"
#include "Emu/VFS.h"
#include "TRP.h"
#include "Crypto/sha1.h"
#include "Utilities/StrUtil.h"
LOG_CHANNEL(trp_log, "Trophy");
TRPLoader::TRPLoader(const fs::file& f)
: trp_f(f)
{
}
bool TRPLoader::Install(std::string_view dest, bool /*show*/)
{
if (!trp_f)
{
fs::g_tls_error = fs::error::noent;
return false;
}
fs::g_tls_error = {};
const std::string& local_path = vfs::get(dest);
const auto temp = fmt::format(u8"%s.temp%u", local_path, utils::get_unique_tsc());
if (!fs::create_dir(temp))
{
return false;
}
// Save TROPUSR.DAT
fs::copy_file(local_path + "/TROPUSR.DAT", temp + "/TROPUSR.DAT", false);
std::vector<char> buffer(65536);
bool success = true;
for (const TRPEntry& entry : m_entries)
{
trp_f.seek(entry.offset);
if (!trp_f.read(buffer, entry.size))
{
trp_log.error("Failed to read TRPEntry at: offset=0x%x, size=0x%x", entry.offset, entry.size);
continue; // ???
}
// Create the file in the temporary directory
success = fs::write_file<true>(temp + '/' + vfs::escape(entry.name), fs::create + fs::excl, buffer);
if (!success)
{
break;
}
}
if (success)
{
success = fs::remove_all(local_path, true, true);
if (success)
{
// Atomically create trophy data (overwrite existing data)
success = fs::rename(temp, local_path, false);
}
}
if (!success)
{
// Remove temporary directory manually on failure (removed automatically on success)
auto old_error = fs::g_tls_error;
fs::remove_all(temp);
fs::g_tls_error = old_error;
}
return success;
}
bool TRPLoader::LoadHeader(bool show)
{
if (!trp_f)
{
return false;
}
trp_f.seek(0);
if (!trp_f.read(m_header))
{
return false;
}
if (m_header.trp_magic != 0xDCA24D00)
{
return false;
}
if (show)
{
trp_log.notice("TRP version: 0x%x", m_header.trp_version);
}
if (m_header.trp_version >= 2)
{
unsigned char hash[20];
std::vector<u8> file_contents;
trp_f.seek(0);
if (!trp_f.read(file_contents, m_header.trp_file_size))
{
trp_log.notice("Failed verifying checksum");
}
else
{
memset(&(reinterpret_cast<TRPHeader*>(file_contents.data()))->sha1, 0, 20);
sha1(reinterpret_cast<const unsigned char*>(file_contents.data()), m_header.trp_file_size, hash);
if (memcmp(hash, m_header.sha1, 20) != 0)
{
trp_log.error("Invalid checksum of TROPHY.TRP file");
return false;
}
}
trp_f.seek(sizeof(m_header));
}
m_entries.clear();
if (!trp_f.read(m_entries, m_header.trp_files_count))
{
return false;
}
if (show)
{
for (const auto& entry : m_entries)
{
trp_log.notice("TRP entry #%u: %s", &entry - m_entries.data(), entry.name);
}
}
return true;
}
u64 TRPLoader::GetRequiredSpace() const
{
const u64 file_size = m_header.trp_file_size;
const u64 file_element_size = u64{1} * m_header.trp_files_count * m_header.trp_element_size;
return file_size - sizeof(m_header) - file_element_size;
}
bool TRPLoader::ContainsEntry(std::string_view filename)
{
if (filename.size() >= sizeof(TRPEntry::name))
{
return false;
}
for (const TRPEntry& entry : m_entries)
{
if (entry.name == filename)
{
return true;
}
}
return false;
}
void TRPLoader::RemoveEntry(std::string_view filename)
{
if (filename.size() >= sizeof(TRPEntry::name))
{
return;
}
std::vector<TRPEntry>::iterator i = m_entries.begin();
while (i != m_entries.end())
{
if (i->name == filename)
{
i = m_entries.erase(i);
}
else
{
i++;
}
}
}
void TRPLoader::RenameEntry(std::string_view oldname, std::string_view newname)
{
if (oldname.size() >= sizeof(TRPEntry::name) || newname.size() >= sizeof(TRPEntry::name))
{
return;
}
for (TRPEntry& entry : m_entries)
{
if (entry.name == oldname)
{
strcpy_trunc(entry.name, newname);
}
}
}