mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Add Wii DVD integrity checking to Dolphin
This allows users to easily check whether their Wii dump is corrupted or not using the Dolphin properties window. Right click on a game, Properties, Filesystem tab, then right click on the game partition and select "Check partition integrity". This may have some false negatives due to the unused clusters heuristic (see the comment in VolumeWiiCrypted.cpp). False positives are unlikely.
This commit is contained in:
parent
6254edcfbc
commit
77f47866df
@ -38,9 +38,11 @@ public:
|
||||
virtual std::string GetUniqueID() const = 0;
|
||||
virtual std::string GetMakerID() const = 0;
|
||||
virtual std::string GetName() const = 0;
|
||||
virtual bool GetWName(std::vector<std::wstring>& _rwNames) const {return false;};
|
||||
virtual bool GetWName(std::vector<std::wstring>& _rwNames) const { return false; }
|
||||
virtual u32 GetFSTSize() const = 0;
|
||||
virtual std::string GetApploaderDate() const = 0;
|
||||
virtual bool SupportsIntegrityCheck() const { return false; }
|
||||
virtual bool CheckIntegrity() const { return false; }
|
||||
|
||||
enum ECountry
|
||||
{
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "VolumeWiiCrypted.h"
|
||||
#include "StringUtil.h"
|
||||
#include "Crypto/sha1.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
@ -232,4 +233,68 @@ u64 CVolumeWiiCrypted::GetSize() const
|
||||
}
|
||||
}
|
||||
|
||||
bool CVolumeWiiCrypted::CheckIntegrity() const
|
||||
{
|
||||
// Get partition data size
|
||||
u32 partSizeDiv4;
|
||||
RAWRead(m_VolumeOffset + 0x2BC, 4, (u8*)&partSizeDiv4);
|
||||
u64 partDataSize = (u64)Common::swap32(partSizeDiv4) * 4;
|
||||
|
||||
u32 nClusters = (u32)(partDataSize / 0x8000);
|
||||
for (u32 clusterID = 0; clusterID < nClusters; ++clusterID)
|
||||
{
|
||||
u64 clusterOff = m_VolumeOffset + dataOffset + (u64)clusterID * 0x8000;
|
||||
|
||||
// Read and decrypt the cluster metadata
|
||||
u8 clusterMDCrypted[0x400];
|
||||
u8 clusterMD[0x400];
|
||||
u8 IV[16] = { 0 };
|
||||
if (!m_pReader->Read(clusterOff, 0x400, clusterMDCrypted))
|
||||
{
|
||||
NOTICE_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read metadata", clusterID);
|
||||
return false;
|
||||
}
|
||||
AES_cbc_encrypt(clusterMDCrypted, clusterMD, 0x400, &m_AES_KEY, IV, AES_DECRYPT);
|
||||
|
||||
// Some clusters have invalid data and metadata because they aren't
|
||||
// meant to be read by the game (for example, holes between files). To
|
||||
// try to avoid reporting errors because of these clusters, we check
|
||||
// the 0x00 paddings in the metadata.
|
||||
//
|
||||
// This may cause some false negatives though: some bad clusters may be
|
||||
// skipped because they are *too* bad and are not even recognized as
|
||||
// valid clusters. To be improved.
|
||||
bool meaningless = false;
|
||||
for (u32 idx = 0x26C; idx < 0x280; ++idx)
|
||||
if (clusterMD[idx] != 0)
|
||||
meaningless = true;
|
||||
|
||||
if (meaningless)
|
||||
continue;
|
||||
|
||||
u8 clusterData[0x7C00];
|
||||
if (!Read((u64)clusterID * 0x7C00, 0x7C00, clusterData))
|
||||
{
|
||||
NOTICE_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read data", clusterID);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (u32 hashID = 0; hashID < 31; ++hashID)
|
||||
{
|
||||
u8 hash[20];
|
||||
|
||||
sha1(clusterData + hashID * 0x400, 0x400, hash);
|
||||
|
||||
// Note that we do not use strncmp here
|
||||
if (memcmp(hash, clusterMD + hashID * 20, 20))
|
||||
{
|
||||
NOTICE_LOG(DISCIO, "Integrity Check: fail at cluster %d: hash %d is invalid", clusterID, hashID);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -43,6 +43,9 @@ public:
|
||||
ECountry GetCountry() const;
|
||||
u64 GetSize() const;
|
||||
|
||||
bool SupportsIntegrityCheck() const { return true; }
|
||||
bool CheckIntegrity() const;
|
||||
|
||||
private:
|
||||
IBlobReader* m_pReader;
|
||||
|
||||
|
@ -75,6 +75,7 @@ BEGIN_EVENT_TABLE(CISOProperties, wxDialog)
|
||||
EVT_MENU(IDM_EXTRACTALL, CISOProperties::OnExtractDir)
|
||||
EVT_MENU(IDM_EXTRACTAPPLOADER, CISOProperties::OnExtractDataFromHeader)
|
||||
EVT_MENU(IDM_EXTRACTDOL, CISOProperties::OnExtractDataFromHeader)
|
||||
EVT_MENU(IDM_CHECKINTEGRITY, CISOProperties::CheckPartitionIntegrity)
|
||||
EVT_CHOICE(ID_LANG, CISOProperties::OnChangeBannerLang)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
@ -635,6 +636,13 @@ void CISOProperties::OnRightClickOnTree(wxTreeEvent& event)
|
||||
popupMenu->Append(IDM_EXTRACTAPPLOADER, _("Extract Apploader..."));
|
||||
popupMenu->Append(IDM_EXTRACTDOL, _("Extract DOL..."));
|
||||
|
||||
if (m_Treectrl->GetItemImage(m_Treectrl->GetSelection()) == 0
|
||||
&& m_Treectrl->GetFirstVisibleItem() != m_Treectrl->GetSelection())
|
||||
{
|
||||
popupMenu->AppendSeparator();
|
||||
popupMenu->Append(IDM_CHECKINTEGRITY, _("Check Partition Integrity"));
|
||||
}
|
||||
|
||||
PopupMenu(popupMenu);
|
||||
|
||||
event.Skip();
|
||||
@ -840,6 +848,71 @@ void CISOProperties::OnExtractDataFromHeader(wxCommandEvent& event)
|
||||
PanicAlertT("Failed to extract to %s!", (const char *)Path.mb_str());
|
||||
}
|
||||
|
||||
class IntegrityCheckThread : public wxThread
|
||||
{
|
||||
public:
|
||||
IntegrityCheckThread(const WiiPartition& Partition)
|
||||
: wxThread(wxTHREAD_JOINABLE), m_Partition(Partition)
|
||||
{
|
||||
Create();
|
||||
}
|
||||
|
||||
virtual ExitCode Entry()
|
||||
{
|
||||
return (ExitCode)m_Partition.Partition->CheckIntegrity();
|
||||
}
|
||||
|
||||
private:
|
||||
const WiiPartition& m_Partition;
|
||||
};
|
||||
|
||||
void CISOProperties::CheckPartitionIntegrity(wxCommandEvent& event)
|
||||
{
|
||||
// Normally we can't enter this function if we aren't analyzing a Wii disc
|
||||
// anyway, but let's still check to be sure.
|
||||
if (!DiscIO::IsVolumeWiiDisc(OpenISO))
|
||||
return;
|
||||
|
||||
wxString PartitionName = m_Treectrl->GetItemText(m_Treectrl->GetSelection());
|
||||
if (!PartitionName)
|
||||
return;
|
||||
|
||||
// Get the partition number from the item text ("Partition N")
|
||||
int PartitionNum = wxAtoi(PartitionName.SubString(10, 11));
|
||||
const WiiPartition& Partition = WiiDisc[PartitionNum];
|
||||
|
||||
wxProgressDialog* dialog = new wxProgressDialog(
|
||||
_("Checking integrity..."), _("Working..."), 1000, this,
|
||||
wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH
|
||||
);
|
||||
|
||||
IntegrityCheckThread thread(Partition);
|
||||
thread.Run();
|
||||
|
||||
while (thread.IsAlive())
|
||||
{
|
||||
dialog->Pulse();
|
||||
wxThread::Sleep(50);
|
||||
}
|
||||
|
||||
delete dialog;
|
||||
|
||||
if (!thread.Wait())
|
||||
{
|
||||
wxMessageBox(
|
||||
wxString::Format(_("Integrity check for partition %d failed. "
|
||||
"Your dump is most likely corrupted or has been "
|
||||
"patched incorrectly."), PartitionNum),
|
||||
_("Integrity Check Error"), wxOK | wxICON_ERROR, this
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(_("Integrity check completed. No errors have been found."),
|
||||
_("Integrity check completed"), wxOK | wxICON_INFORMATION, this);
|
||||
}
|
||||
}
|
||||
|
||||
void CISOProperties::SetRefresh(wxCommandEvent& event)
|
||||
{
|
||||
bRefreshList = true;
|
||||
|
@ -170,6 +170,7 @@ private:
|
||||
IDM_EXTRACTFILE,
|
||||
IDM_EXTRACTAPPLOADER,
|
||||
IDM_EXTRACTDOL,
|
||||
IDM_CHECKINTEGRITY,
|
||||
IDM_BNRSAVEAS
|
||||
};
|
||||
|
||||
@ -186,6 +187,7 @@ private:
|
||||
void OnExtractFile(wxCommandEvent& event);
|
||||
void OnExtractDir(wxCommandEvent& event);
|
||||
void OnExtractDataFromHeader(wxCommandEvent& event);
|
||||
void CheckPartitionIntegrity(wxCommandEvent& event);
|
||||
void SetRefresh(wxCommandEvent& event);
|
||||
void OnChangeBannerLang(wxCommandEvent& event);
|
||||
void PHackButtonClicked(wxCommandEvent& event);
|
||||
|
Loading…
Reference in New Issue
Block a user