[UI] Redesigned "Install Content" window
- Added ID for each ImGui window - Added filesystem::CreateFolder function
This commit is contained in:
committed by
Radosław Gliński
parent
5d5eb03127
commit
56703e52e2
@@ -569,6 +569,93 @@ void EmulatorWindow::DisplayConfigDialog::OnDraw(ImGuiIO& io) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmulatorWindow::ContentInstallDialog::OnDraw(ImGuiIO& io) {
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(20, 20), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
|
bool dialog_open = true;
|
||||||
|
if (!ImGui::Begin(
|
||||||
|
fmt::format("Installation Progress###{}", window_id_).c_str(),
|
||||||
|
&dialog_open,
|
||||||
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
|
||||||
|
ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||||
|
Close();
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_everything_installed = true;
|
||||||
|
for (const auto& entry : *installation_entries_) {
|
||||||
|
ImGui::BeginTable(fmt::format("table_{}", entry.name_).c_str(), 2);
|
||||||
|
ImGui::TableNextRow(0);
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
if (entry.icon_) {
|
||||||
|
ImGui::Image(reinterpret_cast<ImTextureID>(entry.icon_.get()),
|
||||||
|
ui::default_image_icon_size);
|
||||||
|
} else {
|
||||||
|
ImGui::Dummy(ui::default_image_icon_size);
|
||||||
|
}
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
|
ImGui::Text("Name: %s", entry.name_.c_str());
|
||||||
|
ImGui::Text("Installation Path:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::TextLink(
|
||||||
|
xe::path_to_utf8(entry.data_installation_path_).c_str())) {
|
||||||
|
LaunchFileExplorer(emulator_window_.emulator_->content_root() /
|
||||||
|
entry.data_installation_path_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.content_type_ != xe::XContentType::kInvalid) {
|
||||||
|
ImGui::Text("Content Type: %s",
|
||||||
|
XContentTypeMap.at(entry.content_type_).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.installation_result_ != X_ERROR_SUCCESS) {
|
||||||
|
ImGui::Text("Status: %s (0x%08X)",
|
||||||
|
entry.installation_error_message_.c_str(),
|
||||||
|
entry.installation_result_);
|
||||||
|
} else if (entry.currently_installed_size_ == entry.content_size_ &&
|
||||||
|
entry.installation_result_ == X_ERROR_SUCCESS) {
|
||||||
|
ImGui::Text("Status: Success");
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
|
||||||
|
if (entry.content_size_ > 0) {
|
||||||
|
ImGui::ProgressBar(static_cast<float>(entry.currently_installed_size_) /
|
||||||
|
entry.content_size_);
|
||||||
|
|
||||||
|
if (entry.currently_installed_size_ != entry.content_size_ &&
|
||||||
|
entry.installation_result_ == X_ERROR_SUCCESS) {
|
||||||
|
is_everything_installed = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::ProgressBar(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (installation_entries_->size() > 1) {
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(!is_everything_installed);
|
||||||
|
if (ImGui::Button("Close")) {
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
Close();
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
if (!dialog_open && is_everything_installed) {
|
||||||
|
Close();
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
bool EmulatorWindow::Initialize() {
|
bool EmulatorWindow::Initialize() {
|
||||||
window_->AddListener(&window_listener_);
|
window_->AddListener(&window_listener_);
|
||||||
window_->AddInputListener(&window_listener_, kZOrderEmulatorWindowInput);
|
window_->AddInputListener(&window_listener_, kZOrderEmulatorWindowInput);
|
||||||
@@ -1088,62 +1175,27 @@ void EmulatorWindow::InstallContent() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using content_installation_data =
|
std::shared_ptr<std::vector<Emulator::ContentInstallEntry>>
|
||||||
std::tuple<X_STATUS, std::string, std::string>;
|
content_installation_status =
|
||||||
std::map<XContentType, std::vector<content_installation_data>>
|
std::make_shared<std::vector<Emulator::ContentInstallEntry>>();
|
||||||
content_installation_details;
|
|
||||||
|
|
||||||
for (const auto& path : paths) {
|
for (const auto& path : paths) {
|
||||||
// Normalize the path and make absolute.
|
content_installation_status->push_back({path});
|
||||||
auto abs_path = std::filesystem::absolute(path);
|
|
||||||
|
|
||||||
Emulator::ContentInstallationInfo installation_info;
|
|
||||||
auto result = emulator_->InstallContentPackage(abs_path, installation_info);
|
|
||||||
|
|
||||||
auto entry =
|
|
||||||
content_installation_details.find(installation_info.content_type);
|
|
||||||
|
|
||||||
// There is no entry with that specific type of XContent, so we must add it.
|
|
||||||
if (entry == content_installation_details.end()) {
|
|
||||||
content_installation_details.insert({installation_info.content_type, {}});
|
|
||||||
entry = content_installation_details.find(installation_info.content_type);
|
|
||||||
};
|
|
||||||
|
|
||||||
entry->second.push_back({result, installation_info.content_name,
|
|
||||||
installation_info.installation_path});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare installation process summary message
|
for (auto& entry : *content_installation_status) {
|
||||||
std::string summary = "Installation result: \n";
|
emulator_->ProcessContentPackageHeader(entry.path_, entry);
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& content_type : content_installation_details) {
|
auto installationThread = std::thread([this, content_installation_status] {
|
||||||
if (XContentTypeMap.find(content_type.first) != XContentTypeMap.cend()) {
|
for (auto& entry : *content_installation_status) {
|
||||||
summary += XContentTypeMap.at(content_type.first) + ":\n";
|
emulator_->InstallContentPackage(entry.path_, entry);
|
||||||
} else {
|
|
||||||
summary += "Unknown:\n";
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
installationThread.detach();
|
||||||
|
|
||||||
for (const auto& content_installation_entry : content_type.second) {
|
new ContentInstallDialog(imgui_drawer_.get(), *this,
|
||||||
const std::string status =
|
content_installation_status);
|
||||||
std::get<0>(content_installation_entry) == X_STATUS_SUCCESS
|
|
||||||
? "Success"
|
|
||||||
: fmt::format("Failed (0x{:08X})",
|
|
||||||
std::get<0>(content_installation_entry));
|
|
||||||
|
|
||||||
summary += fmt::format("\t{} - {} => {}\n", status,
|
|
||||||
std::get<1>(content_installation_entry),
|
|
||||||
std::get<2>(content_installation_entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
summary += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content_installation_details.count(XContentType::kProfile)) {
|
|
||||||
emulator_->kernel_state()->xam_state()->profile_manager()->ReloadProfiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(),
|
|
||||||
"Content Installation Summary", summary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatorWindow::ExtractZarchive() {
|
void EmulatorWindow::ExtractZarchive() {
|
||||||
|
|||||||
@@ -167,6 +167,34 @@ class EmulatorWindow {
|
|||||||
EmulatorWindow& emulator_window_;
|
EmulatorWindow& emulator_window_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ContentInstallDialog final : public ui::ImGuiDialog {
|
||||||
|
public:
|
||||||
|
ContentInstallDialog(
|
||||||
|
ui::ImGuiDrawer* imgui_drawer, EmulatorWindow& emulator_window,
|
||||||
|
std::shared_ptr<std::vector<Emulator::ContentInstallEntry>> entries)
|
||||||
|
: ui::ImGuiDialog(imgui_drawer),
|
||||||
|
emulator_window_(emulator_window),
|
||||||
|
installation_entries_(entries) {
|
||||||
|
window_id_ = GetWindowId();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ContentInstallDialog() {
|
||||||
|
for (auto& entry : *installation_entries_) {
|
||||||
|
entry.icon_.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void OnDraw(ImGuiIO& io) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t window_id_;
|
||||||
|
|
||||||
|
EmulatorWindow& emulator_window_;
|
||||||
|
std::shared_ptr<std::vector<Emulator::ContentInstallEntry>>
|
||||||
|
installation_entries_;
|
||||||
|
};
|
||||||
|
|
||||||
class DisplayConfigDialog final : public ui::ImGuiDialog {
|
class DisplayConfigDialog final : public ui::ImGuiDialog {
|
||||||
public:
|
public:
|
||||||
DisplayConfigDialog(ui::ImGuiDrawer* imgui_drawer,
|
DisplayConfigDialog(ui::ImGuiDrawer* imgui_drawer,
|
||||||
|
|||||||
@@ -24,6 +24,19 @@ bool CreateParentFolder(const std::filesystem::path& path) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::error_code CreateFolder(const std::filesystem::path& path) {
|
||||||
|
if (std::filesystem::exists(path)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
if (std::filesystem::create_directories(path, ec)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<FileInfo> ListDirectories(const std::filesystem::path& path) {
|
std::vector<FileInfo> ListDirectories(const std::filesystem::path& path) {
|
||||||
std::vector<FileInfo> files = ListFiles(path);
|
std::vector<FileInfo> files = ListFiles(path);
|
||||||
std::vector<FileInfo> directories = {};
|
std::vector<FileInfo> directories = {};
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ std::filesystem::path GetUserFolder();
|
|||||||
// attempting to create it.
|
// attempting to create it.
|
||||||
bool CreateParentFolder(const std::filesystem::path& path);
|
bool CreateParentFolder(const std::filesystem::path& path);
|
||||||
|
|
||||||
|
// Creates folder on specified path.
|
||||||
|
// If folder already exists it returns success (no error).
|
||||||
|
std::error_code CreateFolder(const std::filesystem::path& path);
|
||||||
|
|
||||||
// Creates an empty file at the given path, overwriting if it exists.
|
// Creates an empty file at the given path, overwriting if it exists.
|
||||||
bool CreateEmptyFile(const std::filesystem::path& path);
|
bool CreateEmptyFile(const std::filesystem::path& path);
|
||||||
|
|
||||||
|
|||||||
@@ -785,53 +785,85 @@ X_STATUS Emulator::DataMigration(const uint64_t xuid) {
|
|||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::InstallContentPackage(
|
X_STATUS Emulator::ProcessContentPackageHeader(
|
||||||
const std::filesystem::path& path,
|
const std::filesystem::path& path, ContentInstallEntry& installation_info) {
|
||||||
ContentInstallationInfo& installation_info) {
|
installation_info.name_ = "Invalid Content Package!";
|
||||||
std::unique_ptr<vfs::Device> device =
|
installation_info.content_type_ = XContentType::kInvalid;
|
||||||
|
installation_info.data_installation_path_ = xe::path_to_utf8(path.filename());
|
||||||
|
|
||||||
|
std::unique_ptr<vfs::XContentContainerDevice> device =
|
||||||
vfs::XContentContainerDevice::CreateContentDevice("", path);
|
vfs::XContentContainerDevice::CreateContentDevice("", path);
|
||||||
|
|
||||||
installation_info.content_name = "Invalid Content Package!";
|
if (!device || !device->Initialize()) {
|
||||||
installation_info.content_type = static_cast<XContentType>(0);
|
installation_info.installation_result_ = X_STATUS_INVALID_PARAMETER;
|
||||||
installation_info.installation_path = xe::path_to_utf8(path.filename());
|
installation_info.installation_error_message_ = "Invalid Package Type!";
|
||||||
|
XELOGE("Failed to initialize device");
|
||||||
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
// Always install savefiles to user signed to slot 0.
|
||||||
|
const auto profile =
|
||||||
|
kernel_state_->xam_state()->profile_manager()->GetProfile(
|
||||||
|
static_cast<uint8_t>(0));
|
||||||
|
|
||||||
|
uint64_t xuid = device->xuid();
|
||||||
|
if (device->content_type() ==
|
||||||
|
static_cast<uint32_t>(XContentType::kSavedGame) &&
|
||||||
|
profile) {
|
||||||
|
xuid = profile->xuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
installation_info.data_installation_path_ =
|
||||||
|
fmt::format("{:016X}/{:08X}/{:08X}/{}", xuid, device->title_id(),
|
||||||
|
device->content_type(), path.filename());
|
||||||
|
|
||||||
|
installation_info.header_installation_path_ =
|
||||||
|
fmt::format("{:016X}/{:08X}/Headers/{:08X}/{}", xuid, device->title_id(),
|
||||||
|
device->content_type(), path.filename());
|
||||||
|
|
||||||
|
installation_info.name_ =
|
||||||
|
xe::to_utf8(device->content_header().display_name());
|
||||||
|
installation_info.content_type_ =
|
||||||
|
static_cast<XContentType>(device->content_type());
|
||||||
|
installation_info.content_size_ = device->data_size();
|
||||||
|
|
||||||
|
installation_info.icon_ =
|
||||||
|
imgui_drawer_->LoadImGuiIcon(std::span<const uint8_t>(
|
||||||
|
device->GetContainerHeader()->content_metadata.title_thumbnail,
|
||||||
|
device->GetContainerHeader()->content_metadata.title_thumbnail_size));
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS Emulator::InstallContentPackage(
|
||||||
|
const std::filesystem::path& path, ContentInstallEntry& installation_info) {
|
||||||
|
std::unique_ptr<vfs::XContentContainerDevice> device =
|
||||||
|
vfs::XContentContainerDevice::CreateContentDevice("", path);
|
||||||
|
|
||||||
if (!device || !device->Initialize()) {
|
if (!device || !device->Initialize()) {
|
||||||
XELOGE("Failed to initialize device");
|
XELOGE("Failed to initialize device");
|
||||||
return X_STATUS_INVALID_PARAMETER;
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vfs::XContentContainerDevice* dev =
|
const std::filesystem::path installation_path =
|
||||||
(vfs::XContentContainerDevice*)device.get();
|
content_root() / installation_info.data_installation_path_;
|
||||||
|
|
||||||
// Always install savefiles to user signed to slot 0.
|
const std::filesystem::path header_path =
|
||||||
const auto profile =
|
content_root() / installation_info.header_installation_path_;
|
||||||
kernel_state_->xam_state()->profile_manager()->GetProfile(
|
|
||||||
static_cast<uint8_t>(0));
|
|
||||||
|
|
||||||
uint64_t xuid = dev->xuid();
|
if (!std::filesystem::exists(content_root())) {
|
||||||
if (dev->content_type() == static_cast<uint32_t>(XContentType::kSavedGame) &&
|
const std::error_code ec = xe::filesystem::CreateFolder(content_root());
|
||||||
profile) {
|
if (ec) {
|
||||||
xuid = profile->xuid();
|
installation_info.installation_error_message_ = ec.message();
|
||||||
|
installation_info.installation_result_ = X_STATUS_ACCESS_DENIED;
|
||||||
|
return X_STATUS_ACCESS_DENIED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path installation_path =
|
const auto disk_space = std::filesystem::space(content_root());
|
||||||
content_root() / fmt::format("{:016X}", xuid) /
|
if (disk_space.available < installation_info.content_size_ * 1.1f) {
|
||||||
fmt::format("{:08X}", dev->title_id()) /
|
installation_info.installation_error_message_ = "Insufficient disk space!";
|
||||||
fmt::format("{:08X}", dev->content_type()) / path.filename();
|
installation_info.installation_result_ = X_STATUS_DISK_FULL;
|
||||||
|
return X_STATUS_DISK_FULL;
|
||||||
std::filesystem::path header_path =
|
}
|
||||||
content_root() / fmt::format("{:016X}", xuid) /
|
|
||||||
fmt::format("{:08X}", dev->title_id()) / "Headers" /
|
|
||||||
fmt::format("{:08X}", dev->content_type()) / path.filename();
|
|
||||||
|
|
||||||
installation_info.installation_path =
|
|
||||||
fmt::format("{:016X}/{:08X}/{:08X}/{}", xuid, dev->title_id(),
|
|
||||||
dev->content_type(), path.filename());
|
|
||||||
|
|
||||||
installation_info.content_name =
|
|
||||||
xe::to_utf8(dev->content_header().display_name());
|
|
||||||
installation_info.content_type =
|
|
||||||
static_cast<XContentType>(dev->content_type());
|
|
||||||
|
|
||||||
if (std::filesystem::exists(installation_path)) {
|
if (std::filesystem::exists(installation_path)) {
|
||||||
// TODO(Gliniak): Popup
|
// TODO(Gliniak): Popup
|
||||||
@@ -840,7 +872,9 @@ X_STATUS Emulator::InstallContentPackage(
|
|||||||
std::error_code error_code;
|
std::error_code error_code;
|
||||||
std::filesystem::create_directories(installation_path, error_code);
|
std::filesystem::create_directories(installation_path, error_code);
|
||||||
if (error_code) {
|
if (error_code) {
|
||||||
installation_info.content_name = "Cannot Create Content Directory!";
|
installation_info.installation_error_message_ =
|
||||||
|
"Cannot Create Content Directory!";
|
||||||
|
installation_info.installation_result_ = error_code.value();
|
||||||
return error_code.value();
|
return error_code.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -848,13 +882,19 @@ X_STATUS Emulator::InstallContentPackage(
|
|||||||
vfs::VirtualFileSystem::ExtractContentHeader(device.get(), header_path);
|
vfs::VirtualFileSystem::ExtractContentHeader(device.get(), header_path);
|
||||||
|
|
||||||
X_STATUS error_code = vfs::VirtualFileSystem::ExtractContentFiles(
|
X_STATUS error_code = vfs::VirtualFileSystem::ExtractContentFiles(
|
||||||
device.get(), installation_path);
|
device.get(), installation_path,
|
||||||
|
installation_info.currently_installed_size_);
|
||||||
if (error_code != X_ERROR_SUCCESS) {
|
if (error_code != X_ERROR_SUCCESS) {
|
||||||
return error_code;
|
return error_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
installation_info.currently_installed_size_ = installation_info.content_size_;
|
||||||
kernel_state()->BroadcastNotification(kXNotificationLiveContentInstalled, 0);
|
kernel_state()->BroadcastNotification(kXNotificationLiveContentInstalled, 0);
|
||||||
|
|
||||||
|
if (installation_info.content_type_ == XContentType::kProfile) {
|
||||||
|
kernel_state_->xam_state()->profile_manager()->ReloadProfiles();
|
||||||
|
}
|
||||||
|
|
||||||
return error_code;
|
return error_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -879,7 +919,9 @@ X_STATUS Emulator::ExtractZarchivePackage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return vfs::VirtualFileSystem::ExtractContentFiles(device.get(), extract_dir);
|
uint64_t progress = 0;
|
||||||
|
return vfs::VirtualFileSystem::ExtractContentFiles(device.get(), extract_dir,
|
||||||
|
progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::CreateZarchivePackage(
|
X_STATUS Emulator::CreateZarchivePackage(
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
#include "xenia/patcher/patcher.h"
|
#include "xenia/patcher/patcher.h"
|
||||||
#include "xenia/patcher/plugin_loader.h"
|
#include "xenia/patcher/plugin_loader.h"
|
||||||
|
#include "xenia/ui/immediate_drawer.h"
|
||||||
#include "xenia/vfs/device.h"
|
#include "xenia/vfs/device.h"
|
||||||
#include "xenia/vfs/virtual_file_system.h"
|
#include "xenia/vfs/virtual_file_system.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
@@ -234,18 +235,32 @@ class Emulator {
|
|||||||
|
|
||||||
X_STATUS LaunchDefaultModule(const std::filesystem::path& path);
|
X_STATUS LaunchDefaultModule(const std::filesystem::path& path);
|
||||||
|
|
||||||
struct ContentInstallationInfo {
|
struct ContentInstallEntry {
|
||||||
XContentType content_type;
|
ContentInstallEntry(std::filesystem::path path) : path_(path) {};
|
||||||
std::string installation_path;
|
|
||||||
std::string content_name;
|
std::string name_{};
|
||||||
|
std::filesystem::path path_;
|
||||||
|
std::filesystem::path data_installation_path_;
|
||||||
|
std::filesystem::path header_installation_path_;
|
||||||
|
|
||||||
|
uint64_t content_size_ = 0;
|
||||||
|
uint64_t currently_installed_size_ = 0;
|
||||||
|
X_STATUS installation_result_{};
|
||||||
|
std::string installation_error_message_{};
|
||||||
|
XContentType content_type_{};
|
||||||
|
|
||||||
|
std::unique_ptr<ui::ImmediateTexture> icon_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Migrates data from content to content/xuid with respect to common data.
|
// Migrates data from content to content/xuid with respect to common data.
|
||||||
X_STATUS DataMigration(const uint64_t xuid);
|
X_STATUS DataMigration(const uint64_t xuid);
|
||||||
|
|
||||||
|
X_STATUS ProcessContentPackageHeader(const std::filesystem::path& path,
|
||||||
|
ContentInstallEntry& installation_info);
|
||||||
|
|
||||||
// Extract content of package to content specific directory.
|
// Extract content of package to content specific directory.
|
||||||
X_STATUS InstallContentPackage(const std::filesystem::path& path,
|
X_STATUS InstallContentPackage(const std::filesystem::path& path,
|
||||||
ContentInstallationInfo& installation_info);
|
ContentInstallEntry& installation_info);
|
||||||
|
|
||||||
// Extract content of zar package to desired directory.
|
// Extract content of zar package to desired directory.
|
||||||
X_STATUS ExtractZarchivePackage(const std::filesystem::path& path,
|
X_STATUS ExtractZarchivePackage(const std::filesystem::path& path,
|
||||||
|
|||||||
@@ -661,8 +661,10 @@ dword_result_t XamContentLaunchImageFromFileInternal_entry(
|
|||||||
const std::filesystem::path host_path =
|
const std::filesystem::path host_path =
|
||||||
kernel_state()->emulator()->content_root() / entry->name();
|
kernel_state()->emulator()->content_root() / entry->name();
|
||||||
if (!std::filesystem::exists(host_path)) {
|
if (!std::filesystem::exists(host_path)) {
|
||||||
|
uint64_t progress = 0;
|
||||||
|
|
||||||
vfs::VirtualFileSystem::ExtractContentFile(
|
vfs::VirtualFileSystem::ExtractContentFile(
|
||||||
entry, kernel_state()->emulator()->content_root(), true);
|
entry, kernel_state()->emulator()->content_root(), progress, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
|
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
|
||||||
@@ -716,8 +718,9 @@ dword_result_t XamContentLaunchImageInternal_entry(lpvoid_t content_data_ptr,
|
|||||||
kernel_state()->emulator()->content_root() / entry->name();
|
kernel_state()->emulator()->content_root() / entry->name();
|
||||||
|
|
||||||
if (!std::filesystem::exists(host_path)) {
|
if (!std::filesystem::exists(host_path)) {
|
||||||
|
uint64_t progress = 0;
|
||||||
kernel_state()->file_system()->ExtractContentFile(
|
kernel_state()->file_system()->ExtractContentFile(
|
||||||
entry, kernel_state()->emulator()->content_root(), true);
|
entry, kernel_state()->emulator()->content_root(), progress, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
|
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
|
||||||
|
|||||||
@@ -58,8 +58,6 @@ namespace xam {
|
|||||||
|
|
||||||
extern std::atomic<int> xam_dialogs_shown_;
|
extern std::atomic<int> xam_dialogs_shown_;
|
||||||
|
|
||||||
constexpr ImVec2 default_image_icon_size = ImVec2(75.f, 75.f);
|
|
||||||
|
|
||||||
class XamDialog : public xe::ui::ImGuiDialog {
|
class XamDialog : public xe::ui::ImGuiDialog {
|
||||||
public:
|
public:
|
||||||
void set_close_callback(std::function<void()> close_callback) {
|
void set_close_callback(std::function<void()> close_callback) {
|
||||||
@@ -509,7 +507,8 @@ class GameAchievementsDialog final : public XamDialog {
|
|||||||
: XamDialog(imgui_drawer),
|
: XamDialog(imgui_drawer),
|
||||||
drawing_position_(drawing_position),
|
drawing_position_(drawing_position),
|
||||||
title_info_(*title_info),
|
title_info_(*title_info),
|
||||||
profile_(profile) {
|
profile_(profile),
|
||||||
|
window_id_(GetWindowId()) {
|
||||||
LoadAchievementsData();
|
LoadAchievementsData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,9 +615,9 @@ class GameAchievementsDialog final : public XamDialog {
|
|||||||
const auto icon = GetIcon(achievement_entry);
|
const auto icon = GetIcon(achievement_entry);
|
||||||
if (icon) {
|
if (icon) {
|
||||||
ImGui::Image(reinterpret_cast<ImTextureID>(GetIcon(achievement_entry)),
|
ImGui::Image(reinterpret_cast<ImTextureID>(GetIcon(achievement_entry)),
|
||||||
default_image_icon_size);
|
ui::default_image_icon_size);
|
||||||
} else {
|
} else {
|
||||||
ImGui::Dummy(default_image_icon_size);
|
ImGui::Dummy(ui::default_image_icon_size);
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
@@ -632,7 +631,7 @@ class GameAchievementsDialog final : public XamDialog {
|
|||||||
GetAchievementDescription(achievement_entry).c_str());
|
GetAchievementDescription(achievement_entry).c_str());
|
||||||
ImGui::PopTextWrapPos();
|
ImGui::PopTextWrapPos();
|
||||||
|
|
||||||
ImGui::SetCursorPosY(start_drawing_pos.y + default_image_icon_size.x -
|
ImGui::SetCursorPosY(start_drawing_pos.y + ui::default_image_icon_size.x -
|
||||||
ImGui::GetTextLineHeight());
|
ImGui::GetTextLineHeight());
|
||||||
|
|
||||||
if (achievement_entry.IsUnlocked()) {
|
if (achievement_entry.IsUnlocked()) {
|
||||||
@@ -644,7 +643,8 @@ class GameAchievementsDialog final : public XamDialog {
|
|||||||
// TODO(Gliniak): There is no easy way to align text to middle, so I have to
|
// TODO(Gliniak): There is no easy way to align text to middle, so I have to
|
||||||
// do it manually.
|
// do it manually.
|
||||||
const float achievement_row_middle_alignment =
|
const float achievement_row_middle_alignment =
|
||||||
((default_image_icon_size.x / 2.f) - ImGui::GetTextLineHeight() / 2.f) *
|
((ui::default_image_icon_size.x / 2.f) -
|
||||||
|
ImGui::GetTextLineHeight() / 2.f) *
|
||||||
0.85f;
|
0.85f;
|
||||||
|
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
||||||
@@ -667,10 +667,13 @@ class GameAchievementsDialog final : public XamDialog {
|
|||||||
|
|
||||||
bool dialog_open = true;
|
bool dialog_open = true;
|
||||||
|
|
||||||
if (!ImGui::Begin(fmt::format("{} Achievements List",
|
std::string title_name = xe::to_utf8(title_info_.title_name);
|
||||||
xe::to_utf8(title_info_.title_name))
|
title_name.erase(std::remove(title_name.begin(), title_name.end(), '\0'),
|
||||||
.c_str(),
|
title_name.end());
|
||||||
&dialog_open,
|
|
||||||
|
const std::string window_name =
|
||||||
|
fmt::format("{} Achievements###{}", title_name, window_id_);
|
||||||
|
if (!ImGui::Begin(window_name.c_str(), &dialog_open,
|
||||||
ImGuiWindowFlags_NoCollapse |
|
ImGuiWindowFlags_NoCollapse |
|
||||||
ImGuiWindowFlags_AlwaysAutoResize |
|
ImGuiWindowFlags_AlwaysAutoResize |
|
||||||
ImGuiWindowFlags_HorizontalScrollbar)) {
|
ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||||
@@ -688,7 +691,7 @@ class GameAchievementsDialog final : public XamDialog {
|
|||||||
if (ImGui::BeginTable("", 3,
|
if (ImGui::BeginTable("", 3,
|
||||||
ImGuiTableFlags_::ImGuiTableFlags_BordersInnerH)) {
|
ImGuiTableFlags_::ImGuiTableFlags_BordersInnerH)) {
|
||||||
for (const auto& entry : achievements_info_) {
|
for (const auto& entry : achievements_info_) {
|
||||||
ImGui::TableNextRow(0, default_image_icon_size.y);
|
ImGui::TableNextRow(0, ui::default_image_icon_size.y);
|
||||||
DrawTitleAchievementInfo(io, entry);
|
DrawTitleAchievementInfo(io, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,6 +710,7 @@ class GameAchievementsDialog final : public XamDialog {
|
|||||||
|
|
||||||
bool show_locked_info_ = false;
|
bool show_locked_info_ = false;
|
||||||
|
|
||||||
|
uint64_t window_id_;
|
||||||
const ImVec2 drawing_position_ = {};
|
const ImVec2 drawing_position_ = {};
|
||||||
|
|
||||||
const TitleInfo title_info_;
|
const TitleInfo title_info_;
|
||||||
@@ -724,7 +728,8 @@ class GamesInfoDialog final : public XamDialog {
|
|||||||
drawing_position_(drawing_position),
|
drawing_position_(drawing_position),
|
||||||
profile_(profile),
|
profile_(profile),
|
||||||
profile_manager_(kernel_state()->xam_state()->profile_manager()),
|
profile_manager_(kernel_state()->xam_state()->profile_manager()),
|
||||||
dialog_name_(fmt::format("{}'s Games List", profile->name())) {
|
dialog_name_(fmt::format("{}'s Games List###{}", profile->name(),
|
||||||
|
GetWindowId())) {
|
||||||
LoadProfileGameInfo(imgui_drawer, profile);
|
LoadProfileGameInfo(imgui_drawer, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,9 +767,9 @@ class GamesInfoDialog final : public XamDialog {
|
|||||||
|
|
||||||
if (title_icon.count(entry.id)) {
|
if (title_icon.count(entry.id)) {
|
||||||
ImGui::Image(reinterpret_cast<ImTextureID>(title_icon.at(entry.id).get()),
|
ImGui::Image(reinterpret_cast<ImTextureID>(title_icon.at(entry.id).get()),
|
||||||
default_image_icon_size);
|
ui::default_image_icon_size);
|
||||||
} else {
|
} else {
|
||||||
ImGui::Dummy(default_image_icon_size);
|
ImGui::Dummy(ui::default_image_icon_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second Column
|
// Second Column
|
||||||
@@ -779,7 +784,7 @@ class GamesInfoDialog final : public XamDialog {
|
|||||||
entry.title_earned_gamerscore)
|
entry.title_earned_gamerscore)
|
||||||
.c_str());
|
.c_str());
|
||||||
|
|
||||||
ImGui::SetCursorPosY(start_position.y + default_image_icon_size.y -
|
ImGui::SetCursorPosY(start_position.y + ui::default_image_icon_size.y -
|
||||||
ImGui::GetTextLineHeight());
|
ImGui::GetTextLineHeight());
|
||||||
|
|
||||||
if (entry.WasTitlePlayed()) {
|
if (entry.WasTitlePlayed()) {
|
||||||
@@ -881,7 +886,7 @@ class GamesInfoDialog final : public XamDialog {
|
|||||||
|
|
||||||
if (ImGui::BeginTable("", 2,
|
if (ImGui::BeginTable("", 2,
|
||||||
ImGuiTableFlags_::ImGuiTableFlags_BordersInnerH)) {
|
ImGuiTableFlags_::ImGuiTableFlags_BordersInnerH)) {
|
||||||
ImGui::TableNextRow(0, default_image_icon_size.y);
|
ImGui::TableNextRow(0, ui::default_image_icon_size.y);
|
||||||
for (auto& entry : info_) {
|
for (auto& entry : info_) {
|
||||||
std::string filter(title_name_filter_);
|
std::string filter(title_name_filter_);
|
||||||
if (!filter.empty()) {
|
if (!filter.empty()) {
|
||||||
@@ -1564,9 +1569,10 @@ bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer, const uint64_t xuid,
|
|||||||
// In the future it can be replaced with profile icon.
|
// In the future it can be replaced with profile icon.
|
||||||
const auto icon = imgui_drawer->GetNotificationIcon(user_index);
|
const auto icon = imgui_drawer->GetNotificationIcon(user_index);
|
||||||
if (icon && user_index < XUserMaxUserCount) {
|
if (icon && user_index < XUserMaxUserCount) {
|
||||||
ImGui::Image(reinterpret_cast<ImTextureID>(icon), default_image_icon_size);
|
ImGui::Image(reinterpret_cast<ImTextureID>(icon),
|
||||||
|
ui::default_image_icon_size);
|
||||||
} else {
|
} else {
|
||||||
ImGui::Dummy(default_image_icon_size);
|
ImGui::Dummy(ui::default_image_icon_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|||||||
@@ -16,9 +16,12 @@
|
|||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
|
uint64_t ImGuiDialog::next_window_id_ = 0;
|
||||||
|
|
||||||
ImGuiDialog::ImGuiDialog(ImGuiDrawer* imgui_drawer)
|
ImGuiDialog::ImGuiDialog(ImGuiDrawer* imgui_drawer)
|
||||||
: imgui_drawer_(imgui_drawer) {
|
: imgui_drawer_(imgui_drawer) {
|
||||||
imgui_drawer_->AddDialog(this);
|
imgui_drawer_->AddDialog(this);
|
||||||
|
next_window_id_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiDialog::~ImGuiDialog() {
|
ImGuiDialog::~ImGuiDialog() {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class ImGuiDialog {
|
|||||||
ImGuiDrawer* imgui_drawer() const { return imgui_drawer_; }
|
ImGuiDrawer* imgui_drawer() const { return imgui_drawer_; }
|
||||||
ImGuiIO& GetIO();
|
ImGuiIO& GetIO();
|
||||||
|
|
||||||
|
uint64_t GetWindowId() const { return next_window_id_; }
|
||||||
// Closes the dialog and returns to any waiters.
|
// Closes the dialog and returns to any waiters.
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
@@ -48,6 +49,8 @@ class ImGuiDialog {
|
|||||||
virtual void OnDraw(ImGuiIO& io) {}
|
virtual void OnDraw(ImGuiIO& io) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static uint64_t next_window_id_;
|
||||||
|
|
||||||
ImGuiDrawer* imgui_drawer_ = nullptr;
|
ImGuiDrawer* imgui_drawer_ = nullptr;
|
||||||
bool has_close_pending_ = false;
|
bool has_close_pending_ = false;
|
||||||
std::vector<xe::threading::Fence*> waiting_fences_;
|
std::vector<xe::threading::Fence*> waiting_fences_;
|
||||||
|
|||||||
@@ -234,6 +234,25 @@ std::optional<ImGuiKey> ImGuiDrawer::VirtualKeyToImGuiKey(VirtualKey vkey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ImmediateTexture> ImGuiDrawer::LoadImGuiIcon(
|
||||||
|
std::span<const uint8_t> data) {
|
||||||
|
if (!immediate_drawer_) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int width, height, channels;
|
||||||
|
unsigned char* image_data =
|
||||||
|
stbi_load_from_memory(data.data(), static_cast<int>(data.size()), &width,
|
||||||
|
&height, &channels, STBI_rgb_alpha);
|
||||||
|
if (!image_data) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return immediate_drawer_->CreateTexture(
|
||||||
|
width, height, ImmediateTextureFilter::kLinear, true,
|
||||||
|
reinterpret_cast<uint8_t*>(image_data));
|
||||||
|
}
|
||||||
|
|
||||||
std::map<uint32_t, std::unique_ptr<ImmediateTexture>> ImGuiDrawer::LoadIcons(
|
std::map<uint32_t, std::unique_ptr<ImmediateTexture>> ImGuiDrawer::LoadIcons(
|
||||||
IconsData data) {
|
IconsData data) {
|
||||||
std::map<uint32_t, std::unique_ptr<ImmediateTexture>> icons_;
|
std::map<uint32_t, std::unique_ptr<ImmediateTexture>> icons_;
|
||||||
@@ -242,21 +261,9 @@ std::map<uint32_t, std::unique_ptr<ImmediateTexture>> ImGuiDrawer::LoadIcons(
|
|||||||
return icons_;
|
return icons_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int width, height, channels;
|
|
||||||
|
|
||||||
for (const auto& icon : data) {
|
for (const auto& icon : data) {
|
||||||
unsigned char* image_data = stbi_load_from_memory(
|
icons_[icon.first] = LoadImGuiIcon(icon.second);
|
||||||
icon.second.data(), static_cast<int>(icon.second.size()), &width,
|
|
||||||
&height, &channels, STBI_rgb_alpha);
|
|
||||||
|
|
||||||
if (!image_data) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
icons_[icon.first] = (immediate_drawer_->CreateTexture(
|
|
||||||
width, height, ImmediateTextureFilter::kLinear, true,
|
|
||||||
reinterpret_cast<uint8_t*>(image_data)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return icons_;
|
return icons_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ class Window;
|
|||||||
|
|
||||||
using IconsData = std::map<uint32_t, std::span<const uint8_t>>;
|
using IconsData = std::map<uint32_t, std::span<const uint8_t>>;
|
||||||
|
|
||||||
|
constexpr ImVec2 default_image_icon_size = ImVec2(75.f, 75.f);
|
||||||
|
|
||||||
class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
||||||
public:
|
public:
|
||||||
ImGuiDrawer(Window* window, size_t z_order);
|
ImGuiDrawer(Window* window, size_t z_order);
|
||||||
@@ -64,6 +66,8 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
|||||||
void ClearDialogs();
|
void ClearDialogs();
|
||||||
void EnableNotifications(bool enable) { are_notifications_enabled_ = enable; }
|
void EnableNotifications(bool enable) { are_notifications_enabled_ = enable; }
|
||||||
|
|
||||||
|
std::unique_ptr<ImmediateTexture> LoadImGuiIcon(
|
||||||
|
std::span<const uint8_t> data);
|
||||||
std::map<uint32_t, std::unique_ptr<ImmediateTexture>> LoadIcons(
|
std::map<uint32_t, std::unique_ptr<ImmediateTexture>> LoadIcons(
|
||||||
IconsData data);
|
IconsData data);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
namespace xe {
|
namespace xe {
|
||||||
namespace vfs {
|
namespace vfs {
|
||||||
|
|
||||||
std::unique_ptr<Device> XContentContainerDevice::CreateContentDevice(
|
std::unique_ptr<XContentContainerDevice>
|
||||||
|
XContentContainerDevice::CreateContentDevice(
|
||||||
const std::string_view mount_path, const std::filesystem::path& host_path) {
|
const std::string_view mount_path, const std::filesystem::path& host_path) {
|
||||||
if (!std::filesystem::exists(host_path)) {
|
if (!std::filesystem::exists(host_path)) {
|
||||||
XELOGE("Path to XContent container does not exist: {}", host_path);
|
XELOGE("Path to XContent container does not exist: {}", host_path);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class XContentContainerDevice : public Device {
|
|||||||
public:
|
public:
|
||||||
constexpr static uint32_t kBlockSize = 0x1000;
|
constexpr static uint32_t kBlockSize = 0x1000;
|
||||||
|
|
||||||
static std::unique_ptr<Device> CreateContentDevice(
|
static std::unique_ptr<XContentContainerDevice> CreateContentDevice(
|
||||||
const std::string_view mount_path,
|
const std::string_view mount_path,
|
||||||
const std::filesystem::path& host_path);
|
const std::filesystem::path& host_path);
|
||||||
|
|
||||||
@@ -74,6 +74,10 @@ class XContentContainerDevice : public Device {
|
|||||||
return final_license;
|
return final_license;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const XContentContainerHeader* GetContainerHeader() const {
|
||||||
|
return header_.get();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
XContentContainerDevice(const std::string_view mount_path,
|
XContentContainerDevice(const std::string_view mount_path,
|
||||||
const std::filesystem::path& host_path);
|
const std::filesystem::path& host_path);
|
||||||
@@ -106,10 +110,6 @@ class XContentContainerDevice : public Device {
|
|||||||
|
|
||||||
const std::filesystem::path& GetHostPath() const { return host_path_; }
|
const std::filesystem::path& GetHostPath() const { return host_path_; }
|
||||||
|
|
||||||
const XContentContainerHeader* GetContainerHeader() const {
|
|
||||||
return header_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
std::filesystem::path host_path_;
|
std::filesystem::path host_path_;
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ int vfs_dump_main(const std::vector<std::string>& args) {
|
|||||||
XELOGE("Failed to initialize device");
|
XELOGE("Failed to initialize device");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return VirtualFileSystem::ExtractContentFiles(device.get(), base_path);
|
|
||||||
|
uint64_t progress = 0;
|
||||||
|
return VirtualFileSystem::ExtractContentFiles(device.get(), base_path,
|
||||||
|
progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace vfs
|
} // namespace vfs
|
||||||
|
|||||||
@@ -335,6 +335,7 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry,
|
|||||||
|
|
||||||
X_STATUS VirtualFileSystem::ExtractContentFile(Entry* entry,
|
X_STATUS VirtualFileSystem::ExtractContentFile(Entry* entry,
|
||||||
std::filesystem::path base_path,
|
std::filesystem::path base_path,
|
||||||
|
uint64_t& progress,
|
||||||
bool extract_to_root) {
|
bool extract_to_root) {
|
||||||
// Allocate a buffer when needed.
|
// Allocate a buffer when needed.
|
||||||
size_t buffer_size = 0;
|
size_t buffer_size = 0;
|
||||||
@@ -369,27 +370,33 @@ X_STATUS VirtualFileSystem::ExtractContentFile(Entry* entry,
|
|||||||
in_file->Destroy();
|
in_file->Destroy();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
constexpr size_t write_buffer_size = 4_MiB;
|
||||||
|
|
||||||
if (entry->can_map()) {
|
if (entry->can_map()) {
|
||||||
auto map = entry->OpenMapped(xe::MappedMemory::Mode::kRead);
|
auto map = entry->OpenMapped(xe::MappedMemory::Mode::kRead);
|
||||||
fwrite(map->data(), map->size(), 1, file);
|
|
||||||
|
auto remaining_size = map->size();
|
||||||
|
auto offset = 0;
|
||||||
|
|
||||||
|
while (remaining_size > 0) {
|
||||||
|
fwrite(map->data() + offset, write_buffer_size, 1, file);
|
||||||
|
offset += write_buffer_size;
|
||||||
|
remaining_size -= write_buffer_size;
|
||||||
|
}
|
||||||
map->Close();
|
map->Close();
|
||||||
} else {
|
} else {
|
||||||
// Can't map the file into memory. Read it into a temporary buffer.
|
auto remaining_size = entry->size();
|
||||||
if (!buffer || entry->size() > buffer_size) {
|
size_t offset = 0;
|
||||||
// Resize the buffer.
|
buffer = new uint8_t[write_buffer_size];
|
||||||
if (buffer) {
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a buffer rounded up to the nearest 512MB.
|
while (remaining_size > 0) {
|
||||||
buffer_size = xe::round_up(entry->size(), 512_MiB);
|
size_t bytes_read = 0;
|
||||||
buffer = new uint8_t[buffer_size];
|
in_file->ReadSync(buffer, write_buffer_size, offset, &bytes_read);
|
||||||
|
fwrite(buffer, bytes_read, 1, file);
|
||||||
|
offset += bytes_read;
|
||||||
|
remaining_size -= bytes_read;
|
||||||
|
progress += bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bytes_read = 0;
|
|
||||||
in_file->ReadSync(buffer, entry->size(), 0, &bytes_read);
|
|
||||||
fwrite(buffer, bytes_read, 1, file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
@@ -401,8 +408,9 @@ X_STATUS VirtualFileSystem::ExtractContentFile(Entry* entry,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS VirtualFileSystem::ExtractContentFiles(
|
X_STATUS VirtualFileSystem::ExtractContentFiles(Device* device,
|
||||||
Device* device, std::filesystem::path base_path) {
|
std::filesystem::path base_path,
|
||||||
|
uint64_t& progress) {
|
||||||
// Run through all the files, breadth-first style.
|
// Run through all the files, breadth-first style.
|
||||||
std::queue<vfs::Entry*> queue;
|
std::queue<vfs::Entry*> queue;
|
||||||
auto root = device->ResolvePath("/");
|
auto root = device->ResolvePath("/");
|
||||||
@@ -415,7 +423,7 @@ X_STATUS VirtualFileSystem::ExtractContentFiles(
|
|||||||
queue.push(entry.get());
|
queue.push(entry.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtractContentFile(entry, base_path);
|
ExtractContentFile(entry, base_path, progress);
|
||||||
}
|
}
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,11 @@ class VirtualFileSystem {
|
|||||||
|
|
||||||
static X_STATUS ExtractContentFile(Entry* entry,
|
static X_STATUS ExtractContentFile(Entry* entry,
|
||||||
std::filesystem::path base_path,
|
std::filesystem::path base_path,
|
||||||
|
uint64_t& progress,
|
||||||
bool extract_to_root = false);
|
bool extract_to_root = false);
|
||||||
static X_STATUS ExtractContentFiles(Device* device,
|
static X_STATUS ExtractContentFiles(Device* device,
|
||||||
std::filesystem::path base_path);
|
std::filesystem::path base_path,
|
||||||
|
uint64_t& progress);
|
||||||
static void ExtractContentHeader(Device* device,
|
static void ExtractContentHeader(Device* device,
|
||||||
std::filesystem::path base_path);
|
std::filesystem::path base_path);
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ typedef uint32_t X_STATUS;
|
|||||||
#define X_STATUS_THREAD_IS_TERMINATING ((X_STATUS)0xC000004BL)
|
#define X_STATUS_THREAD_IS_TERMINATING ((X_STATUS)0xC000004BL)
|
||||||
#define X_STATUS_PROCEDURE_NOT_FOUND ((X_STATUS)0xC000007AL)
|
#define X_STATUS_PROCEDURE_NOT_FOUND ((X_STATUS)0xC000007AL)
|
||||||
#define X_STATUS_INVALID_IMAGE_FORMAT ((X_STATUS)0xC000007BL)
|
#define X_STATUS_INVALID_IMAGE_FORMAT ((X_STATUS)0xC000007BL)
|
||||||
|
#define X_STATUS_DISK_FULL ((X_STATUS)0xC000007FL)
|
||||||
#define X_STATUS_INSUFFICIENT_RESOURCES ((X_STATUS)0xC000009AL)
|
#define X_STATUS_INSUFFICIENT_RESOURCES ((X_STATUS)0xC000009AL)
|
||||||
#define X_STATUS_MEMORY_NOT_ALLOCATED ((X_STATUS)0xC00000A0L)
|
#define X_STATUS_MEMORY_NOT_ALLOCATED ((X_STATUS)0xC00000A0L)
|
||||||
#define X_STATUS_FILE_IS_A_DIRECTORY ((X_STATUS)0xC00000BAL)
|
#define X_STATUS_FILE_IS_A_DIRECTORY ((X_STATUS)0xC00000BAL)
|
||||||
|
|||||||
Reference in New Issue
Block a user