PKG installer update for DLC

Also:
1) Don't ask for overwriting. It's outdated feature.
2) Limit cancellation capabilities.
This commit is contained in:
Nekotekina 2017-11-23 00:04:25 +03:00
parent ddbcbd0f1c
commit 5fffef74a2
3 changed files with 77 additions and 98 deletions

View File

@ -4,27 +4,19 @@
#include "sha1.h"
#include "key_vault.h"
#include "Utilities/StrFmt.h"
#include "Emu/System.h"
#include "Emu/VFS.h"
#include "unpkg.h"
bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>& sync, const std::string& pkg_filepath)
bool pkg_install(const std::string& path, atomic_t<double>& sync)
{
const std::size_t BUF_SIZE = 8192 * 1024; // 8 MB
std::vector<fs::file> filelist;
u32 cur_file=0;
fs::file original_file(pkg_filepath);
original_file.seek(pkg_f.pos());
filelist.push_back(std::move(original_file));
// Save current file offset (probably zero)
const u64 start_offset = pkg_f.pos();
u64 cur_offset = start_offset;
u64 cur_file_offset = start_offset;
// Get basic PKG information
PKGHeader header;
filelist.emplace_back(fs::file{path});
u32 cur_file = 0;
u64 cur_offset = 0;
u64 cur_file_offset = 0;
auto archive_seek = [&](const s64 new_offset, const fs::seek_mode damode = fs::seek_set)
{
@ -73,6 +65,8 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
return num_read;
};
// Get basic PKG information
PKGHeader header;
if (archive_read(&header, sizeof(header)) != sizeof(header))
{
@ -108,18 +102,18 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
}
}
if (header.pkg_size > pkg_f.size())
if (header.pkg_size > filelist[0].size())
{
//Check if multi-files pkg
if (pkg_filepath.length() < 7 || pkg_filepath.substr(pkg_filepath.length() - 7).compare("_00.pkg") != 0)
// Check if multi-files pkg
if (path.size() < 7 || path.compare(path.size() - 7, 7, "_00.pkg", 7) != 0)
{
LOG_ERROR(LOADER, "PKG file size mismatch (pkg_size=0x%llx)", header.pkg_size);
return false;
}
std::string name_wo_number = pkg_filepath.substr(0, pkg_filepath.length() - 7);
u64 cursize = pkg_f.size();
while (cursize != header.pkg_size)
std::string name_wo_number = path.substr(0, path.size() - 7);
u64 cursize = filelist[0].size();
while (cursize < header.pkg_size)
{
std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, filelist.size());
@ -131,7 +125,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
}
cursize += archive_file.size();
filelist.push_back(std::move(archive_file));
filelist.emplace_back(std::move(archive_file));
}
}
@ -143,6 +137,12 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
be_t<u32> drm_type{0};
be_t<u32> content_type{0};
std::string install_id;
// Read title ID and use it as an installation directory
install_id.resize(9);
archive_seek(55);
archive_read(&install_id.front(), install_id.size());
archive_seek(header.pkg_info_off);
@ -179,18 +179,50 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
break;
}
case 0xA:
{
if (packet.size > 8)
{
// Read an actual installation directory (DLC)
install_id.resize(packet.size);
archive_read(&install_id.front(), packet.size);
install_id = install_id.c_str() + 8;
continue;
}
break;
}
}
archive_seek(packet.size, fs::seek_cur);
}
// If false, an existing directory is being overwritten: cannot cancel the operation
bool was_null = true;
// Get full path and create the directory
const std::string dir = Emu.GetHddDir() + "game/" + install_id + '/';
if (!fs::create_dir(dir))
{
if (fs::g_tls_error == fs::error::exist)
{
was_null = false;
}
else
{
LOG_ERROR(LOADER, "PKG: Could not create the installation directory %s", dir);
return false;
}
}
// Allocate buffer with BUF_SIZE size or more if required
const std::unique_ptr<u128[]> buf(new u128[std::max<u64>(BUF_SIZE, sizeof(PKGEntry) * header.file_count) / sizeof(u128)]);
// Define decryption subfunction (`psp` arg selects the key for specific block)
auto decrypt = [&](u64 offset, u64 size, const uchar* key) -> u64
{
archive_seek(start_offset + header.data_offset + offset);
archive_seek(header.data_offset + offset);
// Read the data and set available size
const u64 read = archive_read(buf.get(), size);
@ -334,8 +366,16 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
if (sync.fetch_add((block_size + 0.0) / header.data_size) < 0.)
{
LOG_ERROR(LOADER, "Package installation cancelled: %s", dir);
return false;
if (was_null)
{
LOG_ERROR(LOADER, "Package installation cancelled: %s", dir);
out.close();
fs::remove_all(dir, true);
return false;
}
// Cannot cancel the installation
sync += 1.;
}
}

View File

