@@ -0,0 +1,51 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "DiscIO/VolumeFileBlobReader.h"

#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"

namespace DiscIO
{
std::unique_ptr<VolumeFileBlobReader> VolumeFileBlobReader::Create(const Volume& volume,
const FileSystem& file_system,
const std::string& file_path)
{
if (!file_system.IsValid())
return nullptr;

std::unique_ptr<FileInfo> file_info = file_system.FindFileInfo(file_path);
if (!file_info || file_info->IsDirectory())
return nullptr;

return std::unique_ptr<VolumeFileBlobReader>{
new VolumeFileBlobReader(volume, file_system, std::move(file_info))};
}

VolumeFileBlobReader::VolumeFileBlobReader(const Volume& volume, const FileSystem& file_system,
std::unique_ptr<FileInfo> file_info)
: m_volume(volume), m_file_system(file_system), m_file_info(std::move(file_info))
{
}

u64 VolumeFileBlobReader::GetDataSize() const
{
return m_file_info->GetSize();
}

u64 VolumeFileBlobReader::GetRawSize() const
{
return GetDataSize();
}

bool VolumeFileBlobReader::Read(u64 offset, u64 length, u8* out_ptr)
{
if (offset + length > m_file_info->GetSize())
return false;

return m_volume.Read(m_file_info->GetOffset() + offset, length, out_ptr,
m_file_system.GetPartition());
}
} // namespace
@@ -0,0 +1,38 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <memory>
#include <string>

#include "Common/CommonTypes.h"
#include "DiscIO/Blob.h"

namespace DiscIO
{
class FileInfo;
class FileSystem;
class Volume;

class VolumeFileBlobReader final : public BlobReader
{
public:
static std::unique_ptr<VolumeFileBlobReader>
Create(const Volume& volume, const FileSystem& file_system, const std::string& file_path);

BlobType GetBlobType() const override { return BlobType::PLAIN; }
u64 GetDataSize() const override;
u64 GetRawSize() const override;
bool Read(u64 offset, u64 length, u8* out_ptr) override;

private:
VolumeFileBlobReader(const Volume& volume, const FileSystem& file_system,
std::unique_ptr<FileInfo> file_info);

const Volume& m_volume;
const FileSystem& m_file_system;
std::unique_ptr<FileInfo> m_file_info;
};
} // namespace
@@ -44,21 +44,18 @@ bool IsWiiWAD(BlobReader& reader)
}
} // Anonymous namespace

WiiWAD::WiiWAD(const std::string& name) : m_reader(CreateBlobReader(name))
WiiWAD::WiiWAD(const std::string& name) : WiiWAD(CreateBlobReader(name))
{
if (m_reader == nullptr)
{
m_valid = false;
return;
}

m_valid = ParseWAD();
}

WiiWAD::~WiiWAD()
WiiWAD::WiiWAD(std::unique_ptr<BlobReader> blob_reader) : m_reader(std::move(blob_reader))
{
if (m_reader)
m_valid = ParseWAD();
}

WiiWAD::~WiiWAD() = default;

