1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
20 #include <wx/imaglist.h>
21 #include <wx/fontmap.h>
25 #include "FileSearch.h"
26 #include "StringUtil.h"
27 #include "ConfigManager.h"
28 #include "GameListCtrl.h"
31 #include "ISOProperties.h"
38 #include "../resources/Flag_Europe.xpm"
39 #include "../resources/Flag_France.xpm"
40 #include "../resources/Flag_Italy.xpm"
41 #include "../resources/Flag_Japan.xpm"
42 #include "../resources/Flag_USA.xpm"
43 #include "../resources/Flag_Taiwan.xpm"
44 #include "../resources/Flag_Korea.xpm"
45 #include "../resources/Flag_Unknown.xpm"
46 #include "../resources/Platform_Wad.xpm"
47 #include "../resources/Platform_Wii.xpm"
48 #include "../resources/Platform_Gamecube.xpm"
49 #include "../resources/rating_gamelist.h"
51 size_t CGameListCtrl::m_currentItem
= 0;
52 size_t CGameListCtrl::m_numberItem
= 0;
53 std::string
CGameListCtrl::m_currentFilename
;
55 static int currentColumn
= 0;
56 bool operator < (const GameListItem
&one
, const GameListItem
&other
)
61 switch (one
.GetCountry())
63 case DiscIO::IVolume::COUNTRY_JAPAN
:;
64 case DiscIO::IVolume::COUNTRY_USA
:indexOne
= 0; break;
65 default: indexOne
= (int)SConfig::GetInstance().m_InterfaceLanguage
;
68 switch (other
.GetCountry())
70 case DiscIO::IVolume::COUNTRY_JAPAN
:;
71 case DiscIO::IVolume::COUNTRY_USA
:indexOther
= 0; break;
72 default: indexOther
= (int)SConfig::GetInstance().m_InterfaceLanguage
;
77 case CGameListCtrl::COLUMN_TITLE
: return strcasecmp(one
.GetName(indexOne
).c_str(), other
.GetName(indexOther
).c_str()) < 0;
78 case CGameListCtrl::COLUMN_NOTES
:
80 // On Gamecube we show the company string, while it's empty on other platforms, so we show the description instead
81 std::string cmp1
= (one
.GetPlatform() == GameListItem::GAMECUBE_DISC
) ? one
.GetCompany() : one
.GetDescription(indexOne
);
82 std::string cmp2
= (other
.GetPlatform() == GameListItem::GAMECUBE_DISC
) ? other
.GetCompany() : other
.GetDescription(indexOther
);
83 return strcasecmp(cmp1
.c_str(), cmp2
.c_str()) < 0;
85 case CGameListCtrl::COLUMN_COUNTRY
: return (one
.GetCountry() < other
.GetCountry());
86 case CGameListCtrl::COLUMN_SIZE
: return (one
.GetFileSize() < other
.GetFileSize());
87 case CGameListCtrl::COLUMN_PLATFORM
: return (one
.GetPlatform() < other
.GetPlatform());
88 default: return strcasecmp(one
.GetName(indexOne
).c_str(), other
.GetName(indexOther
).c_str()) < 0;
93 BEGIN_EVENT_TABLE(wxEmuStateTip
, wxTipWindow
)
94 EVT_KEY_DOWN(wxEmuStateTip::OnKeyDown
)
97 BEGIN_EVENT_TABLE(CGameListCtrl
, wxListCtrl
)
99 EVT_PAINT(CGameListCtrl::OnPaintDrawImages
)
101 EVT_SIZE(CGameListCtrl::OnSize
)
102 EVT_RIGHT_DOWN(CGameListCtrl::OnRightClick
)
103 EVT_LEFT_DOWN(CGameListCtrl::OnLeftClick
)
104 EVT_LIST_KEY_DOWN(LIST_CTRL
, CGameListCtrl::OnKeyPress
)
105 EVT_MOTION(CGameListCtrl::OnMouseMotion
)
106 EVT_LIST_COL_BEGIN_DRAG(LIST_CTRL
, CGameListCtrl::OnColBeginDrag
)
107 EVT_LIST_COL_CLICK(LIST_CTRL
, CGameListCtrl::OnColumnClick
)
108 EVT_MENU(IDM_PROPERTIES
, CGameListCtrl::OnProperties
)
109 EVT_MENU(IDM_OPENCONTAININGFOLDER
, CGameListCtrl::OnOpenContainingFolder
)
110 EVT_MENU(IDM_OPENSAVEFOLDER
, CGameListCtrl::OnOpenSaveFolder
)
111 EVT_MENU(IDM_EXPORTSAVE
, CGameListCtrl::OnExportSave
)
112 EVT_MENU(IDM_SETDEFAULTGCM
, CGameListCtrl::OnSetDefaultGCM
)
113 EVT_MENU(IDM_COMPRESSGCM
, CGameListCtrl::OnCompressGCM
)
114 EVT_MENU(IDM_MULTICOMPRESSGCM
, CGameListCtrl::OnMultiCompressGCM
)
115 EVT_MENU(IDM_MULTIDECOMPRESSGCM
, CGameListCtrl::OnMultiDecompressGCM
)
116 EVT_MENU(IDM_DELETEGCM
, CGameListCtrl::OnDeleteGCM
)
117 EVT_MENU(IDM_INSTALLWAD
, CGameListCtrl::OnInstallWAD
)
121 CGameListCtrl::CGameListCtrl(wxWindow
* parent
, const wxWindowID id
, const wxPoint
& pos
, const wxSize
& size
, long style
)
122 : wxListCtrl(parent
, id
, pos
, size
, style
), toolTip(0)
126 CGameListCtrl::~CGameListCtrl()
128 if (m_imageListSmall
)
129 delete m_imageListSmall
;
132 void CGameListCtrl::InitBitmaps()
134 m_imageListSmall
= new wxImageList(96, 32);
135 SetImageList(m_imageListSmall
, wxIMAGE_LIST_SMALL
);
137 m_FlagImageIndex
.resize(DiscIO::IVolume::NUMBER_OF_COUNTRIES
);
138 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_EUROPE
] =
139 m_imageListSmall
->Add(wxBitmap(Flag_Europe_xpm
), wxNullBitmap
);
140 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_FRANCE
] =
141 m_imageListSmall
->Add(wxBitmap(Flag_France_xpm
), wxNullBitmap
);
142 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_USA
] =
143 m_imageListSmall
->Add(wxBitmap(Flag_USA_xpm
), wxNullBitmap
);
144 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_JAPAN
] =
145 m_imageListSmall
->Add(wxBitmap(Flag_Japan_xpm
), wxNullBitmap
);
146 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_KOREA
] =
147 m_imageListSmall
->Add(wxBitmap(Flag_Korea_xpm
), wxNullBitmap
);
148 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_ITALY
] =
149 m_imageListSmall
->Add(wxBitmap(Flag_Italy_xpm
), wxNullBitmap
);
150 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_TAIWAN
] =
151 m_imageListSmall
->Add(wxBitmap(Flag_Taiwan_xpm
), wxNullBitmap
);
152 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_SDK
] =
153 m_imageListSmall
->Add(wxBitmap(Flag_Unknown_xpm
), wxNullBitmap
);
154 m_FlagImageIndex
[DiscIO::IVolume::COUNTRY_UNKNOWN
] =
155 m_imageListSmall
->Add(wxBitmap(Flag_Unknown_xpm
), wxNullBitmap
);
157 m_PlatformImageIndex
.resize(3);
158 m_PlatformImageIndex
[0] =
159 m_imageListSmall
->Add(wxBitmap(Platform_Gamecube_xpm
), wxNullBitmap
);
160 m_PlatformImageIndex
[1] =
161 m_imageListSmall
->Add(wxBitmap(Platform_Wii_xpm
), wxNullBitmap
);
162 m_PlatformImageIndex
[2] =
163 m_imageListSmall
->Add(wxBitmap(Platform_Wad_xpm
), wxNullBitmap
);
165 m_EmuStateImageIndex
.resize(6);
166 m_EmuStateImageIndex
[0] =
167 m_imageListSmall
->Add(wxBitmap(rating_0
), wxNullBitmap
);
168 m_EmuStateImageIndex
[1] =
169 m_imageListSmall
->Add(wxBitmap(rating_1
), wxNullBitmap
);
170 m_EmuStateImageIndex
[2] =
171 m_imageListSmall
->Add(wxBitmap(rating_2
), wxNullBitmap
);
172 m_EmuStateImageIndex
[3] =
173 m_imageListSmall
->Add(wxBitmap(rating_3
), wxNullBitmap
);
174 m_EmuStateImageIndex
[4] =
175 m_imageListSmall
->Add(wxBitmap(rating_4
), wxNullBitmap
);
176 m_EmuStateImageIndex
[5] =
177 m_imageListSmall
->Add(wxBitmap(rating_5
), wxNullBitmap
);
180 void CGameListCtrl::BrowseForDirectory()
183 wxGetHomeDir(&dirHome
);
186 wxDirDialog
dialog(this, _("Browse for a directory to add"), dirHome
, wxDD_DEFAULT_STYLE
| wxDD_DIR_MUST_EXIST
);
188 if (dialog
.ShowModal() == wxID_OK
)
190 std::string
sPath(dialog
.GetPath().mb_str());
191 std::vector
<std::string
>::iterator itResult
= std::find(
192 SConfig::GetInstance().m_ISOFolder
.begin(), SConfig::GetInstance().m_ISOFolder
.end(), sPath
195 if (itResult
== SConfig::GetInstance().m_ISOFolder
.end())
197 SConfig::GetInstance().m_ISOFolder
.push_back(sPath
);
198 SConfig::GetInstance().SaveSettings();
205 void CGameListCtrl::Update()
207 // Don't let the user refresh it while a game is running
208 if (Core::GetState() != Core::CORE_UNINITIALIZED
)
211 if (m_imageListSmall
)
213 delete m_imageListSmall
;
214 m_imageListSmall
= NULL
;
217 // NetPlay : Set/Reset the GameList string
227 if (m_ISOFiles
.size() != 0)
229 // Don't load bitmaps unless there are games to list
233 InsertColumn(COLUMN_PLATFORM
, _(""));
234 InsertColumn(COLUMN_BANNER
, _("Banner"));
235 InsertColumn(COLUMN_TITLE
, _("Title"));
237 // Instead of showing the notes + the company, which is unknown with wii titles
238 // We show in the same column : company for GC games and description for wii/wad games
239 InsertColumn(COLUMN_NOTES
, _("Notes"));
240 InsertColumn(COLUMN_COUNTRY
, _(""));
241 InsertColumn(COLUMN_SIZE
, _("Size"));
242 InsertColumn(COLUMN_EMULATION_STATE
, _("State"));
245 // set initial sizes for columns
246 SetColumnWidth(COLUMN_PLATFORM
, 35);
247 SetColumnWidth(COLUMN_BANNER
, 96);
248 SetColumnWidth(COLUMN_TITLE
, 200);
249 SetColumnWidth(COLUMN_NOTES
, 200);
250 SetColumnWidth(COLUMN_COUNTRY
, 32);
251 SetColumnWidth(COLUMN_EMULATION_STATE
, 50);
254 for (int i
= 0; i
< (int)m_ISOFiles
.size(); i
++)
256 InsertItemInReportView(i
);
257 if (m_ISOFiles
[i
].IsCompressed())
258 SetItemTextColour(i
, wxColour(0xFF0000));
261 // Sort items by Title
263 event
.m_col
= COLUMN_TITLE
; last_column
= 0;
264 OnColumnClick(event
);
266 SetColumnWidth(COLUMN_SIZE
, wxLIST_AUTOSIZE
);
270 wxString errorString
;
271 // We just check for one hide setting to be enabled, as we may only have GC games
272 // for example, and hide them, so we should show the second message instead
273 if ((SConfig::GetInstance().m_ListGC
&&
274 SConfig::GetInstance().m_ListWii
&&
275 SConfig::GetInstance().m_ListWad
) &&
276 (SConfig::GetInstance().m_ListJap
&&
277 SConfig::GetInstance().m_ListUsa
&&
278 SConfig::GetInstance().m_ListPal
))
280 errorString
= _("Dolphin could not find any GC/Wii ISOs. Doubleclick here to browse for files...");
284 errorString
= _("Dolphin is currently set to hide all games. Doubleclick here to show all games...");
286 InsertColumn(0, _("No ISOs or WADS found"));
287 long index
= InsertItem(0, errorString
);
288 SetItemFont(index
, *wxITALIC_FONT
);
289 SetColumnWidth(0, wxLIST_AUTOSIZE
);
294 AutomaticColumnWidth();
297 wxString
NiceSizeFormat(s64 _size
)
299 const char* sizes
[] = {"b", "KB", "MB", "GB", "TB", "PB", "EB"};
303 while (_size
> (s64
)1024)
306 frac
= (int)_size
& 1023;
310 float f
= (float)_size
+ ((float)frac
/ 1024.0f
);
314 sprintf(tempstr
,"%3.1f %s", f
, sizes
[s
]);
315 NiceString
= wxString::FromAscii(tempstr
);
319 std::string
CGameListCtrl::GetGamePaths() const
323 std::string
CGameListCtrl::GetGameNames() const
329 // This draws our icons on top of the gamelist, it's only used on Windows
330 void CGameListCtrl::OnPaintDrawImages(wxPaintEvent
& event
)
332 int i
= 0, nState
= 0;
337 // Calls the default drawing code
338 wxControl::OnPaint(event
);
340 // Draw the flags, platform icons and emustate icons on top if there's games to show
341 if (m_ISOFiles
.size() != 0)
343 // Retrieve the topmost shown item and get drawing offsets
344 long top
= GetTopItem();
345 int flagOffset
= GetColumnWidth(0) + GetColumnWidth(1) + GetColumnWidth(2) + GetColumnWidth(3);
346 int stateOffset
= flagOffset
+ GetColumnWidth(4) + GetColumnWidth(5);
348 // Only redraw shown lines
349 for (i
= top
; i
< top
+ GetCountPerPage() + 2; i
++)
351 if (GetItemRect(i
, itemRect
))
353 int itemY
= itemRect
.GetTop();
354 const GameListItem
& rISOFile
= m_ISOFiles
[GetItemData(i
)];
356 m_imageListSmall
->Draw(m_PlatformImageIndex
[rISOFile
.GetPlatform()], dc
, itemRect
.GetX()+3, itemY
);
357 m_imageListSmall
->Draw(m_FlagImageIndex
[rISOFile
.GetCountry()], dc
, flagOffset
, itemY
);
359 ini
.Load((std::string(File::GetUserPath(D_GAMECONFIG_IDX
)) + (rISOFile
.GetUniqueID()) + ".ini").c_str());
360 ini
.Get("EmuState", "EmulationStateId", &nState
);
361 m_imageListSmall
->Draw(m_EmuStateImageIndex
[nState
], dc
, stateOffset
, itemY
);
368 void CGameListCtrl::InsertItemInReportView(long _Index
)
370 // When using wxListCtrl, there is no hope of per-column text colors.
371 // But for reference, here are the old colors that were used: (BGR)
376 wxCSConv
SJISConv(wxFontMapper::GetEncodingName(wxFONTENCODING_EUC_JP
));
378 wxCSConv
SJISConv(wxFontMapper::GetEncodingName(wxFONTENCODING_SHIFT_JIS
));
380 GameListItem
& rISOFile
= m_ISOFiles
[_Index
];
381 m_gamePath
.append(rISOFile
.GetFileName() + '\n');
383 // Insert a first row with the platform image, that will be used as the Index
385 long ItemIndex
= InsertItem(_Index
, wxEmptyString
, m_PlatformImageIndex
[rISOFile
.GetPlatform()]);
387 long ItemIndex
= InsertItem(_Index
, wxEmptyString
, -1);
390 if (rISOFile
.GetImage().IsOk())
391 ImageIndex
= m_imageListSmall
->Add(rISOFile
.GetImage());
393 // Set the game's banner in the second column
394 SetItemColumnImage(_Index
, COLUMN_BANNER
, ImageIndex
);
396 if (rISOFile
.GetPlatform() != GameListItem::WII_WAD
)
400 // We show the company string on Gamecube only
401 // On Wii we show the description instead as the company string is empty
402 if (rISOFile
.GetPlatform() == GameListItem::GAMECUBE_DISC
)
403 company
= rISOFile
.GetCompany().c_str();
405 switch (rISOFile
.GetCountry())
407 case DiscIO::IVolume::COUNTRY_TAIWAN
:
408 case DiscIO::IVolume::COUNTRY_JAPAN
:
410 wxString name
= wxString(rISOFile
.GetName(0).c_str(), SJISConv
);
411 m_gameList
.append(StringFromFormat("%s (J)\n", (const char*)name
.c_str()));
412 SetItem(_Index
, COLUMN_TITLE
, name
, -1);
413 SetItem(_Index
, COLUMN_NOTES
, wxString(company
.size() ? company
.c_str() : rISOFile
.GetDescription(0).c_str(), SJISConv
), -1);
416 case DiscIO::IVolume::COUNTRY_USA
:
417 m_gameList
.append(StringFromFormat("%s (U)\n", rISOFile
.GetName(0).c_str()));
418 SetItem(_Index
, COLUMN_TITLE
,
419 wxString::From8BitData(rISOFile
.GetName(0).c_str()), -1);
420 SetItem(_Index
, COLUMN_NOTES
,
421 wxString::From8BitData(company
.size() ? company
.c_str() : rISOFile
.GetDescription(0).c_str()), -1);
424 m_gameList
.append(StringFromFormat("%s (E)\n", rISOFile
.GetName((int)SConfig::GetInstance().m_InterfaceLanguage
).c_str()));
425 SetItem(_Index
, COLUMN_TITLE
,
426 wxString::From8BitData(rISOFile
.GetName((int)SConfig::GetInstance().m_InterfaceLanguage
).c_str()), -1);
427 SetItem(_Index
, COLUMN_NOTES
, wxString::From8BitData(
428 company
.size() ? company
.c_str() : rISOFile
.GetDescription((int)SConfig::GetInstance().m_InterfaceLanguage
).c_str()), -1);
432 else // It's a Wad file
434 m_gameList
.append(StringFromFormat("%s (WAD)\n", rISOFile
.GetName(0).c_str()));
435 SetItem(_Index
, COLUMN_TITLE
, wxString(rISOFile
.GetName(0).c_str(), SJISConv
), -1);
436 SetItem(_Index
, COLUMN_NOTES
, wxString(rISOFile
.GetDescription(0).c_str(), SJISConv
), -1);
440 // Load the INI file for columns that read from it
442 ini
.Load((std::string(File::GetUserPath(D_GAMECONFIG_IDX
)) + (rISOFile
.GetUniqueID()) + ".ini").c_str());
446 ini
.Get("EmuState", "EmulationStateId", &nState
);
449 SetItemColumnImage(_Index
, COLUMN_EMULATION_STATE
, m_EmuStateImageIndex
[nState
]);
452 SetItemColumnImage(_Index
, COLUMN_COUNTRY
, m_FlagImageIndex
[rISOFile
.GetCountry()]);
456 SetItem(_Index
, COLUMN_SIZE
, NiceSizeFormat(rISOFile
.GetFileSize()), -1);
459 SetBackgroundColor();
462 SetItemData(_Index
, ItemIndex
);
465 wxColour
blend50(const wxColour
& c1
, const wxColour
& c2
)
467 unsigned char r
,g
,b
,a
;
468 r
= c1
.Red()/2 + c2
.Red()/2;
469 g
= c1
.Green()/2 + c2
.Green()/2;
470 b
= c1
.Blue()/2 + c2
.Blue()/2;
471 a
= c1
.Alpha()/2 + c2
.Alpha()/2;
472 return a
<< 24 | b
<< 16 | g
<< 8 | r
;
475 void CGameListCtrl::SetBackgroundColor()
477 for(long i
= 0; i
< GetItemCount(); i
++)
479 wxColour color
= (i
& 1) ? blend50(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
)) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
);
480 CGameListCtrl::SetItemBackgroundColour(i
, color
);
484 void CGameListCtrl::ScanForISOs()
487 CFileSearch::XStringVector
Directories(SConfig::GetInstance().m_ISOFolder
);
489 if (SConfig::GetInstance().m_RecursiveISOFolder
)
491 for (u32 i
= 0; i
< Directories
.size(); i
++)
493 File::FSTEntry FST_Temp
;
494 File::ScanDirectoryTree(Directories
.at(i
).c_str(), FST_Temp
);
495 for (u32 j
= 0; j
< FST_Temp
.children
.size(); j
++)
497 if (FST_Temp
.children
.at(j
).isDirectory
)
499 bool duplicate
= false;
500 NormalizeDirSep(&(FST_Temp
.children
.at(j
).physicalName
));
501 for (u32 k
= 0; k
< Directories
.size(); k
++)
503 NormalizeDirSep(&Directories
.at(k
));
504 if (strcmp(Directories
.at(k
).c_str(), FST_Temp
.children
.at(j
).physicalName
.c_str()) == 0)
511 Directories
.push_back(FST_Temp
.children
.at(j
).physicalName
.c_str());
517 CFileSearch::XStringVector Extensions
;
519 if (SConfig::GetInstance().m_ListGC
)
520 Extensions
.push_back("*.gcm");
521 if (SConfig::GetInstance().m_ListWii
|| SConfig::GetInstance().m_ListGC
)
523 Extensions
.push_back("*.iso");
524 Extensions
.push_back("*.gcz");
526 if (SConfig::GetInstance().m_ListWad
)
527 Extensions
.push_back("*.wad");
529 CFileSearch
FileSearch(Extensions
, Directories
);
530 const CFileSearch::XStringVector
& rFilenames
= FileSearch
.GetFileNames();
532 if (rFilenames
.size() > 0)
534 wxProgressDialog
dialog(_T("Scanning for ISOs"),
536 (int)rFilenames
.size(), // range
539 // wxPD_AUTO_HIDE | -- try this as well
541 wxPD_ESTIMATED_TIME
|
542 wxPD_REMAINING_TIME
|
543 wxPD_SMOOTH
// - makes indeterminate mode bar on WinXP very small
545 dialog
.CenterOnParent();
547 for (u32 i
= 0; i
< rFilenames
.size(); i
++)
549 std::string FileName
;
550 SplitPath(rFilenames
[i
], NULL
, &FileName
, NULL
);
553 char tempstring
[128];
554 sprintf(tempstring
,"Scanning %s", FileName
.c_str());
555 msg
= wxString(tempstring
, *wxConvCurrent
);
557 // With wxWidgets 2.9.1, each Update() sleeps for several seconds
559 // Update with the progress (i) and the message (msg)
560 bool Cont
= dialog
.Update(i
, msg
);
566 GameListItem
ISOFile(rFilenames
[i
]);
567 if (ISOFile
.IsValid())
571 switch(ISOFile
.GetPlatform())
573 case GameListItem::WII_DISC
:
574 if (!SConfig::GetInstance().m_ListWii
)
577 case GameListItem::WII_WAD
:
578 if (!SConfig::GetInstance().m_ListWad
)
582 if (!SConfig::GetInstance().m_ListGC
)
587 switch(ISOFile
.GetCountry())
589 case DiscIO::IVolume::COUNTRY_TAIWAN
:
590 if (!SConfig::GetInstance().m_ListTaiwan
)
592 case DiscIO::IVolume::COUNTRY_KOREA
:
593 if (!SConfig::GetInstance().m_ListKorea
)
596 case DiscIO::IVolume::COUNTRY_JAPAN
:
597 if (!SConfig::GetInstance().m_ListJap
)
600 case DiscIO::IVolume::COUNTRY_USA
:
601 if (!SConfig::GetInstance().m_ListUsa
)
604 case DiscIO::IVolume::COUNTRY_FRANCE
:
605 if (!SConfig::GetInstance().m_ListFrance
)
608 case DiscIO::IVolume::COUNTRY_ITALY
:
609 if (!SConfig::GetInstance().m_ListItaly
)
613 if (!SConfig::GetInstance().m_ListPal
)
618 if (list
) m_ISOFiles
.push_back(ISOFile
);
623 if (SConfig::GetInstance().m_ListDrives
)
625 std::vector
<std::string
> drives
= cdio_get_devices();
626 GameListItem
* Drive
[24];
627 // Another silly Windows limitation of 24 drive letters
628 for (u32 i
= 0; i
< drives
.size() && i
< 24; i
++)
630 Drive
[i
] = new GameListItem(drives
[i
].c_str());
631 if (Drive
[i
]->IsValid()) m_ISOFiles
.push_back(*Drive
[i
]);
635 std::sort(m_ISOFiles
.begin(), m_ISOFiles
.end());
638 void CGameListCtrl::OnColBeginDrag(wxListEvent
& event
)
640 if (event
.GetColumn() != COLUMN_TITLE
&& event
.GetColumn() != COLUMN_NOTES
)
644 const GameListItem
*CGameListCtrl::GetISO(int index
) const
646 return &m_ISOFiles
[index
];
649 CGameListCtrl
*caller
;
650 int wxCALLBACK
wxListCompare(long item1
, long item2
, long sortData
)
652 //return 1 if item1 > item2
653 //return -1 if item1 < item2
655 const GameListItem
*iso1
= caller
->GetISO(item1
);
656 const GameListItem
*iso2
= caller
->GetISO(item2
);
663 sortData
= -sortData
;
669 switch (iso1
->GetCountry())
671 case DiscIO::IVolume::COUNTRY_JAPAN
:;
672 case DiscIO::IVolume::COUNTRY_USA
:indexOne
= 0; break;
673 default: indexOne
= (int)SConfig::GetInstance().m_InterfaceLanguage
;
676 switch (iso2
->GetCountry())
678 case DiscIO::IVolume::COUNTRY_JAPAN
:;
679 case DiscIO::IVolume::COUNTRY_USA
:indexOther
= 0; break;
680 default: indexOther
= (int)SConfig::GetInstance().m_InterfaceLanguage
;
685 case CGameListCtrl::COLUMN_TITLE
:
686 return strcasecmp(iso1
->GetName(indexOne
).c_str(),iso2
->GetName(indexOther
).c_str()) *t
;
687 case CGameListCtrl::COLUMN_NOTES
:
689 std::string cmp1
= (iso1
->GetPlatform() == GameListItem::GAMECUBE_DISC
) ? iso1
->GetCompany() : iso1
->GetDescription(indexOne
);
690 std::string cmp2
= (iso2
->GetPlatform() == GameListItem::GAMECUBE_DISC
) ? iso2
->GetCompany() : iso2
->GetDescription(indexOther
);
691 return strcasecmp(cmp1
.c_str(), cmp2
.c_str()) *t
;
693 case CGameListCtrl::COLUMN_COUNTRY
:
694 if(iso1
->GetCountry() > iso2
->GetCountry()) return 1 *t
;
695 if(iso1
->GetCountry() < iso2
->GetCountry()) return -1 *t
;
697 case CGameListCtrl::COLUMN_SIZE
:
698 if (iso1
->GetFileSize() > iso2
->GetFileSize()) return 1 *t
;
699 if (iso1
->GetFileSize() < iso2
->GetFileSize()) return -1 *t
;
701 case CGameListCtrl::COLUMN_PLATFORM
:
702 if(iso1
->GetPlatform() > iso2
->GetPlatform()) return 1 *t
;
703 if(iso1
->GetPlatform() < iso2
->GetPlatform()) return -1 *t
;
705 case CGameListCtrl::COLUMN_EMULATION_STATE
:
706 IniFile ini
; int nState1
= 0, nState2
= 0;
707 std::string GameIni1
= std::string(File::GetUserPath(D_GAMECONFIG_IDX
)) + iso1
->GetUniqueID() + ".ini";
708 std::string GameIni2
= std::string(File::GetUserPath(D_GAMECONFIG_IDX
)) + iso2
->GetUniqueID() + ".ini";
710 ini
.Load(GameIni1
.c_str());
711 ini
.Get("EmuState", "EmulationStateId", &nState1
);
712 ini
.Load(GameIni2
.c_str());
713 ini
.Get("EmuState", "EmulationStateId", &nState2
);
715 if(nState1
> nState2
) return 1 *t
;
716 if(nState1
< nState2
) return -1 *t
;
723 void CGameListCtrl::OnColumnClick(wxListEvent
& event
)
725 if(event
.GetColumn() != COLUMN_BANNER
)
727 int current_column
= event
.GetColumn();
729 if(last_column
== current_column
)
731 last_sort
= -last_sort
;
735 last_column
= current_column
;
736 last_sort
= current_column
;
740 SortItems(wxListCompare
, last_sort
);
743 SetBackgroundColor();
748 // This is used by keyboard gamelist search
749 void CGameListCtrl::OnKeyPress(wxListEvent
& event
)
751 static int lastKey
= 0, sLoop
= 0;
754 for (int i
= 0; i
< (int)m_ISOFiles
.size(); i
++)
756 // Easy way to get game string
759 bleh
.SetColumn(COLUMN_TITLE
);
760 bleh
.SetMask(wxLIST_MASK_TEXT
);
763 wxString text
= bleh
.GetText();
765 if (text
.MakeUpper().at(0) == event
.GetKeyCode())
767 if (lastKey
== event
.GetKeyCode() && Loop
< sLoop
)
770 if (i
+1 == (int)m_ISOFiles
.size())
774 else if (lastKey
!= event
.GetKeyCode())
777 lastKey
= event
.GetKeyCode();
781 SetItemState(i
, wxLIST_STATE_SELECTED
|wxLIST_STATE_FOCUSED
, wxLIST_STATE_SELECTED
|wxLIST_STATE_FOCUSED
);
786 // If we get past the last game in the list, we'll have to go back to the first one.
787 if (i
+1 == (int)m_ISOFiles
.size() && sLoop
> 0 && Loop
> 0)
794 // This shows a little tooltip with the current Game's emulation state
795 void CGameListCtrl::OnMouseMotion(wxMouseEvent
& event
)
797 int flags
; long subitem
= 0;
798 long item
= HitTest(event
.GetPosition(), flags
, &subitem
);
799 static int lastItem
= -1;
801 if (item
!= wxNOT_FOUND
)
803 if (subitem
== COLUMN_EMULATION_STATE
)
805 if (toolTip
|| lastItem
== item
|| this != FindFocus()) {
810 const GameListItem
& rISO
= m_ISOFiles
[GetItemData(item
)];
813 ini
.Load((std::string(File::GetUserPath(D_GAMECONFIG_IDX
)) + (rISO
.GetUniqueID()) + ".ini").c_str());
816 std::string emuState
[5] = {"Broken", "Intro", "In-Game", "Playable", "Perfect"}, issues
;
819 ini
.Get("EmuState", "EmulationStateId", &nState
);
820 ini
.Get("EmuState", "EmulationIssues", &issues
, "");
822 // Get item Coords then convert from wxWindow coord to Screen coord
824 this->GetItemRect(item
, Rect
);
825 int mx
= Rect
.GetWidth();
826 int my
= Rect
.GetY();
827 this->ClientToScreen(&mx
, &my
);
829 // Show a tooltip containing the EmuState and the state description
830 if (nState
> 0 && nState
< 6)
833 sprintf(temp
, "^ %s%s%s", emuState
[nState
-1].c_str(), issues
.size() > 0 ? " :\n" : "", issues
.c_str());
834 toolTip
= new wxEmuStateTip(this, wxString(temp
, *wxConvCurrent
), &toolTip
);
837 toolTip
= new wxEmuStateTip(this, wxT("Not Set"), &toolTip
);
839 toolTip
->SetBoundingRect(wxRect(mx
- GetColumnWidth(subitem
), my
, GetColumnWidth(subitem
), Rect
.GetHeight()));
840 toolTip
->SetPosition(wxPoint(mx
- GetColumnWidth(subitem
), my
- 5 + Rect
.GetHeight()));
849 void CGameListCtrl::OnLeftClick(wxMouseEvent
& event
)
851 // Focus the clicked item.
853 long item
= HitTest(event
.GetPosition(), flags
);
854 if ((item
!= wxNOT_FOUND
) && (GetSelectedItemCount() == 0) && (!event
.ControlDown()) && (!event
.ShiftDown()))
856 SetItemState(item
, wxLIST_STATE_SELECTED
, wxLIST_STATE_SELECTED
);
857 SetItemState(item
, wxLIST_STATE_FOCUSED
, wxLIST_STATE_FOCUSED
);
858 wxGetApp().GetCFrame()->UpdateGUI();
864 void CGameListCtrl::OnRightClick(wxMouseEvent
& event
)
866 // Focus the clicked item.
868 long item
= HitTest(event
.GetPosition(), flags
);
869 if (item
!= wxNOT_FOUND
)
871 if (GetItemState(item
, wxLIST_STATE_SELECTED
) != wxLIST_STATE_SELECTED
)
874 SetItemState(item
, wxLIST_STATE_SELECTED
, wxLIST_STATE_SELECTED
);
876 SetItemState(item
, wxLIST_STATE_FOCUSED
, wxLIST_STATE_FOCUSED
);
879 if (GetSelectedItemCount() == 1)
881 const GameListItem
*selected_iso
= GetSelectedISO();
884 wxMenu
* popupMenu
= new wxMenu
;
885 popupMenu
->Append(IDM_PROPERTIES
, _("&Properties"));
886 popupMenu
->AppendSeparator();
888 if (selected_iso
->GetPlatform() != GameListItem::GAMECUBE_DISC
)
890 popupMenu
->Append(IDM_OPENSAVEFOLDER
, _("Open Wii &save folder"));
891 popupMenu
->Append(IDM_EXPORTSAVE
, _("Export Wii save (Experimental)"));
893 popupMenu
->Append(IDM_OPENCONTAININGFOLDER
, _("Open &containing folder"));
894 popupMenu
->AppendCheckItem(IDM_SETDEFAULTGCM
, _("Set as &default ISO"));
896 // First we have to decide a starting value when we append it
897 if(selected_iso
->GetFileName() == SConfig::GetInstance().
898 m_LocalCoreStartupParameter
.m_strDefaultGCM
)
899 popupMenu
->FindItem(IDM_SETDEFAULTGCM
)->Check();
901 popupMenu
->AppendSeparator();
902 popupMenu
->Append(IDM_DELETEGCM
, _("&Delete ISO..."));
904 if (selected_iso
->GetPlatform() != GameListItem::WII_WAD
)
906 if (selected_iso
->IsCompressed())
907 popupMenu
->Append(IDM_COMPRESSGCM
, _("Decompress ISO..."));
909 popupMenu
->Append(IDM_COMPRESSGCM
, _("Compress ISO..."));
911 popupMenu
->Append(IDM_INSTALLWAD
, _("Install to Wii Menu"));
913 PopupMenu(popupMenu
);
916 else if (GetSelectedItemCount() > 1)
918 wxMenu
* popupMenu
= new wxMenu
;
919 popupMenu
->Append(IDM_DELETEGCM
, _("&Delete selected ISOs..."));
920 popupMenu
->AppendSeparator();
921 popupMenu
->Append(IDM_MULTICOMPRESSGCM
, _("Compress selected ISOs..."));
922 popupMenu
->Append(IDM_MULTIDECOMPRESSGCM
, _("Decompress selected ISOs..."));
923 PopupMenu(popupMenu
);
927 const GameListItem
* CGameListCtrl::GetSelectedISO()
929 if (m_ISOFiles
.size() == 0)
933 else if (GetSelectedItemCount() == 0)
939 long item
= GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
940 if (item
== wxNOT_FOUND
)
941 return new GameListItem("");
944 // Here is a little workaround for multiselections:
945 // when > 1 item is selected, return info on the first one
946 // and deselect it so the next time GetSelectedISO() is called,
947 // the next item's info is returned
948 if (GetSelectedItemCount() > 1)
949 SetItemState(item
, 0, wxLIST_STATE_SELECTED
);
951 return &m_ISOFiles
[GetItemData(item
)];
956 void CGameListCtrl::OnOpenContainingFolder(wxCommandEvent
& WXUNUSED (event
))
958 const GameListItem
*iso
= GetSelectedISO();
962 SplitPath(iso
->GetFileName(), &path
, 0, 0);
963 WxUtils::Explore(path
.c_str());
966 void CGameListCtrl::OnOpenSaveFolder(wxCommandEvent
& WXUNUSED (event
))
968 const GameListItem
*iso
= GetSelectedISO();
971 if (iso
->GetWiiFSPath() != "NULL")
972 WxUtils::Explore(iso
->GetWiiFSPath().c_str());
975 void CGameListCtrl::OnExportSave(wxCommandEvent
& WXUNUSED (event
))
977 const GameListItem
*iso
= GetSelectedISO();
981 DiscIO::IVolume
*Iso
= DiscIO::CreateVolumeFromFilename(iso
->GetFileName());
984 if (Iso
->GetTitleID((u8
*)&title
))
986 title
= Common::swap64(title
);
987 CWiiSaveCrypted
* exportSave
= new CWiiSaveCrypted("", title
);
994 // =======================================================
995 // Save this file as the default file
997 void CGameListCtrl::OnSetDefaultGCM(wxCommandEvent
& event
)
999 const GameListItem
*iso
= GetSelectedISO();
1002 if (event
.IsChecked()) // Write the new default value and save it the ini file
1004 SConfig::GetInstance().m_LocalCoreStartupParameter
.m_strDefaultGCM
= iso
->GetFileName();
1005 SConfig::GetInstance().SaveSettings();
1007 else // Othwerise blank the value and save it
1009 SConfig::GetInstance().m_LocalCoreStartupParameter
.m_strDefaultGCM
= "";
1010 SConfig::GetInstance().SaveSettings();
1016 void CGameListCtrl::OnDeleteGCM(wxCommandEvent
& WXUNUSED (event
))
1018 if (GetSelectedItemCount() == 1)
1020 const GameListItem
*iso
= GetSelectedISO();
1023 if (wxMessageBox(_("Are you sure you want to delete this file?\nIt will be gone forever!"),
1024 wxMessageBoxCaptionStr
, wxYES_NO
| wxICON_EXCLAMATION
) == wxYES
)
1026 File::Delete(iso
->GetFileName().c_str());
1032 if (wxMessageBox(_("Are you sure you want to delete these files?\nThey will be gone forever!"),
1033 wxMessageBoxCaptionStr
, wxYES_NO
| wxICON_EXCLAMATION
) == wxYES
)
1035 int selected
= GetSelectedItemCount();
1037 for (int i
= 0; i
< selected
; i
++)
1039 const GameListItem
*iso
= GetSelectedISO();
1040 File::Delete(iso
->GetFileName().c_str());
1047 void CGameListCtrl::OnProperties(wxCommandEvent
& WXUNUSED (event
))
1049 const GameListItem
*iso
= GetSelectedISO();
1052 CISOProperties
ISOProperties(iso
->GetFileName(), this);
1053 ISOProperties
.Center();
1054 if(ISOProperties
.ShowModal() == wxID_OK
)
1058 void CGameListCtrl::OnInstallWAD(wxCommandEvent
& WXUNUSED (event
))
1060 const GameListItem
*iso
= GetSelectedISO();
1064 wxProgressDialog
dialog(_T("Installing WAD to Wii Menu..."),
1069 // wxPD_AUTO_HIDE | -- try this as well
1071 wxPD_ESTIMATED_TIME
|
1072 wxPD_REMAINING_TIME
|
1073 wxPD_SMOOTH
// - makes indeterminate mode bar on WinXP very small
1076 dialog
.CenterOnParent();
1078 CBoot::Install_WiiWAD(iso
->GetFileName().c_str());
1081 void CGameListCtrl::MultiCompressCB(const char* text
, float percent
, void* arg
)
1083 percent
= (((float)m_currentItem
) + percent
) / (float)m_numberItem
;
1084 wxString
textString(StringFromFormat("%s (%i/%i) - %s", m_currentFilename
.c_str(), (int)m_currentItem
+1, (int)m_numberItem
, text
).c_str(), *wxConvCurrent
);
1086 ((wxProgressDialog
*)arg
)->Update((int)(percent
*1000), textString
);
1089 void CGameListCtrl::OnMultiCompressGCM(wxCommandEvent
& /*event*/)
1091 CompressSelection(true);
1094 void CGameListCtrl::OnMultiDecompressGCM(wxCommandEvent
& /*event*/)
1096 CompressSelection(false);
1099 void CGameListCtrl::CompressSelection(bool _compress
)
1102 wxGetHomeDir(&dirHome
);
1104 wxDirDialog
browseDialog(this, _("Browse for output directory"), dirHome
, wxDD_DEFAULT_STYLE
| wxDD_DIR_MUST_EXIST
);
1105 if (browseDialog
.ShowModal() != wxID_OK
)
1108 wxProgressDialog
progressDialog(_compress
? _("Compressing ISO") : _("Decompressing ISO"),
1113 // wxPD_AUTO_HIDE | -- try this as well
1115 wxPD_ESTIMATED_TIME
|
1116 wxPD_REMAINING_TIME
|
1117 wxPD_SMOOTH
// - makes indeterminate mode bar on WinXP very small
1120 progressDialog
.SetSize(wxSize(600, 180));
1121 progressDialog
.CenterOnParent();
1124 m_numberItem
= GetSelectedItemCount();
1125 for (u32 i
=0; i
<m_numberItem
; i
++)
1127 const GameListItem
*iso
= GetSelectedISO();
1129 if (!iso
->IsCompressed() && _compress
)
1131 std::string FileName
;
1132 SplitPath(iso
->GetFileName(), NULL
, &FileName
, NULL
);
1133 m_currentFilename
= FileName
;
1134 FileName
.append(".gcz");
1136 std::string OutputFileName
;
1137 BuildCompleteFilename(OutputFileName
, (const char *)browseDialog
.GetPath().mb_str(wxConvUTF8
), FileName
);
1139 DiscIO::CompressFileToBlob(iso
->GetFileName().c_str(), OutputFileName
.c_str(), (iso
->GetPlatform() == GameListItem::WII_DISC
) ? 1 : 0, 16384, &MultiCompressCB
, &progressDialog
);
1141 else if (iso
->IsCompressed() && !_compress
)
1143 std::string FileName
;
1144 SplitPath(iso
->GetFileName(), NULL
, &FileName
, NULL
);
1145 m_currentFilename
= FileName
;
1146 if (iso
->GetPlatform() == GameListItem::WII_DISC
)
1148 FileName
.append(".iso");
1152 FileName
.append(".gcm");
1155 std::string OutputFileName
;
1156 BuildCompleteFilename(OutputFileName
, (const char *)browseDialog
.GetPath().mb_str(wxConvUTF8
), FileName
);
1158 DiscIO::DecompressBlobToFile(iso
->GetFileName().c_str(), OutputFileName
.c_str(), &MultiCompressCB
, &progressDialog
);
1165 void CGameListCtrl::CompressCB(const char* text
, float percent
, void* arg
)
1167 ((wxProgressDialog
*)arg
)->Update((int)(percent
*1000), wxString(text
, *wxConvCurrent
));
1170 void CGameListCtrl::OnCompressGCM(wxCommandEvent
& WXUNUSED (event
))
1172 const GameListItem
*iso
= GetSelectedISO();
1178 std::string FileName
;
1179 SplitPath(iso
->GetFileName(), NULL
, &FileName
, NULL
);
1181 if (iso
->IsCompressed())
1183 if (iso
->GetPlatform() == GameListItem::WII_DISC
)
1191 path
= wxFileSelector(
1192 _T("Save decompressed ISO"),
1193 wxEmptyString
, wxString(FileName
.c_str(), *wxConvCurrent
), wxEmptyString
,
1196 _T("All GC/Wii ISO files (%s)|%s|All files (%s)|%s"),
1197 (char *)Ext
.After('.').char_str(wxConvUTF8
),
1198 (char *)Ext
.char_str(wxConvUTF8
),
1199 wxFileSelectorDefaultWildcardStr
,
1200 wxFileSelectorDefaultWildcardStr
1212 path
= wxFileSelector(
1213 _T("Save compressed ISO"),
1214 wxEmptyString
, wxString(FileName
.c_str(), *wxConvCurrent
), wxEmptyString
,
1217 _T("All compressed GC/Wii ISO files (gcz)|*.gcz|All files (%s)|%s"),
1218 wxFileSelectorDefaultWildcardStr
,
1219 wxFileSelectorDefaultWildcardStr
1230 wxProgressDialog
dialog(iso
->IsCompressed() ? _T("Decompressing ISO") : _T("Compressing ISO"),
1235 // wxPD_AUTO_HIDE | -- try this as well
1237 wxPD_ESTIMATED_TIME
|
1238 wxPD_REMAINING_TIME
|
1239 wxPD_SMOOTH
// - makes indeterminate mode bar on WinXP very small
1242 dialog
.SetSize(wxSize(280, 180));
1243 dialog
.CenterOnParent();
1245 if (iso
->IsCompressed())
1246 DiscIO::DecompressBlobToFile(iso
->GetFileName().c_str(), path
.char_str(), &CompressCB
, &dialog
);
1248 DiscIO::CompressFileToBlob(iso
->GetFileName().c_str(), path
.char_str(), (iso
->GetPlatform() == GameListItem::WII_DISC
) ? 1 : 0, 16384, &CompressCB
, &dialog
);
1253 void CGameListCtrl::OnSize(wxSizeEvent
& event
)
1255 if (lastpos
== event
.GetSize()) return;
1256 lastpos
= event
.GetSize();
1257 AutomaticColumnWidth();
1262 void CGameListCtrl::AutomaticColumnWidth()
1264 wxRect
rc(GetParent()->GetRect());
1266 if (GetColumnCount() == 1)
1268 SetColumnWidth(0, rc
.GetWidth());
1270 else if (GetColumnCount() > 4)
1272 int resizable
= rc
.GetWidth() - (
1273 GetColumnWidth(COLUMN_BANNER
)
1274 + GetColumnWidth(COLUMN_COUNTRY
)
1275 + GetColumnWidth(COLUMN_SIZE
)
1276 + GetColumnWidth(COLUMN_EMULATION_STATE
)
1277 + GetColumnWidth(COLUMN_PLATFORM
)
1278 + 5); // some pad to keep the horizontal scrollbar away :)
1280 // We hide the Company column if the window is too small
1281 if (0.5*resizable
> 200)
1283 SetColumnWidth(COLUMN_TITLE
, 0.5*resizable
);
1284 SetColumnWidth(COLUMN_NOTES
, 0.5*resizable
);
1288 SetColumnWidth(COLUMN_TITLE
, resizable
);
1289 SetColumnWidth(COLUMN_NOTES
, 0);
1294 void CGameListCtrl::UnselectAll()
1296 for (int i
=0; i
<GetItemCount(); i
++)
1298 SetItemState(i
, 0, wxLIST_STATE_SELECTED
);