Merge pull request #5333 from leoetlino/es-functions
IOS/ES: Handle contexts properly
This commit is contained in:
commit
da880c2a98
|
@ -154,9 +154,10 @@ ReturnCode Device::Open(const OpenRequest& request)
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::Close()
|
ReturnCode Device::Close(u32 fd)
|
||||||
{
|
{
|
||||||
m_is_active = false;
|
m_is_active = false;
|
||||||
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult Device::Unsupported(const Request& request)
|
IPCCommandResult Device::Unsupported(const Request& request)
|
||||||
|
|
|
@ -185,7 +185,7 @@ public:
|
||||||
// Replies to Open and Close requests are sent by the IPC request handler (HandleCommand),
|
// Replies to Open and Close requests are sent by the IPC request handler (HandleCommand),
|
||||||
// not by the devices themselves.
|
// not by the devices themselves.
|
||||||
virtual ReturnCode Open(const OpenRequest& request);
|
virtual ReturnCode Open(const OpenRequest& request);
|
||||||
virtual void Close();
|
virtual ReturnCode Close(u32 fd);
|
||||||
virtual IPCCommandResult Seek(const SeekRequest& seek) { return Unsupported(seek); }
|
virtual IPCCommandResult Seek(const SeekRequest& seek) { return Unsupported(seek); }
|
||||||
virtual IPCCommandResult Read(const ReadWriteRequest& read) { return Unsupported(read); }
|
virtual IPCCommandResult Read(const ReadWriteRequest& read) { return Unsupported(read); }
|
||||||
virtual IPCCommandResult Write(const ReadWriteRequest& write) { return Unsupported(write); }
|
virtual IPCCommandResult Write(const ReadWriteRequest& write) { return Unsupported(write); }
|
||||||
|
|
|
@ -22,12 +22,6 @@ ReturnCode Stub::Open(const OpenRequest& request)
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stub::Close()
|
|
||||||
{
|
|
||||||
WARN_LOG(IOS, "%s faking Close()", m_name.c_str());
|
|
||||||
m_is_active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPCCommandResult Stub::IOCtl(const IOCtlRequest& request)
|
IPCCommandResult Stub::IOCtl(const IOCtlRequest& request)
|
||||||
{
|
{
|
||||||
WARN_LOG(IOS, "%s faking IOCtl()", m_name.c_str());
|
WARN_LOG(IOS, "%s faking IOCtl()", m_name.c_str());
|
||||||
|
|
|
@ -22,7 +22,6 @@ public:
|
||||||
Stub(u32 device_id, const std::string& device_name);
|
Stub(u32 device_id, const std::string& device_name);
|
||||||
|
|
||||||
ReturnCode Open(const OpenRequest& request) override;
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
void Close() override;
|
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "Core/IOS/ES/ES.h"
|
#include "Core/IOS/ES/ES.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -27,17 +28,13 @@ namespace HLE
|
||||||
{
|
{
|
||||||
namespace Device
|
namespace Device
|
||||||
{
|
{
|
||||||
// Shared across all ES instances.
|
// TODO: drop this and convert the title context into a member once the WAD launch hack is gone.
|
||||||
static std::string s_content_file;
|
static std::string s_content_file;
|
||||||
static TitleContext s_title_context;
|
static TitleContext s_title_context;
|
||||||
|
|
||||||
// Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys).
|
// Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys).
|
||||||
static u64 s_title_to_launch;
|
static u64 s_title_to_launch;
|
||||||
|
|
||||||
ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FinishAllStaleImports()
|
static void FinishAllStaleImports()
|
||||||
{
|
{
|
||||||
const std::vector<u64> titles = IOS::ES::GetTitleImports();
|
const std::vector<u64> titles = IOS::ES::GetTitleImports();
|
||||||
|
@ -58,7 +55,7 @@ static void FinishAllStaleImports()
|
||||||
File::CreateDir(import_dir);
|
File::CreateDir(import_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES::Init()
|
ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
|
||||||
{
|
{
|
||||||
FinishAllStaleImports();
|
FinishAllStaleImports();
|
||||||
|
|
||||||
|
@ -182,14 +179,14 @@ static ReturnCode CheckIsAllowedToSetUID(const u32 caller_uid)
|
||||||
return caller_uid == system_menu_uid ? IPC_SUCCESS : ES_EINVAL;
|
return caller_uid == system_menu_uid ? IPC_SUCCESS : ES_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::SetUID(const IOCtlVRequest& request)
|
IPCCommandResult ES::SetUID(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 8)
|
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 8)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
|
|
||||||
const s32 ret = CheckIsAllowedToSetUID(m_caller_uid);
|
const s32 ret = CheckIsAllowedToSetUID(uid);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_ES, "SetUID: Permission check failed with error %d", ret);
|
ERROR_LOG(IOS_ES, "SetUID: Permission check failed with error %d", ret);
|
||||||
|
@ -279,6 +276,24 @@ bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload)
|
||||||
return BootstrapPPC(content_loader);
|
return BootstrapPPC(content_loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ES::Context::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
p.Do(uid);
|
||||||
|
p.Do(gid);
|
||||||
|
|
||||||
|
title_import.tmd.DoState(p);
|
||||||
|
p.Do(title_import.content_id);
|
||||||
|
p.Do(title_import.content_buffer);
|
||||||
|
|
||||||
|
p.Do(title_export.valid);
|
||||||
|
title_export.tmd.DoState(p);
|
||||||
|
p.Do(title_export.title_key);
|
||||||
|
p.Do(title_export.contents);
|
||||||
|
|
||||||
|
p.Do(active);
|
||||||
|
p.Do(ipc_fd);
|
||||||
|
}
|
||||||
|
|
||||||
void ES::DoState(PointerWrap& p)
|
void ES::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
Device::DoState(p);
|
Device::DoState(p);
|
||||||
|
@ -286,17 +301,8 @@ void ES::DoState(PointerWrap& p)
|
||||||
p.Do(m_AccessIdentID);
|
p.Do(m_AccessIdentID);
|
||||||
s_title_context.DoState(p);
|
s_title_context.DoState(p);
|
||||||
|
|
||||||
m_addtitle_tmd.DoState(p);
|
for (auto& context : m_contexts)
|
||||||
p.Do(m_addtitle_content_id);
|
context.DoState(p);
|
||||||
p.Do(m_addtitle_content_buffer);
|
|
||||||
|
|
||||||
p.Do(m_caller_uid);
|
|
||||||
p.Do(m_caller_gid);
|
|
||||||
|
|
||||||
p.Do(m_export_title_context.valid);
|
|
||||||
m_export_title_context.tmd.DoState(p);
|
|
||||||
p.Do(m_export_title_context.title_key);
|
|
||||||
p.Do(m_export_title_context.contents);
|
|
||||||
|
|
||||||
u32 Count = (u32)(m_ContentAccessMap.size());
|
u32 Count = (u32)(m_ContentAccessMap.size());
|
||||||
p.Do(Count);
|
p.Do(Count);
|
||||||
|
@ -322,16 +328,41 @@ void ES::DoState(PointerWrap& p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ES::ContextArray::iterator ES::FindActiveContext(u32 fd)
|
||||||
|
{
|
||||||
|
return std::find_if(m_contexts.begin(), m_contexts.end(),
|
||||||
|
[fd](const auto& context) { return context.ipc_fd == fd && context.active; });
|
||||||
|
}
|
||||||
|
|
||||||
|
ES::ContextArray::iterator ES::FindInactiveContext()
|
||||||
|
{
|
||||||
|
return std::find_if(m_contexts.begin(), m_contexts.end(),
|
||||||
|
[](const auto& context) { return !context.active; });
|
||||||
|
}
|
||||||
|
|
||||||
ReturnCode ES::Open(const OpenRequest& request)
|
ReturnCode ES::Open(const OpenRequest& request)
|
||||||
{
|
{
|
||||||
m_caller_uid = request.uid;
|
auto context = FindInactiveContext();
|
||||||
m_caller_gid = request.gid;
|
if (context == m_contexts.end())
|
||||||
|
return ES_FD_EXHAUSTED;
|
||||||
|
|
||||||
|
context->active = true;
|
||||||
|
context->uid = request.uid;
|
||||||
|
context->gid = request.gid;
|
||||||
|
context->ipc_fd = request.fd;
|
||||||
return Device::Open(request);
|
return Device::Open(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES::Close()
|
ReturnCode ES::Close(u32 fd)
|
||||||
{
|
{
|
||||||
// XXX: does IOS really clear the content access map here?
|
auto context = FindActiveContext(fd);
|
||||||
|
if (context == m_contexts.end())
|
||||||
|
return ES_EINVAL;
|
||||||
|
|
||||||
|
context->active = false;
|
||||||
|
context->ipc_fd = -1;
|
||||||
|
|
||||||
|
// FIXME: IOS doesn't clear the content access map here.
|
||||||
m_ContentAccessMap.clear();
|
m_ContentAccessMap.clear();
|
||||||
m_AccessIdentID = 0;
|
m_AccessIdentID = 0;
|
||||||
|
|
||||||
|
@ -339,48 +370,52 @@ void ES::Close()
|
||||||
m_is_active = false;
|
m_is_active = false;
|
||||||
// clear the NAND content cache to make sure nothing remains open.
|
// clear the NAND content cache to make sure nothing remains open.
|
||||||
DiscIO::CNANDContentManager::Access().ClearCache();
|
DiscIO::CNANDContentManager::Access().ClearCache();
|
||||||
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
|
IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
DEBUG_LOG(IOS_ES, "%s (0x%x)", GetDeviceName().c_str(), request.request);
|
DEBUG_LOG(IOS_ES, "%s (0x%x)", GetDeviceName().c_str(), request.request);
|
||||||
|
auto context = FindActiveContext(request.fd);
|
||||||
|
if (context == m_contexts.end())
|
||||||
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
switch (request.request)
|
switch (request.request)
|
||||||
{
|
{
|
||||||
case IOCTL_ES_ADDTICKET:
|
case IOCTL_ES_ADDTICKET:
|
||||||
return AddTicket(request);
|
return AddTicket(request);
|
||||||
case IOCTL_ES_ADDTMD:
|
case IOCTL_ES_ADDTMD:
|
||||||
return AddTMD(request);
|
return AddTMD(*context, request);
|
||||||
case IOCTL_ES_ADDTITLESTART:
|
case IOCTL_ES_ADDTITLESTART:
|
||||||
return AddTitleStart(request);
|
return AddTitleStart(*context, request);
|
||||||
case IOCTL_ES_ADDCONTENTSTART:
|
case IOCTL_ES_ADDCONTENTSTART:
|
||||||
return AddContentStart(request);
|
return AddContentStart(*context, request);
|
||||||
case IOCTL_ES_ADDCONTENTDATA:
|
case IOCTL_ES_ADDCONTENTDATA:
|
||||||
return AddContentData(request);
|
return AddContentData(*context, request);
|
||||||
case IOCTL_ES_ADDCONTENTFINISH:
|
case IOCTL_ES_ADDCONTENTFINISH:
|
||||||
return AddContentFinish(request);
|
return AddContentFinish(*context, request);
|
||||||
case IOCTL_ES_ADDTITLEFINISH:
|
case IOCTL_ES_ADDTITLEFINISH:
|
||||||
return AddTitleFinish(request);
|
return AddTitleFinish(*context, request);
|
||||||
case IOCTL_ES_ADDTITLECANCEL:
|
case IOCTL_ES_ADDTITLECANCEL:
|
||||||
return AddTitleCancel(request);
|
return AddTitleCancel(*context, request);
|
||||||
case IOCTL_ES_GETDEVICEID:
|
case IOCTL_ES_GETDEVICEID:
|
||||||
return GetConsoleID(request);
|
return GetConsoleID(request);
|
||||||
case IOCTL_ES_OPENTITLECONTENT:
|
case IOCTL_ES_OPENTITLECONTENT:
|
||||||
return OpenTitleContent(request);
|
return OpenTitleContent(context->uid, request);
|
||||||
case IOCTL_ES_OPENCONTENT:
|
case IOCTL_ES_OPENCONTENT:
|
||||||
return OpenContent(request);
|
return OpenContent(context->uid, request);
|
||||||
case IOCTL_ES_READCONTENT:
|
case IOCTL_ES_READCONTENT:
|
||||||
return ReadContent(request);
|
return ReadContent(context->uid, request);
|
||||||
case IOCTL_ES_CLOSECONTENT:
|
case IOCTL_ES_CLOSECONTENT:
|
||||||
return CloseContent(request);
|
return CloseContent(context->uid, request);
|
||||||
case IOCTL_ES_SEEKCONTENT:
|
case IOCTL_ES_SEEKCONTENT:
|
||||||
return SeekContent(request);
|
return SeekContent(context->uid, request);
|
||||||
case IOCTL_ES_GETTITLEDIR:
|
case IOCTL_ES_GETTITLEDIR:
|
||||||
return GetTitleDirectory(request);
|
return GetTitleDirectory(request);
|
||||||
case IOCTL_ES_GETTITLEID:
|
case IOCTL_ES_GETTITLEID:
|
||||||
return GetTitleID(request);
|
return GetTitleID(request);
|
||||||
case IOCTL_ES_SETUID:
|
case IOCTL_ES_SETUID:
|
||||||
return SetUID(request);
|
return SetUID(context->uid, request);
|
||||||
case IOCTL_ES_DIVERIFY:
|
case IOCTL_ES_DIVERIFY:
|
||||||
return DIVerify(request);
|
return DIVerify(request);
|
||||||
|
|
||||||
|
@ -441,23 +476,23 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
|
||||||
case IOCTL_ES_GETSTOREDTMD:
|
case IOCTL_ES_GETSTOREDTMD:
|
||||||
return GetStoredTMD(request);
|
return GetStoredTMD(request);
|
||||||
case IOCTL_ES_ENCRYPT:
|
case IOCTL_ES_ENCRYPT:
|
||||||
return Encrypt(request);
|
return Encrypt(context->uid, request);
|
||||||
case IOCTL_ES_DECRYPT:
|
case IOCTL_ES_DECRYPT:
|
||||||
return Decrypt(request);
|
return Decrypt(context->uid, request);
|
||||||
case IOCTL_ES_LAUNCH:
|
case IOCTL_ES_LAUNCH:
|
||||||
return Launch(request);
|
return Launch(request);
|
||||||
case IOCTL_ES_LAUNCHBC:
|
case IOCTL_ES_LAUNCHBC:
|
||||||
return LaunchBC(request);
|
return LaunchBC(request);
|
||||||
case IOCTL_ES_EXPORTTITLEINIT:
|
case IOCTL_ES_EXPORTTITLEINIT:
|
||||||
return ExportTitleInit(request);
|
return ExportTitleInit(*context, request);
|
||||||
case IOCTL_ES_EXPORTCONTENTBEGIN:
|
case IOCTL_ES_EXPORTCONTENTBEGIN:
|
||||||
return ExportContentBegin(request);
|
return ExportContentBegin(*context, request);
|
||||||
case IOCTL_ES_EXPORTCONTENTDATA:
|
case IOCTL_ES_EXPORTCONTENTDATA:
|
||||||
return ExportContentData(request);
|
return ExportContentData(*context, request);
|
||||||
case IOCTL_ES_EXPORTCONTENTEND:
|
case IOCTL_ES_EXPORTCONTENTEND:
|
||||||
return ExportContentEnd(request);
|
return ExportContentEnd(*context, request);
|
||||||
case IOCTL_ES_EXPORTTITLEDONE:
|
case IOCTL_ES_EXPORTTITLEDONE:
|
||||||
return ExportTitleDone(request);
|
return ExportTitleDone(*context, request);
|
||||||
case IOCTL_ES_CHECKKOREAREGION:
|
case IOCTL_ES_CHECKKOREAREGION:
|
||||||
return CheckKoreaRegion(request);
|
return CheckKoreaRegion(request);
|
||||||
case IOCTL_ES_GETDEVICECERT:
|
case IOCTL_ES_GETDEVICECERT:
|
||||||
|
|
|
@ -45,9 +45,6 @@ class ES final : public Device
|
||||||
public:
|
public:
|
||||||
ES(u32 device_id, const std::string& device_name);
|
ES(u32 device_id, const std::string& device_name);
|
||||||
|
|
||||||
// Called after an IOS reload.
|
|
||||||
static void Init();
|
|
||||||
|
|
||||||
static s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket);
|
static s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket);
|
||||||
static void LoadWAD(const std::string& _rContentFile);
|
static void LoadWAD(const std::string& _rContentFile);
|
||||||
static bool LaunchTitle(u64 title_id, bool skip_reload = false);
|
static bool LaunchTitle(u64 title_id, bool skip_reload = false);
|
||||||
|
@ -58,7 +55,7 @@ public:
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
ReturnCode Open(const OpenRequest& request) override;
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
void Close() override;
|
ReturnCode Close(u32 fd) override;
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -155,20 +152,57 @@ private:
|
||||||
u8 padding[0x3c];
|
u8 padding[0x3c];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TitleImportContext
|
||||||
|
{
|
||||||
|
IOS::ES::TMDReader tmd;
|
||||||
|
u32 content_id = 0xFFFFFFFF;
|
||||||
|
std::vector<u8> content_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: merge this with TitleImportContext. Also reuse the global content table.
|
||||||
|
struct TitleExportContext
|
||||||
|
{
|
||||||
|
struct ExportContent
|
||||||
|
{
|
||||||
|
OpenedContent content;
|
||||||
|
std::array<u8, 16> iv{};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool valid = false;
|
||||||
|
IOS::ES::TMDReader tmd;
|
||||||
|
std::vector<u8> title_key;
|
||||||
|
std::map<u32, ExportContent> contents;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Context
|
||||||
|
{
|
||||||
|
void DoState(PointerWrap& p);
|
||||||
|
|
||||||
|
u16 gid = 0;
|
||||||
|
u32 uid = 0;
|
||||||
|
TitleImportContext title_import;
|
||||||
|
TitleExportContext title_export;
|
||||||
|
bool active = false;
|
||||||
|
// We use this to associate an IPC fd with an ES context.
|
||||||
|
u32 ipc_fd = -1;
|
||||||
|
};
|
||||||
|
// ES can only have 3 contexts at one time.
|
||||||
|
using ContextArray = std::array<Context, 3>;
|
||||||
|
|
||||||
// Title management
|
// Title management
|
||||||
IPCCommandResult AddTicket(const IOCtlVRequest& request);
|
IPCCommandResult AddTicket(const IOCtlVRequest& request);
|
||||||
IPCCommandResult AddTMD(const IOCtlVRequest& request);
|
IPCCommandResult AddTMD(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult AddTitleStart(const IOCtlVRequest& request);
|
IPCCommandResult AddTitleStart(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult AddContentStart(const IOCtlVRequest& request);
|
IPCCommandResult AddContentStart(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult AddContentData(const IOCtlVRequest& request);
|
IPCCommandResult AddContentData(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult AddContentFinish(const IOCtlVRequest& request);
|
IPCCommandResult AddContentFinish(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult AddTitleFinish(const IOCtlVRequest& request);
|
IPCCommandResult AddTitleFinish(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult AddTitleCancel(const IOCtlVRequest& request);
|
IPCCommandResult AddTitleCancel(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult ExportTitleInit(const IOCtlVRequest& request);
|
IPCCommandResult ExportTitleInit(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult ExportContentBegin(const IOCtlVRequest& request);
|
IPCCommandResult ExportContentBegin(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult ExportContentData(const IOCtlVRequest& request);
|
IPCCommandResult ExportContentData(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult ExportContentEnd(const IOCtlVRequest& request);
|
IPCCommandResult ExportContentEnd(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult ExportTitleDone(const IOCtlVRequest& request);
|
IPCCommandResult ExportTitleDone(Context& context, const IOCtlVRequest& request);
|
||||||
IPCCommandResult DeleteTitle(const IOCtlVRequest& request);
|
IPCCommandResult DeleteTitle(const IOCtlVRequest& request);
|
||||||
IPCCommandResult DeleteTicket(const IOCtlVRequest& request);
|
IPCCommandResult DeleteTicket(const IOCtlVRequest& request);
|
||||||
IPCCommandResult DeleteTitleContent(const IOCtlVRequest& request);
|
IPCCommandResult DeleteTitleContent(const IOCtlVRequest& request);
|
||||||
|
@ -178,11 +212,11 @@ private:
|
||||||
IPCCommandResult GetDeviceCertificate(const IOCtlVRequest& request);
|
IPCCommandResult GetDeviceCertificate(const IOCtlVRequest& request);
|
||||||
IPCCommandResult CheckKoreaRegion(const IOCtlVRequest& request);
|
IPCCommandResult CheckKoreaRegion(const IOCtlVRequest& request);
|
||||||
IPCCommandResult Sign(const IOCtlVRequest& request);
|
IPCCommandResult Sign(const IOCtlVRequest& request);
|
||||||
IPCCommandResult Encrypt(const IOCtlVRequest& request);
|
IPCCommandResult Encrypt(u32 uid, const IOCtlVRequest& request);
|
||||||
IPCCommandResult Decrypt(const IOCtlVRequest& request);
|
IPCCommandResult Decrypt(u32 uid, const IOCtlVRequest& request);
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
IPCCommandResult SetUID(const IOCtlVRequest& request);
|
IPCCommandResult SetUID(u32 uid, const IOCtlVRequest& request);
|
||||||
IPCCommandResult GetTitleDirectory(const IOCtlVRequest& request);
|
IPCCommandResult GetTitleDirectory(const IOCtlVRequest& request);
|
||||||
IPCCommandResult GetTitleID(const IOCtlVRequest& request);
|
IPCCommandResult GetTitleID(const IOCtlVRequest& request);
|
||||||
IPCCommandResult GetConsumption(const IOCtlVRequest& request);
|
IPCCommandResult GetConsumption(const IOCtlVRequest& request);
|
||||||
|
@ -191,11 +225,11 @@ private:
|
||||||
IPCCommandResult DIVerify(const IOCtlVRequest& request);
|
IPCCommandResult DIVerify(const IOCtlVRequest& request);
|
||||||
|
|
||||||
// Title contents
|
// Title contents
|
||||||
IPCCommandResult OpenTitleContent(const IOCtlVRequest& request);
|
IPCCommandResult OpenTitleContent(u32 uid, const IOCtlVRequest& request);
|
||||||
IPCCommandResult OpenContent(const IOCtlVRequest& request);
|
IPCCommandResult OpenContent(u32 uid, const IOCtlVRequest& request);
|
||||||
IPCCommandResult ReadContent(const IOCtlVRequest& request);
|
IPCCommandResult ReadContent(u32 uid, const IOCtlVRequest& request);
|
||||||
IPCCommandResult CloseContent(const IOCtlVRequest& request);
|
IPCCommandResult CloseContent(u32 uid, const IOCtlVRequest& request);
|
||||||
IPCCommandResult SeekContent(const IOCtlVRequest& request);
|
IPCCommandResult SeekContent(u32 uid, const IOCtlVRequest& request);
|
||||||
|
|
||||||
// Title information
|
// Title information
|
||||||
IPCCommandResult GetTitleCount(const std::vector<u64>& titles, const IOCtlVRequest& request);
|
IPCCommandResult GetTitleCount(const std::vector<u64>& titles, const IOCtlVRequest& request);
|
||||||
|
@ -228,6 +262,9 @@ private:
|
||||||
IPCCommandResult DIGetTMDSize(const IOCtlVRequest& request);
|
IPCCommandResult DIGetTMDSize(const IOCtlVRequest& request);
|
||||||
IPCCommandResult DIGetTMD(const IOCtlVRequest& request);
|
IPCCommandResult DIGetTMD(const IOCtlVRequest& request);
|
||||||
|
|
||||||
|
ContextArray::iterator FindActiveContext(u32 fd);
|
||||||
|
ContextArray::iterator FindInactiveContext();
|
||||||
|
|
||||||
static bool LaunchIOS(u64 ios_title_id);
|
static bool LaunchIOS(u64 ios_title_id);
|
||||||
static bool LaunchPPCTitle(u64 title_id, bool skip_reload);
|
static bool LaunchPPCTitle(u64 title_id, bool skip_reload);
|
||||||
static TitleContext& GetTitleContext();
|
static TitleContext& GetTitleContext();
|
||||||
|
@ -241,27 +278,7 @@ private:
|
||||||
|
|
||||||
u32 m_AccessIdentID = 0;
|
u32 m_AccessIdentID = 0;
|
||||||
|
|
||||||
// For title installation (ioctls IOCTL_ES_ADDTITLE*).
|
ContextArray m_contexts;
|
||||||
IOS::ES::TMDReader m_addtitle_tmd;
|
|
||||||
u32 m_addtitle_content_id = 0xFFFFFFFF;
|
|
||||||
std::vector<u8> m_addtitle_content_buffer;
|
|
||||||
|
|
||||||
u32 m_caller_uid = 0;
|
|
||||||
u16 m_caller_gid = 0;
|
|
||||||
|
|
||||||
struct TitleExportContext
|
|
||||||
{
|
|
||||||
struct ExportContent
|
|
||||||
{
|
|
||||||
OpenedContent content;
|
|
||||||
std::array<u8, 16> iv{};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool valid = false;
|
|
||||||
IOS::ES::TMDReader tmd;
|
|
||||||
std::vector<u8> title_key;
|
|
||||||
std::map<u32, ExportContent> contents;
|
|
||||||
} m_export_title_context;
|
|
||||||
};
|
};
|
||||||
} // namespace Device
|
} // namespace Device
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
|
|
|
@ -63,7 +63,7 @@ IPCCommandResult ES::GetConsoleID(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::Encrypt(const IOCtlVRequest& request)
|
IPCCommandResult ES::Encrypt(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(3, 2))
|
if (!request.HasNumberOfValidVectors(3, 2))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -85,7 +85,7 @@ IPCCommandResult ES::Encrypt(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::Decrypt(const IOCtlVRequest& request)
|
IPCCommandResult ES::Decrypt(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(3, 2))
|
if (!request.HasNumberOfValidVectors(3, 2))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
|
@ -48,7 +48,7 @@ u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index)
|
||||||
return CFD;
|
return CFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request)
|
IPCCommandResult ES::OpenTitleContent(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(3, 0))
|
if (!request.HasNumberOfValidVectors(3, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -64,7 +64,7 @@ IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(CFD);
|
return GetDefaultReply(CFD);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::OpenContent(const IOCtlVRequest& request)
|
IPCCommandResult ES::OpenContent(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 0))
|
if (!request.HasNumberOfValidVectors(1, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -79,7 +79,7 @@ IPCCommandResult ES::OpenContent(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(CFD);
|
return GetDefaultReply(CFD);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::ReadContent(const IOCtlVRequest& request)
|
IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 1))
|
if (!request.HasNumberOfValidVectors(1, 1))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -131,7 +131,7 @@ IPCCommandResult ES::ReadContent(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(Size);
|
return GetDefaultReply(Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::CloseContent(const IOCtlVRequest& request)
|
IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 0))
|
if (!request.HasNumberOfValidVectors(1, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -160,7 +160,7 @@ IPCCommandResult ES::CloseContent(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::SeekContent(const IOCtlVRequest& request)
|
IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(3, 0))
|
if (!request.HasNumberOfValidVectors(3, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
|
@ -66,7 +66,7 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::AddTMD(const IOCtlVRequest& request)
|
IPCCommandResult ES::AddTMD(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 0))
|
if (!request.HasNumberOfValidVectors(1, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -76,18 +76,18 @@ IPCCommandResult ES::AddTMD(const IOCtlVRequest& request)
|
||||||
|
|
||||||
// Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
|
// Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
|
||||||
// to either /import or /title. So here we simply have to set the import TMD.
|
// to either /import or /title. So here we simply have to set the import TMD.
|
||||||
m_addtitle_tmd.SetBytes(std::move(tmd));
|
context.title_import.tmd.SetBytes(std::move(tmd));
|
||||||
// TODO: validate TMDs and return the proper error code (-1027) if the signature type is invalid.
|
// TODO: validate TMDs and return the proper error code (-1027) if the signature type is invalid.
|
||||||
if (!m_addtitle_tmd.IsValid())
|
if (!context.title_import.tmd.IsValid())
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
if (!IOS::ES::InitImport(m_addtitle_tmd.GetTitleId()))
|
if (!IOS::ES::InitImport(context.title_import.tmd.GetTitleId()))
|
||||||
return GetDefaultReply(FS_EIO);
|
return GetDefaultReply(FS_EIO);
|
||||||
|
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
|
IPCCommandResult ES::AddTitleStart(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(4, 0))
|
if (!request.HasNumberOfValidVectors(4, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -96,19 +96,20 @@ IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
|
||||||
std::vector<u8> tmd(request.in_vectors[0].size);
|
std::vector<u8> tmd(request.in_vectors[0].size);
|
||||||
Memory::CopyFromEmu(tmd.data(), request.in_vectors[0].address, request.in_vectors[0].size);
|
Memory::CopyFromEmu(tmd.data(), request.in_vectors[0].address, request.in_vectors[0].size);
|
||||||
|
|
||||||
m_addtitle_tmd.SetBytes(tmd);
|
context.title_import.tmd.SetBytes(tmd);
|
||||||
if (!m_addtitle_tmd.IsValid())
|
if (!context.title_import.tmd.IsValid())
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd.size());
|
ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd.size());
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish a previous import (if it exists).
|
// Finish a previous import (if it exists).
|
||||||
const IOS::ES::TMDReader previous_tmd = IOS::ES::FindImportTMD(m_addtitle_tmd.GetTitleId());
|
const IOS::ES::TMDReader previous_tmd =
|
||||||
|
IOS::ES::FindImportTMD(context.title_import.tmd.GetTitleId());
|
||||||
if (previous_tmd.IsValid())
|
if (previous_tmd.IsValid())
|
||||||
FinishImport(previous_tmd);
|
FinishImport(previous_tmd);
|
||||||
|
|
||||||
if (!IOS::ES::InitImport(m_addtitle_tmd.GetTitleId()))
|
if (!IOS::ES::InitImport(context.title_import.tmd.GetTitleId()))
|
||||||
return GetDefaultReply(FS_EIO);
|
return GetDefaultReply(FS_EIO);
|
||||||
|
|
||||||
// TODO: check and use the other vectors.
|
// TODO: check and use the other vectors.
|
||||||
|
@ -116,7 +117,7 @@ IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request)
|
IPCCommandResult ES::AddContentStart(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(2, 0))
|
if (!request.HasNumberOfValidVectors(2, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -124,28 +125,28 @@ IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request)
|
||||||
u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
|
u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
|
||||||
|
|
||||||
if (m_addtitle_content_id != 0xFFFFFFFF)
|
if (context.title_import.content_id != 0xFFFFFFFF)
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_ES, "Trying to add content when we haven't finished adding "
|
ERROR_LOG(IOS_ES, "Trying to add content when we haven't finished adding "
|
||||||
"another content. Unsupported.");
|
"another content. Unsupported.");
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
}
|
}
|
||||||
m_addtitle_content_id = content_id;
|
context.title_import.content_id = content_id;
|
||||||
|
|
||||||
m_addtitle_content_buffer.clear();
|
context.title_import.content_buffer.clear();
|
||||||
|
|
||||||
INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 ", "
|
INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 ", "
|
||||||
"content id %08x",
|
"content id %08x",
|
||||||
title_id, m_addtitle_content_id);
|
title_id, context.title_import.content_id);
|
||||||
|
|
||||||
if (!m_addtitle_tmd.IsValid())
|
if (!context.title_import.tmd.IsValid())
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
if (title_id != m_addtitle_tmd.GetTitleId())
|
if (title_id != context.title_import.tmd.GetTitleId())
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 " != "
|
ERROR_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 " != "
|
||||||
"TMD title id %016" PRIx64 ", ignoring",
|
"TMD title id %016" PRIx64 ", ignoring",
|
||||||
title_id, m_addtitle_tmd.GetTitleId());
|
title_id, context.title_import.tmd.GetTitleId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're supposed to return a "content file descriptor" here, which is
|
// We're supposed to return a "content file descriptor" here, which is
|
||||||
|
@ -156,7 +157,7 @@ IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(content_fd);
|
return GetDefaultReply(content_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::AddContentData(const IOCtlVRequest& request)
|
IPCCommandResult ES::AddContentData(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(2, 0))
|
if (!request.HasNumberOfValidVectors(2, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -168,7 +169,8 @@ IPCCommandResult ES::AddContentData(const IOCtlVRequest& request)
|
||||||
|
|
||||||
u8* data_start = Memory::GetPointer(request.in_vectors[1].address);
|
u8* data_start = Memory::GetPointer(request.in_vectors[1].address);
|
||||||
u8* data_end = data_start + request.in_vectors[1].size;
|
u8* data_end = data_start + request.in_vectors[1].size;
|
||||||
m_addtitle_content_buffer.insert(m_addtitle_content_buffer.end(), data_start, data_end);
|
context.title_import.content_buffer.insert(context.title_import.content_buffer.end(), data_start,
|
||||||
|
data_end);
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,22 +186,22 @@ static std::string GetImportContentPath(u64 title_id, u32 content_id)
|
||||||
return Common::GetImportTitlePath(title_id) + StringFromFormat("/content/%08x.app", content_id);
|
return Common::GetImportTitlePath(title_id) + StringFromFormat("/content/%08x.app", content_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
|
IPCCommandResult ES::AddContentFinish(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 0))
|
if (!request.HasNumberOfValidVectors(1, 0))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
if (m_addtitle_content_id == 0xFFFFFFFF)
|
if (context.title_import.content_id == 0xFFFFFFFF)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
u32 content_fd = Memory::Read_U32(request.in_vectors[0].address);
|
u32 content_fd = Memory::Read_U32(request.in_vectors[0].address);
|
||||||
INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd);
|
INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd);
|
||||||
|
|
||||||
if (!m_addtitle_tmd.IsValid())
|
if (!context.title_import.tmd.IsValid())
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
// Try to find the title key from a pre-installed ticket.
|
// Try to find the title key from a pre-installed ticket.
|
||||||
IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId());
|
IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(context.title_import.tmd.GetTitleId());
|
||||||
if (!ticket.IsValid())
|
if (!ticket.IsValid())
|
||||||
{
|
{
|
||||||
return GetDefaultReply(ES_NO_TICKET);
|
return GetDefaultReply(ES_NO_TICKET);
|
||||||
|
@ -211,16 +213,16 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
|
||||||
// The IV for title content decryption is the lower two bytes of the
|
// The IV for title content decryption is the lower two bytes of the
|
||||||
// content index, zero extended.
|
// content index, zero extended.
|
||||||
IOS::ES::Content content_info;
|
IOS::ES::Content content_info;
|
||||||
if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info))
|
if (!context.title_import.tmd.FindContentById(context.title_import.content_id, &content_info))
|
||||||
{
|
{
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
}
|
}
|
||||||
u8 iv[16] = {0};
|
u8 iv[16] = {0};
|
||||||
iv[0] = (content_info.index >> 8) & 0xFF;
|
iv[0] = (content_info.index >> 8) & 0xFF;
|
||||||
iv[1] = content_info.index & 0xFF;
|
iv[1] = content_info.index & 0xFF;
|
||||||
std::vector<u8> decrypted_data(m_addtitle_content_buffer.size());
|
std::vector<u8> decrypted_data(context.title_import.content_buffer.size());
|
||||||
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, m_addtitle_content_buffer.size(), iv,
|
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, context.title_import.content_buffer.size(),
|
||||||
m_addtitle_content_buffer.data(), decrypted_data.data());
|
iv, context.title_import.content_buffer.data(), decrypted_data.data());
|
||||||
if (!CheckIfContentHashMatches(decrypted_data, content_info))
|
if (!CheckIfContentHashMatches(decrypted_data, content_info))
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_ES, "AddContentFinish: Hash for content %08x doesn't match", content_info.id);
|
ERROR_LOG(IOS_ES, "AddContentFinish: Hash for content %08x doesn't match", content_info.id);
|
||||||
|
@ -235,12 +237,13 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
content_path = GetImportContentPath(m_addtitle_tmd.GetTitleId(), m_addtitle_content_id);
|
content_path = GetImportContentPath(context.title_import.tmd.GetTitleId(),
|
||||||
|
context.title_import.content_id);
|
||||||
}
|
}
|
||||||
File::CreateFullPath(content_path);
|
File::CreateFullPath(content_path);
|
||||||
|
|
||||||
const std::string temp_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) +
|
const std::string temp_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) +
|
||||||
StringFromFormat("/tmp/%08x.app", m_addtitle_content_id);
|
StringFromFormat("/tmp/%08x.app", context.title_import.content_id);
|
||||||
File::CreateFullPath(temp_path);
|
File::CreateFullPath(temp_path);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -258,32 +261,33 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(ES_EIO);
|
return GetDefaultReply(ES_EIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_addtitle_content_id = 0xFFFFFFFF;
|
context.title_import.content_id = 0xFFFFFFFF;
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::AddTitleFinish(const IOCtlVRequest& request)
|
IPCCommandResult ES::AddTitleFinish(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(0, 0) || !m_addtitle_tmd.IsValid())
|
if (!request.HasNumberOfValidVectors(0, 0) || !context.title_import.tmd.IsValid())
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
if (!WriteImportTMD(m_addtitle_tmd))
|
if (!WriteImportTMD(context.title_import.tmd))
|
||||||
return GetDefaultReply(ES_EIO);
|
return GetDefaultReply(ES_EIO);
|
||||||
|
|
||||||
if (!FinishImport(m_addtitle_tmd))
|
if (!FinishImport(context.title_import.tmd))
|
||||||
return GetDefaultReply(FS_EIO);
|
return GetDefaultReply(FS_EIO);
|
||||||
|
|
||||||
INFO_LOG(IOS_ES, "IOCTL_ES_ADDTITLEFINISH");
|
INFO_LOG(IOS_ES, "IOCTL_ES_ADDTITLEFINISH");
|
||||||
m_addtitle_tmd.SetBytes({});
|
context.title_import.tmd.SetBytes({});
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::AddTitleCancel(const IOCtlVRequest& request)
|
IPCCommandResult ES::AddTitleCancel(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(0, 0) || !m_addtitle_tmd.IsValid())
|
if (!request.HasNumberOfValidVectors(0, 0) || !context.title_import.tmd.IsValid())
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
const IOS::ES::TMDReader original_tmd = IOS::ES::FindInstalledTMD(m_addtitle_tmd.GetTitleId());
|
const IOS::ES::TMDReader original_tmd =
|
||||||
|
IOS::ES::FindInstalledTMD(context.title_import.tmd.GetTitleId());
|
||||||
if (!original_tmd.IsValid())
|
if (!original_tmd.IsValid())
|
||||||
{
|
{
|
||||||
// This should never happen unless someone messed with the installed TMD directly.
|
// This should never happen unless someone messed with the installed TMD directly.
|
||||||
|
@ -294,7 +298,7 @@ IPCCommandResult ES::AddTitleCancel(const IOCtlVRequest& request)
|
||||||
if (!FinishImport(original_tmd))
|
if (!FinishImport(original_tmd))
|
||||||
return GetDefaultReply(FS_EIO);
|
return GetDefaultReply(FS_EIO);
|
||||||
|
|
||||||
m_addtitle_tmd.SetBytes({});
|
context.title_import.tmd.SetBytes({});
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,40 +367,40 @@ IPCCommandResult ES::DeleteTitleContent(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::ExportTitleInit(const IOCtlVRequest& request)
|
IPCCommandResult ES::ExportTitleInit(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 8)
|
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 8)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
// No concurrent title import/export is allowed.
|
// No concurrent title import/export is allowed.
|
||||||
if (m_export_title_context.valid)
|
if (context.title_export.valid)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
const auto tmd = IOS::ES::FindInstalledTMD(Memory::Read_U64(request.in_vectors[0].address));
|
const auto tmd = IOS::ES::FindInstalledTMD(Memory::Read_U64(request.in_vectors[0].address));
|
||||||
if (!tmd.IsValid())
|
if (!tmd.IsValid())
|
||||||
return GetDefaultReply(FS_ENOENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
|
|
||||||
m_export_title_context.tmd = tmd;
|
context.title_export.tmd = tmd;
|
||||||
|
|
||||||
const auto ticket = DiscIO::FindSignedTicket(m_export_title_context.tmd.GetTitleId());
|
const auto ticket = DiscIO::FindSignedTicket(context.title_export.tmd.GetTitleId());
|
||||||
if (!ticket.IsValid())
|
if (!ticket.IsValid())
|
||||||
return GetDefaultReply(ES_NO_TICKET);
|
return GetDefaultReply(ES_NO_TICKET);
|
||||||
if (ticket.GetTitleId() != m_export_title_context.tmd.GetTitleId())
|
if (ticket.GetTitleId() != context.title_export.tmd.GetTitleId())
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
m_export_title_context.title_key = ticket.GetTitleKey();
|
context.title_export.title_key = ticket.GetTitleKey();
|
||||||
|
|
||||||
const auto& raw_tmd = m_export_title_context.tmd.GetRawTMD();
|
const auto& raw_tmd = context.title_export.tmd.GetRawTMD();
|
||||||
if (request.io_vectors[0].size != raw_tmd.size())
|
if (request.io_vectors[0].size != raw_tmd.size())
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
|
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
|
||||||
|
|
||||||
m_export_title_context.valid = true;
|
context.title_export.valid = true;
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
|
IPCCommandResult ES::ExportContentBegin(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(2, 0) || request.in_vectors[0].size != 8 ||
|
if (!request.HasNumberOfValidVectors(2, 0) || request.in_vectors[0].size != 8 ||
|
||||||
request.in_vectors[1].size != 4)
|
request.in_vectors[1].size != 4)
|
||||||
|
@ -405,7 +409,7 @@ IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
|
||||||
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
const u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
|
const u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
|
||||||
|
|
||||||
if (!m_export_title_context.valid || m_export_title_context.tmd.GetTitleId() != title_id)
|
if (!context.title_export.valid || context.title_export.tmd.GetTitleId() != title_id)
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_ES, "Tried to use ExportContentBegin with an invalid title export context.");
|
ERROR_LOG(IOS_ES, "Tried to use ExportContentBegin with an invalid title export context.");
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -426,7 +430,7 @@ IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
|
||||||
content->m_Data->Open();
|
content->m_Data->Open();
|
||||||
|
|
||||||
u32 cid = 0;
|
u32 cid = 0;
|
||||||
while (m_export_title_context.contents.find(cid) != m_export_title_context.contents.end())
|
while (context.title_export.contents.find(cid) != context.title_export.contents.end())
|
||||||
cid++;
|
cid++;
|
||||||
|
|
||||||
TitleExportContext::ExportContent content_export;
|
TitleExportContext::ExportContent content_export;
|
||||||
|
@ -434,12 +438,12 @@ IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
|
||||||
content_export.iv[0] = (content->m_metadata.index >> 8) & 0xFF;
|
content_export.iv[0] = (content->m_metadata.index >> 8) & 0xFF;
|
||||||
content_export.iv[1] = content->m_metadata.index & 0xFF;
|
content_export.iv[1] = content->m_metadata.index & 0xFF;
|
||||||
|
|
||||||
m_export_title_context.contents.emplace(cid, content_export);
|
context.title_export.contents.emplace(cid, content_export);
|
||||||
// IOS returns a content ID which is passed to further content calls.
|
// IOS returns a content ID which is passed to further content calls.
|
||||||
return GetDefaultReply(cid);
|
return GetDefaultReply(cid);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
|
IPCCommandResult ES::ExportContentData(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 4 ||
|
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 4 ||
|
||||||
request.io_vectors[0].size == 0)
|
request.io_vectors[0].size == 0)
|
||||||
|
@ -450,8 +454,8 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
|
||||||
const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
|
const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
|
||||||
const u32 bytes_to_read = request.io_vectors[0].size;
|
const u32 bytes_to_read = request.io_vectors[0].size;
|
||||||
|
|
||||||
const auto iterator = m_export_title_context.contents.find(content_id);
|
const auto iterator = context.title_export.contents.find(content_id);
|
||||||
if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() ||
|
if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
|
||||||
iterator->second.content.m_position >= iterator->second.content.m_content.size)
|
iterator->second.content.m_position >= iterator->second.content.m_content.size)
|
||||||
{
|
{
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -479,7 +483,7 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
|
||||||
std::vector<u8> output(buffer.size());
|
std::vector<u8> output(buffer.size());
|
||||||
|
|
||||||
mbedtls_aes_context aes_ctx;
|
mbedtls_aes_context aes_ctx;
|
||||||
mbedtls_aes_setkey_enc(&aes_ctx, m_export_title_context.title_key.data(), 128);
|
mbedtls_aes_setkey_enc(&aes_ctx, context.title_export.title_key.data(), 128);
|
||||||
const int ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer.size(),
|
const int ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer.size(),
|
||||||
iterator->second.iv.data(), buffer.data(), output.data());
|
iterator->second.iv.data(), buffer.data(), output.data());
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
|
@ -494,15 +498,15 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::ExportContentEnd(const IOCtlVRequest& request)
|
IPCCommandResult ES::ExportContentEnd(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 4)
|
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 4)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
|
const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
|
||||||
|
|
||||||
const auto iterator = m_export_title_context.contents.find(content_id);
|
const auto iterator = context.title_export.contents.find(content_id);
|
||||||
if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() ||
|
if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
|
||||||
iterator->second.content.m_position != iterator->second.content.m_content.size)
|
iterator->second.content.m_position != iterator->second.content.m_content.size)
|
||||||
{
|
{
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
@ -513,16 +517,16 @@ IPCCommandResult ES::ExportContentEnd(const IOCtlVRequest& request)
|
||||||
const auto& content_loader = AccessContentDevice(iterator->second.content.m_title_id);
|
const auto& content_loader = AccessContentDevice(iterator->second.content.m_title_id);
|
||||||
content_loader.GetContentByID(iterator->second.content.m_content.id)->m_Data->Close();
|
content_loader.GetContentByID(iterator->second.content.m_content.id)->m_Data->Close();
|
||||||
|
|
||||||
m_export_title_context.contents.erase(iterator);
|
context.title_export.contents.erase(iterator);
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::ExportTitleDone(const IOCtlVRequest& request)
|
IPCCommandResult ES::ExportTitleDone(Context& context, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!m_export_title_context.valid)
|
if (!context.title_export.valid)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
m_export_title_context.valid = false;
|
context.title_export.valid = false;
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
} // namespace Device
|
} // namespace Device
|
||||||
|
|
|
@ -76,7 +76,7 @@ FileIO::FileIO(u32 device_id, const std::string& device_name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileIO::Close()
|
ReturnCode FileIO::Close(u32 fd)
|
||||||
{
|
{
|
||||||
INFO_LOG(IOS_FILEIO, "FileIO: Close %s (DeviceID=%08x)", m_name.c_str(), m_device_id);
|
INFO_LOG(IOS_FILEIO, "FileIO: Close %s (DeviceID=%08x)", m_name.c_str(), m_device_id);
|
||||||
m_Mode = 0;
|
m_Mode = 0;
|
||||||
|
@ -86,6 +86,7 @@ void FileIO::Close()
|
||||||
m_file.reset();
|
m_file.reset();
|
||||||
|
|
||||||
m_is_active = false;
|
m_is_active = false;
|
||||||
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnCode FileIO::Open(const OpenRequest& request)
|
ReturnCode FileIO::Open(const OpenRequest& request)
|
||||||
|
|
|
@ -32,7 +32,7 @@ class FileIO : public Device
|
||||||
public:
|
public:
|
||||||
FileIO(u32 device_id, const std::string& device_name);
|
FileIO(u32 device_id, const std::string& device_name);
|
||||||
|
|
||||||
void Close() override;
|
ReturnCode Close(u32 fd) override;
|
||||||
ReturnCode Open(const OpenRequest& request) override;
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
IPCCommandResult Seek(const SeekRequest& request) override;
|
IPCCommandResult Seek(const SeekRequest& request) override;
|
||||||
IPCCommandResult Read(const ReadWriteRequest& request) override;
|
IPCCommandResult Read(const ReadWriteRequest& request) override;
|
||||||
|
|
|
@ -75,9 +75,7 @@ static std::mutex s_device_map_mutex;
|
||||||
|
|
||||||
// STATE_TO_SAVE
|
// STATE_TO_SAVE
|
||||||
constexpr u8 IPC_MAX_FDS = 0x18;
|
constexpr u8 IPC_MAX_FDS = 0x18;
|
||||||
constexpr u8 ES_MAX_COUNT = 3;
|
|
||||||
static std::shared_ptr<Device::Device> s_fdmap[IPC_MAX_FDS];
|
static std::shared_ptr<Device::Device> s_fdmap[IPC_MAX_FDS];
|
||||||
static std::shared_ptr<Device::ES> s_es_handles[ES_MAX_COUNT];
|
|
||||||
|
|
||||||
using IPCMsgQueue = std::deque<u32>;
|
using IPCMsgQueue = std::deque<u32>;
|
||||||
static IPCMsgQueue s_request_queue; // ppc -> arm
|
static IPCMsgQueue s_request_queue; // ppc -> arm
|
||||||
|
@ -595,11 +593,7 @@ static void AddStaticDevices()
|
||||||
AddDevice<Device::STMImmediate>("/dev/stm/immediate");
|
AddDevice<Device::STMImmediate>("/dev/stm/immediate");
|
||||||
AddDevice<Device::STMEventHook>("/dev/stm/eventhook");
|
AddDevice<Device::STMEventHook>("/dev/stm/eventhook");
|
||||||
AddDevice<Device::FS>("/dev/fs");
|
AddDevice<Device::FS>("/dev/fs");
|
||||||
|
AddDevice<Device::ES>("/dev/es");
|
||||||
// IOS allows three ES devices at a time
|
|
||||||
for (auto& es_device : s_es_handles)
|
|
||||||
es_device = AddDevice<Device::ES>("/dev/es");
|
|
||||||
|
|
||||||
AddDevice<Device::DI>("/dev/di");
|
AddDevice<Device::DI>("/dev/di");
|
||||||
AddDevice<Device::NetKDRequest>("/dev/net/kd/request");
|
AddDevice<Device::NetKDRequest>("/dev/net/kd/request");
|
||||||
AddDevice<Device::NetKDTime>("/dev/net/kd/time");
|
AddDevice<Device::NetKDTime>("/dev/net/kd/time");
|
||||||
|
@ -643,7 +637,7 @@ static void Reset()
|
||||||
{
|
{
|
||||||
if (!device)
|
if (!device)
|
||||||
continue;
|
continue;
|
||||||
device->Close();
|
device->Close(0);
|
||||||
device.reset();
|
device.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,7 +695,6 @@ bool Reload(const u64 ios_title_id)
|
||||||
|
|
||||||
AddStaticDevices();
|
AddStaticDevices();
|
||||||
|
|
||||||
Device::ES::Init();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -855,13 +848,6 @@ void DoState(PointerWrap& p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& es_device : s_es_handles)
|
|
||||||
{
|
|
||||||
const u32 handle_id = es_device->GetDeviceID();
|
|
||||||
p.Do(handle_id);
|
|
||||||
es_device = std::static_pointer_cast<Device::ES>(AccessDeviceByID(handle_id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -884,24 +870,11 @@ void DoState(PointerWrap& p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& es_device : s_es_handles)
|
|
||||||
{
|
|
||||||
const u32 handle_id = es_device->GetDeviceID();
|
|
||||||
p.Do(handle_id);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::shared_ptr<Device::Device> GetUnusedESDevice()
|
|
||||||
{
|
|
||||||
const auto iterator = std::find_if(std::begin(s_es_handles), std::end(s_es_handles),
|
|
||||||
[](const auto& es_device) { return !es_device->IsOpened(); });
|
|
||||||
return (iterator != std::end(s_es_handles)) ? *iterator : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the FD for the newly opened device (on success) or an error code.
|
// Returns the FD for the newly opened device (on success) or an error code.
|
||||||
static s32 OpenDevice(const OpenRequest& request)
|
static s32 OpenDevice(OpenRequest& request)
|
||||||
{
|
{
|
||||||
const s32 new_fd = GetFreeDeviceID();
|
const s32 new_fd = GetFreeDeviceID();
|
||||||
INFO_LOG(IOS, "Opening %s (mode %d, fd %d)", request.path.c_str(), request.flags, new_fd);
|
INFO_LOG(IOS, "Opening %s (mode %d, fd %d)", request.path.c_str(), request.flags, new_fd);
|
||||||
|
@ -910,15 +883,10 @@ static s32 OpenDevice(const OpenRequest& request)
|
||||||
ERROR_LOG(IOS, "Couldn't get a free fd, too many open files");
|
ERROR_LOG(IOS, "Couldn't get a free fd, too many open files");
|
||||||
return FS_EFDEXHAUSTED;
|
return FS_EFDEXHAUSTED;
|
||||||
}
|
}
|
||||||
|
request.fd = new_fd;
|
||||||
|
|
||||||
std::shared_ptr<Device::Device> device;
|
std::shared_ptr<Device::Device> device;
|
||||||
if (request.path == "/dev/es")
|
if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
|
||||||
{
|
|
||||||
device = GetUnusedESDevice();
|
|
||||||
if (!device)
|
|
||||||
return ES_FD_EXHAUSTED;
|
|
||||||
}
|
|
||||||
else if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
|
|
||||||
{
|
{
|
||||||
device = std::make_shared<Device::OH0Device>(new_fd, request.path);
|
device = std::make_shared<Device::OH0Device>(new_fd, request.path);
|
||||||
}
|
}
|
||||||
|
@ -961,8 +929,7 @@ static IPCCommandResult HandleCommand(const Request& request)
|
||||||
{
|
{
|
||||||
case IPC_CMD_CLOSE:
|
case IPC_CMD_CLOSE:
|
||||||
s_fdmap[request.fd].reset();
|
s_fdmap[request.fd].reset();
|
||||||
device->Close();
|
return Device::Device::GetDefaultReply(device->Close(request.fd));
|
||||||
return Device::Device::GetDefaultReply(IPC_SUCCESS);
|
|
||||||
case IPC_CMD_READ:
|
case IPC_CMD_READ:
|
||||||
return device->Read(ReadWriteRequest{request.address});
|
return device->Read(ReadWriteRequest{request.address});
|
||||||
case IPC_CMD_WRITE:
|
case IPC_CMD_WRITE:
|
||||||
|
|
|
@ -83,13 +83,13 @@ ReturnCode SDIOSlot0::Open(const OpenRequest& request)
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDIOSlot0::Close()
|
ReturnCode SDIOSlot0::Close(u32 fd)
|
||||||
{
|
{
|
||||||
m_Card.Close();
|
m_Card.Close();
|
||||||
m_BlockLength = 0;
|
m_BlockLength = 0;
|
||||||
m_BusWidth = 0;
|
m_BusWidth = 0;
|
||||||
|
|
||||||
m_is_active = false;
|
return Device::Close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult SDIOSlot0::IOCtl(const IOCtlRequest& request)
|
IPCCommandResult SDIOSlot0::IOCtl(const IOCtlRequest& request)
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
ReturnCode Open(const OpenRequest& request) override;
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
void Close() override;
|
ReturnCode Close(u32 fd) override;
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,10 @@ IPCCommandResult STMImmediate::IOCtl(const IOCtlRequest& request)
|
||||||
return GetDefaultReply(return_value);
|
return GetDefaultReply(return_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void STMEventHook::Close()
|
ReturnCode STMEventHook::Close(u32 fd)
|
||||||
{
|
{
|
||||||
s_event_hook_request.reset();
|
s_event_hook_request.reset();
|
||||||
m_is_active = false;
|
return Device::Close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult STMEventHook::IOCtl(const IOCtlRequest& request)
|
IPCCommandResult STMEventHook::IOCtl(const IOCtlRequest& request)
|
||||||
|
|
|
@ -55,7 +55,7 @@ class STMEventHook final : public Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
STMEventHook(u32 device_id, const std::string& device_name) : Device(device_id, device_name) {}
|
STMEventHook(u32 device_id, const std::string& device_name) : Device(device_id, device_name) {}
|
||||||
void Close() override;
|
ReturnCode Close(u32 fd) override;
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ bool BluetoothEmu::RemoteDisconnect(u16 _connectionHandle)
|
||||||
return SendEventDisconnect(_connectionHandle, 0x13);
|
return SendEventDisconnect(_connectionHandle, 0x13);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothEmu::Close()
|
ReturnCode BluetoothEmu::Close(u32 fd)
|
||||||
{
|
{
|
||||||
// Clean up state
|
// Clean up state
|
||||||
m_ScanEnable = 0;
|
m_ScanEnable = 0;
|
||||||
|
@ -156,7 +156,7 @@ void BluetoothEmu::Close()
|
||||||
m_HCIEndpoint.reset();
|
m_HCIEndpoint.reset();
|
||||||
m_ACLEndpoint.reset();
|
m_ACLEndpoint.reset();
|
||||||
|
|
||||||
m_is_active = false;
|
return Device::Close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult BluetoothEmu::IOCtlV(const IOCtlVRequest& request)
|
IPCCommandResult BluetoothEmu::IOCtlV(const IOCtlVRequest& request)
|
||||||
|
|
|
@ -49,7 +49,7 @@ public:
|
||||||
|
|
||||||
virtual ~BluetoothEmu();
|
virtual ~BluetoothEmu();
|
||||||
|
|
||||||
void Close() override;
|
ReturnCode Close(u32 fd) override;
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
|
|
||||||
void Update() override;
|
void Update() override;
|
||||||
|
|
|
@ -149,7 +149,7 @@ ReturnCode BluetoothReal::Open(const OpenRequest& request)
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothReal::Close()
|
ReturnCode BluetoothReal::Close(u32 fd)
|
||||||
{
|
{
|
||||||
if (m_handle)
|
if (m_handle)
|
||||||
{
|
{
|
||||||
|
@ -159,7 +159,7 @@ void BluetoothReal::Close()
|
||||||
m_handle = nullptr;
|
m_handle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_is_active = false;
|
return Device::Close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult BluetoothReal::IOCtlV(const IOCtlVRequest& request)
|
IPCCommandResult BluetoothReal::IOCtlV(const IOCtlVRequest& request)
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
~BluetoothReal() override;
|
~BluetoothReal() override;
|
||||||
|
|
||||||
ReturnCode Open(const OpenRequest& request) override;
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
void Close() override;
|
ReturnCode Close(u32 fd) override;
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
|
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
|
@ -68,9 +68,10 @@ ReturnCode OH0Device::Open(const OpenRequest& request)
|
||||||
return return_code;
|
return return_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OH0Device::Close()
|
ReturnCode OH0Device::Close(u32 fd)
|
||||||
{
|
{
|
||||||
m_oh0->DeviceClose(m_device_id);
|
m_oh0->DeviceClose(m_device_id);
|
||||||
|
return Device::Close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult OH0Device::IOCtl(const IOCtlRequest& request)
|
IPCCommandResult OH0Device::IOCtl(const IOCtlRequest& request)
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
OH0Device(u32 device_id, const std::string& device_name);
|
OH0Device(u32 device_id, const std::string& device_name);
|
||||||
|
|
||||||
ReturnCode Open(const OpenRequest& request) override;
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
void Close() override;
|
ReturnCode Close(u32 fd) override;
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
|
@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
static const u32 STATE_VERSION = 81; // Last changed in PR 5317
|
static const u32 STATE_VERSION = 82; // Last changed in PR 5333
|
||||||
|
|
||||||
// Maps savestate versions to Dolphin versions.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
|
Loading…
Reference in New Issue