@ -57,4 +57,4 @@ struct PKGEntry
be_t<u32> pad; // Padding (zeros)
};
bool pkg_install(const class fs::file& pkg_f, const std::string& dir, atomic_t<double>&, const std::string& pkg_filepath);
bool pkg_install(const std::string& path, atomic_t<double>&);

View File

@ -342,62 +342,17 @@ void main_window::InstallPkg(const QString& dropPath)
}
}
if (filePath == NULL)
if (filePath.isEmpty())
{
return;
}
Emu.Stop();
guiSettings->SetValue(gui::fd_install_pkg, QFileInfo(filePath).path());
const std::string fileName = sstr(QFileInfo(filePath).fileName());
const std::string path = sstr(filePath);
// Open PKG file
fs::file pkg_f(path);
if (!pkg_f || pkg_f.size() < 64)
{
LOG_ERROR(LOADER, "PKG: Failed to open %s", path);
return;
}
//Check header
u32 pkg_signature;
pkg_f.seek(0);
pkg_f.read(pkg_signature);
if (pkg_signature != "\x7FPKG"_u32)
{
LOG_ERROR(LOADER, "PKG: %s is not a pkg file", fileName);
return;
}
// Get title ID
std::vector<char> title_id(9);
pkg_f.seek(55);
pkg_f.read(title_id);
pkg_f.seek(0);
// Get full path
const auto& local_path = Emu.GetHddDir() + "game/" + std::string(std::begin(title_id), std::end(title_id));
if (!fs::create_dir(local_path))
{
if (fs::is_dir(local_path))
{
if (QMessageBox::question(this, tr("PKG Decrypter / Installer"), tr("Another installation found. Do you want to overwrite it?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
{
LOG_ERROR(LOADER, "PKG: Cancelled installation to existing directory %s", local_path);
return;
}
}
else
{
LOG_ERROR(LOADER, "PKG: Could not create the installation directory %s", local_path);
return;
}
}
QProgressDialog pdlg(tr("Installing package ... please wait ..."), tr("Cancel"), 0, 1000, this);
pdlg.setWindowTitle(tr("RPCS3 Package Installer"));
pdlg.setWindowModality(Qt::WindowModal);
@ -405,12 +360,11 @@ void main_window::InstallPkg(const QString& dropPath)
pdlg.show();
#ifdef _WIN32
QWinTaskbarButton *taskbar_button = new QWinTaskbarButton();
std::unique_ptr<QWinTaskbarButton> taskbar_button = std::make_unique<QWinTaskbarButton>();
taskbar_button->setWindow(windowHandle());
QWinTaskbarProgress *taskbar_progress = taskbar_button->progress();
QWinTaskbarProgress* taskbar_progress = taskbar_button->progress();
taskbar_progress->setRange(0, 1000);
taskbar_progress->setVisible(true);
#endif
// Synchronization variable
@ -419,39 +373,29 @@ void main_window::InstallPkg(const QString& dropPath)
// Run PKG unpacking asynchronously
scope_thread worker("PKG Installer", [&]
{
if (pkg_install(pkg_f, local_path + '/', progress, path))
if (pkg_install(path, progress))
{
progress = 1.;
return;
}
// TODO: Ask user to delete files on cancellation/failure?
progress = -1.;
});
// Wait for the completion
while (std::this_thread::sleep_for(5ms), std::abs(progress) < 1.)
{
if (pdlg.wasCanceled())
{
progress -= 1.;
#ifdef _WIN32
taskbar_progress->hide();
taskbar_button->~QWinTaskbarButton();
#endif
if (QMessageBox::question(this, tr("PKG Decrypter / Installer"), tr("Remove incomplete folder?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes)
{
fs::remove_all(local_path);
m_gameListFrame->Refresh(true);
LOG_SUCCESS(LOADER, "PKG: removed incomplete installation in %s", local_path);
return;
}
break;
}
// Update progress window
pdlg.setValue(static_cast<int>(progress * pdlg.maximum()));
double pval = progress;
pval < 0 ? pval += 1. : pval;
pdlg.setValue(static_cast<int>(pval * pdlg.maximum()));
#ifdef _WIN32
taskbar_progress->setValue(static_cast<int>(progress * taskbar_progress->maximum()));
taskbar_progress->setValue(static_cast<int>(pval * taskbar_progress->maximum()));
#endif
QCoreApplication::processEvents();
}
@ -471,11 +415,6 @@ void main_window::InstallPkg(const QString& dropPath)
m_gameListFrame->Refresh(true);
LOG_SUCCESS(GENERAL, "Successfully installed %s.", fileName);
guiSettings->ShowInfoBox(gui::ib_pkg_success, tr("Success!"), tr("Successfully installed software from package!"), this);
#ifdef _WIN32
taskbar_progress->hide();
taskbar_button->~QWinTaskbarButton();
#endif
}
}