Merge pull request #11072 from SketchMaster2001/wiiconnect24
Add initial WiiConnect24 support
This commit is contained in:
commit
c0476fdac3
|
@ -408,4 +408,21 @@ std::unique_ptr<Context> CreateContextDecrypt(const u8* key)
|
||||||
return CreateContext<Mode::Decrypt>(key);
|
return CreateContext<Mode::Decrypt>(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OFB encryption and decryption are the exact same. We don't encrypt though.
|
||||||
|
void CryptOFB(const u8* key, const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out, size_t size)
|
||||||
|
{
|
||||||
|
mbedtls_aes_context aes_ctx;
|
||||||
|
size_t iv_offset = 0;
|
||||||
|
|
||||||
|
std::array<u8, 16> iv_tmp{};
|
||||||
|
if (iv)
|
||||||
|
std::memcpy(&iv_tmp[0], iv, 16);
|
||||||
|
|
||||||
|
ASSERT(!mbedtls_aes_setkey_enc(&aes_ctx, key, 128));
|
||||||
|
mbedtls_aes_crypt_ofb(&aes_ctx, size, &iv_offset, &iv_tmp[0], buf_in, buf_out);
|
||||||
|
|
||||||
|
if (iv_out)
|
||||||
|
std::memcpy(iv_out, &iv_tmp[0], 16);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common::AES
|
} // namespace Common::AES
|
||||||
|
|
|
@ -46,4 +46,7 @@ public:
|
||||||
std::unique_ptr<Context> CreateContextEncrypt(const u8* key);
|
std::unique_ptr<Context> CreateContextEncrypt(const u8* key);
|
||||||
std::unique_ptr<Context> CreateContextDecrypt(const u8* key);
|
std::unique_ptr<Context> CreateContextDecrypt(const u8* key);
|
||||||
|
|
||||||
|
// OFB decryption for WiiConnect24
|
||||||
|
void CryptOFB(const u8* key, const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out, size_t size);
|
||||||
|
|
||||||
} // namespace Common::AES
|
} // namespace Common::AES
|
||||||
|
|
|
@ -363,6 +363,11 @@ add_library(core
|
||||||
IOS/Network/KD/NetKDTime.h
|
IOS/Network/KD/NetKDTime.h
|
||||||
IOS/Network/KD/NWC24Config.cpp
|
IOS/Network/KD/NWC24Config.cpp
|
||||||
IOS/Network/KD/NWC24Config.h
|
IOS/Network/KD/NWC24Config.h
|
||||||
|
IOS/Network/KD/NWC24DL.cpp
|
||||||
|
IOS/Network/KD/NWC24DL.h
|
||||||
|
IOS/Network/KD/VFF/VFFUtil.cpp
|
||||||
|
IOS/Network/KD/VFF/VFFUtil.h
|
||||||
|
IOS/Network/KD/WC24File.h
|
||||||
IOS/Network/MACUtils.cpp
|
IOS/Network/MACUtils.cpp
|
||||||
IOS/Network/MACUtils.h
|
IOS/Network/MACUtils.h
|
||||||
IOS/Network/NCD/Manage.cpp
|
IOS/Network/NCD/Manage.cpp
|
||||||
|
@ -598,6 +603,7 @@ PUBLIC
|
||||||
videosoftware
|
videosoftware
|
||||||
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
FatFs
|
||||||
fmt::fmt
|
fmt::fmt
|
||||||
${LZO}
|
${LZO}
|
||||||
ZLIB::ZLIB
|
ZLIB::ZLIB
|
||||||
|
|
|
@ -19,6 +19,14 @@ enum ErrorCode : s32
|
||||||
{
|
{
|
||||||
WC24_OK = 0,
|
WC24_OK = 0,
|
||||||
WC24_ERR_FATAL = -1,
|
WC24_ERR_FATAL = -1,
|
||||||
|
WC24_ERR_NOT_FOUND = -13,
|
||||||
|
WC24_ERR_BROKEN = -14,
|
||||||
|
WC24_ERR_FILE_OPEN = -16,
|
||||||
|
WC24_ERR_FILE_CLOSE = -17,
|
||||||
|
WC24_ERR_FILE_READ = -18,
|
||||||
|
WC24_ERR_FILE_WRITE = -19,
|
||||||
|
WC24_ERR_NETWORK = -31,
|
||||||
|
WC24_ERR_SERVER = -32,
|
||||||
WC24_ERR_ID_NONEXISTANCE = -34,
|
WC24_ERR_ID_NONEXISTANCE = -34,
|
||||||
WC24_ERR_ID_GENERATED = -35,
|
WC24_ERR_ID_GENERATED = -35,
|
||||||
WC24_ERR_ID_REGISTERED = -36,
|
WC24_ERR_ID_REGISTERED = -36,
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "Core/IOS/Network/KD/NWC24DL.h"
|
||||||
|
|
||||||
|
#include "Common/BitUtils.h"
|
||||||
|
#include "Common/CommonPaths.h"
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Common/Swap.h"
|
||||||
|
#include "Core/IOS/FS/FileSystem.h"
|
||||||
|
#include "Core/IOS/Uids.h"
|
||||||
|
|
||||||
|
namespace IOS::HLE::NWC24
|
||||||
|
{
|
||||||
|
constexpr const char DL_LIST_PATH[] = "/" WII_WC24CONF_DIR "/nwc24dl.bin";
|
||||||
|
|
||||||
|
NWC24Dl::NWC24Dl(std::shared_ptr<FS::FileSystem> fs) : m_fs{std::move(fs)}
|
||||||
|
{
|
||||||
|
ReadDlList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NWC24Dl::ReadDlList()
|
||||||
|
{
|
||||||
|
const auto file = m_fs->OpenFile(PID_KD, PID_KD, DL_LIST_PATH, FS::Mode::Read);
|
||||||
|
if (!file || !file->Read(&m_data, 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const s32 file_error = CheckNwc24DlList();
|
||||||
|
if (file_error)
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "There is an error in the DL list for WC24: {}", file_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 NWC24Dl::CheckNwc24DlList() const
|
||||||
|
{
|
||||||
|
// 'WcDl' magic
|
||||||
|
if (Magic() != DL_LIST_MAGIC)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "DL list magic mismatch");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Version() != 1)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "DL list version mismatch");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NWC24Dl::WriteDlList() const
|
||||||
|
{
|
||||||
|
constexpr FS::Modes public_modes{FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::ReadWrite};
|
||||||
|
m_fs->CreateFullPath(PID_KD, PID_KD, DL_LIST_PATH, 0, public_modes);
|
||||||
|
const auto file = m_fs->CreateAndOpenFile(PID_KD, PID_KD, DL_LIST_PATH, public_modes);
|
||||||
|
|
||||||
|
if (!file || !file->Write(&m_data, 1))
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to open or write WC24 DL list file");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NWC24Dl::DoesEntryExist(u16 entry_index)
|
||||||
|
{
|
||||||
|
return m_data.entries[entry_index].low_title_id != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NWC24Dl::GetDownloadURL(u16 entry_index, std::optional<u8> subtask_id) const
|
||||||
|
{
|
||||||
|
std::string url(m_data.entries[entry_index].dl_url);
|
||||||
|
|
||||||
|
// Determine if we need to append the subtask to the URL.
|
||||||
|
if (subtask_id &&
|
||||||
|
Common::ExtractBit(Common::swap32(m_data.entries[entry_index].subtask_bitmask), 1))
|
||||||
|
{
|
||||||
|
url.append(fmt::format(".{:02d}", *subtask_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NWC24Dl::GetVFFContentName(u16 entry_index, std::optional<u8> subtask_id) const
|
||||||
|
{
|
||||||
|
std::string content(m_data.entries[entry_index].filename);
|
||||||
|
|
||||||
|
// Determine if we need to append the subtask to the name.
|
||||||
|
if (subtask_id &&
|
||||||
|
Common::ExtractBit(Common::swap32(m_data.entries[entry_index].subtask_bitmask), 0))
|
||||||
|
{
|
||||||
|
content.append(fmt::format(".{:02d}", *subtask_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NWC24Dl::GetVFFPath(u16 entry_index) const
|
||||||
|
{
|
||||||
|
const u32 lower_title_id = Common::swap32(m_data.entries[entry_index].low_title_id);
|
||||||
|
const u32 high_title_id = Common::swap32(m_data.entries[entry_index].high_title_id);
|
||||||
|
|
||||||
|
return fmt::format("/title/{0:08x}/{1:08x}/data/wc24dl.vff", lower_title_id, high_title_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
WC24PubkMod NWC24Dl::GetWC24PubkMod(u16 entry_index) const
|
||||||
|
{
|
||||||
|
WC24PubkMod pubkMod;
|
||||||
|
const u32 lower_title_id = Common::swap32(m_data.entries[entry_index].low_title_id);
|
||||||
|
const u32 high_title_id = Common::swap32(m_data.entries[entry_index].high_title_id);
|
||||||
|
|
||||||
|
const std::string path =
|
||||||
|
fmt::format("/title/{0:08x}/{1:08x}/data/wc24pubk.mod", lower_title_id, high_title_id);
|
||||||
|
|
||||||
|
const auto file = m_fs->OpenFile(PID_KD, PID_KD, path, IOS::HLE::FS::Mode::Read);
|
||||||
|
file->Read(&pubkMod, 1);
|
||||||
|
|
||||||
|
return pubkMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NWC24Dl::IsEncrypted(u16 entry_index) const
|
||||||
|
{
|
||||||
|
return !!Common::ExtractBit(Common::swap32(m_data.entries[entry_index].flags), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 NWC24Dl::Magic() const
|
||||||
|
{
|
||||||
|
return Common::swap32(m_data.header.magic);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NWC24Dl::SetMagic(u32 magic)
|
||||||
|
{
|
||||||
|
m_data.header.magic = Common::swap32(magic);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 NWC24Dl::Version() const
|
||||||
|
{
|
||||||
|
return Common::swap32(m_data.header.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NWC24Dl::SetVersion(u32 version)
|
||||||
|
{
|
||||||
|
m_data.header.version = Common::swap32(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IOS::HLE::NWC24
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/Network/KD/WC24File.h"
|
||||||
|
|
||||||
|
namespace IOS::HLE
|
||||||
|
{
|
||||||
|
namespace FS
|
||||||
|
{
|
||||||
|
class FileSystem;
|
||||||
|
}
|
||||||
|
namespace NWC24
|
||||||
|
{
|
||||||
|
class NWC24Dl final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NWC24Dl(std::shared_ptr<FS::FileSystem> fs);
|
||||||
|
|
||||||
|
void ReadDlList();
|
||||||
|
void WriteDlList() const;
|
||||||
|
|
||||||
|
s32 CheckNwc24DlList() const;
|
||||||
|
|
||||||
|
bool DoesEntryExist(u16 entry_index);
|
||||||
|
bool IsEncrypted(u16 entry_index) const;
|
||||||
|
std::string GetVFFContentName(u16 entry_index, std::optional<u8> subtask_id) const;
|
||||||
|
std::string GetDownloadURL(u16 entry_index, std::optional<u8> subtask_id) const;
|
||||||
|
std::string GetVFFPath(u16 entry_index) const;
|
||||||
|
WC24PubkMod GetWC24PubkMod(u16 entry_index) const;
|
||||||
|
|
||||||
|
u32 Magic() const;
|
||||||
|
void SetMagic(u32 magic);
|
||||||
|
|
||||||
|
u32 Version() const;
|
||||||
|
void SetVersion(u32 version);
|
||||||
|
|
||||||
|
static constexpr u32 MAX_ENTRIES = 120;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr u32 DL_LIST_MAGIC = 0x5763446C; // WcDl
|
||||||
|
static constexpr u32 MAX_SUBENTRIES = 32;
|
||||||
|
|
||||||
|
enum EntryType : u8
|
||||||
|
{
|
||||||
|
UNK = 1,
|
||||||
|
MAIL,
|
||||||
|
CHANNEL_CONTENT,
|
||||||
|
UNUSED = 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
// The following format is partially based off of https://wiibrew.org/wiki//dev/net/kd/request.
|
||||||
|
struct DLListHeader final
|
||||||
|
{
|
||||||
|
u32 magic; // 'WcDl' 0x5763446c
|
||||||
|
u32 version; // must be 1
|
||||||
|
u32 unk1;
|
||||||
|
u32 unk2;
|
||||||
|
u16 max_subentries; // Asserted to be less than max_entries
|
||||||
|
u16 reserved_mailnum;
|
||||||
|
u16 max_entries;
|
||||||
|
u8 reserved[106];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DLListRecord final
|
||||||
|
{
|
||||||
|
u32 low_title_id;
|
||||||
|
u32 next_dl_timestamp;
|
||||||
|
u32 last_modified_timestamp;
|
||||||
|
u8 flags;
|
||||||
|
u8 padding[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DLListEntry final
|
||||||
|
{
|
||||||
|
u16 index;
|
||||||
|
EntryType type;
|
||||||
|
u8 record_flags;
|
||||||
|
u32 flags;
|
||||||
|
u32 high_title_id;
|
||||||
|
u32 low_title_id;
|
||||||
|
u32 unknown1;
|
||||||
|
u16 group_id;
|
||||||
|
u16 padding1;
|
||||||
|
u16 remaining_downloads;
|
||||||
|
u16 error_count;
|
||||||
|
u16 dl_frequency;
|
||||||
|
u16 dl_frequency_when_err;
|
||||||
|
s32 error_code;
|
||||||
|
u8 subtask_id;
|
||||||
|
u8 subtask_type;
|
||||||
|
u8 subtask_flags;
|
||||||
|
u8 padding2;
|
||||||
|
u32 subtask_bitmask;
|
||||||
|
s32 unknown2;
|
||||||
|
u32 dl_timestamp; // Last DL time
|
||||||
|
u32 subtask_timestamps[32];
|
||||||
|
char dl_url[236];
|
||||||
|
char filename[64];
|
||||||
|
u8 unk6[29];
|
||||||
|
u8 should_use_rootca;
|
||||||
|
u16 unknown3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DLList final
|
||||||
|
{
|
||||||
|
DLListHeader header;
|
||||||
|
DLListRecord records[MAX_ENTRIES];
|
||||||
|
DLListEntry entries[MAX_ENTRIES];
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
std::shared_ptr<FS::FileSystem> m_fs;
|
||||||
|
DLList m_data;
|
||||||
|
};
|
||||||
|
} // namespace NWC24
|
||||||
|
} // namespace IOS::HLE
|
|
@ -9,6 +9,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "Common/BitUtils.h"
|
||||||
#include "Common/CommonPaths.h"
|
#include "Common/CommonPaths.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
#include "Core/CommonTitles.h"
|
#include "Core/CommonTitles.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/IOS/FS/FileSystem.h"
|
#include "Core/IOS/FS/FileSystem.h"
|
||||||
|
#include "Core/IOS/Network/KD/VFF/VFFUtil.h"
|
||||||
#include "Core/IOS/Network/Socket.h"
|
#include "Core/IOS/Network/Socket.h"
|
||||||
#include "Core/IOS/Uids.h"
|
#include "Core/IOS/Uids.h"
|
||||||
|
|
||||||
|
@ -145,8 +147,15 @@ s32 NWC24MakeUserID(u64* nwc24_id, u32 hollywood_id, u16 id_ctr, HardwareModel h
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
NetKDRequestDevice::NetKDRequestDevice(Kernel& ios, const std::string& device_name)
|
NetKDRequestDevice::NetKDRequestDevice(Kernel& ios, const std::string& device_name)
|
||||||
: Device(ios, device_name), config{ios.GetFS()}
|
: Device(ios, device_name), config{ios.GetFS()}, m_dl_list{ios.GetFS()}
|
||||||
{
|
{
|
||||||
|
m_work_queue.Reset([this](AsyncTask task) {
|
||||||
|
const IPCReply reply = task.handler();
|
||||||
|
{
|
||||||
|
std::lock_guard lg(m_async_reply_lock);
|
||||||
|
m_async_replies.emplace(AsyncReply{task.request, reply.return_value});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
NetKDRequestDevice::~NetKDRequestDevice()
|
NetKDRequestDevice::~NetKDRequestDevice()
|
||||||
|
@ -154,6 +163,133 @@ NetKDRequestDevice::~NetKDRequestDevice()
|
||||||
WiiSockMan::GetInstance().Clean();
|
WiiSockMan::GetInstance().Clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetKDRequestDevice::Update()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard lg(m_async_reply_lock);
|
||||||
|
while (!m_async_replies.empty())
|
||||||
|
{
|
||||||
|
const auto& reply = m_async_replies.front();
|
||||||
|
GetIOS()->EnqueueIPCReply(reply.request, reply.return_value);
|
||||||
|
m_async_replies.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index,
|
||||||
|
const std::optional<u8> subtask_id)
|
||||||
|
{
|
||||||
|
std::vector<u8> file_data;
|
||||||
|
|
||||||
|
// Content metadata
|
||||||
|
const std::string content_name = m_dl_list.GetVFFContentName(entry_index, subtask_id);
|
||||||
|
const std::string url = m_dl_list.GetDownloadURL(entry_index, subtask_id);
|
||||||
|
|
||||||
|
INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_DOWNLOAD_NOW_EX - NI - URL: {}", url);
|
||||||
|
|
||||||
|
INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_DOWNLOAD_NOW_EX - NI - Name: {}", content_name);
|
||||||
|
|
||||||
|
const Common::HttpRequest::Response response = m_http.Get(url);
|
||||||
|
|
||||||
|
if (!response)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to request data at {}", url);
|
||||||
|
return NWC24::WC24_ERR_SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the filesize is smaller than the header size.
|
||||||
|
if (response->size() < sizeof(NWC24::WC24File))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "File at {} is too small to be a valid file.", url);
|
||||||
|
return NWC24::WC24_ERR_BROKEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we read the file
|
||||||
|
NWC24::WC24File wc24File;
|
||||||
|
std::memcpy(&wc24File, response->data(), sizeof(NWC24::WC24File));
|
||||||
|
|
||||||
|
std::vector<u8> temp_buffer(response->begin() + 320, response->end());
|
||||||
|
|
||||||
|
if (m_dl_list.IsEncrypted(entry_index))
|
||||||
|
{
|
||||||
|
NWC24::WC24PubkMod pubkMod = m_dl_list.GetWC24PubkMod(entry_index);
|
||||||
|
|
||||||
|
file_data = std::vector<u8>(response->size() - 320);
|
||||||
|
|
||||||
|
Common::AES::CryptOFB(pubkMod.aes_key, wc24File.iv, wc24File.iv, temp_buffer.data(),
|
||||||
|
file_data.data(), temp_buffer.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file_data = std::move(temp_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
NWC24::ErrorCode reply = IOS::HLE::NWC24::OpenVFF(m_dl_list.GetVFFPath(entry_index), content_name,
|
||||||
|
m_ios.GetFS(), file_data);
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCReply NetKDRequestDevice::HandleNWC24DownloadNowEx(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
const u32 flags = Memory::Read_U32(request.buffer_in);
|
||||||
|
// Nintendo converts the entry ID between a u32 and u16
|
||||||
|
// several times, presumably for alignment purposes.
|
||||||
|
// We'll skip past buffer_in+4 and keep the entry index as a u16.
|
||||||
|
const u16 entry_index = Memory::Read_U16(request.buffer_in + 6);
|
||||||
|
const u32 subtask_bitmask = Memory::Read_U32(request.buffer_in + 8);
|
||||||
|
|
||||||
|
INFO_LOG_FMT(IOS_WC24,
|
||||||
|
"NET_KD_REQ: IOCTL_NWC24_DOWNLOAD_NOW_EX - NI - flags: {}, index: {}, bitmask: {}",
|
||||||
|
flags, entry_index, subtask_bitmask);
|
||||||
|
|
||||||
|
if (entry_index >= NWC24::NWC24Dl::MAX_ENTRIES)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: Entry index out of range.");
|
||||||
|
WriteReturnValue(NWC24::WC24_ERR_BROKEN, request.buffer_out);
|
||||||
|
return IPCReply(NWC24::WC24_ERR_BROKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_dl_list.DoesEntryExist(entry_index))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: Requested entry does not exist in download list!");
|
||||||
|
WriteReturnValue(NWC24::WC24_ERR_NOT_FOUND, request.buffer_out);
|
||||||
|
return IPCReply(NWC24::WC24_ERR_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// While in theory reply will always get initialized by KDDownload, things happen.
|
||||||
|
// Returning NWC24::WC24_ERR_BROKEN or anything that isn't OK will prompt the channel to fix the
|
||||||
|
// entry's data.
|
||||||
|
NWC24::ErrorCode reply = NWC24::WC24_ERR_BROKEN;
|
||||||
|
|
||||||
|
// Determine if we have subtasks to handle
|
||||||
|
if (Common::ExtractBit(flags, 2))
|
||||||
|
{
|
||||||
|
for (u8 subtask_id = 0; subtask_id < 32; subtask_id++)
|
||||||
|
{
|
||||||
|
// Check if we are done
|
||||||
|
if (!Common::ExtractBit(subtask_bitmask, subtask_id))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = KDDownload(entry_index, subtask_id);
|
||||||
|
if (reply != NWC24::WC24_OK)
|
||||||
|
{
|
||||||
|
// An error has occurred, break out and return error.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reply = KDDownload(entry_index, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteReturnValue(reply, request.buffer_out);
|
||||||
|
return IPCReply(reply);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
|
std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
|
||||||
{
|
{
|
||||||
enum : u32
|
enum : u32
|
||||||
|
@ -288,6 +424,9 @@ std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
|
||||||
INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW - NI");
|
INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW - NI");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IOCTL_NWC24_DOWNLOAD_NOW_EX:
|
||||||
|
return LaunchAsyncTask(&NetKDRequestDevice::HandleNWC24DownloadNowEx, request);
|
||||||
|
|
||||||
case IOCTL_NWC24_REQUEST_SHUTDOWN:
|
case IOCTL_NWC24_REQUEST_SHUTDOWN:
|
||||||
{
|
{
|
||||||
if (request.buffer_in == 0 || request.buffer_in % 4 != 0 || request.buffer_in_size < 8 ||
|
if (request.buffer_in == 0 || request.buffer_in % 4 != 0 || request.buffer_in_size < 8 ||
|
||||||
|
|
|
@ -3,13 +3,21 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "Common/CommonPaths.h"
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/HttpRequest.h"
|
||||||
|
#include "Common/WorkQueueThread.h"
|
||||||
#include "Core/IOS/Device.h"
|
#include "Core/IOS/Device.h"
|
||||||
#include "Core/IOS/Network/KD/NWC24Config.h"
|
#include "Core/IOS/Network/KD/NWC24Config.h"
|
||||||
|
#include "Core/IOS/Network/KD/NWC24DL.h"
|
||||||
|
|
||||||
namespace IOS::HLE
|
namespace IOS::HLE
|
||||||
{
|
{
|
||||||
|
constexpr const char DL_CNT_PATH[] = "/" WII_WC24CONF_DIR "/dlcnt.bin";
|
||||||
|
|
||||||
// KD is the IOS module responsible for implementing WiiConnect24 functionality.
|
// KD is the IOS module responsible for implementing WiiConnect24 functionality.
|
||||||
// It can perform HTTPS downloads, send and receive mail via SMTP, and execute a
|
// It can perform HTTPS downloads, send and receive mail via SMTP, and execute a
|
||||||
// JavaScript-like language while the Wii is in standby mode.
|
// JavaScript-like language while the Wii is in standby mode.
|
||||||
|
@ -17,11 +25,39 @@ class NetKDRequestDevice : public Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NetKDRequestDevice(Kernel& ios, const std::string& device_name);
|
NetKDRequestDevice(Kernel& ios, const std::string& device_name);
|
||||||
|
IPCReply HandleNWC24DownloadNowEx(const IOCtlRequest& request);
|
||||||
|
NWC24::ErrorCode KDDownload(const u16 entry_index, const std::optional<u8> subtask_id);
|
||||||
~NetKDRequestDevice() override;
|
~NetKDRequestDevice() override;
|
||||||
|
|
||||||
std::optional<IPCReply> IOCtl(const IOCtlRequest& request) override;
|
std::optional<IPCReply> IOCtl(const IOCtlRequest& request) override;
|
||||||
|
void Update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct AsyncTask
|
||||||
|
{
|
||||||
|
IOS::HLE::Request request;
|
||||||
|
std::function<IPCReply()> handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AsyncReply
|
||||||
|
{
|
||||||
|
IOS::HLE::Request request;
|
||||||
|
s32 return_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Method, typename Request>
|
||||||
|
std::optional<IPCReply> LaunchAsyncTask(Method method, const Request& request)
|
||||||
|
{
|
||||||
|
m_work_queue.EmplaceItem(AsyncTask{request, std::bind(method, this, request)});
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
NWC24::NWC24Config config;
|
NWC24::NWC24Config config;
|
||||||
|
NWC24::NWC24Dl m_dl_list;
|
||||||
|
Common::WorkQueueThread<AsyncTask> m_work_queue;
|
||||||
|
std::mutex m_async_reply_lock;
|
||||||
|
std::queue<AsyncReply> m_async_replies;
|
||||||
|
// TODO: Maybe move away from Common::HttpRequest?
|
||||||
|
Common::HttpRequest m_http{std::chrono::minutes{1}};
|
||||||
};
|
};
|
||||||
} // namespace IOS::HLE
|
} // namespace IOS::HLE
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "Core/IOS/Network/KD/VFF/VFFUtil.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
// Does not compile if diskio.h is included first.
|
||||||
|
// clang-format off
|
||||||
|
#include "ff.h"
|
||||||
|
#include "diskio.h"
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#include "Common/Align.h"
|
||||||
|
#include "Common/FatFsUtil.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Common/ScopeGuard.h"
|
||||||
|
#include "Common/Swap.h"
|
||||||
|
|
||||||
|
#include "Core/IOS/Uids.h"
|
||||||
|
|
||||||
|
static DRESULT read_vff_header(IOS::HLE::FS::FileHandle* vff, FATFS* fs)
|
||||||
|
{
|
||||||
|
struct IOS::HLE::NWC24::VFFHeader header;
|
||||||
|
if (!vff->Read(&header, 1))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to read VFF header.");
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 cluster_size = 0;
|
||||||
|
u16 cluster_count = 0;
|
||||||
|
|
||||||
|
switch (Common::swap16(header.endianness))
|
||||||
|
{
|
||||||
|
case IOS::HLE::NWC24::VF_BIG_ENDIAN:
|
||||||
|
cluster_size = Common::swap16(header.cluster_size) * 16;
|
||||||
|
cluster_count = Common::swap32(header.volume_size) / cluster_size;
|
||||||
|
break;
|
||||||
|
case IOS::HLE::NWC24::VF_LITTLE_ENDIAN:
|
||||||
|
// TODO: Actually implement.
|
||||||
|
// Another option is to just delete these VFFs and let the current channel create a Big Endian
|
||||||
|
// one.
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cluster_count < 4085)
|
||||||
|
{
|
||||||
|
fs->fs_type = FS_FAT12;
|
||||||
|
|
||||||
|
u32 table_size = ((cluster_count + 1) / 2) * 3;
|
||||||
|
table_size = Common::AlignUp(table_size, cluster_size);
|
||||||
|
|
||||||
|
// Fsize is the full table size divided by 512 (Cluster size).
|
||||||
|
fs->fsize = table_size / 512;
|
||||||
|
}
|
||||||
|
else if (cluster_count < 65525)
|
||||||
|
{
|
||||||
|
fs->fs_type = FS_FAT16;
|
||||||
|
|
||||||
|
u32 table_size = cluster_count * 2;
|
||||||
|
table_size = Common::AlignUp(table_size, cluster_size);
|
||||||
|
fs->fsize = table_size / 512;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "VFF not FAT12 or 16! Cluster size: {}", cluster_size);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs->n_fats = 2;
|
||||||
|
fs->csize = 1;
|
||||||
|
|
||||||
|
// Root directory entry is 4096 bytes long, with each entry being 32 bytes. 4096 / 32 = 128
|
||||||
|
fs->n_rootdir = 128;
|
||||||
|
|
||||||
|
u32 sysect = 1 + (fs->fsize * 2) + fs->n_rootdir / (512 / 32);
|
||||||
|
|
||||||
|
// cluster_count is the total count whereas this is the actual amount of clusters we can use
|
||||||
|
u32 actual_cluster_count = cluster_count - sysect;
|
||||||
|
|
||||||
|
fs->n_fatent = actual_cluster_count + 2;
|
||||||
|
fs->volbase = 0;
|
||||||
|
fs->fatbase = 1;
|
||||||
|
fs->database = sysect;
|
||||||
|
// Root directory entry
|
||||||
|
fs->dirbase = fs->fatbase + fs->fsize * 2;
|
||||||
|
|
||||||
|
// Initialize cluster allocation information
|
||||||
|
fs->last_clst = fs->free_clst = 0xFFFFFFFF;
|
||||||
|
fs->fsi_flag = 0x80;
|
||||||
|
|
||||||
|
fs->id = 0;
|
||||||
|
fs->cdir = 0;
|
||||||
|
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FRESULT vff_mount(IOS::HLE::FS::FileHandle* vff, FATFS* fs)
|
||||||
|
{
|
||||||
|
fs->fs_type = 0; // Clear the filesystem object
|
||||||
|
fs->pdrv = 0; // Volume hosting physical drive
|
||||||
|
|
||||||
|
DRESULT ret = read_vff_header(vff, fs);
|
||||||
|
if (ret != RES_OK)
|
||||||
|
return FR_DISK_ERR;
|
||||||
|
|
||||||
|
return FR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DRESULT vff_read(IOS::HLE::FS::FileHandle* vff, BYTE pdrv, BYTE* buff, LBA_t sector,
|
||||||
|
UINT count)
|
||||||
|
{
|
||||||
|
// We cannot read or write data to the 0th sector in a VFF.
|
||||||
|
if (sector == 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Attempted to read the 0th sector in the VFF: Invalid VFF?");
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 offset = static_cast<u64>(sector) * IOS::HLE::NWC24::SECTOR_SIZE - 480;
|
||||||
|
if (!vff->Seek(offset, IOS::HLE::FS::SeekMode::Set))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "VFF seek failed (offset={})", offset);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t size = static_cast<size_t>(count) * IOS::HLE::NWC24::SECTOR_SIZE;
|
||||||
|
if (!vff->Read(buff, size))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "VFF read failed (offset={}, size={})", offset, size);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DRESULT vff_write(IOS::HLE::FS::FileHandle* vff, BYTE pdrv, const BYTE* buff, LBA_t sector,
|
||||||
|
UINT count)
|
||||||
|
{
|
||||||
|
if (sector == 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Attempted to write to the 0th sector in the VFF: Invalid VFF?");
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 offset = static_cast<u64>(sector) * IOS::HLE::NWC24::SECTOR_SIZE - 480;
|
||||||
|
if (!vff->Seek(offset, IOS::HLE::FS::SeekMode::Set))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "VFF seek failed (offset={})", offset);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t size = static_cast<size_t>(count) * IOS::HLE::NWC24::SECTOR_SIZE;
|
||||||
|
const auto res = vff->Write(buff, size);
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "VFF write failed (offset={}, size={})", offset, size);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DRESULT vff_ioctl(IOS::HLE::FS::FileHandle* vff, BYTE pdrv, BYTE cmd, void* buff)
|
||||||
|
{
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case CTRL_SYNC:
|
||||||
|
return RES_OK;
|
||||||
|
case GET_SECTOR_COUNT:
|
||||||
|
*reinterpret_cast<LBA_t*>(buff) = vff->GetStatus()->size / IOS::HLE::NWC24::SECTOR_SIZE;
|
||||||
|
return RES_OK;
|
||||||
|
default:
|
||||||
|
WARN_LOG_FMT(IOS_WC24, "Unexpected FAT ioctl {}", cmd);
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace IOS::HLE::NWC24
|
||||||
|
{
|
||||||
|
static ErrorCode WriteFile(const std::string& filename, const std::vector<u8>& tmp_buffer)
|
||||||
|
{
|
||||||
|
FIL dst;
|
||||||
|
const auto open_error_code = f_open(&dst, filename.c_str(), FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
|
if (open_error_code != FR_OK)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to open file {} in VFF", filename);
|
||||||
|
return WC24_ERR_FILE_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = tmp_buffer.size();
|
||||||
|
size_t offset = 0;
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
constexpr size_t MAX_CHUNK_SIZE = 32768;
|
||||||
|
u32 chunk_size = static_cast<u32>(std::min(size, MAX_CHUNK_SIZE));
|
||||||
|
|
||||||
|
u32 written_size;
|
||||||
|
const auto write_error_code =
|
||||||
|
f_write(&dst, tmp_buffer.data() + offset, chunk_size, &written_size);
|
||||||
|
if (write_error_code != FR_OK)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to write file {} to VFF {}", filename, write_error_code);
|
||||||
|
return WC24_ERR_FILE_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (written_size != chunk_size)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to write bytes of file {} to VFF ({} != {})", filename,
|
||||||
|
written_size, chunk_size);
|
||||||
|
return WC24_ERR_FILE_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
size -= chunk_size;
|
||||||
|
offset += chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto close_error_code = f_close(&dst);
|
||||||
|
if (close_error_code != FR_OK)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to close file {} in VFF", filename);
|
||||||
|
return WC24_ERR_FILE_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WC24_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class VffFatFsCallbacks : public Common::FatFsCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int DiskRead(u8 pdrv, u8* buff, u32 sector, unsigned int count) override
|
||||||
|
{
|
||||||
|
return vff_read(m_vff, pdrv, buff, sector, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DiskWrite(u8 pdrv, const u8* buff, u32 sector, unsigned int count) override
|
||||||
|
{
|
||||||
|
return vff_write(m_vff, pdrv, buff, sector, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DiskIOCtl(u8 pdrv, u8 cmd, void* buff) override { return vff_ioctl(m_vff, pdrv, cmd, buff); }
|
||||||
|
|
||||||
|
IOS::HLE::FS::FileHandle* m_vff;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ErrorCode OpenVFF(const std::string& path, const std::string& filename,
|
||||||
|
const std::shared_ptr<FS::FileSystem>& fs, const std::vector<u8>& data)
|
||||||
|
{
|
||||||
|
VffFatFsCallbacks callbacks;
|
||||||
|
ErrorCode return_value;
|
||||||
|
Common::RunInFatFsContext(callbacks, [&]() {
|
||||||
|
auto temp = fs->OpenFile(PID_KD, PID_KD, path, FS::Mode::ReadWrite);
|
||||||
|
if (!temp)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to open VFF at: {}", path);
|
||||||
|
return_value = WC24_ERR_NOT_FOUND;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.m_vff = &*temp;
|
||||||
|
|
||||||
|
Common::ScopeGuard vff_delete_guard{[&] { fs->Delete(PID_KD, PID_KD, path); }};
|
||||||
|
|
||||||
|
FATFS fatfs;
|
||||||
|
const FRESULT fatfs_mount_error_code = f_mount(&fatfs, "", 0);
|
||||||
|
if (fatfs_mount_error_code != FR_OK)
|
||||||
|
{
|
||||||
|
// The VFF is most likely broken.
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to mount VFF at: {}", path);
|
||||||
|
return_value = WC24_ERR_BROKEN;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FRESULT vff_mount_error_code = vff_mount(callbacks.m_vff, &fatfs);
|
||||||
|
if (vff_mount_error_code != FR_OK)
|
||||||
|
{
|
||||||
|
// The VFF is most likely broken.
|
||||||
|
ERROR_LOG_FMT(IOS_WC24, "Failed to mount VFF at: {}", path);
|
||||||
|
return_value = WC24_ERR_BROKEN;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::ScopeGuard unmount_guard{[] { f_unmount(""); }};
|
||||||
|
|
||||||
|
const auto write_error_code = WriteFile(filename, data);
|
||||||
|
if (write_error_code != WC24_OK)
|
||||||
|
{
|
||||||
|
return_value = write_error_code;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vff_delete_guard.Dismiss();
|
||||||
|
|
||||||
|
return_value = WC24_OK;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
} // namespace IOS::HLE::NWC24
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/FS/FileSystem.h"
|
||||||
|
#include "Core/IOS/Network/KD/NWC24Config.h"
|
||||||
|
|
||||||
|
namespace IOS::HLE
|
||||||
|
{
|
||||||
|
namespace FS
|
||||||
|
{
|
||||||
|
class FileSystem;
|
||||||
|
}
|
||||||
|
namespace NWC24
|
||||||
|
{
|
||||||
|
constexpr u16 SECTOR_SIZE = 512;
|
||||||
|
constexpr u16 VF_LITTLE_ENDIAN = 0xFFFE;
|
||||||
|
constexpr u16 VF_BIG_ENDIAN = 0xFEFF;
|
||||||
|
ErrorCode OpenVFF(const std::string& path, const std::string& filename,
|
||||||
|
const std::shared_ptr<FS::FileSystem>& fs, const std::vector<u8>& data);
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct VFFHeader final
|
||||||
|
{
|
||||||
|
u8 magic[4];
|
||||||
|
u16 endianness;
|
||||||
|
u16 unknown_marker;
|
||||||
|
u32 volume_size;
|
||||||
|
u16 cluster_size;
|
||||||
|
u16 empty;
|
||||||
|
u16 unknown;
|
||||||
|
u8 padding[14];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(VFFHeader) == 32);
|
||||||
|
#pragma pack(pop)
|
||||||
|
} // namespace NWC24
|
||||||
|
} // namespace IOS::HLE
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
namespace IOS::HLE::NWC24
|
||||||
|
{
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct WC24File final
|
||||||
|
{
|
||||||
|
char magic[4];
|
||||||
|
u32 version;
|
||||||
|
u32 filler;
|
||||||
|
u8 crypt_type;
|
||||||
|
u8 padding[3];
|
||||||
|
u8 reserved[32];
|
||||||
|
u8 iv[16];
|
||||||
|
u8 rsa_signature[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WC24PubkMod final
|
||||||
|
{
|
||||||
|
u8 rsa_public[256];
|
||||||
|
u8 rsa_reserved[256];
|
||||||
|
u8 aes_key[16];
|
||||||
|
u8 aes_reserved[16];
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
} // namespace IOS::HLE::NWC24
|
|
@ -354,6 +354,9 @@
|
||||||
<ClInclude Include="Core\IOS\Network\KD\NetKDRequest.h" />
|
<ClInclude Include="Core\IOS\Network\KD\NetKDRequest.h" />
|
||||||
<ClInclude Include="Core\IOS\Network\KD\NetKDTime.h" />
|
<ClInclude Include="Core\IOS\Network\KD\NetKDTime.h" />
|
||||||
<ClInclude Include="Core\IOS\Network\KD\NWC24Config.h" />
|
<ClInclude Include="Core\IOS\Network\KD\NWC24Config.h" />
|
||||||
|
<ClInclude Include="Core\IOS\Network\KD\NWC24DL.h" />
|
||||||
|
<ClInclude Include="Core\IOS\Network\KD\VFF\VFFUtil.h" />
|
||||||
|
<ClInclude Include="Core\IOS\Network\KD\WC24File.h" />
|
||||||
<ClInclude Include="Core\IOS\Network\MACUtils.h" />
|
<ClInclude Include="Core\IOS\Network\MACUtils.h" />
|
||||||
<ClInclude Include="Core\IOS\Network\NCD\Manage.h" />
|
<ClInclude Include="Core\IOS\Network\NCD\Manage.h" />
|
||||||
<ClInclude Include="Core\IOS\Network\NCD\WiiNetConfig.h" />
|
<ClInclude Include="Core\IOS\Network\NCD\WiiNetConfig.h" />
|
||||||
|
@ -980,6 +983,8 @@
|
||||||
<ClCompile Include="Core\IOS\Network\KD\NetKDRequest.cpp" />
|
<ClCompile Include="Core\IOS\Network\KD\NetKDRequest.cpp" />
|
||||||
<ClCompile Include="Core\IOS\Network\KD\NetKDTime.cpp" />
|
<ClCompile Include="Core\IOS\Network\KD\NetKDTime.cpp" />
|
||||||
<ClCompile Include="Core\IOS\Network\KD\NWC24Config.cpp" />
|
<ClCompile Include="Core\IOS\Network\KD\NWC24Config.cpp" />
|
||||||
|
<ClCompile Include="Core\IOS\Network\KD\NWC24DL.cpp" />
|
||||||
|
<ClCompile Include="Core\IOS\Network\KD\VFF\VFFUtil.cpp" />
|
||||||
<ClCompile Include="Core\IOS\Network\MACUtils.cpp" />
|
<ClCompile Include="Core\IOS\Network\MACUtils.cpp" />
|
||||||
<ClCompile Include="Core\IOS\Network\NCD\Manage.cpp" />
|
<ClCompile Include="Core\IOS\Network\NCD\Manage.cpp" />
|
||||||
<ClCompile Include="Core\IOS\Network\NCD\WiiNetConfig.cpp" />
|
<ClCompile Include="Core\IOS\Network\NCD\WiiNetConfig.cpp" />
|
||||||
|
|
Loading…
Reference in New Issue