bool WiiWAD::ParseWAD()
{
if (!IsWiiWAD(*m_reader))
@@ -19,6 +19,7 @@ class WiiWAD
{
public:
explicit WiiWAD(const std::string& name);
explicit WiiWAD(std::unique_ptr<BlobReader> blob_reader);
~WiiWAD();

bool IsValid() const { return m_valid; }
@@ -32,7 +33,7 @@ class WiiWAD
private:
bool ParseWAD();

bool m_valid;
bool m_valid = false;

std::unique_ptr<BlobReader> m_reader;

@@ -29,6 +29,7 @@
#include "DolphinQt2/GameList/ListProxyModel.h"
#include "DolphinQt2/QtUtils/DoubleClickEventFilter.h"
#include "DolphinQt2/Settings.h"
#include "DolphinQt2/WiiUpdate.h"

static bool CompressCB(const std::string&, float, void*);

@@ -164,6 +165,15 @@ void GameList::ShowContextMenu(const QPoint&)

menu->addSeparator();
}

if (platform == DiscIO::Platform::WII_DISC)
{
menu->addAction(tr("Perform System Update"), [this] {
WiiUpdate::PerformDiscUpdate(GetSelectedGame().toStdString(), this);
});
menu->setEnabled(!Core::IsRunning() || !SConfig::GetInstance().bWii);
}

if (platform == DiscIO::Platform::WII_WAD)
{
QAction* wad_install_action = new QAction(tr("Install to the NAND"), menu);
@@ -22,14 +22,59 @@

namespace WiiUpdate
{
void PerformOnlineUpdate(const std::string& region, QWidget* parent)
static void ShowResult(QWidget* parent, WiiUtils::UpdateResult result)
{
const int confirm = QMessageBox::question(
parent, QObject::tr("Confirm"),
QObject::tr("Connect to the Internet and perform an online system update?"));
if (confirm != QMessageBox::Yes)
return;
switch (result)
{
case WiiUtils::UpdateResult::Succeeded:
QMessageBox::information(parent, QObject::tr("Update completed"),
QObject::tr("The emulated Wii console has been updated."));
DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
break;
case WiiUtils::UpdateResult::AlreadyUpToDate:
QMessageBox::information(parent, QObject::tr("Update completed"),
QObject::tr("The emulated Wii console is already up-to-date."));
DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
break;
case WiiUtils::UpdateResult::ServerFailed:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("Could not download update information from Nintendo. "
"Please check your Internet connection and try again."));
break;
case WiiUtils::UpdateResult::DownloadFailed:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("Could not download update files from Nintendo. "
"Please check your Internet connection and try again."));
break;
case WiiUtils::UpdateResult::ImportFailed:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("Could not install an update to the Wii system memory. "
"Please refer to logs for more information."));
break;
case WiiUtils::UpdateResult::Cancelled:
QMessageBox::warning(
parent, QObject::tr("Update cancelled"),
QObject::tr("The update has been cancelled. It is strongly recommended to "
"finish it in order to avoid inconsistent system software versions."));
break;
case WiiUtils::UpdateResult::RegionMismatch:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("The game's region does not match your console's. "
"To avoid issues with the system menu, it is not possible "
"to update the emulated console using this disc."));
break;
case WiiUtils::UpdateResult::MissingUpdatePartition:
case WiiUtils::UpdateResult::DiscReadFailed:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("The game disc does not contain any usable "
"update information."));
break;
}
}

