Skip to content

Commit

Permalink
Add an early version of WIABlobReader
Browse files Browse the repository at this point in the history
It can currently only read the first 0x80 bytes of a disc image,
which is enough for identifying it but not for doing anything else.
  • Loading branch information
JosJuice committed Jun 17, 2020
1 parent 0bc7dcd commit 8da5d0c
Show file tree
Hide file tree
Showing 14 changed files with 218 additions and 9 deletions.
Expand Up @@ -22,7 +22,7 @@
public final class FileBrowserHelper
{
public static final HashSet<String> GAME_EXTENSIONS = new HashSet<>(Arrays.asList(
"gcm", "tgc", "iso", "ciso", "gcz", "wbfs", "wad", "dol", "elf", "dff"));
"gcm", "tgc", "iso", "ciso", "gcz", "wbfs", "wia", "wad", "dol", "elf", "dff"));

public static final HashSet<String> RAW_EXTENSION = new HashSet<>(Collections.singletonList(
"raw"));
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/Boot/Boot.cpp
Expand Up @@ -159,7 +159,7 @@ BootParameters::GenerateFromFile(std::vector<std::string> paths,
paths.clear();

static const std::unordered_set<std::string> disc_image_extensions = {
{".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz", ".dol", ".elf"}};
{".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz", ".wia", ".dol", ".elf"}};
if (disc_image_extensions.find(extension) != disc_image_extensions.end() || is_drive)
{
std::unique_ptr<DiscIO::VolumeDisc> disc = DiscIO::CreateDisc(path);
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/DiscIO/Blob.cpp
Expand Up @@ -20,6 +20,7 @@
#include "DiscIO/DriveBlob.h"
#include "DiscIO/FileBlob.h"
#include "DiscIO/TGCBlob.h"
#include "DiscIO/WIABlob.h"
#include "DiscIO/WbfsBlob.h"

namespace DiscIO
Expand Down Expand Up @@ -205,6 +206,8 @@ std::unique_ptr<BlobReader> CreateBlobReader(const std::string& filename)
return TGCFileReader::Create(std::move(file));
case WBFS_MAGIC:
return WbfsFileReader::Create(std::move(file), filename);
case WIA_MAGIC:
return WIAFileReader::Create(std::move(file), filename);
default:
if (auto directory_blob = DirectoryBlobReader::Create(filename))
return std::move(directory_blob);
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/DiscIO/Blob.h
Expand Up @@ -34,7 +34,8 @@ enum class BlobType
GCZ,
CISO,
WBFS,
TGC
TGC,
WIA
};

