mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-30 01:29:42 -06:00
Unify ISOFile (wx) with GameFile (Qt) and put it in UICommon
The original reason I wanted to do this was so that we can replace the Android-specific code with this in the future, but of course, just deduplicating between DolphinWX and DolphinQt2 is nice too. Fixes: - DolphinQt2 showing the wrong size for split WBFS disc images. - DolphinQt2 being case sensitive when checking if a file is a DOL/ELF. - DolphinQt2 not detecting when a Wii banner has become available after the game list cache was created. Removes: - DolphinWX's ability to load PNGs as custom banners. But it was already rather broken (see https://bugs.dolphin-emu.org/issues/10365 and https://bugs.dolphin-emu.org/issues/10366). The reason I removed this was because PNG decoding relied on wx code and we don't have any good non-wx/Qt code for loading PNG files right now (let's not use SOIL), but we should be able to use libpng directly to implement PNG loading in the future. - DolphinQt2's ability to ignore a cached game if the last modified time differs. We currently don't have a non-wx/Qt way to get the time.
This commit is contained in:
@ -67,7 +67,6 @@ set(SRCS
|
||||
FrameAui.cpp
|
||||
FrameTools.cpp
|
||||
GameListCtrl.cpp
|
||||
ISOFile.cpp
|
||||
LogConfigWindow.cpp
|
||||
LogWindow.cpp
|
||||
Main.cpp
|
||||
|
@ -111,7 +111,6 @@
|
||||
<ClCompile Include="Input\GuitarInputConfigDiag.cpp" />
|
||||
<ClCompile Include="Input\DrumsInputConfigDiag.cpp" />
|
||||
<ClCompile Include="Input\TurntableInputConfigDiag.cpp" />
|
||||
<ClCompile Include="ISOFile.cpp" />
|
||||
<ClCompile Include="LogConfigWindow.cpp" />
|
||||
<ClCompile Include="LogWindow.cpp" />
|
||||
<ClCompile Include="Main.cpp" />
|
||||
@ -194,7 +193,6 @@
|
||||
<ClInclude Include="Input\GuitarInputConfigDiag.h" />
|
||||
<ClInclude Include="Input\DrumsInputConfigDiag.h" />
|
||||
<ClInclude Include="Input\TurntableInputConfigDiag.h" />
|
||||
<ClInclude Include="ISOFile.h" />
|
||||
<ClInclude Include="LogConfigWindow.h" />
|
||||
<ClInclude Include="LogWindow.h" />
|
||||
<ClInclude Include="Main.h" />
|
||||
@ -299,4 +297,4 @@
|
||||
<Message Text="Copy: @(BinaryFiles) -> $(BinaryOutputDir)" Importance="High" />
|
||||
<Copy SourceFiles="@(BinaryFiles)" DestinationFolder="$(BinaryOutputDir)" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
@ -19,9 +19,6 @@
|
||||
<Filter Include="GUI\Video">
|
||||
<UniqueIdentifier>{80626e3b-e13b-41c3-bd63-4ef1faf92924}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Misc">
|
||||
<UniqueIdentifier>{4352dc64-398e-4a96-ba4a-824dffa2004c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>{d6bc4dd6-06ed-46ad-b327-04afb26e10ec}</UniqueIdentifier>
|
||||
</Filter>
|
||||
@ -154,9 +151,6 @@
|
||||
<ClCompile Include="VideoConfigDiag.cpp">
|
||||
<Filter>GUI\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ISOFile.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AboutDolphin.cpp">
|
||||
<Filter>GUI</Filter>
|
||||
</ClCompile>
|
||||
@ -381,9 +375,6 @@
|
||||
<ClInclude Include="VideoConfigDiag.h">
|
||||
<Filter>GUI\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ISOFile.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Resources</Filter>
|
||||
</ClInclude>
|
||||
@ -507,4 +498,4 @@
|
||||
<ItemGroup>
|
||||
<Image Include="$(CoreDir)..\..\Installer\Dolphin.ico" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -77,7 +77,6 @@
|
||||
#include "DolphinWX/Frame.h"
|
||||
#include "DolphinWX/GameListCtrl.h"
|
||||
#include "DolphinWX/Globals.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/Input/HotkeyInputConfigDiag.h"
|
||||
#include "DolphinWX/Input/InputConfigDiag.h"
|
||||
#include "DolphinWX/LogWindow.h"
|
||||
@ -92,6 +91,7 @@
|
||||
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
@ -318,7 +318,7 @@ void CFrame::BootGame(const std::string& filename, const std::optional<std::stri
|
||||
if (m_game_list_ctrl->GetSelectedISO() != nullptr)
|
||||
{
|
||||
if (m_game_list_ctrl->GetSelectedISO()->IsValid())
|
||||
bootfile = m_game_list_ctrl->GetSelectedISO()->GetFileName();
|
||||
bootfile = m_game_list_ctrl->GetSelectedISO()->GetFilePath();
|
||||
}
|
||||
else if (!StartUp.m_strDefaultISO.empty() && File::Exists(StartUp.m_strDefaultISO))
|
||||
{
|
||||
@ -1229,10 +1229,10 @@ void CFrame::OnInstallWAD(wxCommandEvent& event)
|
||||
{
|
||||
case IDM_LIST_INSTALL_WAD:
|
||||
{
|
||||
const GameListItem* iso = m_game_list_ctrl->GetSelectedISO();
|
||||
const UICommon::GameFile* iso = m_game_list_ctrl->GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
fileName = iso->GetFileName();
|
||||
fileName = iso->GetFilePath();
|
||||
break;
|
||||
}
|
||||
case IDM_MENU_INSTALL_WAD:
|
||||
@ -1258,7 +1258,7 @@ void CFrame::OnInstallWAD(wxCommandEvent& event)
|
||||
|
||||
void CFrame::OnUninstallWAD(wxCommandEvent&)
|
||||
{
|
||||
const GameListItem* file = m_game_list_ctrl->GetSelectedISO();
|
||||
const UICommon::GameFile* file = m_game_list_ctrl->GetSelectedISO();
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
@ -1268,9 +1268,8 @@ void CFrame::OnUninstallWAD(wxCommandEvent&)
|
||||
return;
|
||||
}
|
||||
|
||||
u64 title_id = file->GetTitleID();
|
||||
IOS::HLE::Kernel ios;
|
||||
if (ios.GetES()->DeleteTitleContent(title_id) < 0)
|
||||
const u64 title_id = file->GetTitleID();
|
||||
if (!WiiUtils::UninstallTitle(title_id))
|
||||
{
|
||||
PanicAlertT("Failed to remove this title from the NAND.");
|
||||
return;
|
||||
@ -1490,11 +1489,11 @@ void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event)
|
||||
|
||||
void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&)
|
||||
{
|
||||
const GameListItem* iso = m_game_list_ctrl->GetSelectedISO();
|
||||
const UICommon::GameFile* iso = m_game_list_ctrl->GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
|
||||
const std::string file_name = iso->GetFileName();
|
||||
const std::string file_name = iso->GetFilePath();
|
||||
|
||||
const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoDiscUpdate, file_name);
|
||||
ShowUpdateResult(result);
|
||||
|
@ -4,13 +4,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <wx/app.h>
|
||||
@ -39,9 +37,7 @@
|
||||
#include "Common/CDUtils.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileSearch.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/SysConf.h"
|
||||
#include "Common/Thread.h"
|
||||
@ -53,18 +49,20 @@
|
||||
#include "Core/HW/WiiSaveCrypted.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/TitleDatabase.h"
|
||||
#include "Core/WiiUtils.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/DirectoryBlob.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "DolphinWX/Frame.h"
|
||||
#include "DolphinWX/GameListCtrl.h"
|
||||
#include "DolphinWX/Globals.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/ISOProperties/ISOProperties.h"
|
||||
#include "DolphinWX/Main.h"
|
||||
#include "DolphinWX/NetPlay/NetPlayLauncher.h"
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
#include "UICommon/GameFile.h"
|
||||
#include "UICommon/GameFileCache.h"
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
struct CompressionProgress final
|
||||
{
|
||||
@ -82,11 +80,9 @@ public:
|
||||
wxProgressDialog* dialog;
|
||||
};
|
||||
|
||||
static constexpr u32 CACHE_REVISION = 6; // Last changed in PR 6109
|
||||
|
||||
static bool sorted = false;
|
||||
|
||||
static int CompareGameListItems(const GameListItem* iso1, const GameListItem* iso2,
|
||||
static int CompareGameListItems(const UICommon::GameFile* iso1, const UICommon::GameFile* iso2,
|
||||
long sortData = GameListCtrl::COLUMN_TITLE)
|
||||
{
|
||||
int t = 1;
|
||||
@ -101,14 +97,14 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
|
||||
{
|
||||
case GameListCtrl::COLUMN_MAKER:
|
||||
{
|
||||
int maker_cmp = strcasecmp(iso1->GetCompany().c_str(), iso2->GetCompany().c_str()) * t;
|
||||
int maker_cmp = strcasecmp(iso1->GetMaker().c_str(), iso2->GetMaker().c_str()) * t;
|
||||
if (maker_cmp != 0)
|
||||
return maker_cmp;
|
||||
break;
|
||||
}
|
||||
case GameListCtrl::COLUMN_FILENAME:
|
||||
return wxStricmp(wxFileNameFromPath(iso1->GetFileName()),
|
||||
wxFileNameFromPath(iso2->GetFileName())) *
|
||||
return wxStricmp(wxFileNameFromPath(iso1->GetFilePath()),
|
||||
wxFileNameFromPath(iso2->GetFilePath())) *
|
||||
t;
|
||||
case GameListCtrl::COLUMN_ID:
|
||||
{
|
||||
@ -162,8 +158,8 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
|
||||
if (iso1->GetDiscNumber() != iso2->GetDiscNumber())
|
||||
return t * (iso1->GetDiscNumber() > iso2->GetDiscNumber() ? 1 : -1);
|
||||
|
||||
wxString iso1_filename = wxFileNameFromPath(iso1->GetFileName());
|
||||
wxString iso2_filename = wxFileNameFromPath(iso2->GetFileName());
|
||||
wxString iso1_filename = wxFileNameFromPath(iso1->GetFilePath());
|
||||
wxString iso2_filename = wxFileNameFromPath(iso2->GetFilePath());
|
||||
|
||||
if (iso1_filename != iso2_filename)
|
||||
return t * wxStricmp(iso1_filename, iso2_filename);
|
||||
@ -171,7 +167,7 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ShouldDisplayGameListItem(const GameListItem& item)
|
||||
static bool ShouldDisplayGameListItem(const UICommon::GameFile& item)
|
||||
{
|
||||
const bool show_platform = [&item] {
|
||||
switch (item.GetPlatform())
|
||||
@ -281,9 +277,9 @@ GameListCtrl::GameListCtrl(bool disable_scanning, wxWindow* parent, const wxWind
|
||||
if (!disable_scanning)
|
||||
{
|
||||
m_scan_thread = std::thread([&] {
|
||||
Common::SetCurrentThreadName("gamelist scanner");
|
||||
Common::SetCurrentThreadName("Game list scanner");
|
||||
|
||||
if (SyncCacheFile(false))
|
||||
if (m_cache.Load())
|
||||
QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
|
||||
|
||||
// Always do an initial scan to catch new files and perform the more expensive per-file
|
||||
@ -422,11 +418,10 @@ void GameListCtrl::RefreshList()
|
||||
m_shown_files.clear();
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_cache_mutex);
|
||||
for (auto& item : m_cached_files)
|
||||
{
|
||||
if (ShouldDisplayGameListItem(*item))
|
||||
m_shown_files.push_back(item);
|
||||
}
|
||||
m_cache.ForEach([this](const std::shared_ptr<const UICommon::GameFile>& game_file) {
|
||||
if (ShouldDisplayGameListItem(*game_file))
|
||||
m_shown_files.push_back(game_file);
|
||||
});
|
||||
}
|
||||
|
||||
// Drives are not cached. Not sure if this is required, but better to err on the
|
||||
@ -436,7 +431,7 @@ void GameListCtrl::RefreshList()
|
||||
std::unique_lock<std::mutex> lk(m_title_database_mutex);
|
||||
for (const auto& drive : cdio_get_devices())
|
||||
{
|
||||
auto file = std::make_shared<GameListItem>(drive);
|
||||
auto file = std::make_shared<UICommon::GameFile>(drive);
|
||||
if (file->IsValid())
|
||||
{
|
||||
if (file->EmuStateChanged())
|
||||
@ -538,24 +533,6 @@ void GameListCtrl::RefreshList()
|
||||
SetFocus();
|
||||
}
|
||||
|
||||
static wxString NiceSizeFormat(u64 size)
|
||||
{
|
||||
// Return a pretty filesize string from byte count.
|
||||
// e.g. 1134278 -> "1.08 MiB"
|
||||
|
||||
const char* const unit_symbols[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
|
||||
|
||||
// Find largest power of 2 less than size.
|
||||
// div 10 to get largest named unit less than size
|
||||
// 10 == log2(1024) (number of B in a KiB, KiB in a MiB, etc)
|
||||
// Max value is 63 / 10 = 6
|
||||
const int unit = IntLog2(std::max<u64>(size, 1)) / 10;
|
||||
|
||||
// Don't need exact values, only 5 most significant digits
|
||||
double unit_size = std::pow(2, unit * 10);
|
||||
return wxString::Format("%.2f %s", size / unit_size, unit_symbols[unit]);
|
||||
}
|
||||
|
||||
// Update the column content of the item at index
|
||||
void GameListCtrl::UpdateItemAtColumn(long index, int column)
|
||||
{
|
||||
@ -573,11 +550,11 @@ void GameListCtrl::UpdateItemAtColumn(long index, int column)
|
||||
{
|
||||
int image_index = m_image_indexes.utility_banner[0]; // nobanner
|
||||
|
||||
if (iso_file.GetBannerImage().IsOk())
|
||||
wxImage banner = WxUtils::ToWxImage(iso_file.GetBannerImage());
|
||||
if (banner.IsOk())
|
||||
{
|
||||
wxImageList* img_list = GetImageList(wxIMAGE_LIST_SMALL);
|
||||
image_index = img_list->Add(
|
||||
WxUtils::ScaleImageToBitmap(iso_file.GetBannerImage(), this, img_list->GetSize()));
|
||||
image_index = img_list->Add(WxUtils::ScaleImageToBitmap(banner, this, img_list->GetSize()));
|
||||
}
|
||||
|
||||
SetItemColumnImage(index, COLUMN_BANNER, image_index);
|
||||
@ -599,10 +576,10 @@ void GameListCtrl::UpdateItemAtColumn(long index, int column)
|
||||
break;
|
||||
}
|
||||
case COLUMN_MAKER:
|
||||
SetItem(index, COLUMN_MAKER, StrToWxStr(iso_file.GetCompany()), -1);
|
||||
SetItem(index, COLUMN_MAKER, StrToWxStr(iso_file.GetMaker()), -1);
|
||||
break;
|
||||
case COLUMN_FILENAME:
|
||||
SetItem(index, COLUMN_FILENAME, wxFileNameFromPath(StrToWxStr(iso_file.GetFileName())), -1);
|
||||
SetItem(index, COLUMN_FILENAME, wxFileNameFromPath(StrToWxStr(iso_file.GetFilePath())), -1);
|
||||
break;
|
||||
case COLUMN_EMULATION_STATE:
|
||||
SetItemColumnImage(index, COLUMN_EMULATION_STATE,
|
||||
@ -613,7 +590,7 @@ void GameListCtrl::UpdateItemAtColumn(long index, int column)
|
||||
m_image_indexes.flag[static_cast<size_t>(iso_file.GetCountry())]);
|
||||
break;
|
||||
case COLUMN_SIZE:
|
||||
SetItem(index, COLUMN_SIZE, NiceSizeFormat(iso_file.GetFileSize()), -1);
|
||||
SetItem(index, COLUMN_SIZE, UICommon::FormatSize(iso_file.GetFileSize()), -1);
|
||||
break;
|
||||
case COLUMN_ID:
|
||||
SetItem(index, COLUMN_ID, iso_file.GetGameID(), -1);
|
||||
@ -676,76 +653,6 @@ void GameListCtrl::SetColors()
|
||||
}
|
||||
}
|
||||
|
||||
void GameListCtrl::DoState(PointerWrap* p, u32 size)
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 Revision;
|
||||
u32 ExpectedSize;
|
||||
} header = {CACHE_REVISION, size};
|
||||
p->Do(header);
|
||||
if (p->GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
if (header.Revision != CACHE_REVISION || header.ExpectedSize != size)
|
||||
{
|
||||
p->SetMode(PointerWrap::MODE_MEASURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
p->DoEachElement(m_cached_files, [](PointerWrap& state, std::shared_ptr<GameListItem>& elem) {
|
||||
if (state.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
elem = std::make_shared<GameListItem>();
|
||||
}
|
||||
elem->DoState(state);
|
||||
});
|
||||
}
|
||||
|
||||
bool GameListCtrl::SyncCacheFile(bool write)
|
||||
{
|
||||
std::string filename(File::GetUserPath(D_CACHE_IDX) + "wx_gamelist.cache");
|
||||
const char* open_mode = write ? "wb" : "rb";
|
||||
File::IOFile f(filename, open_mode);
|
||||
if (!f)
|
||||
return false;
|
||||
bool success = false;
|
||||
if (write)
|
||||
{
|
||||
// Measure the size of the buffer.
|
||||
u8* ptr = nullptr;
|
||||
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
||||
DoState(&p);
|
||||
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
||||
|
||||
// Then actually do the write.
|
||||
std::vector<u8> buffer(buffer_size);
|
||||
ptr = &buffer[0];
|
||||
p.SetMode(PointerWrap::MODE_WRITE);
|
||||
DoState(&p, buffer_size);
|
||||
if (f.WriteBytes(buffer.data(), buffer.size()))
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<u8> buffer(f.GetSize());
|
||||
if (buffer.size() && f.ReadBytes(buffer.data(), buffer.size()))
|
||||
{
|
||||
u8* ptr = buffer.data();
|
||||
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
||||
DoState(&p, buffer.size());
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
// If some file operation failed, try to delete the probably-corrupted cache
|
||||
f.Close();
|
||||
File::Delete(filename);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void GameListCtrl::RescanList()
|
||||
{
|
||||
auto post_status = [&](const wxString& status) {
|
||||
@ -757,107 +664,39 @@ void GameListCtrl::RescanList()
|
||||
|
||||
post_status(_("Scanning..."));
|
||||
|
||||
const std::vector<std::string> search_extensions = {".gcm", ".tgc", ".iso", ".ciso", ".gcz",
|
||||
".wbfs", ".wad", ".dol", ".elf"};
|
||||
// TODO This could process paths iteratively as they are found
|
||||
const std::vector<std::string> search_results_vector =
|
||||
Common::DoFileSearch(SConfig::GetInstance().m_ISOFolder, search_extensions,
|
||||
SConfig::GetInstance().m_RecursiveISOFolder);
|
||||
|
||||
// Copy search results into a set, except ones that match DiscIO::ShouldHideFromGameList.
|
||||
// TODO Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all?
|
||||
// TODO Make DoFileSearch support filter predicates so we don't have remove things afterwards?
|
||||
std::unordered_set<std::string> search_results;
|
||||
search_results.reserve(search_results_vector.size());
|
||||
for (const std::string& path : search_results_vector)
|
||||
{
|
||||
if (!DiscIO::ShouldHideFromGameList(path))
|
||||
search_results.insert(path);
|
||||
}
|
||||
const std::vector<std::string> game_paths = UICommon::FindAllGamePaths(
|
||||
SConfig::GetInstance().m_ISOFolder, SConfig::GetInstance().m_RecursiveISOFolder);
|
||||
|
||||
// Reload the TitleDatabase
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_title_database_mutex);
|
||||
std::unique_lock<std::mutex> lock(m_title_database_mutex);
|
||||
m_title_database = {};
|
||||
}
|
||||
|
||||
bool cache_changed = false;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_cache_mutex);
|
||||
|
||||
// Delete paths that aren't in search_results from m_cached_files,
|
||||
// while simultaneously deleting paths that aren't in m_cached_files from search_results.
|
||||
// For the sake of speed, we don't care about maintaining the order of m_cached_files.
|
||||
if (m_cache.Update(game_paths))
|
||||
{
|
||||
auto it = m_cached_files.begin();
|
||||
auto end = m_cached_files.end();
|
||||
while (it != end)
|
||||
{
|
||||
if (search_results.erase((*it)->GetFileName()))
|
||||
{
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
cache_changed = true;
|
||||
--end;
|
||||
*it = std::move(*end);
|
||||
}
|
||||
}
|
||||
m_cached_files.erase(it, m_cached_files.end());
|
||||
}
|
||||
|
||||
// Now that the previous loop has run, search_results only contains paths that
|
||||
// aren't in m_cached_files, so we simply add all of them to m_cached_files.
|
||||
for (const auto& path : search_results)
|
||||
{
|
||||
auto file = std::make_shared<GameListItem>(path);
|
||||
if (file->IsValid())
|
||||
{
|
||||
cache_changed = true;
|
||||
m_cached_files.push_back(std::move(file));
|
||||
}
|
||||
cache_changed = true;
|
||||
QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
|
||||
}
|
||||
}
|
||||
// The common case is that just a file has been added/removed, so trigger a refresh ASAP with the
|
||||
// assumption that other properties of files will not change at the same time (which will be fine
|
||||
// and just causes a double refresh).
|
||||
if (cache_changed)
|
||||
QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
|
||||
|
||||
// If any cached files need updates, apply the updates to a copy and delete the original - this
|
||||
// makes the UI thread's use of cached files safe. Note however, it is assumed that RefreshList
|
||||
// will not iterate m_cached_files while the scan thread is modifying the list itself.
|
||||
bool refresh_needed = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_cache_mutex);
|
||||
for (auto& file : m_cached_files)
|
||||
if (m_cache.UpdateAdditionalMetadata(m_title_database))
|
||||
{
|
||||
bool emu_state_changed = file->EmuStateChanged();
|
||||
bool banner_changed = file->BannerChanged();
|
||||
bool custom_title_changed = file->CustomNameChanged(m_title_database);
|
||||
if (emu_state_changed || banner_changed || custom_title_changed)
|
||||
{
|
||||
cache_changed = refresh_needed = true;
|
||||
auto copy = std::make_shared<GameListItem>(*file);
|
||||
if (emu_state_changed)
|
||||
copy->EmuStateCommit();
|
||||
if (banner_changed)
|
||||
copy->BannerCommit();
|
||||
if (custom_title_changed)
|
||||
copy->CustomNameCommit();
|
||||
file = std::move(copy);
|
||||
}
|
||||
cache_changed = true;
|
||||
QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
|
||||
}
|
||||
}
|
||||
// Only post UI event to update the displayed list if something actually changed
|
||||
if (refresh_needed)
|
||||
QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
|
||||
|
||||
post_status("");
|
||||
|
||||
if (cache_changed)
|
||||
SyncCacheFile(true);
|
||||
m_cache.Save();
|
||||
}
|
||||
|
||||
void GameListCtrl::OnRefreshGameList(wxCommandEvent& WXUNUSED(event))
|
||||
@ -871,7 +710,7 @@ void GameListCtrl::OnRescanGameList(wxCommandEvent& event)
|
||||
{
|
||||
// Knock out the cache on a purge event
|
||||
std::unique_lock<std::mutex> lk(m_cache_mutex);
|
||||
m_cached_files.clear();
|
||||
m_cache.Clear();
|
||||
}
|
||||
m_scan_trigger.Set();
|
||||
}
|
||||
@ -893,7 +732,7 @@ void GameListCtrl::OnColBeginDrag(wxListEvent& event)
|
||||
event.Veto();
|
||||
}
|
||||
|
||||
const GameListItem* GameListCtrl::GetISO(size_t index) const
|
||||
const UICommon::GameFile* GameListCtrl::GetISO(size_t index) const
|
||||
{
|
||||
if (index < m_shown_files.size())
|
||||
return m_shown_files[index].get();
|
||||
@ -907,8 +746,8 @@ static int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sor
|
||||
// return 1 if item1 > item2
|
||||
// return -1 if item1 < item2
|
||||
// return 0 for identity
|
||||
const GameListItem* iso1 = caller->GetISO(item1);
|
||||
const GameListItem* iso2 = caller->GetISO(item2);
|
||||
const UICommon::GameFile* iso1 = caller->GetISO(item1);
|
||||
const UICommon::GameFile* iso2 = caller->GetISO(item2);
|
||||
|
||||
if (iso1 == iso2)
|
||||
return 0;
|
||||
@ -1034,7 +873,7 @@ void GameListCtrl::OnMouseMotion(wxMouseEvent& event)
|
||||
// Emulation status
|
||||
static const char* const emuState[] = {"Broken", "Intro", "In-Game", "Playable", "Perfect"};
|
||||
|
||||
const GameListItem* iso = GetISO(GetItemData(item));
|
||||
const UICommon::GameFile* iso = GetISO(GetItemData(item));
|
||||
|
||||
const int emu_state = iso->GetEmuState();
|
||||
const std::string& issues = iso->GetIssues();
|
||||
@ -1093,22 +932,6 @@ void GameListCtrl::OnLeftClick(wxMouseEvent& event)
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
static bool IsWADInstalled(const GameListItem& wad)
|
||||
{
|
||||
const std::string content_dir =
|
||||
Common::GetTitleContentPath(wad.GetTitleID(), Common::FromWhichRoot::FROM_CONFIGURED_ROOT);
|
||||
|
||||
if (!File::IsDirectory(content_dir))
|
||||
return false;
|
||||
|
||||
// Since this isn't IOS and we only need a simple way to figure out if a title is installed,
|
||||
// we make the (reasonable) assumption that having more than just the TMD in the content
|
||||
// directory means that the title is installed.
|
||||
const auto entries = File::ScanDirectoryTree(content_dir, false);
|
||||
return std::any_of(entries.children.begin(), entries.children.end(),
|
||||
[](const auto& file) { return file.virtualName != "title.tmd"; });
|
||||
}
|
||||
|
||||
void GameListCtrl::OnRightClick(wxMouseEvent& event)
|
||||
{
|
||||
// Focus the clicked item.
|
||||
@ -1125,7 +948,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
|
||||
}
|
||||
if (GetSelectedItemCount() == 1)
|
||||
{
|
||||
const GameListItem* selected_iso = GetSelectedISO();
|
||||
const UICommon::GameFile* selected_iso = GetSelectedISO();
|
||||
if (selected_iso)
|
||||
{
|
||||
wxMenu popupMenu;
|
||||
@ -1160,7 +983,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
|
||||
popupMenu.AppendCheckItem(IDM_SET_DEFAULT_ISO, _("Set as &default ISO"));
|
||||
|
||||
// First we have to decide a starting value when we append it
|
||||
if (selected_iso->GetFileName() == SConfig::GetInstance().m_strDefaultISO)
|
||||
if (selected_iso->GetFilePath() == SConfig::GetInstance().m_strDefaultISO)
|
||||
popupMenu.FindItem(IDM_SET_DEFAULT_ISO)->Check();
|
||||
|
||||
popupMenu.AppendSeparator();
|
||||
@ -1194,7 +1017,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
|
||||
for (auto* menu_item : {install_wad_item, uninstall_wad_item})
|
||||
menu_item->Enable(!Core::IsRunning() || !SConfig::GetInstance().bWii);
|
||||
|
||||
if (!IsWADInstalled(*selected_iso))
|
||||
if (!WiiUtils::IsTitleInstalled(selected_iso->GetTitleID()))
|
||||
uninstall_wad_item->Enable(false);
|
||||
}
|
||||
|
||||
@ -1214,7 +1037,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
|
||||
}
|
||||
}
|
||||
|
||||
const GameListItem* GameListCtrl::GetSelectedISO() const
|
||||
const UICommon::GameFile* GameListCtrl::GetSelectedISO() const
|
||||
{
|
||||
if (m_shown_files.empty())
|
||||
return nullptr;
|
||||
@ -1229,9 +1052,9 @@ const GameListItem* GameListCtrl::GetSelectedISO() const
|
||||
return GetISO(GetItemData(item));
|
||||
}
|
||||
|
||||
std::vector<const GameListItem*> GameListCtrl::GetAllSelectedISOs() const
|
||||
std::vector<const UICommon::GameFile*> GameListCtrl::GetAllSelectedISOs() const
|
||||
{
|
||||
std::vector<const GameListItem*> result;
|
||||
std::vector<const UICommon::GameFile*> result;
|
||||
long item = -1;
|
||||
while (true)
|
||||
{
|
||||
@ -1257,18 +1080,18 @@ bool GameListCtrl::IsHidingItems()
|
||||
|
||||
void GameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
|
||||
wxFileName path = wxFileName::FileName(StrToWxStr(iso->GetFileName()));
|
||||
wxFileName path = wxFileName::FileName(StrToWxStr(iso->GetFilePath()));
|
||||
path.MakeAbsolute();
|
||||
WxUtils::Explore(WxStrToStr(path.GetPath()));
|
||||
}
|
||||
|
||||
void GameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
std::string path = iso->GetWiiFSPath();
|
||||
@ -1278,7 +1101,7 @@ void GameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void GameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (iso)
|
||||
CWiiSaveCrypted::ExportWiiSave(iso->GetTitleID());
|
||||
}
|
||||
@ -1286,14 +1109,14 @@ void GameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
|
||||
// Save this file as the default file
|
||||
void GameListCtrl::OnSetDefaultISO(wxCommandEvent& event)
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
|
||||
if (event.IsChecked())
|
||||
{
|
||||
// Write the new default value and save it the ini file
|
||||
SConfig::GetInstance().m_strDefaultISO = iso->GetFileName();
|
||||
SConfig::GetInstance().m_strDefaultISO = iso->GetFilePath();
|
||||
SConfig::GetInstance().SaveSettings();
|
||||
}
|
||||
else
|
||||
@ -1313,15 +1136,15 @@ void GameListCtrl::OnDeleteISO(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
if (wxMessageBox(message, _("Warning"), wxYES_NO | wxICON_EXCLAMATION) == wxYES)
|
||||
{
|
||||
for (const GameListItem* iso : GetAllSelectedISOs())
|
||||
File::Delete(iso->GetFileName());
|
||||
for (const UICommon::GameFile* iso : GetAllSelectedISOs())
|
||||
File::Delete(iso->GetFilePath());
|
||||
m_scan_trigger.Set();
|
||||
}
|
||||
}
|
||||
|
||||
void GameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
|
||||
@ -1331,7 +1154,7 @@ void GameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void GameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
|
||||
@ -1342,7 +1165,7 @@ void GameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void GameListCtrl::OnNetPlayHost(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
|
||||
@ -1380,9 +1203,9 @@ void GameListCtrl::OnMultiDecompressISO(wxCommandEvent& /*event*/)
|
||||
|
||||
void GameListCtrl::CompressSelection(bool _compress)
|
||||
{
|
||||
std::vector<const GameListItem*> items_to_compress;
|
||||
std::vector<const UICommon::GameFile*> items_to_compress;
|
||||
bool wii_compression_warning_accepted = false;
|
||||
for (const GameListItem* iso : GetAllSelectedISOs())
|
||||
for (const UICommon::GameFile* iso : GetAllSelectedISOs())
|
||||
{
|
||||
// Don't include items that we can't do anything with
|
||||
if (iso->GetPlatform() != DiscIO::Platform::GAMECUBE_DISC &&
|
||||
@ -1425,12 +1248,12 @@ void GameListCtrl::CompressSelection(bool _compress)
|
||||
|
||||
CompressionProgress progress(0, items_to_compress.size(), "", &progressDialog);
|
||||
|
||||
for (const GameListItem* iso : items_to_compress)
|
||||
for (const UICommon::GameFile* iso : items_to_compress)
|
||||
{
|
||||
if (iso->GetBlobType() != DiscIO::BlobType::GCZ && _compress)
|
||||
{
|
||||
std::string FileName;
|
||||
SplitPath(iso->GetFileName(), nullptr, &FileName, nullptr);
|
||||
SplitPath(iso->GetFilePath(), nullptr, &FileName, nullptr);
|
||||
progress.current_filename = FileName;
|
||||
FileName.append(".gcz");
|
||||
|
||||
@ -1445,14 +1268,14 @@ void GameListCtrl::CompressSelection(bool _compress)
|
||||
continue;
|
||||
|
||||
all_good &=
|
||||
DiscIO::CompressFileToBlob(iso->GetFileName(), OutputFileName,
|
||||
DiscIO::CompressFileToBlob(iso->GetFilePath(), OutputFileName,
|
||||
(iso->GetPlatform() == DiscIO::Platform::WII_DISC) ? 1 : 0,
|
||||
16384, &MultiCompressCB, &progress);
|
||||
}
|
||||
else if (iso->GetBlobType() == DiscIO::BlobType::GCZ && !_compress)
|
||||
{
|
||||
std::string FileName;
|
||||
SplitPath(iso->GetFileName(), nullptr, &FileName, nullptr);
|
||||
SplitPath(iso->GetFilePath(), nullptr, &FileName, nullptr);
|
||||
progress.current_filename = FileName;
|
||||
if (iso->GetPlatform() == DiscIO::Platform::WII_DISC)
|
||||
FileName.append(".iso");
|
||||
@ -1469,7 +1292,7 @@ void GameListCtrl::CompressSelection(bool _compress)
|
||||
_("Confirm File Overwrite"), wxYES_NO) == wxNO)
|
||||
continue;
|
||||
|
||||
all_good &= DiscIO::DecompressBlobToFile(iso->GetFileName().c_str(), OutputFileName.c_str(),
|
||||
all_good &= DiscIO::DecompressBlobToFile(iso->GetFilePath().c_str(), OutputFileName.c_str(),
|
||||
&MultiCompressCB, &progress);
|
||||
}
|
||||
|
||||
@ -1490,7 +1313,7 @@ bool GameListCtrl::CompressCB(const std::string& text, float percent, void* arg)
|
||||
|
||||
void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso)
|
||||
return;
|
||||
|
||||
@ -1498,7 +1321,7 @@ void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
|
||||
wxString path;
|
||||
|
||||
std::string FileName, FilePath, FileExtension;
|
||||
SplitPath(iso->GetFileName(), &FilePath, &FileName, &FileExtension);
|
||||
SplitPath(iso->GetFilePath(), &FilePath, &FileName, &FileExtension);
|
||||
|
||||
do
|
||||
{
|
||||
@ -1543,10 +1366,10 @@ void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
if (is_compressed)
|
||||
all_good =
|
||||
DiscIO::DecompressBlobToFile(iso->GetFileName(), WxStrToStr(path), &CompressCB, &dialog);
|
||||
DiscIO::DecompressBlobToFile(iso->GetFilePath(), WxStrToStr(path), &CompressCB, &dialog);
|
||||
else
|
||||
all_good = DiscIO::CompressFileToBlob(
|
||||
iso->GetFileName(), WxStrToStr(path),
|
||||
iso->GetFilePath(), WxStrToStr(path),
|
||||
(iso->GetPlatform() == DiscIO::Platform::WII_DISC) ? 1 : 0, 16384, &CompressCB, &dialog);
|
||||
}
|
||||
|
||||
@ -1558,10 +1381,10 @@ void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void GameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const GameListItem* iso = GetSelectedISO();
|
||||
const UICommon::GameFile* iso = GetSelectedISO();
|
||||
if (!iso || !Core::IsRunning())
|
||||
return;
|
||||
Core::RunAsCPUThread([&iso] { DVDInterface::ChangeDisc(WxStrToStr(iso->GetFileName())); });
|
||||
Core::RunAsCPUThread([&iso] { DVDInterface::ChangeDisc(WxStrToStr(iso->GetFilePath())); });
|
||||
}
|
||||
|
||||
void GameListCtrl::OnSize(wxSizeEvent& event)
|
||||
|
@ -16,7 +16,12 @@
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "UICommon/GameFileCache.h"
|
||||
|
||||
namespace UICommon
|
||||
{
|
||||
class GameFile;
|
||||
}
|
||||
|
||||
class wxEmuStateTip : public wxTipWindow
|
||||
{
|
||||
@ -46,8 +51,8 @@ public:
|
||||
~GameListCtrl();
|
||||
|
||||
void BrowseForDirectory();
|
||||
const GameListItem* GetISO(size_t index) const;
|
||||
const GameListItem* GetSelectedISO() const;
|
||||
const UICommon::GameFile* GetISO(size_t index) const;
|
||||
const UICommon::GameFile* GetSelectedISO() const;
|
||||
|
||||
static bool IsHidingItems();
|
||||
|
||||
@ -80,9 +85,7 @@ private:
|
||||
void SetColors();
|
||||
void RefreshList();
|
||||
void RescanList();
|
||||
void DoState(PointerWrap* p, u32 size = 0);
|
||||
bool SyncCacheFile(bool write);
|
||||
std::vector<const GameListItem*> GetAllSelectedISOs() const;
|
||||
std::vector<const UICommon::GameFile*> GetAllSelectedISOs() const;
|
||||
|
||||
// events
|
||||
void OnRefreshGameList(wxCommandEvent& event);
|
||||
@ -124,9 +127,9 @@ private:
|
||||
std::vector<int> emu_state;
|
||||
} m_image_indexes;
|
||||
|
||||
// Actual backing GameListItems are maintained in a background thread and cached to file
|
||||
std::vector<std::shared_ptr<GameListItem>> m_cached_files;
|
||||
// Locks the list, not the contents
|
||||
// Actual backing GameFiles are maintained in a background thread and cached to file
|
||||
UICommon::GameFileCache m_cache;
|
||||
// Locks the cache object, not the shared_ptr<GameFile>s obtained from it
|
||||
std::mutex m_cache_mutex;
|
||||
Core::TitleDatabase m_title_database;
|
||||
std::mutex m_title_database_mutex;
|
||||
@ -134,7 +137,7 @@ private:
|
||||
Common::Event m_scan_trigger;
|
||||
Common::Flag m_scan_exiting;
|
||||
// UI thread's view into the cache
|
||||
std::vector<std::shared_ptr<GameListItem>> m_shown_files;
|
||||
std::vector<std::shared_ptr<const UICommon::GameFile>> m_shown_files;
|
||||
|
||||
int m_last_column;
|
||||
int m_last_sort;
|
||||
|
@ -1,370 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <wx/app.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/filefn.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/toplevel.h>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Hash.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "Common/NandPaths.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/IOS/ES/Formats.h"
|
||||
#include "Core/TitleDatabase.h"
|
||||
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "DiscIO/WiiSaveBanner.h"
|
||||
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
|
||||
static std::string GetLanguageString(DiscIO::Language language,
|
||||
std::map<DiscIO::Language, std::string> strings)
|
||||
{
|
||||
auto end = strings.end();
|
||||
auto it = strings.find(language);
|
||||
if (it != end)
|
||||
return it->second;
|
||||
|
||||
// English tends to be a good fallback when the requested language isn't available
|
||||
if (language != DiscIO::Language::LANGUAGE_ENGLISH)
|
||||
{
|
||||
it = strings.find(DiscIO::Language::LANGUAGE_ENGLISH);
|
||||
if (it != end)
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// If English isn't available either, just pick something
|
||||
if (!strings.empty())
|
||||
return strings.cbegin()->second;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
GameListItem::GameListItem(const std::string& filename)
|
||||
: m_file_name(filename), m_region(DiscIO::Region::UNKNOWN_REGION),
|
||||
m_country(DiscIO::Country::COUNTRY_UNKNOWN)
|
||||
{
|
||||
{
|
||||
std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(m_file_name));
|
||||
if (volume != nullptr)
|
||||
{
|
||||
m_platform = volume->GetVolumeType();
|
||||
|
||||
m_descriptions = volume->GetDescriptions();
|
||||
m_names = volume->GetLongNames();
|
||||
if (m_names.empty())
|
||||
m_names = volume->GetShortNames();
|
||||
m_company = GetLanguageString(DiscIO::Language::LANGUAGE_ENGLISH, volume->GetLongMakers());
|
||||
if (m_company.empty())
|
||||
m_company = GetLanguageString(DiscIO::Language::LANGUAGE_ENGLISH, volume->GetShortMakers());
|
||||
|
||||
m_region = volume->GetRegion();
|
||||
m_country = volume->GetCountry();
|
||||
m_blob_type = volume->GetBlobType();
|
||||
m_file_size = volume->GetRawSize();
|
||||
m_volume_size = volume->GetSize();
|
||||
|
||||
m_game_id = volume->GetGameID();
|
||||
m_title_id = volume->GetTitleID().value_or(0);
|
||||
m_disc_number = volume->GetDiscNumber().value_or(0);
|
||||
m_revision = volume->GetRevision().value_or(0);
|
||||
|
||||
auto& banner = m_volume_banner;
|
||||
std::vector<u32> buffer = volume->GetBanner(&banner.width, &banner.height);
|
||||
ReadVolumeBanner(&banner.buffer, buffer, banner.width, banner.height);
|
||||
|
||||
m_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_company.empty() && m_game_id.size() >= 6)
|
||||
m_company = DiscIO::GetCompanyFromID(m_game_id.substr(4, 2));
|
||||
|
||||
if (!IsValid() && IsElfOrDol())
|
||||
{
|
||||
m_valid = true;
|
||||
m_file_size = File::GetSize(m_file_name);
|
||||
m_platform = DiscIO::Platform::ELF_DOL;
|
||||
m_blob_type = DiscIO::BlobType::DIRECTORY;
|
||||
|
||||
std::string path, name;
|
||||
SplitPath(m_file_name, &path, &name, nullptr);
|
||||
|
||||
// A bit like the Homebrew Channel icon, except there can be multiple files
|
||||
// in a folder with their own icons. Useful for those who don't want to have
|
||||
// a Homebrew Channel-style folder structure.
|
||||
if (SetWxBannerFromPNGFile(path + name + ".png"))
|
||||
return;
|
||||
|
||||
// Homebrew Channel icon. The most typical icon format for DOLs and ELFs.
|
||||
if (SetWxBannerFromPNGFile(path + "icon.png"))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Volume banner. Typical for everything that isn't a DOL or ELF.
|
||||
SetWxBannerFromRaw(m_volume_banner);
|
||||
}
|
||||
}
|
||||
|
||||
bool GameListItem::IsValid() const
|
||||
{
|
||||
if (!m_valid)
|
||||
return false;
|
||||
|
||||
if (m_platform == DiscIO::Platform::WII_WAD && !IOS::ES::IsChannel(m_title_id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameListItem::CustomNameChanged(const Core::TitleDatabase& title_database)
|
||||
{
|
||||
const auto type = m_platform == DiscIO::Platform::WII_WAD ?
|
||||
Core::TitleDatabase::TitleType::Channel :
|
||||
Core::TitleDatabase::TitleType::Other;
|
||||
m_pending.custom_name = title_database.GetTitleName(m_game_id, type);
|
||||
return m_custom_name != m_pending.custom_name;
|
||||
}
|
||||
|
||||
void GameListItem::CustomNameCommit()
|
||||
{
|
||||
m_custom_name = std::move(m_pending.custom_name);
|
||||
}
|
||||
|
||||
bool GameListItem::EmuStateChanged()
|
||||
{
|
||||
IniFile ini = SConfig::LoadGameIni(m_game_id, m_revision);
|
||||
ini.GetIfExists("EmuState", "EmulationStateId", &m_pending.emu_state.rating, 0);
|
||||
ini.GetIfExists("EmuState", "EmulationIssues", &m_pending.emu_state.issues, std::string());
|
||||
return m_emu_state != m_pending.emu_state;
|
||||
}
|
||||
|
||||
void GameListItem::EmuStateCommit()
|
||||
{
|
||||
m_emu_state = std::move(m_pending.emu_state);
|
||||
}
|
||||
|
||||
void GameListItem::EmuState::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(rating);
|
||||
p.Do(issues);
|
||||
}
|
||||
|
||||
void GameListItem::Banner::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(buffer);
|
||||
p.Do(width);
|
||||
p.Do(height);
|
||||
}
|
||||
|
||||
void GameListItem::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_valid);
|
||||
p.Do(m_file_name);
|
||||
p.Do(m_file_size);
|
||||
p.Do(m_volume_size);
|
||||
p.Do(m_names);
|
||||
p.Do(m_descriptions);
|
||||
p.Do(m_company);
|
||||
p.Do(m_game_id);
|
||||
p.Do(m_title_id);
|
||||
p.Do(m_region);
|
||||
p.Do(m_country);
|
||||
p.Do(m_platform);
|
||||
p.Do(m_blob_type);
|
||||
p.Do(m_revision);
|
||||
p.Do(m_disc_number);
|
||||
m_volume_banner.DoState(p);
|
||||
m_emu_state.DoState(p);
|
||||
p.Do(m_custom_name);
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
SetWxBannerFromRaw(m_volume_banner);
|
||||
}
|
||||
}
|
||||
|
||||
bool GameListItem::IsElfOrDol() const
|
||||
{
|
||||
if (m_file_name.size() < 4)
|
||||
return false;
|
||||
|
||||
std::string name_end = m_file_name.substr(m_file_name.size() - 4);
|
||||
std::transform(name_end.begin(), name_end.end(), name_end.begin(), ::tolower);
|
||||
return name_end == ".elf" || name_end == ".dol";
|
||||
}
|
||||
|
||||
void GameListItem::ReadVolumeBanner(std::vector<u8>* image, const std::vector<u32>& buffer,
|
||||
int width, int height)
|
||||
{
|
||||
image->resize(width * height * 3);
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
(*image)[i * 3 + 0] = (buffer[i] & 0xFF0000) >> 16;
|
||||
(*image)[i * 3 + 1] = (buffer[i] & 0x00FF00) >> 8;
|
||||
(*image)[i * 3 + 2] = (buffer[i] & 0x0000FF) >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GameListItem::SetWxBannerFromPNGFile(const std::string& path)
|
||||
{
|
||||
if (!File::Exists(path))
|
||||
return false;
|
||||
|
||||
wxImage image(StrToWxStr(path), wxBITMAP_TYPE_PNG);
|
||||
if (!image.IsOk())
|
||||
return false;
|
||||
|
||||
m_banner_wx = image;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameListItem::SetWxBannerFromRaw(const Banner& banner)
|
||||
{
|
||||
if (banner.empty())
|
||||
return;
|
||||
|
||||
// Need to make explicit copy as wxImage uses reference counting for copies combined with only
|
||||
// taking a pointer, not the content, when given a buffer to its constructor.
|
||||
m_banner_wx.Create(banner.width, banner.height, false);
|
||||
std::memcpy(m_banner_wx.GetData(), banner.buffer.data(), banner.buffer.size());
|
||||
}
|
||||
|
||||
bool GameListItem::BannerChanged()
|
||||
{
|
||||
// Wii banners can only be read if there is a savefile,
|
||||
// so sometimes caches don't contain banners. Let's check
|
||||
// if a banner has become available after the cache was made.
|
||||
|
||||
if (!m_volume_banner.empty())
|
||||
return false;
|
||||
if (!DiscIO::IsWii(m_platform))
|
||||
return false;
|
||||
|
||||
auto& banner = m_pending.volume_banner;
|
||||
std::vector<u32> buffer =
|
||||
DiscIO::WiiSaveBanner(m_title_id).GetBanner(&banner.width, &banner.height);
|
||||
if (buffer.empty())
|
||||
return false;
|
||||
|
||||
ReadVolumeBanner(&banner.buffer, buffer, banner.width, banner.height);
|
||||
// We only reach here if m_volume_banner was empty, so we don't need to explicitly
|
||||
// compare to see if they are different
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameListItem::BannerCommit()
|
||||
{
|
||||
m_volume_banner = std::move(m_pending.volume_banner);
|
||||
SetWxBannerFromRaw(m_volume_banner);
|
||||
}
|
||||
|
||||
std::string GameListItem::GetDescription(DiscIO::Language language) const
|
||||
{
|
||||
return GetLanguageString(language, m_descriptions);
|
||||
}
|
||||
|
||||
std::string GameListItem::GetDescription() const
|
||||
{
|
||||
const bool wii = DiscIO::IsWii(m_platform);
|
||||
return GetDescription(SConfig::GetInstance().GetCurrentLanguage(wii));
|
||||
}
|
||||
|
||||
std::string GameListItem::GetName(DiscIO::Language language) const
|
||||
{
|
||||
return GetLanguageString(language, m_names);
|
||||
}
|
||||
|
||||
std::string GameListItem::GetName() const
|
||||
{
|
||||
if (!m_custom_name.empty())
|
||||
return m_custom_name;
|
||||
|
||||
const bool wii = DiscIO::IsWii(m_platform);
|
||||
std::string name = GetName(SConfig::GetInstance().GetCurrentLanguage(wii));
|
||||
if (!name.empty())
|
||||
return name;
|
||||
|
||||
// No usable name, return filename (better than nothing)
|
||||
std::string ext;
|
||||
SplitPath(GetFileName(), nullptr, &name, &ext);
|
||||
return name + ext;
|
||||
}
|
||||
|
||||
std::string GameListItem::GetUniqueIdentifier() const
|
||||
{
|
||||
const DiscIO::Language lang = DiscIO::Language::LANGUAGE_ENGLISH;
|
||||
std::vector<std::string> info;
|
||||
if (!GetGameID().empty())
|
||||
info.push_back(GetGameID());
|
||||
if (GetRevision() != 0)
|
||||
{
|
||||
std::string rev_str = "Revision ";
|
||||
info.push_back(rev_str + std::to_string((long long)GetRevision()));
|
||||
}
|
||||
|
||||
std::string name(GetName(lang));
|
||||
if (name.empty())
|
||||
name = GetName();
|
||||
|
||||
int disc_number = GetDiscNumber() + 1;
|
||||
|
||||
std::string lower_name = name;
|
||||
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower);
|
||||
if (disc_number > 1 &&
|
||||
lower_name.find(std::string(wxString::Format("disc %i", disc_number))) == std::string::npos &&
|
||||
lower_name.find(std::string(wxString::Format("disc%i", disc_number))) == std::string::npos)
|
||||
{
|
||||
std::string disc_text = "Disc ";
|
||||
info.push_back(disc_text + std::to_string(disc_number));
|
||||
}
|
||||
if (info.empty())
|
||||
return name;
|
||||
std::ostringstream ss;
|
||||
std::copy(info.begin(), info.end() - 1, std::ostream_iterator<std::string>(ss, ", "));
|
||||
ss << info.back();
|
||||
return name + " (" + ss.str() + ")";
|
||||
}
|
||||
|
||||
std::vector<DiscIO::Language> GameListItem::GetLanguages() const
|
||||
{
|
||||
std::vector<DiscIO::Language> languages;
|
||||
for (std::pair<DiscIO::Language, std::string> name : m_names)
|
||||
languages.push_back(name.first);
|
||||
return languages;
|
||||
}
|
||||
|
||||
const std::string GameListItem::GetWiiFSPath() const
|
||||
{
|
||||
if (!DiscIO::IsWii(m_platform))
|
||||
return "";
|
||||
|
||||
const std::string path = Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT);
|
||||
|
||||
if (path[0] == '.')
|
||||
return WxStrToStr(wxGetCwd()) + path.substr(strlen(ROOT_DIR));
|
||||
|
||||
return path;
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Common.h"
|
||||
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class TitleDatabase;
|
||||
}
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
enum class BlobType;
|
||||
enum class Country;
|
||||
enum class Language;
|
||||
enum class Region;
|
||||
enum class Platform;
|
||||
}
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
class GameListItem
|
||||
{
|
||||
public:
|
||||
GameListItem() = default;
|
||||
explicit GameListItem(const std::string& file_name);
|
||||
~GameListItem() = default;
|
||||
|
||||
bool IsValid() const;
|
||||
const std::string& GetFileName() const { return m_file_name; }
|
||||
std::string GetName(DiscIO::Language language) const;
|
||||
std::string GetName() const;
|
||||
std::string GetUniqueIdentifier() const;
|
||||
std::string GetDescription(DiscIO::Language language) const;
|
||||
std::string GetDescription() const;
|
||||
std::vector<DiscIO::Language> GetLanguages() const;
|
||||
std::string GetCompany() const { return m_company; }
|
||||
u16 GetRevision() const { return m_revision; }
|
||||
const std::string& GetGameID() const { return m_game_id; }
|
||||
u64 GetTitleID() const { return m_title_id; }
|
||||
const std::string GetWiiFSPath() const;
|
||||
DiscIO::Region GetRegion() const { return m_region; }
|
||||
DiscIO::Country GetCountry() const { return m_country; }
|
||||
DiscIO::Platform GetPlatform() const { return m_platform; }
|
||||
DiscIO::BlobType GetBlobType() const { return m_blob_type; }
|
||||
const std::string& GetIssues() const { return m_emu_state.issues; }
|
||||
int GetEmuState() const { return m_emu_state.rating; }
|
||||
u64 GetFileSize() const { return m_file_size; }
|
||||
u64 GetVolumeSize() const { return m_volume_size; }
|
||||
// 0 is the first disc, 1 is the second disc
|
||||
u8 GetDiscNumber() const { return m_disc_number; }
|
||||
// NOTE: Banner image is at the original resolution, use WxUtils::ScaleImageToBitmap
|
||||
// to display it
|
||||
const wxImage& GetBannerImage() const { return m_banner_wx; }
|
||||
void DoState(PointerWrap& p);
|
||||
bool BannerChanged();
|
||||
void BannerCommit();
|
||||
bool EmuStateChanged();
|
||||
void EmuStateCommit();
|
||||
bool CustomNameChanged(const Core::TitleDatabase& title_database);
|
||||
void CustomNameCommit();
|
||||
|
||||
private:
|
||||
struct EmuState
|
||||
{
|
||||
int rating{};
|
||||
std::string issues{};
|
||||
bool operator!=(const EmuState& rhs) const
|
||||
{
|
||||
return rating != rhs.rating || issues != rhs.issues;
|
||||
}
|
||||
void DoState(PointerWrap& p);
|
||||
};
|
||||
struct Banner
|
||||
{
|
||||
std::vector<u8> buffer{};
|
||||
int width{};
|
||||
int height{};
|
||||
bool empty() const { return buffer.empty(); }
|
||||
void DoState(PointerWrap& p);
|
||||
};
|
||||
|
||||
bool IsElfOrDol() const;
|
||||
void ReadVolumeBanner(std::vector<u8>* image, const std::vector<u32>& buffer, int width,
|
||||
int height);
|
||||
// Outputs to m_banner_wx
|
||||
bool SetWxBannerFromPNGFile(const std::string& path);
|
||||
// Outputs to m_banner_wx
|
||||
void SetWxBannerFromRaw(const Banner& banner);
|
||||
|
||||
// IMPORTANT: Nearly all data members must be save/restored in DoState.
|
||||
// If anything is changed, make sure DoState handles it properly and
|
||||
// GameListCtrl::CACHE_REVISION is incremented.
|
||||
|
||||
bool m_valid{};
|
||||
std::string m_file_name{};
|
||||
|
||||
u64 m_file_size{};
|
||||
u64 m_volume_size{};
|
||||
|
||||
std::map<DiscIO::Language, std::string> m_names{};
|
||||
std::map<DiscIO::Language, std::string> m_descriptions{};
|
||||
std::string m_company{};
|
||||
std::string m_game_id{};
|
||||
u64 m_title_id{};
|
||||
|
||||
DiscIO::Region m_region{};
|
||||
DiscIO::Country m_country{};
|
||||
DiscIO::Platform m_platform{};
|
||||
DiscIO::BlobType m_blob_type{};
|
||||
u16 m_revision{};
|
||||
u8 m_disc_number{};
|
||||
|
||||
Banner m_volume_banner{};
|
||||
EmuState m_emu_state{};
|
||||
// Overridden name from TitleDatabase
|
||||
std::string m_custom_name{};
|
||||
|
||||
// wxImage is not handled in DoState
|
||||
wxImage m_banner_wx{};
|
||||
|
||||
// The following data members allow GameListCtrl to construct new GameListItems in a threadsafe
|
||||
// way. They should not be handled in DoState.
|
||||
struct
|
||||
{
|
||||
EmuState emu_state;
|
||||
Banner volume_banner;
|
||||
std::string custom_name;
|
||||
} m_pending{};
|
||||
};
|
@ -28,7 +28,6 @@
|
||||
#include "DiscIO/Enums.h"
|
||||
#include "DiscIO/Filesystem.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
|
||||
namespace
|
||||
|
@ -54,12 +54,12 @@
|
||||
#include "DolphinWX/DolphinSlider.h"
|
||||
#include "DolphinWX/Frame.h"
|
||||
#include "DolphinWX/Globals.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/ISOProperties/FilesystemPanel.h"
|
||||
#include "DolphinWX/ISOProperties/InfoPanel.h"
|
||||
#include "DolphinWX/Main.h"
|
||||
#include "DolphinWX/PatchAddEdit.h"
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
// A warning message displayed on the ARCodes and GeckoCodes pages when cheats are
|
||||
// disabled globally to explain why turning cheats on does not work.
|
||||
@ -190,15 +190,15 @@ EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked)
|
||||
EVT_BUTTON(ID_REMOVEPATCH, CISOProperties::PatchButtonClicked)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id,
|
||||
const wxString& title, const wxPoint& position, const wxSize& size,
|
||||
long style)
|
||||
CISOProperties::CISOProperties(const UICommon::GameFile& game_list_item, wxWindow* parent,
|
||||
wxWindowID id, const wxString& title, const wxPoint& position,
|
||||
const wxSize& size, long style)
|
||||
: wxDialog(parent, id, title, position, size, style), m_open_gamelist_item(game_list_item)
|
||||
{
|
||||
Bind(DOLPHIN_EVT_CHANGE_ISO_PROPERTIES_TITLE, &CISOProperties::OnChangeTitle, this);
|
||||
|
||||
// Load ISO data
|
||||
m_open_iso = DiscIO::CreateVolumeFromFilename(m_open_gamelist_item.GetFileName());
|
||||
m_open_iso = DiscIO::CreateVolumeFromFilename(m_open_gamelist_item.GetFilePath());
|
||||
|
||||
m_game_id = m_open_iso->GetGameID();
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
#include <wx/treebase.h>
|
||||
|
||||
#include "Common/IniFile.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/PatchAddEdit.h"
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
class ActionReplayCodesPanel;
|
||||
class CheatWarningMessage;
|
||||
@ -52,9 +52,9 @@ wxDECLARE_EVENT(DOLPHIN_EVT_CHANGE_ISO_PROPERTIES_TITLE, wxCommandEvent);
|
||||
class CISOProperties : public wxDialog
|
||||
{
|
||||
public:
|
||||
CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id = wxID_ANY,
|
||||
const wxString& title = _("Properties"), const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
CISOProperties(const UICommon::GameFile& game_list_item, wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY, const wxString& title = _("Properties"),
|
||||
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
|
||||
long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
virtual ~CISOProperties();
|
||||
|
||||
@ -141,7 +141,7 @@ private:
|
||||
void OnCheatCodeToggled(wxCommandEvent& event);
|
||||
void OnChangeTitle(wxCommandEvent& event);
|
||||
|
||||
const GameListItem m_open_gamelist_item;
|
||||
const UICommon::GameFile m_open_gamelist_item;
|
||||
|
||||
IniFile m_gameini_default;
|
||||
IniFile m_gameini_local;
|
||||
|
@ -32,9 +32,9 @@
|
||||
#include "Core/IOS/ES/Formats.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/ISOProperties/ISOProperties.h"
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -142,7 +142,7 @@ int FindPreferredLanguageIndex(DiscIO::Language preferred_language,
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
InfoPanel::InfoPanel(wxWindow* parent, wxWindowID id, const GameListItem& item,
|
||||
InfoPanel::InfoPanel(wxWindow* parent, wxWindowID id, const UICommon::GameFile& item,
|
||||
const std::unique_ptr<DiscIO::Volume>& opened_iso)
|
||||
: wxPanel{parent, id}, m_game_list_item{item}, m_opened_iso{opened_iso}
|
||||
{
|
||||
@ -209,8 +209,8 @@ void InfoPanel::LoadBannerDetails()
|
||||
|
||||
void InfoPanel::LoadBannerImage()
|
||||
{
|
||||
const auto& banner_image = m_game_list_item.GetBannerImage();
|
||||
const auto banner_min_size = m_banner->GetMinSize();
|
||||
const wxImage banner_image = WxUtils::ToWxImage(m_game_list_item.GetBannerImage());
|
||||
const wxSize banner_min_size = m_banner->GetMinSize();
|
||||
|
||||
if (banner_image.IsOk())
|
||||
{
|
||||
@ -337,7 +337,7 @@ void InfoPanel::OnComputeMD5(wxCommandEvent& WXUNUSED(event))
|
||||
wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
|
||||
wxPD_REMAINING_TIME | wxPD_SMOOTH);
|
||||
|
||||
const auto result = MD5::MD5Sum(m_game_list_item.GetFileName(), [&progress_dialog](int progress) {
|
||||
const auto result = MD5::MD5Sum(m_game_list_item.GetFilePath(), [&progress_dialog](int progress) {
|
||||
return progress_dialog.Update(progress);
|
||||
});
|
||||
|
||||
@ -367,7 +367,7 @@ void InfoPanel::OnSaveBannerImage(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
m_game_list_item.GetBannerImage().SaveFile(dialog.GetPath());
|
||||
WxUtils::ToWxImage(m_game_list_item.GetBannerImage()).SaveFile(dialog.GetPath());
|
||||
}
|
||||
|
||||
Raise();
|
||||
@ -375,16 +375,16 @@ void InfoPanel::OnSaveBannerImage(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void InfoPanel::ChangeBannerDetails(DiscIO::Language language)
|
||||
{
|
||||
const auto name = StrToWxStr(m_game_list_item.GetName(language));
|
||||
const auto name = StrToWxStr(m_game_list_item.GetLongName(language));
|
||||
const auto comment = StrToWxStr(m_game_list_item.GetDescription(language));
|
||||
const auto maker = StrToWxStr(m_game_list_item.GetCompany());
|
||||
const auto maker = StrToWxStr(m_game_list_item.GetLongMaker(language));
|
||||
|
||||
m_name->SetValue(name);
|
||||
m_comment->SetValue(comment);
|
||||
m_maker->SetValue(maker);
|
||||
|
||||
std::string path, filename, extension;
|
||||
SplitPath(m_game_list_item.GetFileName(), &path, &filename, &extension);
|
||||
SplitPath(m_game_list_item.GetFilePath(), &path, &filename, &extension);
|
||||
|
||||
// Real disk drives don't have filenames on Windows
|
||||
if (filename.empty() && extension.empty())
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <memory>
|
||||
#include <wx/panel.h>
|
||||
|
||||
class GameListItem;
|
||||
class wxButton;
|
||||
class wxChoice;
|
||||
class wxStaticBitmap;
|
||||
@ -16,14 +15,19 @@ class wxTextCtrl;
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
class Volume;
|
||||
enum class Language;
|
||||
class Volume;
|
||||
}
|
||||
|
||||
namespace UICommon
|
||||
{
|
||||
class GameFile;
|
||||
}
|
||||
|
||||
class InfoPanel final : public wxPanel
|
||||
{
|
||||
public:
|
||||
InfoPanel(wxWindow* parent, wxWindowID id, const GameListItem& item,
|
||||
InfoPanel(wxWindow* parent, wxWindowID id, const UICommon::GameFile& item,
|
||||
const std::unique_ptr<DiscIO::Volume>& opened_iso);
|
||||
|
||||
private:
|
||||
@ -37,6 +41,7 @@ private:
|
||||
void LoadGUIData();
|
||||
void LoadISODetails();
|
||||
void LoadBannerDetails();
|
||||
wxImage ConvertBannerImage();
|
||||
void LoadBannerImage();
|
||||
|
||||
wxStaticBoxSizer* CreateISODetailsSizer();
|
||||
@ -52,7 +57,7 @@ private:
|
||||
|
||||
void EmitTitleChangeEvent(const wxString& new_title);
|
||||
|
||||
const GameListItem& m_game_list_item;
|
||||
const UICommon::GameFile& m_game_list_item;
|
||||
const std::unique_ptr<DiscIO::Volume>& m_opened_iso;
|
||||
|
||||
wxTextCtrl* m_internal_name;
|
||||
|
@ -47,11 +47,12 @@
|
||||
|
||||
#include "DolphinWX/Frame.h"
|
||||
#include "DolphinWX/GameListCtrl.h"
|
||||
#include "DolphinWX/ISOFile.h"
|
||||
#include "DolphinWX/NetPlay/ChangeGameDialog.h"
|
||||
#include "DolphinWX/NetPlay/MD5Dialog.h"
|
||||
#include "DolphinWX/NetPlay/PadMapDialog.h"
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
#include "MD5Dialog.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
@ -335,7 +336,7 @@ std::string NetPlayDialog::FindGame(const std::string& target_game)
|
||||
// find path for selected game, sloppy..
|
||||
for (u32 i = 0; auto game = m_game_list->GetISO(i); ++i)
|
||||
if (target_game == game->GetUniqueIdentifier())
|
||||
return game->GetFileName();
|
||||
return game->GetFilePath();
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
@ -285,6 +287,27 @@ wxSize GetTextWidgetMinSize(const wxSpinCtrl* spinner)
|
||||
return size;
|
||||
}
|
||||
|
||||
wxImage ToWxImage(const UICommon::GameBanner& banner)
|
||||
{
|
||||
return ToWxImage(banner.buffer, banner.width, banner.height);
|
||||
}
|
||||
|
||||
wxImage ToWxImage(const std::vector<u32>& buffer, int width, int height)
|
||||
{
|
||||
wxImage image(width, height, false);
|
||||
if (buffer.empty())
|
||||
return image;
|
||||
|
||||
unsigned char* data = image.GetData();
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
data[i * 3 + 0] = (buffer[i] & 0xFF0000) >> 16;
|
||||
data[i * 3 + 1] = (buffer[i] & 0x00FF00) >> 8;
|
||||
data[i * 3 + 2] = (buffer[i] & 0x0000FF) >> 0;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
static wxImage LoadScaledImage(const std::string& file_path, const wxWindow* context,
|
||||
const wxSize& output_size, const wxRect& usable_rect, LSIFlags flags,
|
||||
const wxColour& fill_color)
|
||||
|
@ -5,10 +5,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/colour.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class wxControl;
|
||||
class wxBitmap;
|
||||
class wxImage;
|
||||
@ -18,6 +22,11 @@ class wxToolBar;
|
||||
class wxTopLevelWindow;
|
||||
class wxWindow;
|
||||
|
||||
namespace UICommon
|
||||
{
|
||||
struct GameBanner;
|
||||
}
|
||||
|
||||
namespace WxUtils
|
||||
{
|
||||
// Launch a file according to its mime type
|
||||
@ -95,6 +104,9 @@ constexpr LSIFlags operator&(LSIFlags left, LSIFlags right)
|
||||
return static_cast<LSIFlags>(static_cast<unsigned int>(left) & right);
|
||||
}
|
||||
|
||||
wxImage ToWxImage(const UICommon::GameBanner& banner);
|
||||
wxImage ToWxImage(const std::vector<u32>& buffer, int width, int height);
|
||||
|
||||
// Swiss army knife loader function for preparing a scaled resource image file.
|
||||
// Only the path and context are mandatory, other parameters can be ignored.
|
||||
// NOTE: All size parameters are in window pixels, not DIPs or framebuffer pixels.
|
||||
|
Reference in New Issue
Block a user