template <typename Callable, typename... Args>
static WiiUtils::UpdateResult ShowProgress(QWidget* parent, Callable function, Args&&... args)
{
// Do not allow the user to close the dialog. Instead, wait until the update is finished
// or cancelled.
class UpdateProgressDialog final : public QProgressDialog
@@ -53,17 +98,17 @@ void PerformOnlineUpdate(const std::string& region, QWidget* parent)
auto* cancel_button = new QPushButton(QObject::tr("&Cancel"), parent);
dialog.setCancelButton(cancel_button);
Common::Flag was_cancelled;
QObject::disconnect(&dialog, &QProgressDialog::canceled, &dialog, &QProgressDialog::cancel);
QObject::disconnect(&dialog, &QProgressDialog::canceled, nullptr, nullptr);
QObject::connect(&dialog, &QProgressDialog::canceled, [&] {
dialog.setLabelText(QObject::tr("Finishing the update...\nThis can take a while."));
cancel_button->setEnabled(false);
was_cancelled.Set();
});

std::future<WiiUtils::UpdateResult> result = std::async(std::launch::async, [&] {
const WiiUtils::UpdateResult res = WiiUtils::DoOnlineUpdate(
const WiiUtils::UpdateResult res = function(
[&](size_t processed, size_t total, u64 title_id) {
QueueOnObject(&dialog, [&dialog, &was_cancelled, processed, total, title_id]() {
QueueOnObject(&dialog, [&dialog, &was_cancelled, processed, total, title_id] {
if (was_cancelled.IsSet())
return;

@@ -74,46 +119,30 @@ void PerformOnlineUpdate(const std::string& region, QWidget* parent)
});
return !was_cancelled.IsSet();
},
region);
QueueOnObject(&dialog, [&dialog] { dialog.close(); });
std::forward<Args>(args)...);
QueueOnObject(&dialog, [&dialog] { dialog.done(0); });
return res;
});

dialog.exec();
return result.get();
}

switch (result.get())
{
case WiiUtils::UpdateResult::Succeeded:
QMessageBox::information(parent, QObject::tr("Update completed"),
QObject::tr("The emulated Wii console has been updated."));
DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
break;
case WiiUtils::UpdateResult::AlreadyUpToDate:
QMessageBox::information(parent, QObject::tr("Update completed"),
QObject::tr("The emulated Wii console is already up-to-date."));
DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
break;
case WiiUtils::UpdateResult::ServerFailed:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("Could not download update information from Nintendo. "
"Please check your Internet connection and try again."));
break;
case WiiUtils::UpdateResult::DownloadFailed:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("Could not download update files from Nintendo. "
"Please check your Internet connection and try again."));
break;
case WiiUtils::UpdateResult::ImportFailed:
QMessageBox::critical(parent, QObject::tr("Update failed"),
QObject::tr("Could not install an update to the Wii system memory. "
"Please refer to logs for more information."));
break;
case WiiUtils::UpdateResult::Cancelled:
QMessageBox::warning(
parent, QObject::tr("Update cancelled"),
QObject::tr("The update has been cancelled. It is strongly recommended to "
"finish it in order to avoid inconsistent system software versions."));
break;
}
void PerformOnlineUpdate(const std::string& region, QWidget* parent)
{
const int confirm = QMessageBox::question(
parent, QObject::tr("Confirm"),
QObject::tr("Connect to the Internet and perform an online system update?"));
if (confirm != QMessageBox::Yes)
return;

const WiiUtils::UpdateResult result = ShowProgress(parent, WiiUtils::DoOnlineUpdate, region);
ShowResult(parent, result);
}

void PerformDiscUpdate(const std::string& file_path, QWidget* parent)
{
const WiiUtils::UpdateResult result = ShowProgress(parent, WiiUtils::DoDiscUpdate, file_path);
ShowResult(parent, result);
}
} // namespace WiiUpdate
@@ -11,4 +11,5 @@ class QWidget;
namespace WiiUpdate
{
void PerformOnlineUpdate(const std::string& region, QWidget* parent = nullptr);
void PerformDiscUpdate(const std::string& file_path, QWidget* parent = nullptr);
} // namespace WiiUpdate
@@ -245,6 +245,7 @@ BEGIN_EVENT_TABLE(CFrame, CRenderFrame)
EVT_MENU_RANGE(IDM_FLOAT_LOG_WINDOW, IDM_FLOAT_CODE_WINDOW, CFrame::OnFloatWindow)

// Game list context menu
EVT_MENU(IDM_LIST_PERFORM_DISC_UPDATE, CFrame::OnPerformDiscWiiUpdate)
EVT_MENU(IDM_LIST_INSTALL_WAD, CFrame::OnInstallWAD)
EVT_MENU(IDM_LIST_UNINSTALL_WAD, CFrame::OnUninstallWAD)

@@ -349,6 +349,7 @@ class CFrame : public CRenderFrame
void OnImportBootMiiBackup(wxCommandEvent& event);
void OnExtractCertificates(wxCommandEvent& event);
void OnPerformOnlineWiiUpdate(wxCommandEvent& event);
void OnPerformDiscWiiUpdate(wxCommandEvent& event);
void OnFifoPlayer(wxCommandEvent& event);
void OnConnectWiimote(wxCommandEvent& event);
void GameListChanged(wxCommandEvent& event);
@@ -1320,39 +1320,9 @@ static std::string GetUpdateRegionFromIdm(int idm)
}
}