class BlobReader
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/DiscIO/CMakeLists.txt
Expand Up @@ -42,6 +42,8 @@ add_library(discio
VolumeWii.h
WbfsBlob.cpp
WbfsBlob.h
WIABlob.cpp
WIABlob.h
WiiEncryptionCache.cpp
WiiEncryptionCache.h
WiiSaveBanner.cpp
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/DiscIO/DiscIO.vcxproj
Expand Up @@ -65,6 +65,7 @@
<ClCompile Include="VolumeWad.cpp" />
<ClCompile Include="VolumeWii.cpp" />
<ClCompile Include="WbfsBlob.cpp" />
<ClCompile Include="WIABlob.cpp" />
<ClCompile Include="WiiEncryptionCache.cpp" />
<ClCompile Include="WiiSaveBanner.cpp" />
</ItemGroup>
Expand All @@ -91,6 +92,7 @@
<ClInclude Include="VolumeWad.h" />
<ClInclude Include="VolumeWii.h" />
<ClInclude Include="WbfsBlob.h" />
<ClInclude Include="WIABlob.h" />
<ClInclude Include="WiiEncryptionCache.h" />
<ClInclude Include="WiiSaveBanner.h" />
</ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/DiscIO/DiscIO.vcxproj.filters
Expand Up @@ -90,6 +90,9 @@
<ClCompile Include="ScrubbedBlob.cpp">
<Filter>Volume\Blob</Filter>
</ClCompile>
<ClCompile Include="WIABlob.cpp">
<Filter>Volume\Blob</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="DiscScrubber.h">
Expand Down Expand Up @@ -164,6 +167,9 @@
<ClInclude Include="MultithreadedCompressor.h">
<Filter>Volume\Blob</Filter>
</ClInclude>
<ClInclude Include="WIABlob.h">
<Filter>Volume\Blob</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
Expand Down
92 changes: 92 additions & 0 deletions Source/Core/DiscIO/WIABlob.cpp
@@ -0,0 +1,92 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "DiscIO/WIABlob.h"

#include <memory>

#include "Common/CommonTypes.h"
#include "Common/File.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Swap.h"

#include "DiscIO/VolumeWii.h"

namespace DiscIO
{
WIAFileReader::WIAFileReader(File::IOFile file, const std::string& path) : m_file(std::move(file))
{
m_valid = Initialize(path);
}

WIAFileReader::~WIAFileReader() = default;

bool WIAFileReader::Initialize(const std::string& path)
{
if (!m_file.Seek(0, SEEK_SET) || !m_file.ReadArray(&m_header_1, 1))
return false;

if (m_header_1.magic != WIA_MAGIC)
return false;

const u32 version = Common::swap32(m_header_1.version);
const u32 version_compatible = Common::swap32(m_header_1.version_compatible);
if (WIA_VERSION < version_compatible || WIA_VERSION_READ_COMPATIBLE > version)
{
ERROR_LOG(DISCIO, "Unsupported WIA version %s in %s", VersionToString(version).c_str(),
path.c_str());
return false;
}

if (Common::swap64(m_header_1.wia_file_size) != m_file.GetSize())
{
ERROR_LOG(DISCIO, "File size is incorrect for %s", path.c_str());
return false;
}

if (Common::swap32(m_header_1.header_2_size) < sizeof(WIAHeader2))
return false;

if (!m_file.ReadArray(&m_header_2, 1))
return false;

const u32 chunk_size = Common::swap32(m_header_2.chunk_size);
if (chunk_size % VolumeWii::GROUP_TOTAL_SIZE != 0)
return false;

return true;
}

std::unique_ptr<WIAFileReader> WIAFileReader::Create(File::IOFile file, const std::string& path)
{
std::unique_ptr<WIAFileReader> blob(new WIAFileReader(std::move(file), path));
return blob->m_valid ? std::move(blob) : nullptr;
}

bool WIAFileReader::Read(u64 offset, u64 size, u8* out_ptr)
{
if (offset + size <= sizeof(WIAHeader2::disc_header))
{
std::memcpy(out_ptr, m_header_2.disc_header.data() + offset, size);
return true;
}

// Not implemented
return false;
}

std::string WIAFileReader::VersionToString(u32 version)
{
const u8 a = version >> 24;
const u8 b = (version >> 16) & 0xff;
const u8 c = (version >> 8) & 0xff;
const u8 d = version & 0xff;

if (d == 0 || d == 0xff)
return StringFromFormat("%u.%02x.%02x", a, b, c);
else
return StringFromFormat("%u.%02x.%02x.beta%u", a, b, c, d);
}
} // namespace DiscIO
102 changes: 102 additions & 0 deletions Source/Core/DiscIO/WIABlob.h
@@ -0,0 +1,102 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <memory>

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

namespace DiscIO
{
constexpr u32 WIA_MAGIC = 0x01414957; // "WIA\x1" (byteswapped to little endian)

class WIAFileReader : public BlobReader
{
public:
~WIAFileReader();

static std::unique_ptr<WIAFileReader> Create(File::IOFile file, const std::string& path);

BlobType GetBlobType() const override { return BlobType::WIA; }

u64 GetRawSize() const override { return Common::swap64(m_header_1.wia_file_size); }
u64 GetDataSize() const override { return Common::swap64(m_header_1.iso_file_size); }
bool IsDataSizeAccurate() const override { return true; }

u64 GetBlockSize() const override { return Common::swap32(m_header_2.chunk_size); }
bool HasFastRandomAccessInBlock() const override { return false; }

bool Read(u64 offset, u64 size, u8* out_ptr) override;

private:
explicit WIAFileReader(File::IOFile file, const std::string& path);
bool Initialize(const std::string& path);

static std::string VersionToString(u32 version);

using SHA1 = std::array<u8, 20>;

#pragma pack(push, 1)
struct WIAHeader1
{
u32 magic;
u32 version;
u32 version_compatible;
u32 header_2_size;
SHA1 header_2_hash;
u64 iso_file_size;
u64 wia_file_size;
SHA1 header_1_hash;
};
#pragma pack(pop)
static_assert(sizeof(WIAHeader1) == 0x48, "Wrong size for WIA header 1");

#pragma pack(push, 1)
struct WIAHeader2
{
u32 disc_type;
u32 compression_type;
u32 compression_level; // Informative only
u32 chunk_size;

std::array<u8, 0x80> disc_header;

u32 number_of_partitions_entries;
u32 partition_entry_size;
u64 partition_entries_offset;
SHA1 partition_entries_hash;

u32 number_of_raw_data_entries;
u64 raw_data_entries_offset;
u32 raw_data_entries_size;

u32 number_of_group_entries;
u64 group_entries_offset;
u32 group_entries_size;
};
#pragma pack(pop)
static_assert(sizeof(WIAHeader2) == 0xd4, "Wrong size for WIA header 2");

bool m_valid;

File::IOFile m_file;

WIAHeader1 m_header_1;
WIAHeader2 m_header_2;

static constexpr u32 WIA_VERSION = 0x01000000;
static constexpr u32 WIA_VERSION_WRITE_COMPATIBLE = 0x01000000;
static constexpr u32 WIA_VERSION_READ_COMPATIBLE = 0x00080000;

// Perhaps we could set WIA_VERSION_WRITE_COMPATIBLE to 0.9, but WIA version 0.9 was never in
// any official release of wit, and interim versions (either source or binaries) are hard to find.
// Since we've been unable to check if we're write compatible with 0.9, we set it 1.0 to be safe.
};

} // namespace DiscIO
4 changes: 2 additions & 2 deletions Source/Core/DolphinQt/GameList/GameTracker.cpp
Expand Up @@ -24,8 +24,8 @@ static const QStringList game_filters{
QStringLiteral("*.[gG][cC][mM]"), QStringLiteral("*.[iI][sS][oO]"),
QStringLiteral("*.[tT][gG][cC]"), QStringLiteral("*.[cC][iI][sS][oO]"),
QStringLiteral("*.[gG][cC][zZ]"), QStringLiteral("*.[wW][bB][fF][sS]"),
QStringLiteral("*.[wW][aA][dD]"), QStringLiteral("*.[eE][lL][fF]"),
QStringLiteral("*.[dD][oO][lL]")};
QStringLiteral("*.[wW][iI][aA]"), QStringLiteral("*.[wW][aA][dD]"),
QStringLiteral("*.[eE][lL][fF]"), QStringLiteral("*.[dD][oO][lL]")};

GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
{
Expand Down
1 change: 1 addition & 0 deletions Source/Core/DolphinQt/Info.plist.in
Expand Up @@ -16,6 +16,7 @@
<string>m3u</string>
<string>tgc</string>
<string>wad</string>
<string>wia</string>
<string>wbfs</string>
</array>
<key>CFBundleTypeIconFile</key>
Expand Down
4 changes: 2 additions & 2 deletions Source/Core/DolphinQt/MainWindow.cpp
Expand Up @@ -686,8 +686,8 @@ QStringList MainWindow::PromptFileNames()
QStringList paths = QFileDialog::getOpenFileNames(
this, tr("Select a File"),
settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(),
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wad *.dff *.m3u);;"
"All Files (*)"));
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.wad *.dff "
"*.m3u);;All Files (*)"));

if (!paths.isEmpty())
{
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/DolphinQt/Settings/PathPane.cpp
Expand Up @@ -44,7 +44,7 @@ void PathPane::BrowseDefaultGame()
{
QString file = QDir::toNativeSeparators(QFileDialog::getOpenFileName(
this, tr("Select a Game"), Settings::Instance().GetDefaultGame(),
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wad *.m3u);;"
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.wad *.m3u);;"
"All Files (*)")));

if (!file.isEmpty())
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/UICommon/GameFileCache.cpp
Expand Up @@ -33,7 +33,7 @@ std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& direct
bool recursive_scan)
{
static const std::vector<std::string> search_extensions = {
".gcm", ".tgc", ".iso", ".ciso", ".gcz", ".wbfs", ".wad", ".dol", ".elf"};
".gcm", ".tgc", ".iso", ".ciso", ".gcz", ".wbfs", ".wia", ".wad", ".dol", ".elf"};

// TODO: We could process paths iteratively as they are found
return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan);
Expand Down

0 comments on commit 8da5d0c

Please sign in to comment.