From 08f36100df9f3f7e839ef5f7a9c8f24d3b80e19f Mon Sep 17 00:00:00 2001 From: hexkyz Date: Wed, 30 Sep 2026 22:42:13 +0100 Subject: [PATCH 1/2] fs: add StorageForBatchRead commands --- nx/include/switch/services/fs.h | 19 +++++ nx/source/services/fs.c | 125 ++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/nx/include/switch/services/fs.h b/nx/include/switch/services/fs.h index 4f276c014..0cf991709 100644 --- a/nx/include/switch/services/fs.h +++ b/nx/include/switch/services/fs.h @@ -40,6 +40,10 @@ typedef struct { Service s; } FsStorage; +typedef struct { + Service s; +} FsStorageForBatchRead; + typedef struct { Service s; } FsSaveDataInfoReader; @@ -540,6 +544,11 @@ Result fsOpenDataStorageByProgramId(FsStorage *out, u64 program_id); /// <[3.0.0 Result fsOpenDataStorageByDataId(FsStorage* out, u64 dataId, NcmStorageId storageId); Result fsOpenPatchDataStorageByCurrentProcess(FsStorage* out); +Result fsOpenDataStorageByCurrentProcessForBatchRead(FsStorageForBatchRead* out); /// <[23.0.0+] +Result fsOpenDataStorageByProgramIdForBatchRead(FsStorageForBatchRead* out, u64 id); /// <[23.0.0+] +Result fsOpenDataStorageWithProgramIndexForBatchRead(FsStorageForBatchRead* out, u8 program_index); /// <[23.0.0+] +Result fsOpenDataStorageByPathForBatchRead(FsStorageForBatchRead* out, const char* contentPath, FsContentAttributes attributes, FsFileSystemType fsType); /// <[23.0.0+] + Result fsOpenDeviceOperator(FsDeviceOperator* out); Result fsOpenSdCardDetectionEventNotifier(FsEventNotifier* out); @@ -659,6 +668,16 @@ Result fsStorageGetSize(FsStorage* s, s64* out); Result fsStorageOperateRange(FsStorage* s, FsOperationId op_id, s64 off, s64 len, FsRangeInfo* out); ///< [4.0.0+] void fsStorageClose(FsStorage* s); +// IStorageForBatchRead +Result fsStorageForBatchReadRead(FsStorageForBatchRead* s, s64 off, void* buf, u64 read_size); +Result fsStorageForBatchReadWrite(FsStorageForBatchRead* s, s64 off, const void* buf, u64 write_size); +Result fsStorageForBatchReadFlush(FsStorageForBatchRead* s); +Result fsStorageForBatchReadSetSize(FsStorageForBatchRead* s, s64 sz); +Result fsStorageForBatchReadGetSize(FsStorageForBatchRead* s, s64* out); +Result fsStorageForBatchReadOperateRange(FsStorageForBatchRead* s, FsOperationId op_id, s64 off, s64 len, FsRangeInfo* out); +Result fsStorageForBatchReadBatchRead(FsStorageForBatchRead* s, void* out0, void* out1, void* out2, void* out3, void* out4, void* out5, void* out6, const void* in); +void fsStorageForBatchReadClose(FsStorageForBatchRead* s); + // ISaveDataInfoReader /// Read FsSaveDataInfo data into the buf array. diff --git a/nx/source/services/fs.c b/nx/source/services/fs.c index 227396824..ee9996d1b 100644 --- a/nx/source/services/fs.c +++ b/nx/source/services/fs.c @@ -538,6 +538,51 @@ Result fsOpenPatchDataStorageByCurrentProcess(FsStorage* out) { return _fsCmdGetSession(&g_fsSrv, &out->s, 203); } +Result fsOpenDataStorageByCurrentProcessForBatchRead(FsStorageForBatchRead* out) { + if (hosversionBefore(23,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _fsCmdGetSession(&g_fsSrv, &out->s, 210); +} + +Result fsOpenDataStorageByProgramIdForBatchRead(FsStorageForBatchRead* out, u64 id) { + if (hosversionBefore(23,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _fsObjectDispatchIn(&g_fsSrv, 211, id, + .out_num_objects = 1, + .out_objects = &out->s, + ); +} + +Result fsOpenDataStorageWithProgramIndexForBatchRead(FsStorageForBatchRead* out, u8 program_index) { + if (hosversionBefore(23,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _fsObjectDispatchIn(&g_fsSrv, 212, program_index, + .out_num_objects = 1, + .out_objects = &out->s, + ); +} + +Result fsOpenDataStorageByPathForBatchRead(FsStorageForBatchRead* out, const char* contentPath, FsContentAttributes attributes, FsFileSystemType fsType) { + if (hosversionBefore(23,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + const struct { + u8 attributes; + u8 pad[3]; + u32 fsType; + } in = { attributes, {0}, fsType }; + + return _fsObjectDispatchIn(&g_fsSrv, 213, in, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In }, + .buffers = { { contentPath, FS_MAX_PATH } }, + .out_num_objects = 1, + .out_objects = &out->s + ); +} + Result fsOpenDeviceOperator(FsDeviceOperator* out) { return _fsCmdGetSession(&g_fsSrv, &out->s, 400); } @@ -1109,6 +1154,86 @@ void fsStorageClose(FsStorage* s) { _fsObjectClose(&s->s); } +//----------------------------------------------------------------------------- +// IStorageForBatchRead +//----------------------------------------------------------------------------- + +Result fsStorageForBatchReadRead(FsStorageForBatchRead* s, s64 off, void* buf, u64 read_size) { + const struct { + s64 offset; + u64 read_size; + } in = { off, read_size }; + + return _fsObjectDispatchIn(&s->s, 0, in, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure }, + .buffers = { { buf, read_size } }, + ); +} + +Result fsStorageForBatchReadWrite(FsStorageForBatchRead* s, s64 off, const void* buf, u64 write_size) { + const struct { + s64 offset; + u64 write_size; + } in = { off, write_size }; + + return _fsObjectDispatchIn(&s->s, 1, in, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In | SfBufferAttr_HipcMapTransferAllowsNonSecure }, + .buffers = { { buf, write_size } }, + ); +} + +Result fsStorageForBatchReadFlush(FsStorageForBatchRead* s) { + return _fsCmdNoIO(&s->s, 2); +} + +Result fsStorageForBatchReadSetSize(FsStorageForBatchRead* s, s64 sz) { + return _fsObjectDispatchIn(&s->s, 3, sz); +} + +Result fsStorageForBatchReadGetSize(FsStorageForBatchRead* s, s64* out) { + return _fsObjectDispatchOut(&s->s, 4, *out); +} + +Result fsStorageForBatchReadOperateRange(FsStorageForBatchRead* s, FsOperationId op_id, s64 off, s64 len, FsRangeInfo* out) { + const struct { + u32 op_id; + u32 pad; + s64 off; + s64 len; + } in = { op_id, 0, off, len }; + + return _fsObjectDispatchInOut(&s->s, 5, in, *out); +} + +Result fsStorageForBatchReadBatchRead(FsStorageForBatchRead* s, void* out0, void* out1, void* out2, void* out3, void* out4, void* out5, void* out6, const void* in) { + return _fsObjectDispatch(&g_fsSrv, 10, + .buffer_attrs = { + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out | SfBufferAttr_HipcMapTransferAllowsNonSecure, + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + }, + .buffers = { + { out0, sizeof(u64)}, + { out1, sizeof(u64)}, + { out2, sizeof(u64)}, + { out3, sizeof(u64)}, + { out4, sizeof(u64)}, + { out5, sizeof(u64)}, + { out6, sizeof(u64)}, + { in, 7*sizeof(u64)}, + }, + ); +} + +void fsStorageForBatchReadClose(FsStorageForBatchRead* s) { + _fsObjectClose(&s->s); +} + //----------------------------------------------------------------------------- // ISaveDataInfoReader //----------------------------------------------------------------------------- From 5246221963ade16de30e4d14a83c1ecdda921a7a Mon Sep 17 00:00:00 2001 From: hexkyz Date: Wed, 30 Sep 2026 22:59:54 +0100 Subject: [PATCH 2/2] fs: fix comment style typos --- nx/include/switch/services/fs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nx/include/switch/services/fs.h b/nx/include/switch/services/fs.h index 0cf991709..7f56dfbfd 100644 --- a/nx/include/switch/services/fs.h +++ b/nx/include/switch/services/fs.h @@ -540,14 +540,14 @@ Result fsOpenContentStorageFileSystem(FsFileSystem* out, FsContentStorageId cont Result fsOpenCustomStorageFileSystem(FsFileSystem* out, FsCustomStorageId custom_storage_id); ///< [7.0.0+] Result fsOpenDataStorageByCurrentProcess(FsStorage* out); -Result fsOpenDataStorageByProgramId(FsStorage *out, u64 program_id); /// <[3.0.0+] +Result fsOpenDataStorageByProgramId(FsStorage *out, u64 program_id); ///< [3.0.0+] Result fsOpenDataStorageByDataId(FsStorage* out, u64 dataId, NcmStorageId storageId); Result fsOpenPatchDataStorageByCurrentProcess(FsStorage* out); -Result fsOpenDataStorageByCurrentProcessForBatchRead(FsStorageForBatchRead* out); /// <[23.0.0+] -Result fsOpenDataStorageByProgramIdForBatchRead(FsStorageForBatchRead* out, u64 id); /// <[23.0.0+] -Result fsOpenDataStorageWithProgramIndexForBatchRead(FsStorageForBatchRead* out, u8 program_index); /// <[23.0.0+] -Result fsOpenDataStorageByPathForBatchRead(FsStorageForBatchRead* out, const char* contentPath, FsContentAttributes attributes, FsFileSystemType fsType); /// <[23.0.0+] +Result fsOpenDataStorageByCurrentProcessForBatchRead(FsStorageForBatchRead* out); ///< [23.0.0+] +Result fsOpenDataStorageByProgramIdForBatchRead(FsStorageForBatchRead* out, u64 id); ///< [23.0.0+] +Result fsOpenDataStorageWithProgramIndexForBatchRead(FsStorageForBatchRead* out, u8 program_index); ///< [23.0.0+] +Result fsOpenDataStorageByPathForBatchRead(FsStorageForBatchRead* out, const char* contentPath, FsContentAttributes attributes, FsFileSystemType fsType); ///< [23.0.0+] Result fsOpenDeviceOperator(FsDeviceOperator* out); Result fsOpenSdCardDetectionEventNotifier(FsEventNotifier* out);