void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event)
static void ShowUpdateResult(WiiUtils::UpdateResult result)
{
int confirm = wxMessageBox(_("Connect to the Internet and perform an online system update?"),
_("System Update"), wxYES_NO, this);
if (confirm != wxYES)
return;

wxProgressDialog dialog(_("Updating"), _("Preparing to update...\nThis can take a while."), 1,
this, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT);

const std::string region = GetUpdateRegionFromIdm(event.GetId());
std::future<WiiUtils::UpdateResult> result = std::async(std::launch::async, [&] {
const WiiUtils::UpdateResult res = WiiUtils::DoOnlineUpdate(
[&](size_t processed, size_t total, u64 title_id) {
Core::QueueHostJob(
[&dialog, processed, total, title_id] {
dialog.SetRange(total);
dialog.Update(processed, wxString::Format(_("Updating title %016" PRIx64 "...\n"
"This can take a while."),
title_id));
dialog.Fit();
},
true);
return !dialog.WasCancelled();
},
region);
Core::QueueHostJob([&dialog] { dialog.EndModal(0); }, true);
return res;
});

dialog.ShowModal();

switch (result.get())
switch (result)
{
case WiiUtils::UpdateResult::Succeeded:
wxMessageBox(_("The emulated Wii console has been updated."), _("Update completed"),
@@ -1384,8 +1354,73 @@ void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event)
"finish it in order to avoid inconsistent system software versions."),
_("Update cancelled"), wxOK | wxICON_WARNING);
break;
case WiiUtils::UpdateResult::RegionMismatch:
wxMessageBox(_("The game's region does not match your console's. "
"To avoid issues with the system menu, it is not possible to update "
"the emulated console using this disc."),
_("Update failed"), wxOK | wxICON_ERROR);
break;
case WiiUtils::UpdateResult::MissingUpdatePartition:
case WiiUtils::UpdateResult::DiscReadFailed:
wxMessageBox(_("The game disc does not contain any usable update information."),
_("Update failed"), wxOK | wxICON_ERROR);
break;
}
}

template <typename Callable, typename... Args>
static WiiUtils::UpdateResult ShowUpdateProgress(CFrame* frame, Callable function, Args&&... args)
{
wxProgressDialog dialog(_("Updating"), _("Preparing to update...\nThis can take a while."), 1,
frame, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT);

std::future<WiiUtils::UpdateResult> result = std::async(std::launch::async, [&] {
const WiiUtils::UpdateResult res = function(
[&](size_t processed, size_t total, u64 title_id) {
Core::QueueHostJob(
[&dialog, processed, total, title_id] {
dialog.SetRange(total);
dialog.Update(processed, wxString::Format(_("Updating title %016" PRIx64 "...\n"
"This can take a while."),
title_id));
dialog.Fit();
},
true);
return !dialog.WasCancelled();
},
std::forward<Args>(args)...);
Core::QueueHostJob([&dialog] { dialog.EndModal(0); }, true);
return res;
});

dialog.ShowModal();
return result.get();
}

void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event)
{
int confirm = wxMessageBox(_("Connect to the Internet and perform an online system update?"),
_("System Update"), wxYES_NO, this);
if (confirm != wxYES)
return;

const std::string region = GetUpdateRegionFromIdm(event.GetId());

const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoOnlineUpdate, region);
ShowUpdateResult(result);
UpdateLoadWiiMenuItem();
}

void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&)
{
const GameListItem* iso = m_game_list_ctrl->GetSelectedISO();
if (!iso)
return;

const std::string file_name = iso->GetFileName();

const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoDiscUpdate, file_name);
ShowUpdateResult(result);
UpdateLoadWiiMenuItem();
}

@@ -1173,6 +1173,13 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
changeDiscItem->Enable(Core::IsRunning());
}

if (platform == DiscIO::Platform::WII_DISC)
{
auto* const perform_update_item =
popupMenu.Append(IDM_LIST_PERFORM_DISC_UPDATE, _("Perform System Update"));
perform_update_item->Enable(!Core::IsRunning() || !SConfig::GetInstance().bWii);
}

if (platform == DiscIO::Platform::WII_WAD)
{
auto* const install_wad_item =
@@ -100,6 +100,7 @@ enum
IDM_GAME_WIKI,
IDM_LOAD_WII_MENU,
IDM_MENU_INSTALL_WAD,
IDM_LIST_PERFORM_DISC_UPDATE,
IDM_LIST_INSTALL_WAD,
IDM_LIST_UNINSTALL_WAD,
IDM_IMPORT_NAND,