Merge pull request #4521 from JosJuice/tgc
Add TGC disc image compatibility
This commit is contained in:
commit
ca91b6df52
|
@ -41,7 +41,8 @@ public class FileListItem implements Comparable<FileListItem>
|
|||
String fileExtension = mPath.substring(extensionStart);
|
||||
|
||||
// The extensions we care about.
|
||||
Set<String> allowedExtensions = new HashSet<String>(Arrays.asList(".ciso", ".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".wad", ".wbfs"));
|
||||
Set<String> allowedExtensions = new HashSet<String>(Arrays.asList(
|
||||
".ciso", ".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".tgc", ".wad", ".wbfs"));
|
||||
|
||||
// Check that the file has an extension we care about before trying to read out of it.
|
||||
if (allowedExtensions.contains(fileExtension.toLowerCase()))
|
||||
|
|
|
@ -150,7 +150,8 @@ public final class GameDatabase extends SQLiteOpenHelper
|
|||
null,
|
||||
null); // Order of folders is irrelevant.
|
||||
|
||||
Set<String> allowedExtensions = new HashSet<String>(Arrays.asList(".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".wad", ".wbfs"));
|
||||
Set<String> allowedExtensions = new HashSet<String>(Arrays.asList(
|
||||
".ciso", ".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".tgc", ".wad", ".wbfs"));
|
||||
|
||||
// Possibly overly defensive, but ensures that moveToNext() does not skip a row.
|
||||
folderCursor.moveToPosition(-1);
|
||||
|
|
|
@ -844,8 +844,9 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2)
|
|||
std::string Extension;
|
||||
SplitPath(m_strFilename, nullptr, nullptr, &Extension);
|
||||
if (!strcasecmp(Extension.c_str(), ".gcm") || !strcasecmp(Extension.c_str(), ".iso") ||
|
||||
!strcasecmp(Extension.c_str(), ".wbfs") || !strcasecmp(Extension.c_str(), ".ciso") ||
|
||||
!strcasecmp(Extension.c_str(), ".gcz") || bootDrive)
|
||||
!strcasecmp(Extension.c_str(), ".tgc") || !strcasecmp(Extension.c_str(), ".wbfs") ||
|
||||
!strcasecmp(Extension.c_str(), ".ciso") || !strcasecmp(Extension.c_str(), ".gcz") ||
|
||||
bootDrive)
|
||||
{
|
||||
m_BootType = BOOT_ISO;
|
||||
std::unique_ptr<DiscIO::IVolume> pVolume(DiscIO::CreateVolumeFromFilename(m_strFilename));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "DiscIO/CompressedBlob.h"
|
||||
#include "DiscIO/DriveBlob.h"
|
||||
#include "DiscIO/FileBlob.h"
|
||||
#include "DiscIO/TGCBlob.h"
|
||||
#include "DiscIO/WbfsBlob.h"
|
||||
|
||||
namespace DiscIO
|
||||
|
@ -188,6 +189,9 @@ std::unique_ptr<IBlobReader> CreateBlobReader(const std::string& filename)
|
|||
if (IsCISOBlob(filename))
|
||||
return CISOFileReader::Create(filename);
|
||||
|
||||
if (IsTGCBlob(filename))
|
||||
return TGCFileReader::Create(filename);
|
||||
|
||||
// Still here? Assume plain file - since we know it exists due to the File::Exists check above.
|
||||
return PlainFileReader::Create(filename);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ enum class BlobType
|
|||
DIRECTORY,
|
||||
GCZ,
|
||||
CISO,
|
||||
WBFS
|
||||
WBFS,
|
||||
TGC
|
||||
};
|
||||
|
||||
class IBlobReader
|
||||
|
|
|
@ -10,6 +10,7 @@ set(SRCS Blob.cpp
|
|||
FileSystemGCWii.cpp
|
||||
Filesystem.cpp
|
||||
NANDContentLoader.cpp
|
||||
TGCBlob.cpp
|
||||
Volume.cpp
|
||||
VolumeCreator.cpp
|
||||
VolumeDirectory.cpp
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
<ClCompile Include="Filesystem.cpp" />
|
||||
<ClCompile Include="FileSystemGCWii.cpp" />
|
||||
<ClCompile Include="NANDContentLoader.cpp" />
|
||||
<ClCompile Include="TGCBlob.cpp" />
|
||||
<ClCompile Include="Volume.cpp" />
|
||||
<ClCompile Include="VolumeCreator.cpp" />
|
||||
<ClCompile Include="VolumeDirectory.cpp" />
|
||||
|
@ -67,6 +68,7 @@
|
|||
<ClInclude Include="Filesystem.h" />
|
||||
<ClInclude Include="FileSystemGCWii.h" />
|
||||
<ClInclude Include="NANDContentLoader.h" />
|
||||
<ClInclude Include="TGCBlob.h" />
|
||||
<ClInclude Include="Volume.h" />
|
||||
<ClInclude Include="VolumeCreator.h" />
|
||||
<ClInclude Include="VolumeDirectory.h" />
|
||||
|
|
|
@ -75,6 +75,9 @@
|
|||
<ClCompile Include="Volume.cpp">
|
||||
<Filter>Volume</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TGCBlob.cpp">
|
||||
<Filter>Volume\Blob</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DiscScrubber.h">
|
||||
|
@ -134,6 +137,9 @@
|
|||
<ClInclude Include="Enums.h">
|
||||
<Filter>Volume</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="TGCBlob.h">
|
||||
<Filter>Volume\Blob</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "DiscIO/TGCBlob.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
struct Interval
|
||||
{
|
||||
T start;
|
||||
T length;
|
||||
|
||||
T End() const { return start + length; }
|
||||
bool IsEmpty() const { return length == 0; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void SplitInterval(T split_point, Interval<T> interval, Interval<T>* out_1, Interval<T>* out_2)
|
||||
{
|
||||
if (interval.start < split_point)
|
||||
*out_1 = {interval.start, std::min(interval.length, split_point - interval.start)};
|
||||
else
|
||||
*out_1 = {0, 0};
|
||||
|
||||
if (interval.End() > split_point)
|
||||
*out_2 = {std::max(interval.start, split_point),
|
||||
std::min(interval.length, interval.End() - split_point)};
|
||||
else
|
||||
*out_2 = {0, 0};
|
||||
}
|
||||
|
||||
u32 SubtractBE32(u32 minuend_be, u32 subtrahend_le)
|
||||
{
|
||||
return Common::swap32(Common::swap32(minuend_be) - subtrahend_le);
|
||||
}
|
||||
|
||||
void Replace8(u64 offset, u64 nbytes, u8* out_ptr, u64 replace_offset, u8 replace_value)
|
||||
{
|
||||
if (offset <= replace_offset && offset + nbytes > replace_offset)
|
||||
out_ptr[replace_offset - offset] = replace_value;
|
||||
}
|
||||
|
||||
void Replace32(u64 offset, u64 nbytes, u8* out_ptr, u64 replace_offset, u32 replace_value)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(u32); ++i)
|
||||
Replace8(offset, nbytes, out_ptr, replace_offset + i, reinterpret_cast<u8*>(&replace_value)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
bool IsTGCBlob(const std::string& path)
|
||||
{
|
||||
File::IOFile file(path, "rb");
|
||||
|
||||
TGCHeader header;
|
||||
return (file.ReadArray(&header, 1) && header.magic == Common::swap32(0xAE0F38A2));
|
||||
}
|
||||
|
||||
std::unique_ptr<TGCFileReader> TGCFileReader::Create(const std::string& path)
|
||||
{
|
||||
if (IsTGCBlob(path))
|
||||
{
|
||||
File::IOFile file(path, "rb");
|
||||
return std::unique_ptr<TGCFileReader>(new TGCFileReader(std::move(file)));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TGCFileReader::TGCFileReader(File::IOFile&& file) : m_file(std::move(file))
|
||||
{
|
||||
m_file.ReadArray(&m_header, 1);
|
||||
u32 header_size = Common::swap32(m_header.header_size);
|
||||
m_size = m_file.GetSize();
|
||||
m_file_offset = Common::swap32(m_header.unknown_important_2) -
|
||||
Common::swap32(m_header.unknown_important_1) + header_size;
|
||||
}
|
||||
|
||||
u64 TGCFileReader::GetDataSize() const
|
||||
{
|
||||
return m_size + Common::swap32(m_header.unknown_important_2) -
|
||||
Common::swap32(m_header.unknown_important_1);
|
||||
}
|
||||
|
||||
bool TGCFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
{
|
||||
Interval<u64> first_part;
|
||||
Interval<u64> empty_part;
|
||||
Interval<u64> last_part;
|
||||
|
||||
u32 header_size = Common::swap32(m_header.header_size);
|
||||
SplitInterval(m_file_offset, Interval<u64>{offset, nbytes}, &first_part, &last_part);
|
||||
SplitInterval(m_size - header_size, first_part, &first_part, &empty_part);
|
||||
|
||||
// Offsets before m_file_offset are read as usual
|
||||
if (!first_part.IsEmpty())
|
||||
{
|
||||
if (!InternalRead(first_part.start, first_part.length, out_ptr + (first_part.start - offset)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// If any offset before m_file_offset isn't actually in the file,
|
||||
// treat it as 0x00 bytes (so e.g. MD5 calculation of the whole disc won't fail)
|
||||
if (!empty_part.IsEmpty())
|
||||
std::fill_n(out_ptr + (empty_part.start - offset), empty_part.length, 0);
|
||||
|
||||
// Offsets after m_file_offset are read as if they are (offset - m_file_offset)
|
||||
if (!last_part.IsEmpty())
|
||||
{
|
||||
if (!InternalRead(last_part.start - m_file_offset, last_part.length,
|
||||
out_ptr + (last_part.start - offset)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TGCFileReader::InternalRead(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
{
|
||||
u32 header_size = Common::swap32(m_header.header_size);
|
||||
|
||||
if (m_file.Seek(offset + header_size, SEEK_SET) && m_file.ReadBytes(out_ptr, nbytes))
|
||||
{
|
||||
Replace32(offset, nbytes, out_ptr, 0x420, SubtractBE32(m_header.dol_offset, header_size));
|
||||
Replace32(offset, nbytes, out_ptr, 0x424, SubtractBE32(m_header.fst_offset, header_size));
|
||||
return true;
|
||||
}
|
||||
|
||||
m_file.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace DiscIO
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
bool IsTGCBlob(const std::string& path);
|
||||
|
||||
struct TGCHeader
|
||||
{
|
||||
u32 magic;
|
||||
u32 unknown_1;
|
||||
u32 header_size;
|
||||
u32 unknown_2;
|
||||
|
||||
u32 fst_offset;
|
||||
u32 fst_size;
|
||||
u32 fst_max_size;
|
||||
u32 dol_offset;
|
||||
|
||||
u32 dol_size;
|
||||
u32 unknown_important_1;
|
||||
u32 unknown_3;
|
||||
u32 unknown_4;
|
||||
|
||||
u32 unknown_5;
|
||||
u32 unknown_important_2;
|
||||
};
|
||||
|
||||
class TGCFileReader final : public IBlobReader
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<TGCFileReader> Create(const std::string& filename);
|
||||
|
||||
BlobType GetBlobType() const override { return BlobType::TGC; }
|
||||
u64 GetDataSize() const override;
|
||||
u64 GetRawSize() const override { return m_size; }
|
||||
bool Read(u64 offset, u64 nbytes, u8* out_ptr) override;
|
||||
|
||||
private:
|
||||
TGCFileReader(File::IOFile&& file);
|
||||
|
||||
bool InternalRead(u64 offset, u64 nbytes, u8* out_ptr);
|
||||
|
||||
File::IOFile m_file;
|
||||
u64 m_size;
|
||||
|
||||
u64 m_file_offset;
|
||||
|
||||
// Stored as big endian in memory, regardless of the host endianness
|
||||
TGCHeader m_header = {};
|
||||
};
|
||||
|
||||
} // namespace DiscIO
|
|
@ -54,7 +54,7 @@ void PathDialog::BrowseDefaultGame()
|
|||
{
|
||||
QString file = QFileDialog::getOpenFileName(
|
||||
this, tr("Select a Game"), QDir::currentPath(),
|
||||
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.wbfs *.ciso *.gcz *.wad);;"
|
||||
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wad);;"
|
||||
"All Files (*)"));
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
#include "DolphinQt2/GameList/GameTracker.h"
|
||||
#include "DolphinQt2/Settings.h"
|
||||
|
||||
static const QStringList game_filters{QStringLiteral("*.gcm"), QStringLiteral("*.iso"),
|
||||
QStringLiteral("*.ciso"), QStringLiteral("*.gcz"),
|
||||
QStringLiteral("*.wbfs"), QStringLiteral("*.wad"),
|
||||
QStringLiteral("*.elf"), QStringLiteral("*.dol")};
|
||||
static const QStringList game_filters{
|
||||
QStringLiteral("*.gcm"), QStringLiteral("*.iso"), QStringLiteral("*.tgc"),
|
||||
QStringLiteral("*.ciso"), QStringLiteral("*.gcz"), QStringLiteral("*.wbfs"),
|
||||
QStringLiteral("*.wad"), QStringLiteral("*.elf"), QStringLiteral("*.dol")};
|
||||
|
||||
GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<string>gcm</string>
|
||||
<string>gcz</string>
|
||||
<string>iso</string>
|
||||
<string>tgc</string>
|
||||
<string>wad</string>
|
||||
<string>wbfs</string>
|
||||
</array>
|
||||
|
|
|
@ -137,7 +137,7 @@ void MainWindow::Open()
|
|||
{
|
||||
QString file = QFileDialog::getOpenFileName(
|
||||
this, tr("Select a File"), QDir::currentPath(),
|
||||
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.wbfs *.ciso *.gcz *.wad);;"
|
||||
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wad);;"
|
||||
"All Files (*)"));
|
||||
if (!file.isEmpty())
|
||||
StartGame(file);
|
||||
|
|
|
@ -41,8 +41,8 @@ void PathConfigPane::InitializeGUI()
|
|||
|
||||
m_default_iso_filepicker = new wxFilePickerCtrl(
|
||||
this, wxID_ANY, wxEmptyString, _("Choose a default ISO:"),
|
||||
_("All GC/Wii files (elf, dol, gcm, iso, wbfs, ciso, gcz, wad)") +
|
||||
wxString::Format("|*.elf;*.dol;*.gcm;*.iso;*.wbfs;*.ciso;*.gcz;*.wad|%s",
|
||||
_("All GC/Wii files (elf, dol, gcm, iso, tgc, wbfs, ciso, gcz, wad)") +
|
||||
wxString::Format("|*.elf;*.dol;*.gcm;*.iso;*.tgc;*.wbfs;*.ciso;*.gcz;*.wad|%s",
|
||||
wxGetTranslation(wxALL_FILES)),
|
||||
wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_OPEN | wxFLP_SMALL);
|
||||
m_dvd_root_dirpicker =
|
||||
|
|
|
@ -332,8 +332,9 @@ void CFrame::DoOpen(bool Boot)
|
|||
|
||||
wxString path = wxFileSelector(
|
||||
_("Select the file to load"), wxEmptyString, wxEmptyString, wxEmptyString,
|
||||
_("All GC/Wii files (elf, dol, gcm, iso, wbfs, ciso, gcz, wad)") +
|
||||
wxString::Format("|*.elf;*.dol;*.gcm;*.iso;*.wbfs;*.ciso;*.gcz;*.wad;*.dff;*.tmd|%s",
|
||||
_("All GC/Wii files (elf, dol, gcm, iso, tgc, wbfs, ciso, gcz, wad)") +
|
||||
wxString::Format(
|
||||
"|*.elf;*.dol;*.gcm;*.iso;*.tgc;*.wbfs;*.ciso;*.gcz;*.wad;*.dff;*.tmd|%s",
|
||||
wxGetTranslation(wxALL_FILES)),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST, this);
|
||||
|
||||
|
|
|
@ -566,7 +566,10 @@ void CGameListCtrl::ScanForISOs()
|
|||
std::vector<std::string> Extensions;
|
||||
|
||||
if (SConfig::GetInstance().m_ListGC)
|
||||
{
|
||||
Extensions.push_back(".gcm");
|
||||
Extensions.push_back(".tgc");
|
||||
}
|
||||
if (SConfig::GetInstance().m_ListWii || SConfig::GetInstance().m_ListGC)
|
||||
{
|
||||
Extensions.push_back(".iso");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<string>gcm</string>
|
||||
<string>gcz</string>
|
||||
<string>iso</string>
|
||||
<string>tgc</string>
|
||||
<string>wad</string>
|
||||
<string>wbfs</string>
|
||||
</array>
|
||||
|
|
|
@ -140,8 +140,8 @@ void DolphinApp::OnInitCmdLine(wxCmdLineParser& parser)
|
|||
{wxCMD_LINE_SWITCH, "l", "logger", "Opens the logger", wxCMD_LINE_VAL_NONE,
|
||||
wxCMD_LINE_PARAM_OPTIONAL},
|
||||
{wxCMD_LINE_OPTION, "e", "exec",
|
||||
"Loads the specified file (ELF, DOL, GCM, ISO, WBFS, CISO, GCZ, WAD)", wxCMD_LINE_VAL_STRING,
|
||||
wxCMD_LINE_PARAM_OPTIONAL},
|
||||
"Loads the specified file (ELF, DOL, GCM, ISO, TGC, WBFS, CISO, GCZ, WAD)",
|
||||
wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL},
|
||||
{wxCMD_LINE_SWITCH, "b", "batch", "Exit Dolphin with emulator", wxCMD_LINE_VAL_NONE,
|
||||
wxCMD_LINE_PARAM_OPTIONAL},
|
||||
{wxCMD_LINE_OPTION, "c", "confirm", "Set Confirm on Stop", wxCMD_LINE_VAL_STRING,
|
||||
|
|
Loading…
Reference in New Issue