libnx/nx/source/services/ncm.c

516 lines
20 KiB
C

#define NX_SERVICE_ASSUME_NON_DOMAIN
#include <string.h>
#include "service_guard.h"
#include "runtime/hosversion.h"
#include "services/ncm.h"
static Service g_ncmSrv;
NX_GENERATE_SERVICE_GUARD(ncm);
Result _ncmInitialize(void) {
return smGetService(&g_ncmSrv, "ncm");
}
void _ncmCleanup(void) {
serviceClose(&g_ncmSrv);
}
Service* ncmGetServiceSession(void) {
return &g_ncmSrv;
}
static Result _ncmGetInterfaceInU8(Service* srv_out, u8 inval, u32 cmd_id) {
return serviceDispatchIn(&g_ncmSrv, cmd_id, inval,
.out_num_objects = 1,
.out_objects = srv_out,
);
}
static Result _ncmCmdNoIO(Service* srv, u32 cmd_id) {
return serviceDispatch(srv, cmd_id);
}
static Result _ncmCmdNoInOutU64(Service* srv, u64* outval, u32 cmd_id) {
return serviceDispatchOut(srv, cmd_id, *outval);
}
static Result _ncmCmdOutPlaceHolderId(Service* srv, NcmPlaceHolderId* outval, u32 cmd_id) {
return serviceDispatchOut(srv, cmd_id, *outval);
}
static Result _ncmCmdInU8(Service* srv, u8 inval, u32 cmd_id) {
return serviceDispatchIn(srv, cmd_id, inval);
}
static Result _ncmCmdInContentId(Service* srv, const NcmContentId* inval, u32 cmd_id) {
return serviceDispatchIn(srv, cmd_id, *inval);
}
static Result _ncmCmdInPlaceHolderId(Service* srv, const NcmPlaceHolderId* inval, u32 cmd_id) {
return serviceDispatchIn(srv, cmd_id, *inval);
}
static Result _ncmCmdInContentIdOutU64(Service* srv, const NcmContentId* inval, u64* outval, u32 cmd_id) {
return serviceDispatchInOut(srv, cmd_id, *inval, *outval);
}
static Result _ncmCmdInPlaceHolderIdOutU64(Service* srv, const NcmPlaceHolderId* inval, u64* outval, u32 cmd_id) {
return serviceDispatchInOut(srv, cmd_id, *inval, *outval);
}
Result ncmCreateContentStorage(FsStorageId storage_id) {
return _ncmCmdInU8(&g_ncmSrv, storage_id, 0);
}
Result ncmCreateContentMetaDatabase(FsStorageId storage_id) {
return _ncmCmdInU8(&g_ncmSrv, storage_id, 1);
}
Result ncmVerifyContentStorage(FsStorageId storage_id) {
return _ncmCmdInU8(&g_ncmSrv, storage_id, 2);
}
Result ncmVerifyContentMetaDatabase(FsStorageId storage_id) {
return _ncmCmdInU8(&g_ncmSrv, storage_id, 3);
}
Result ncmOpenContentStorage(NcmContentStorage* out_content_storage, FsStorageId storage_id) {
return _ncmGetInterfaceInU8(&out_content_storage->s, storage_id, 4);
}
Result ncmOpenContentMetaDatabase(NcmContentMetaDatabase* out_content_meta_database, FsStorageId storage_id) {
return _ncmGetInterfaceInU8(&out_content_meta_database->s, storage_id, 5);
}
Result ncmCloseContentStorageForcibly(FsStorageId storage_id) {
if (hosversionAtLeast(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdInU8(&g_ncmSrv, storage_id, 6);
}
Result ncmCloseContentMetaDatabaseForcibly(FsStorageId storage_id) {
if (hosversionAtLeast(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdInU8(&g_ncmSrv, storage_id, 7);
}
Result ncmCleanupContentMetaDatabase(FsStorageId storage_id) {
return _ncmCmdInU8(&g_ncmSrv, storage_id, 8);
}
Result ncmActivateContentStorage(FsStorageId storage_id) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdInU8(&g_ncmSrv, storage_id, 9);
}
Result ncmInactivateContentStorage(FsStorageId storage_id) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdInU8(&g_ncmSrv, storage_id, 10);
}
Result ncmActivateContentMetaDatabase(FsStorageId storage_id) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdInU8(&g_ncmSrv, storage_id, 11);
}
Result ncmInactivateContentMetaDatabase(FsStorageId storage_id) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdInU8(&g_ncmSrv, storage_id, 12);
}
Result ncmInvalidateRightsIdCache(void) {
if (hosversionBefore(9,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdNoIO(&g_ncmSrv, 13);
}
void ncmContentStorageClose(NcmContentStorage* cs) {
serviceClose(&cs->s);
}
Result ncmContentStorageGeneratePlaceHolderId(NcmContentStorage* cs, NcmPlaceHolderId* out_id) {
return _ncmCmdOutPlaceHolderId(&cs->s, out_id, 0);
}
Result ncmContentStorageCreatePlaceHolder(NcmContentStorage* cs, const NcmContentId* content_id, const NcmPlaceHolderId* placeholder_id, u64 size) {
const struct {
NcmContentId content_id;
NcmPlaceHolderId placeholder_id;
u64 size;
} in = { *content_id, *placeholder_id, size };
return serviceDispatchIn(&cs->s, 1, in);
}
Result ncmContentStorageDeletePlaceHolder(NcmContentStorage* cs, const NcmPlaceHolderId* placeholder_id) {
return _ncmCmdInPlaceHolderId(&cs->s, placeholder_id, 2);
}
Result ncmContentStorageHasPlaceHolder(NcmContentStorage* cs, bool* out, const NcmPlaceHolderId* placeholder_id) {
u8 tmp=0;
Result rc = serviceDispatchInOut(&cs->s, 3, *placeholder_id, tmp);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
Result ncmContentStorageWritePlaceHolder(NcmContentStorage* cs, const NcmPlaceHolderId* placeholder_id, u64 offset, const void* data, size_t data_size) {
const struct {
NcmPlaceHolderId placeholder_id;
u64 offset;
} in = { *placeholder_id, offset };
return serviceDispatchIn(&cs->s, 4, in,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { data, data_size } },
);
}
Result ncmContentStorageRegister(NcmContentStorage* cs, const NcmContentId* content_id, const NcmPlaceHolderId* placeholder_id) {
const struct {
NcmContentId content_id;
NcmPlaceHolderId placeholder_id;
} in = { *content_id, *placeholder_id };
return serviceDispatchIn(&cs->s, 5, in);
}
Result ncmContentStorageDelete(NcmContentStorage* cs, const NcmContentId* content_id) {
return _ncmCmdInContentId(&cs->s, content_id, 6);
}
Result ncmContentStorageHas(NcmContentStorage* cs, bool* out, const NcmContentId* content_id) {
u8 tmp=0;
Result rc = serviceDispatchInOut(&cs->s, 7, *content_id, tmp);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
Result ncmContentStorageGetPath(NcmContentStorage* cs, char* out_path, size_t out_size, const NcmContentId* content_id) {
char tmpbuf[0x300]={0};
Result rc = serviceDispatchIn(&cs->s, 8, *content_id,
.buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_HipcPointer | SfBufferAttr_Out },
.buffers = { { tmpbuf, sizeof(tmpbuf) } },
);
if (R_SUCCEEDED(rc) && out_path) {
strncpy(out_path, tmpbuf, out_size-1);
out_path[out_size-1] = 0;
}
return rc;
}
Result ncmContentStorageGetPlaceHolderPath(NcmContentStorage* cs, char* out_path, size_t out_size, const NcmPlaceHolderId* placeholder_id) {
char tmpbuf[0x300]={0};
Result rc = serviceDispatchIn(&cs->s, 9, *placeholder_id,
.buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_HipcPointer | SfBufferAttr_Out },
.buffers = { { tmpbuf, sizeof(tmpbuf) } },
);
if (R_SUCCEEDED(rc) && out_path) {
strncpy(out_path, tmpbuf, out_size-1);
out_path[out_size-1] = 0;
}
return rc;
}
Result ncmContentStorageCleanupAllPlaceHolder(NcmContentStorage* cs) {
return _ncmCmdNoIO(&cs->s, 10);
}
Result ncmContentStorageListPlaceHolder(NcmContentStorage* cs, NcmPlaceHolderId* out_ids, s32 count, s32* out_count) {
return serviceDispatchOut(&cs->s, 11, *out_count,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_ids, count*sizeof(NcmPlaceHolderId) } },
);
}
Result ncmContentStorageGetContentCount(NcmContentStorage* cs, s32* out_count) {
return serviceDispatchOut(&cs->s, 12, *out_count);
}
Result ncmContentStorageListContentId(NcmContentStorage* cs, NcmContentId* out_ids, s32 count, s32* out_count, s32 start_offset) {
return serviceDispatchInOut(&cs->s, 13, start_offset, *out_count,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_ids, count*sizeof(NcmContentId) } },
);
}
Result ncmContentStorageGetSizeFromContentId(NcmContentStorage* cs, s64* out_size, const NcmContentId* content_id) {
return _ncmCmdInContentIdOutU64(&cs->s, content_id, (u64*)out_size, 14);
}
Result ncmContentStorageDisableForcibly(NcmContentStorage* cs) {
return _ncmCmdNoIO(&cs->s, 15);
}
Result ncmContentStorageRevertToPlaceHolder(NcmContentStorage* cs, const NcmPlaceHolderId* placeholder_id, const NcmContentId* old_content_id, const NcmContentId* new_content_id) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
const struct {
NcmContentId old_content_id;
NcmContentId new_content_id;
NcmPlaceHolderId placeholder_id;
} in = { *old_content_id, *new_content_id, *placeholder_id };
return serviceDispatchIn(&cs->s, 16, in);
}
Result ncmContentStorageSetPlaceHolderSize(NcmContentStorage* cs, const NcmPlaceHolderId* placeholder_id, s64 size) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
const struct {
NcmPlaceHolderId placeholder_id;
s64 size;
} in = { *placeholder_id, size };
return serviceDispatchIn(&cs->s, 17, in);
}
Result ncmContentStorageReadContentIdFile(NcmContentStorage* cs, void* out_data, size_t out_data_size, const NcmContentId* content_id, s64 offset) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
const struct {
NcmContentId content_id;
s64 offset;
} in = { *content_id, offset };
return serviceDispatchIn(&cs->s, 18, in,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_data, out_data_size } },
);
}
Result ncmContentStorageGetRightsIdFromPlaceHolderId(NcmContentStorage* cs, NcmRightsId* out_rights_id, const NcmPlaceHolderId* placeholder_id) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
if (hosversionBefore(3,0,0))
return serviceDispatchInOut(&cs->s, 19, *placeholder_id, out_rights_id->rights_id);
else
return serviceDispatchInOut(&cs->s, 19, *placeholder_id, *out_rights_id);
}
Result ncmContentStorageGetRightsIdFromContentId(NcmContentStorage* cs, NcmRightsId* out_rights_id, const NcmContentId* content_id) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
memset(out_rights_id, 0, sizeof(*out_rights_id));
if (hosversionBefore(3,0,0))
return serviceDispatchInOut(&cs->s, 20, *content_id, out_rights_id->rights_id);
else
return serviceDispatchInOut(&cs->s, 20, *content_id, *out_rights_id);
}
Result ncmContentStorageWriteContentForDebug(NcmContentStorage* cs, const NcmContentId* content_id, s64 offset, const void* data, size_t data_size) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
const struct {
NcmContentId content_id;
s64 offset;
} in = { *content_id, offset };
return serviceDispatchIn(&cs->s, 21, in,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { data, data_size } },
);
}
Result ncmContentStorageGetFreeSpaceSize(NcmContentStorage* cs, s64* out_size) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdNoInOutU64(&cs->s, (u64*)out_size, 22);
}
Result ncmContentStorageGetTotalSpaceSize(NcmContentStorage* cs, s64* out_size) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdNoInOutU64(&cs->s, (u64*)out_size, 23);
}
Result ncmContentStorageFlushPlaceHolder(NcmContentStorage* cs) {
if (hosversionBefore(3,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdNoIO(&cs->s, 24);
}
Result ncmContentStorageGetSizeFromPlaceHolderId(NcmContentStorage* cs, s64* out_size, const NcmPlaceHolderId* placeholder_id) {
if (hosversionBefore(4,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdInPlaceHolderIdOutU64(&cs->s, placeholder_id, (u64*)out_size, 25);
}
Result ncmContentStorageRepairInvalidFileAttribute(NcmContentStorage* cs) {
if (hosversionBefore(4,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _ncmCmdNoIO(&cs->s, 26);
}
Result ncmContentStorageGetRightsIdFromPlaceHolderIdWithCache(NcmContentStorage* cs, NcmRightsId* out_rights_id, const NcmPlaceHolderId* placeholder_id, const NcmContentId* cache_content_id) {
if (hosversionBefore(8,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
const struct {
NcmContentId cache_content_id;
NcmPlaceHolderId placeholder_id;
} in = { *cache_content_id, *placeholder_id };
if (hosversionBefore(3,0,0))
return serviceDispatchInOut(&cs->s, 27, in, out_rights_id->rights_id);
else
return serviceDispatchInOut(&cs->s, 27, in, *out_rights_id);
}
void ncmContentMetaDatabaseClose(NcmContentMetaDatabase* db) {
serviceClose(&db->s);
}
Result ncmContentMetaDatabaseSet(NcmContentMetaDatabase* db, const NcmContentMetaKey* key, const void* data, u64 data_size) {
return serviceDispatchIn(&db->s, 0, *key,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { data, data_size } },
);
}
Result ncmContentMetaDatabaseGet(NcmContentMetaDatabase* db, const NcmContentMetaKey* key, u64* out_size, void* out_data, u64 out_data_size) {
return serviceDispatchInOut(&db->s, 1, *key, *out_size,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_data, out_data_size } },
);
}
Result ncmContentMetaDatabaseRemove(NcmContentMetaDatabase* db, const NcmContentMetaKey *key) {
return serviceDispatchIn(&db->s, 2, *key);
}
Result ncmContentMetaDatabaseGetContentIdByType(NcmContentMetaDatabase* db, NcmContentId* out_content_id, const NcmContentMetaKey* key, NcmContentType type) {
const struct {
u8 type;
u8 padding[7];
NcmContentMetaKey key;
} in = { type, {0}, *key };
return serviceDispatchInOut(&db->s, 3, in, *out_content_id);
}
Result ncmContentMetaDatabaseListContentInfo(NcmContentMetaDatabase* db, s32* out_entries_written, NcmContentInfo* out_info, s32 count, const NcmContentMetaKey* key, s32 start_index) {
const struct {
s32 start_index;
u32 pad;
NcmContentMetaKey key;
} in = { start_index, 0, *key };
return serviceDispatchInOut(&db->s, 4, in, *out_entries_written,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_info, count*sizeof(NcmContentInfo) } },
);
}
Result ncmContentMetaDatabaseList(NcmContentMetaDatabase* db, s32* out_entries_total, s32* out_entries_written, NcmContentMetaKey* out_keys, s32 count, NcmContentMetaType meta_type, u64 application_title_id, u64 title_id_min, u64 title_id_max, NcmContentInstallType install_type) {
const struct {
u8 meta_type;
u8 install_type;
u8 padding[6];
u64 application_title_id;
u64 title_id_min;
u64 title_id_max;
} in = { meta_type, install_type, {0}, application_title_id, title_id_min, title_id_max };
struct {
s32 out_entries_total;
s32 out_entries_written;
} out;
Result rc = serviceDispatchInOut(&db->s, 5, in, out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_keys, count*sizeof(NcmContentMetaKey) } },
);
if (R_SUCCEEDED(rc)) {
if (out_entries_total) *out_entries_total = out.out_entries_total;
if (out_entries_written) *out_entries_written = out.out_entries_written;
}
return rc;
}
Result ncmContentMetaDatabaseGetLatestContentMetaKey(NcmContentMetaDatabase* db, NcmContentMetaKey* out_key, u64 title_id) {
return serviceDispatchInOut(&db->s, 6, title_id, *out_key);
}
Result ncmContentMetaDatabaseListApplication(NcmContentMetaDatabase* db, s32* out_entries_total, s32* out_entries_written, NcmApplicationContentMetaKey* out_keys, s32 count, NcmContentMetaType meta_type) {
struct {
s32 out_entries_total;
s32 out_entries_written;
} out;
Result rc = serviceDispatchInOut(&db->s, 7, meta_type, out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_keys, count*sizeof(NcmApplicationContentMetaKey) } },
);
if (R_SUCCEEDED(rc)) {
if (out_entries_total) *out_entries_total = out.out_entries_total;
if (out_entries_written) *out_entries_written = out.out_entries_written;
}
return rc;
}
Result ncmContentMetaDatabaseHas(NcmContentMetaDatabase* db, bool* out, const NcmContentMetaKey* key) {
u8 tmp=0;
Result rc = serviceDispatchInOut(&db->s, 8, *key, tmp);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
Result ncmContentMetaDatabaseHasAll(NcmContentMetaDatabase* db, bool* out, const NcmContentMetaKey* keys, s32 count) {
u8 tmp=0;
Result rc = serviceDispatchOut(&db->s, 9, *out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { keys, count*sizeof(NcmContentMetaKey) } },
);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
Result ncmContentMetaDatabaseGetSize(NcmContentMetaDatabase* db, u64* out_size, const NcmContentMetaKey* key) {
return serviceDispatchInOut(&db->s, 10, *key, *out_size);
}
Result ncmContentMetaDatabaseGetRequiredSystemVersion(NcmContentMetaDatabase* db, u32* out_version, const NcmContentMetaKey* key) {
return serviceDispatchInOut(&db->s, 11, *key, *out_version);
}
Result ncmContentMetaDatabaseGetPatchId(NcmContentMetaDatabase* db, u64* out_patch_id, const NcmContentMetaKey* key) {
return serviceDispatchInOut(&db->s, 12, *key, *out_patch_id);
}
Result ncmContentMetaDatabaseDisableForcibly(NcmContentMetaDatabase* db) {
return _ncmCmdNoIO(&db->s, 13);
}
Result ncmContentMetaDatabaseLookupOrphanContent(NcmContentMetaDatabase* db, bool* out_orphaned, const NcmContentId* content_ids, s32 count) {
return serviceDispatch(&db->s, 14,
.buffer_attrs = {
SfBufferAttr_HipcMapAlias | SfBufferAttr_Out,
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
},
.buffers = {
{ out_orphaned, count },
{ content_ids, count*sizeof(NcmContentId) },
},
);
}
Result ncmContentMetaDatabaseCommit(NcmContentMetaDatabase* db) {
return _ncmCmdNoIO(&db->s, 15);
}
Result ncmContentMetaDatabaseHasContent(NcmContentMetaDatabase* db, bool* out, const NcmContentMetaKey* key, const NcmContentId* content_id) {
const struct {
NcmContentId content_id;
NcmContentMetaKey key;
} in = { *content_id, *key };
u8 tmp=0;
Result rc = serviceDispatchInOut(&db->s, 16, in, tmp);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
Result ncmContentMetaDatabaseListContentMetaInfo(NcmContentMetaDatabase* db, s32* out_entries_written, void* out_meta_info, s32 count, const NcmContentMetaKey* key, s32 start_index) {
const struct {
s32 start_index;
u32 padding;
NcmContentMetaKey key;
} in = { start_index, 0, *key };
return serviceDispatchInOut(&db->s, 17, in, *out_entries_written,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_meta_info, count*sizeof(NcmContentMetaInfo) } },
);
}
Result ncmContentMetaDatabaseGetAttributes(NcmContentMetaDatabase* db, const NcmContentMetaKey* key, u8* out) {
return serviceDispatchInOut(&db->s, 18, *key, *out);
}
Result ncmContentMetaDatabaseGetRequiredApplicationVersion(NcmContentMetaDatabase* db, u32* out_version, const NcmContentMetaKey* key) {
if (hosversionBefore(2,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return serviceDispatchInOut(&db->s, 19, *key, *out_version);
}
Result ncmContentMetaDatabaseGetContentIdByTypeAndIdOffset(NcmContentMetaDatabase* db, NcmContentId* out_content_id, const NcmContentMetaKey* key, NcmContentType type, u8 id_offset) {
if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
const struct {
u8 type;
u8 id_offset;
u8 padding[6];
NcmContentMetaKey key;
} in = { type, id_offset, {0}, *key };
return serviceDispatchInOut(&db->s, 20, in, *out_content_id);
}