From c3e98443f7075b95d74e5ac74c1aaf0984df30e6 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Mon, 28 May 2018 21:23:14 -0400 Subject: [PATCH] ncm: add a few IPC wrappers for content storage and content meta database --- nx/include/switch/services/ncm.h | 49 ++++++ nx/source/services/ncm.c | 284 +++++++++++++++++++++++++++++++ 2 files changed, 333 insertions(+) create mode 100644 nx/include/switch/services/ncm.h create mode 100644 nx/source/services/ncm.c diff --git a/nx/include/switch/services/ncm.h b/nx/include/switch/services/ncm.h new file mode 100644 index 00000000..d6330e2d --- /dev/null +++ b/nx/include/switch/services/ncm.h @@ -0,0 +1,49 @@ +/** + * @file ncm.h + * @brief Content Manager (ncm) service IPC wrapper. + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../services/fs.h" +#include "../services/sm.h" +typedef enum NCMContentType { + NCMContentType_CNMT = 0, + NCMContentType_Program = 1, + NCMContentType_Data = 2, + NCMContentType_Icon = 3, + NCMContentType_Doc = 4, + NCMContentType_Info = 5, +} NCMContentType; + +typedef struct { + Service s; +} NCMContentStorage; + +typedef struct { + Service s; +} NCMContentMetaDatabase; + +typedef struct { + u64 titleID; // 0x0 + u32 version; // 0x8 + u8 type; // 0xc + u8 flags; // 0xd + u8 padding[2]; // 0xe +} NCMMetaRecord; + +typedef struct { + char c[0x10]; +} NCMNCAID; + +Result ncmInitialize(); +void ncmExit(); + +Result ncmOpenContentStorage(FsStorageId storage, NCMContentStorage* out); +Result ncmContentStorageGetPath(NCMContentStorage* cs, const NCMNCAID* ncaId, char* out); +Result ncmContentStorageGetSize(NCMContentStorage* cs, const NCMNCAID* ncaId, u64* out); +Result ncmContentStorageReadContentIdFile(NCMContentStorage* cs, const NCMNCAID* ncaId, u64 offset, char* outBuf, size_t bufSize); + +Result ncmOpenContentMetaDatabase(FsStorageId storage, NCMContentMetaDatabase* out); +Result ncmContentMetaDatabaseGetLatestContentMetaKey(NCMContentMetaDatabase* db, u64 titleID, NCMMetaRecord* out); +Result ncmContentMetaDatabaseGetContentIdByType(NCMContentMetaDatabase* db, NCMContentType contentType, const NCMMetaRecord* record, NCMNCAID* out); \ No newline at end of file diff --git a/nx/source/services/ncm.c b/nx/source/services/ncm.c new file mode 100644 index 00000000..8d44a6fd --- /dev/null +++ b/nx/source/services/ncm.c @@ -0,0 +1,284 @@ +#include +#include "types.h" +#include "result.h" +#include "arm/atomics.h" +#include "kernel/ipc.h" +#include "services/fs.h" +#include "services/sm.h" +#include "services/ncm.h" + +static Service g_ncmSrv; + +static u64 g_ncmRefCnt; + +Result ncmInitialize() { + atomicIncrement64(&g_ncmRefCnt); + Result rc = smGetService(&g_ncmSrv, "ncm"); + return rc; +} + +void ncmExit() { + if (atomicDecrement64(&g_ncmRefCnt) == 0) { + serviceClose(&g_ncmSrv); + } +} + +Result ncmOpenContentStorage(FsStorageId storage, NCMContentStorage* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 storage_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 4; + raw->storage_id = (u32)storage; + + Result rc = serviceIpcDispatch(&g_ncmSrv); + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(&out->s, r.Handles[0]); + } + } + + return rc; +} + +Result ncmContentStorageGetPath(NCMContentStorage* cs, const NCMNCAID* ncaId, char* out) { + char out_path[FS_MAX_PATH] = {0}; + IpcCommand c; + ipcInitialize(&c); + ipcAddRecvStatic(&c, out_path, FS_MAX_PATH, 0); + + struct { + u64 magic; + u64 cmd_id; + NCMNCAID ncaId; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 8; + memcpy(&raw->ncaId, ncaId, sizeof(NCMNCAID)); + + Result rc = serviceIpcDispatch(&cs->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + strncpy(out, out_path, FS_MAX_PATH); + } + } + + return rc; +} + +Result ncmContentStorageGetSize(NCMContentStorage* cs, const NCMNCAID* ncaId, u64* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + NCMNCAID ncaId; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 14; + memcpy(&raw->ncaId, ncaId, sizeof(NCMNCAID)); + + Result rc = serviceIpcDispatch(&cs->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 size; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *out = resp->size; + } + } + + return rc; +} + +Result ncmContentStorageReadContentIdFile(NCMContentStorage* cs, const NCMNCAID* ncaId, u64 offset, char* outBuf, size_t bufSize) { + IpcCommand c; + ipcInitialize(&c); + ipcAddRecvBuffer(&c, outBuf, bufSize, BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + NCMNCAID ncaId; + u64 offset; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 18; + memcpy(&raw->ncaId, ncaId, sizeof(NCMNCAID)); + raw->offset = offset; + + Result rc = serviceIpcDispatch(&cs->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result ncmOpenContentMetaDatabase(FsStorageId storage, NCMContentMetaDatabase* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 storage_id; // Actually u8 + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->storage_id = (u32)storage; + + Result rc = serviceIpcDispatch(&g_ncmSrv); + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(&out->s, r.Handles[0]); + } + } + + return rc; +} + +Result ncmContentMetaDatabaseGetLatestContentMetaKey(NCMContentMetaDatabase* db, u64 titleID, NCMMetaRecord* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 title_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 6; + raw->title_id = titleID; + + Result rc = serviceIpcDispatch(&db->s); + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + NCMMetaRecord record; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + memcpy(out, &resp->record, sizeof(NCMMetaRecord)); + } + } + + return rc; +} + +Result ncmContentMetaDatabaseGetContentIdByType(NCMContentMetaDatabase* db, NCMContentType contentType, const NCMMetaRecord* record, NCMNCAID* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 contentType; + u32 padding0; + NCMMetaRecord metaRecord; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 3; + raw->contentType = contentType; + raw->padding0 = 0; + memcpy(&raw->metaRecord, record, sizeof(NCMMetaRecord)); + + Result rc = serviceIpcDispatch(&db->s); + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + NCMNCAID ncaId; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + memcpy(out, &resp->ncaId, sizeof(NCMNCAID)); + } + } + + return rc; +} \ No newline at end of file