1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <cstddef>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <mbedtls/aes.h>
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h"
#include "Common/Logging/Log.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Volume.h"
#include "DiscIO/VolumeCreator.h"
#include "DiscIO/VolumeDirectory.h"
#include "DiscIO/VolumeGC.h"
#include "DiscIO/VolumeWad.h"
#include "DiscIO/VolumeWiiCrypted.h"
namespace DiscIO
{
enum EDiscType
{
DISC_TYPE_UNK,
DISC_TYPE_WII,
DISC_TYPE_WII_CONTAINER,
DISC_TYPE_GC,
DISC_TYPE_WAD
};
static const unsigned char s_master_key[16] = {
0xeb,0xe4,0x2a,0x22,0x5e,0x85,0x93,0xe4,
0x48,0xd9,0xc5,0x45,0x73,0x81,0xaa,0xf7
};
static const unsigned char s_master_key_korean[16] = {
0x63,0xb8,0x2b,0xb4,0xf4,0x61,0x4e,0x2e,
0x13,0xf2,0xfe,0xfb,0xba,0x4c,0x9b,0x7e
};
static std::unique_ptr<IVolume> CreateVolumeFromCryptedWiiImage(std::unique_ptr<IBlobReader> reader, u32 partition_group, u32 volume_type, u32 volume_number);
EDiscType GetDiscType(IBlobReader& _rReader);
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename, u32 partition_group, u32 volume_number)
{
std::unique_ptr<IBlobReader> reader(CreateBlobReader(filename));
if (reader == nullptr)
return nullptr;
switch (GetDiscType(*reader))
{
case DISC_TYPE_WII:
case DISC_TYPE_GC:
return std::make_unique<CVolumeGC>(std::move(reader));
case DISC_TYPE_WAD:
return std::make_unique<CVolumeWAD>(std::move(reader));
case DISC_TYPE_WII_CONTAINER:
return CreateVolumeFromCryptedWiiImage(std::move(reader), partition_group, 0, volume_number);
case DISC_TYPE_UNK:
default:
std::string name, extension;
SplitPath(filename, nullptr, &name, &extension);
name += extension;
NOTICE_LOG(DISCIO, "%s does not have the Magic word for a gcm, wiidisc or wad file\n"
"Set Log Verbosity to Warning and attempt to load the game again to view the values", name.c_str());
}
return nullptr;
}
std::unique_ptr<IVolume> CreateVolumeFromDirectory(const std::string& directory, bool is_wii, const std::string& apploader, const std::string& dol)
{
if (CVolumeDirectory::IsValidDirectory(directory))
return std::make_unique<CVolumeDirectory>(directory, is_wii, apploader, dol);
return nullptr;
}
void VolumeKeyForPartition(IBlobReader& _rReader, u64 offset, u8* VolumeKey)
{
u8 SubKey[16];
_rReader.Read(offset + 0x1bf, 16, SubKey);
u8 IV[16];
memset(IV, 0, 16);
_rReader.Read(offset + 0x44c, 8, IV);
// Issue: 6813
// Magic value is at partition's offset + 0x1f1 (1byte)
// If encrypted with the Korean key, the magic value would be 1
// Otherwise it is zero
u8 using_korean_key = 0;
_rReader.Read(offset + 0x1f1, sizeof(u8), &using_korean_key);
u8 region = 0;
_rReader.Read(0x3, sizeof(u8), ®ion);
mbedtls_aes_context AES_ctx;
mbedtls_aes_setkey_dec(&AES_ctx, (using_korean_key == 1 && region == 'K' ? s_master_key_korean : s_master_key), 128);
mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_DECRYPT, 16, IV, SubKey, VolumeKey);
}
static std::unique_ptr<IVolume> CreateVolumeFromCryptedWiiImage(std::unique_ptr<IBlobReader> reader, u32 partition_group, u32 volume_type, u32 volume_number)
{
CBlobBigEndianReader big_endian_reader(*reader);
u32 num_partitions;
if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8), &num_partitions))
return nullptr;
// Check if we're looking for a valid partition
if ((int)volume_number != -1 && volume_number > num_partitions)
return nullptr;
u32 partitions_offset_unshifted;
if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8) + 4, &partitions_offset_unshifted))
return nullptr;
u64 partitions_offset = (u64)partitions_offset_unshifted << 2;
struct SPartition
{
SPartition(u64 offset_, u32 type_) : offset(offset_), type(type_) {}
u64 offset;
u32 type;
};
struct SPartitionGroup
{
u32 num_partitions;
u64 partitions_offset;
std::vector<SPartition> partitions;
};
SPartitionGroup partition_groups[4];
// Read all partitions
for (SPartitionGroup& group : partition_groups)
{
for (u32 i = 0; i < num_partitions; i++)
{
u32 partition_offset, partition_type;
if (big_endian_reader.ReadSwapped(partitions_offset + (i * 8) + 0, &partition_offset) &&
big_endian_reader.ReadSwapped(partitions_offset + (i * 8) + 4, &partition_type))
{
group.partitions.emplace_back((u64)partition_offset << 2, partition_type);
}
}
}
// Return the partition type specified or number
// types: 0 = game, 1 = firmware update, 2 = channel installer
// some partitions on SSBB use the ASCII title id of the demo VC game they hold...
for (size_t i = 0; i < partition_groups[partition_group].partitions.size(); i++)
{
const SPartition& partition = partition_groups[partition_group].partitions.at(i);
if ((partition.type == volume_type && (int)volume_number == -1) || i == volume_number)
{
u8 volume_key[16];
VolumeKeyForPartition(*reader, partition.offset, volume_key);
return std::make_unique<CVolumeWiiCrypted>(std::move(reader), partition.offset, volume_key);
}
}
return nullptr;
}
EDiscType GetDiscType(IBlobReader& _rReader)
{
CBlobBigEndianReader Reader(_rReader);
// Check for Wii
u32 WiiMagic = 0;
Reader.ReadSwapped(0x18, &WiiMagic);
u32 WiiContainerMagic = 0;
Reader.ReadSwapped(0x60, &WiiContainerMagic);
if (WiiMagic == 0x5D1C9EA3 && WiiContainerMagic != 0)
return DISC_TYPE_WII;
if (WiiMagic == 0x5D1C9EA3 && WiiContainerMagic == 0)
return DISC_TYPE_WII_CONTAINER;
// Check for WAD
// 0x206962 for boot2 wads
u32 WADMagic = 0;
Reader.ReadSwapped(0x02, &WADMagic);
if (WADMagic == 0x00204973 || WADMagic == 0x00206962)
return DISC_TYPE_WAD;
// Check for GC
u32 GCMagic = 0;
Reader.ReadSwapped(0x1C, &GCMagic);
if (GCMagic == 0xC2339F3D)
return DISC_TYPE_GC;
WARN_LOG(DISCIO, "No known magic words found");
WARN_LOG(DISCIO, "Wii offset: 0x18 value: 0x%08x", WiiMagic);
WARN_LOG(DISCIO, "WiiC offset: 0x60 value: 0x%08x", WiiContainerMagic);
WARN_LOG(DISCIO, "WAD offset: 0x02 value: 0x%08x", WADMagic);
WARN_LOG(DISCIO, "GC offset: 0x1C value: 0x%08x", GCMagic);
return DISC_TYPE_UNK;
}
} // namespace
|