2020-12-05 13:08:24 +01:00
|
|
|
|
#include "stdafx.h"
|
2020-02-15 23:36:20 +01:00
|
|
|
|
#include "Emu/VFS.h"
|
2014-02-15 01:06:12 +01:00
|
|
|
|
#include "TRP.h"
|
2017-04-13 19:29:47 +02:00
|
|
|
|
#include "Crypto/sha1.h"
|
2025-04-08 18:46:57 +02:00
|
|
|
|
#include "util/StrUtil.h"
|
2014-02-15 01:06:12 +01:00
|
|
|
|
|
2020-02-02 12:09:20 +01:00
|
|
|
|
LOG_CHANNEL(trp_log, "Trophy");
|
2020-02-01 05:36:53 +01:00
|
|
|
|
|
2016-02-01 22:47:09 +01:00
|
|
|
|
TRPLoader::TRPLoader(const fs::file& f)
|
|
|
|
|
|
: trp_f(f)
|
2014-02-15 01:06:12 +01:00
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-25 08:39:58 +02:00
|
|
|
|
bool TRPLoader::Install(std::string_view dest, bool /*show*/)
|
2014-03-17 20:34:19 +01:00
|
|
|
|
{
|
2016-02-01 22:47:09 +01:00
|
|
|
|
if (!trp_f)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2020-09-20 08:27:08 +02:00
|
|
|
|
fs::g_tls_error = fs::error::noent;
|
2014-03-17 20:34:19 +01:00
|
|
|
|
return false;
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-02-16 02:51:04 +01:00
|
|
|
|
|
2020-09-20 08:27:08 +02:00
|
|
|
|
fs::g_tls_error = {};
|
|
|
|
|
|
|
2023-03-12 23:33:35 +01:00
|
|
|
|
const std::string local_path = vfs::get(dest);
|
2014-02-16 02:51:04 +01:00
|
|
|
|
|
2023-03-12 23:33:35 +01:00
|
|
|
|
const std::string temp = fmt::format(u8"%s.$temp$%u", local_path, utils::get_unique_tsc());
|
2020-09-20 08:27:08 +02:00
|
|
|
|
|
|
|
|
|
|
if (!fs::create_dir(temp))
|
2015-07-15 15:21:41 +02:00
|
|
|
|
{
|
2023-03-12 23:33:35 +01:00
|
|
|
|
trp_log.error("Failed to create temp dir: '%s' (error=%s)", temp, fs::g_tls_error);
|
2016-02-01 22:47:09 +01:00
|
|
|
|
return false;
|
2015-07-15 15:21:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-22 20:29:08 +02:00
|
|
|
|
// Save TROPUSR.DAT
|
2023-03-12 23:33:35 +01:00
|
|
|
|
if (!fs::copy_file(local_path + "/TROPUSR.DAT", temp + "/TROPUSR.DAT", false))
|
|
|
|
|
|
{
|
|
|
|
|
|
trp_log.error("Failed to copy TROPUSR.DAT from '%s' to '%s' (error=%s)", local_path, temp, fs::g_tls_error);
|
|
|
|
|
|
}
|
2020-09-22 20:29:08 +02:00
|
|
|
|
|
2017-09-02 13:43:44 +02:00
|
|
|
|
std::vector<char> buffer(65536);
|
2016-02-01 22:47:09 +01:00
|
|
|
|
|
2020-09-20 08:27:08 +02:00
|
|
|
|
bool success = true;
|
2014-02-15 01:06:12 +01:00
|
|
|
|
for (const TRPEntry& entry : m_entries)
|
|
|
|
|
|
{
|
2016-02-01 22:47:09 +01:00
|
|
|
|
trp_f.seek(entry.offset);
|
2021-03-21 06:03:21 +01:00
|
|
|
|
|
2021-12-01 17:09:07 +01:00
|
|
|
|
if (!trp_f.read(buffer, entry.size))
|
2020-09-11 13:06:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
trp_log.error("Failed to read TRPEntry at: offset=0x%x, size=0x%x", entry.offset, entry.size);
|
|
|
|
|
|
continue; // ???
|
|
|
|
|
|
}
|
2020-09-20 08:27:08 +02:00
|
|
|
|
|
|
|
|
|
|
// Create the file in the temporary directory
|
2023-03-12 23:33:35 +01:00
|
|
|
|
const std::string filename = temp + '/' + vfs::escape(entry.name);
|
|
|
|
|
|
success = fs::write_file<true>(filename, fs::create + fs::excl, buffer);
|
2020-09-20 08:27:08 +02:00
|
|
|
|
if (!success)
|
|
|
|
|
|
{
|
2023-03-12 23:33:35 +01:00
|
|
|
|
trp_log.error("Failed to write file '%s' (error=%s)", filename, fs::g_tls_error);
|
2020-09-20 08:27:08 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
|
|
{
|
2022-01-07 18:16:03 +01:00
|
|
|
|
success = fs::remove_all(local_path, true, true);
|
2020-09-20 08:27:08 +02:00
|
|
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Atomically create trophy data (overwrite existing data)
|
|
|
|
|
|
success = fs::rename(temp, local_path, false);
|
2023-03-12 23:33:35 +01:00
|
|
|
|
if (!success)
|
|
|
|
|
|
{
|
|
|
|
|
|
trp_log.error("Failed to move directory '%s' to '%s' (error=%s)", temp, local_path, fs::g_tls_error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
trp_log.error("Failed to remove directory '%s' (error=%s)", local_path, fs::g_tls_error);
|
2020-09-20 08:27:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Remove temporary directory manually on failure (removed automatically on success)
|
2020-10-01 21:00:57 +02:00
|
|
|
|
auto old_error = fs::g_tls_error;
|
2020-09-20 08:27:08 +02:00
|
|
|
|
fs::remove_all(temp);
|
|
|
|
|
|
fs::g_tls_error = old_error;
|
2014-02-15 01:06:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-20 08:27:08 +02:00
|
|
|
|
return success;
|
2014-02-15 01:06:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TRPLoader::LoadHeader(bool show)
|
|
|
|
|
|
{
|
2016-02-01 22:47:09 +01:00
|
|
|
|
if (!trp_f)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2014-03-17 20:34:19 +01:00
|
|
|
|
return false;
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-03-17 20:34:19 +01:00
|
|
|
|
|
2016-02-01 22:47:09 +01:00
|
|
|
|
trp_f.seek(0);
|
2015-07-26 10:14:56 +02:00
|
|
|
|
|
2016-02-01 22:47:09 +01:00
|
|
|
|
if (!trp_f.read(m_header))
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2014-02-15 01:06:12 +01:00
|
|
|
|
return false;
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-02-15 01:06:12 +01:00
|
|
|
|
|
2014-02-16 02:51:04 +01:00
|
|
|
|
if (m_header.trp_magic != 0xDCA24D00)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2014-02-15 01:06:12 +01:00
|
|
|
|
return false;
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-02-15 01:06:12 +01:00
|
|
|
|
|
|
|
|
|
|
if (show)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2020-02-01 05:36:53 +01:00
|
|
|
|
trp_log.notice("TRP version: 0x%x", m_header.trp_version);
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-02-15 01:06:12 +01:00
|
|
|
|
|
2017-04-13 19:29:47 +02:00
|
|
|
|
if (m_header.trp_version >= 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned char hash[20];
|
2021-03-21 06:03:21 +01:00
|
|
|
|
std::vector<u8> file_contents;
|
2017-04-13 19:29:47 +02:00
|
|
|
|
|
|
|
|
|
|
trp_f.seek(0);
|
2021-12-01 17:09:07 +01:00
|
|
|
|
if (!trp_f.read(file_contents, m_header.trp_file_size))
|
2017-04-13 19:29:47 +02:00
|
|
|
|
{
|
2020-02-01 05:36:53 +01:00
|
|
|
|
trp_log.notice("Failed verifying checksum");
|
2017-04-13 19:29:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2020-02-01 05:36:53 +01:00
|
|
|
|
trp_log.error("Invalid checksum of TROPHY.TRP file");
|
2017-04-13 19:29:47 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
trp_f.seek(sizeof(m_header));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-15 01:06:12 +01:00
|
|
|
|
m_entries.clear();
|
|
|
|
|
|
|
2021-12-01 17:09:07 +01:00
|
|
|
|
if (!trp_f.read(m_entries, m_header.trp_files_count))
|
2014-02-15 01:06:12 +01:00
|
|
|
|
{
|
2021-03-21 06:03:21 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2014-02-15 01:06:12 +01:00
|
|
|
|
|
2021-03-21 06:03:21 +01:00
|
|
|
|
if (show)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (const auto& entry : m_entries)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2021-03-21 06:03:21 +01:00
|
|
|
|
trp_log.notice("TRP entry #%u: %s", &entry - m_entries.data(), entry.name);
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-02-15 01:06:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2014-03-17 20:34:19 +01:00
|
|
|
|
|
2017-11-20 14:08:35 +01:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-25 08:39:58 +02:00
|
|
|
|
bool TRPLoader::ContainsEntry(std::string_view filename)
|
2014-03-17 20:34:19 +01:00
|
|
|
|
{
|
2022-04-25 08:39:58 +02:00
|
|
|
|
if (filename.size() >= sizeof(TRPEntry::name))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-07-26 10:14:56 +02:00
|
|
|
|
for (const TRPEntry& entry : m_entries)
|
|
|
|
|
|
{
|
2022-04-25 08:39:58 +02:00
|
|
|
|
if (entry.name == filename)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2014-03-17 20:34:19 +01:00
|
|
|
|
return true;
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-03-17 20:34:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-25 08:39:58 +02:00
|
|
|
|
void TRPLoader::RemoveEntry(std::string_view filename)
|
2014-03-17 20:34:19 +01:00
|
|
|
|
{
|
2022-04-25 08:39:58 +02:00
|
|
|
|
if (filename.size() >= sizeof(TRPEntry::name))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-17 20:34:19 +01:00
|
|
|
|
std::vector<TRPEntry>::iterator i = m_entries.begin();
|
2015-07-26 10:14:56 +02:00
|
|
|
|
while (i != m_entries.end())
|
|
|
|
|
|
{
|
2022-04-25 08:39:58 +02:00
|
|
|
|
if (i->name == filename)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2014-03-17 20:34:19 +01:00
|
|
|
|
i = m_entries.erase(i);
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-03-17 20:34:19 +01:00
|
|
|
|
else
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2014-03-17 20:34:19 +01:00
|
|
|
|
i++;
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-03-17 20:34:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-25 08:39:58 +02:00
|
|
|
|
void TRPLoader::RenameEntry(std::string_view oldname, std::string_view newname)
|
2014-03-17 20:34:19 +01:00
|
|
|
|
{
|
2022-04-25 08:39:58 +02:00
|
|
|
|
if (oldname.size() >= sizeof(TRPEntry::name) || newname.size() >= sizeof(TRPEntry::name))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-10 06:59:33 +02:00
|
|
|
|
for (TRPEntry& entry : m_entries)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2022-04-25 08:39:58 +02:00
|
|
|
|
if (entry.name == oldname)
|
2015-07-26 10:14:56 +02:00
|
|
|
|
{
|
2022-04-25 08:39:58 +02:00
|
|
|
|
strcpy_trunc(entry.name, newname);
|
2015-07-26 10:14:56 +02:00
|
|
|
|
}
|
2014-03-17 20:34:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|