2020-04-26 09:35:01 +01:00
/*
2020-07-03 10:31:22 +01:00
* romfs . h
2020-04-26 09:35:01 +01:00
*
2023-04-08 12:42:22 +01:00
* Copyright ( c ) 2020 - 2023 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-07-03 10:31:22 +01:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 19:26:58 +00:00
* nxdumptool is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2020-04-26 09:35:01 +01:00
*
2021-03-25 19:26:58 +00:00
* nxdumptool is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2020-04-26 09:35:01 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-04-26 09:35:01 +01:00
*/
# pragma once
# ifndef __ROMFS_H__
# define __ROMFS_H__
2022-07-04 01:01:12 +01:00
# include "nca_storage.h"
2020-04-26 09:35:01 +01:00
2021-03-24 17:25:19 +00:00
# ifdef __cplusplus
extern " C " {
# endif
2022-07-06 10:57:31 +01:00
# define ROMFS_OLD_HEADER_SIZE 0x28
# define ROMFS_HEADER_SIZE 0x50
2020-04-26 09:35:01 +01:00
2022-07-06 10:57:31 +01:00
# define ROMFS_VOID_ENTRY UINT32_MAX
# define ROMFS_TABLE_ENTRY_ALIGNMENT 0x4
2020-04-26 09:35:01 +01:00
/// Header used by NCA0 RomFS sections.
typedef struct {
u32 header_size ; ///< Header size. Must be equal to ROMFS_OLD_HEADER_SIZE.
u32 directory_bucket_offset ; ///< Directory buckets table offset.
u32 directory_bucket_size ; ///< Directory buckets table size.
u32 directory_entry_offset ; ///< Directory entries table offset.
u32 directory_entry_size ; ///< Directory entries table size.
u32 file_bucket_offset ; ///< File buckets table offset.
u32 file_bucket_size ; ///< File buckets table size.
u32 file_entry_offset ; ///< File entries table offset.
u32 file_entry_size ; ///< File entries table size.
u32 body_offset ; ///< File data body offset.
} RomFileSystemInformationOld ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( RomFileSystemInformationOld , ROMFS_OLD_HEADER_SIZE ) ;
2020-04-26 09:35:01 +01:00
/// Header used by NCA2/NCA3 RomFS sections.
typedef struct {
u64 header_size ; ///< Header size. Must be equal to ROMFS_HEADER_SIZE.
u64 directory_bucket_offset ; ///< Directory buckets table offset.
u64 directory_bucket_size ; ///< Directory buckets table size.
u64 directory_entry_offset ; ///< Directory entries table offset.
u64 directory_entry_size ; ///< Directory entries table size.
u64 file_bucket_offset ; ///< File buckets table offset.
u64 file_bucket_size ; ///< File buckets table size.
u64 file_entry_offset ; ///< File entries table offset.
u64 file_entry_size ; ///< File entries table size.
u64 body_offset ; ///< File data body offset.
} RomFileSystemInformation ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( RomFileSystemInformation , ROMFS_HEADER_SIZE ) ;
2020-04-26 09:35:01 +01:00
/// Header union.
typedef struct {
union {
struct {
RomFileSystemInformationOld old_format ;
u8 padding [ ROMFS_OLD_HEADER_SIZE ] ;
} ;
RomFileSystemInformation cur_format ;
} ;
} RomFileSystemHeader ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( RomFileSystemHeader , ROMFS_HEADER_SIZE ) ;
2022-07-06 10:57:31 +01:00
/// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the directory name.
2020-04-26 09:35:01 +01:00
typedef struct {
u32 parent_offset ; ///< Parent directory offset.
2022-07-06 10:57:31 +01:00
u32 next_offset ; ///< Next sibling directory offset. May be set to ROMFS_VOID_ENTRY if there are no other directory entries at this level.
u32 directory_offset ; ///< First child directory offset. May be set to ROMFS_VOID_ENTRY if there are no child directories entries.
u32 file_offset ; ///< First child file offset. May be set to ROMFS_VOID_ENTRY if there are no child file entries.
2020-04-26 09:35:01 +01:00
u32 bucket_offset ; ///< Directory bucket offset.
u32 name_length ; ///< Name length.
2022-07-06 10:57:31 +01:00
char name [ ] ; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
2020-04-26 09:35:01 +01:00
} RomFileSystemDirectoryEntry ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( RomFileSystemDirectoryEntry , 0x18 ) ;
2022-07-06 10:57:31 +01:00
/// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the file name.
2020-04-26 09:35:01 +01:00
typedef struct {
u32 parent_offset ; ///< Parent directory offset.
2022-07-06 10:57:31 +01:00
u32 next_offset ; ///< Next sibling file offset. May be set to ROMFS_VOID_ENTRY if there are no other file entries at this level.
2020-04-26 09:35:01 +01:00
u64 offset ; ///< File data offset.
u64 size ; ///< File data size.
u32 bucket_offset ; ///< File bucket offset.
u32 name_length ; ///< Name length.
2022-07-06 10:57:31 +01:00
char name [ ] ; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
2020-04-26 09:35:01 +01:00
} RomFileSystemFileEntry ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( RomFileSystemFileEntry , 0x20 ) ;
2020-04-26 09:35:01 +01:00
typedef struct {
2022-07-04 13:30:48 +01:00
bool is_patch ; ///< Set to true if this we're dealing with a Patch RomFS.
NcaStorageContext storage_ctx [ 2 ] ; ///< Used to read NCA FS section data. Index 0: base storage. Index 1: patch storage.
NcaStorageContext * default_storage_ctx ; ///< Default NCA storage context. Points to one of the two contexts from 'storage_ctx'. Placed here for convenience.
u64 offset ; ///< RomFS offset (relative to the start of the NCA FS section).
u64 size ; ///< RomFS size.
RomFileSystemHeader header ; ///< RomFS header.
u64 dir_table_size ; ///< RomFS directory entries table size.
RomFileSystemDirectoryEntry * dir_table ; ///< RomFS directory entries table.
u64 file_table_size ; ///< RomFS file entries table size.
RomFileSystemFileEntry * file_table ; ///< RomFS file entries table.
u64 body_offset ; ///< RomFS file data body offset (relative to the start of the RomFS).
2022-07-06 10:57:31 +01:00
u64 cur_dir_offset ; ///< Current RomFS directory offset (relative to the start of the directory entries table). Used for RomFS browsing.
u64 cur_file_offset ; ///< Current RomFS file offset (relative to the start of the file entries table). Used for RomFS browsing.
2020-04-26 09:35:01 +01:00
} RomFileSystemContext ;
2020-04-28 09:58:17 +01:00
typedef struct {
bool use_old_format_patch ; ///< Old format patch flag.
2020-10-28 22:48:46 +00:00
bool written ; ///< Set to true if the patch has been completely written.
2020-04-28 09:58:17 +01:00
NcaHierarchicalSha256Patch old_format_patch ; ///< Used with NCA0 RomFS sections.
NcaHierarchicalIntegrityPatch cur_format_patch ; ///< Used with NCA2/NCA3 RomFS sections.
} RomFileSystemFileEntryPatch ;
2020-05-10 17:40:12 +01:00
typedef enum {
RomFileSystemPathIllegalCharReplaceType_None = 0 ,
RomFileSystemPathIllegalCharReplaceType_IllegalFsChars = 1 ,
RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly = 2
} RomFileSystemPathIllegalCharReplaceType ;
2022-07-04 13:30:48 +01:00
/// Initializes a RomFS or Patch RomFS context.
/// 'base_nca_fs_ctx' must always be provided.
/// 'patch_nca_fs_ctx' shall be NULL if not dealing with a Patch RomFS.
bool romfsInitializeContext ( RomFileSystemContext * out , NcaFsSectionContext * base_nca_fs_ctx , NcaFsSectionContext * patch_nca_fs_ctx ) ;
2020-04-26 09:35:01 +01:00
2020-04-26 11:04:31 +01:00
/// Reads raw filesystem data using a RomFS context.
2020-04-28 00:30:35 +01:00
/// Input offset must be relative to the start of the RomFS.
2020-04-26 11:04:31 +01:00
bool romfsReadFileSystemData ( RomFileSystemContext * ctx , void * out , u64 read_size , u64 offset ) ;
2020-04-26 09:35:01 +01:00
/// Reads data from a previously retrieved RomFileSystemFileEntry using a RomFS context.
2020-04-28 09:58:17 +01:00
/// Input offset must be relative to the start of the RomFS file entry data.
2020-04-26 09:35:01 +01:00
bool romfsReadFileEntryData ( RomFileSystemContext * ctx , RomFileSystemFileEntry * file_entry , void * out , u64 read_size , u64 offset ) ;
/// Calculates the extracted RomFS size.
2022-07-07 01:30:45 +01:00
/// If 'only_updated' is set to true and the provided RomFS context was initialized as a Patch RomFS context, only files modified by the update will be considered.
bool romfsGetTotalDataSize ( RomFileSystemContext * ctx , bool only_updated , u64 * out_size ) ;
2020-04-26 09:35:01 +01:00
/// Calculates the extracted size from a RomFS directory.
2020-04-29 22:11:27 +01:00
bool romfsGetDirectoryDataSize ( RomFileSystemContext * ctx , RomFileSystemDirectoryEntry * dir_entry , u64 * out_size ) ;
2020-04-27 23:37:15 +01:00
/// Retrieves a RomFS directory entry by path.
/// Input path must have a leading slash ('/'). If just a single slash is provided, a pointer to the root directory entry shall be returned.
RomFileSystemDirectoryEntry * romfsGetDirectoryEntryByPath ( RomFileSystemContext * ctx , const char * path ) ;
/// Retrieves a RomFS file entry by path.
/// Input path must have a leading slash ('/').
RomFileSystemFileEntry * romfsGetFileEntryByPath ( RomFileSystemContext * ctx , const char * path ) ;
/// Generates a path string from a RomFS directory entry.
2020-05-10 17:40:12 +01:00
bool romfsGeneratePathFromDirectoryEntry ( RomFileSystemContext * ctx , RomFileSystemDirectoryEntry * dir_entry , char * out_path , size_t out_path_size , u8 illegal_char_replace_type ) ;
2020-04-27 23:37:15 +01:00
/// Generates a path string from a RomFS file entry.
2020-05-10 17:40:12 +01:00
bool romfsGeneratePathFromFileEntry ( RomFileSystemContext * ctx , RomFileSystemFileEntry * file_entry , char * out_path , size_t out_path_size , u8 illegal_char_replace_type ) ;
2020-04-26 09:35:01 +01:00
2022-07-07 01:30:45 +01:00
/// Checks if a RomFS file entry is updated by the Patch RomFS.
/// Only works if the provided RomFileSystemContext was initialized as a Patch RomFS context.
bool romfsIsFileEntryUpdated ( RomFileSystemContext * ctx , RomFileSystemFileEntry * file_entry , bool * out ) ;
2020-07-22 09:03:28 +01:00
/// Generates HierarchicalSha256 (NCA0) / HierarchicalIntegrity (NCA2/NCA3) FS section patch data using a RomFS context + file entry, which can be used to seamlessly replace NCA data.
2020-04-28 09:58:17 +01:00
/// Input offset must be relative to the start of the RomFS file entry data.
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch() / ncaGenerateHierarchicalIntegrityPatch().
2020-07-22 21:35:23 +01:00
/// Use the romfsWriteFileEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
2020-04-28 09:58:17 +01:00
bool romfsGenerateFileEntryPatch ( RomFileSystemContext * ctx , RomFileSystemFileEntry * file_entry , const void * data , u64 data_size , u64 data_offset , RomFileSystemFileEntryPatch * out ) ;
2022-07-06 10:57:31 +01:00
/// Resets a previously initialized RomFileSystemContext.
2020-07-22 21:35:23 +01:00
NX_INLINE void romfsFreeContext ( RomFileSystemContext * ctx )
2020-04-28 09:58:17 +01:00
{
2020-07-22 21:35:23 +01:00
if ( ! ctx ) return ;
2022-07-04 13:30:48 +01:00
ncaStorageFreeContext ( & ( ctx - > storage_ctx [ 0 ] ) ) ;
ncaStorageFreeContext ( & ( ctx - > storage_ctx [ 1 ] ) ) ;
2020-07-22 21:35:23 +01:00
if ( ctx - > dir_table ) free ( ctx - > dir_table ) ;
if ( ctx - > file_table ) free ( ctx - > file_table ) ;
memset ( ctx , 0 , sizeof ( RomFileSystemContext ) ) ;
2020-04-28 09:58:17 +01:00
}
2022-07-06 10:57:31 +01:00
/// Functions to reset the current directory/file entry offset.
NX_INLINE void romfsResetDirectoryTableOffset ( RomFileSystemContext * ctx )
{
if ( ctx ) ctx - > cur_dir_offset = 0 ;
}
NX_INLINE void romfsResetFileTableOffset ( RomFileSystemContext * ctx )
{
if ( ctx ) ctx - > cur_file_offset = 0 ;
}
/// Checks if the provided RomFileSystemContext is valid.
NX_INLINE bool romfsIsValidContext ( RomFileSystemContext * ctx )
{
return ( ctx & & ncaStorageIsValidContext ( ctx - > default_storage_ctx ) & & ctx - > size & & ctx - > dir_table_size & & ctx - > dir_table & & ctx - > file_table_size & & ctx - > file_table & & \
ctx - > body_offset > = ctx - > header . old_format . header_size & & ctx - > body_offset < ctx - > size ) ;
}
/// Functions to retrieve a directory/file entry.
NX_INLINE void * romfsGetEntryByOffset ( RomFileSystemContext * ctx , void * entry_table , u64 entry_table_size , u64 entry_size , u64 entry_offset )
{
if ( ! romfsIsValidContext ( ctx ) | | ! entry_table | | ! entry_table_size | | ! entry_size | | ( entry_offset + entry_size ) > entry_table_size ) return NULL ;
return ( ( u8 * ) entry_table + entry_offset ) ;
}
NX_INLINE RomFileSystemDirectoryEntry * romfsGetDirectoryEntryByOffset ( RomFileSystemContext * ctx , u64 dir_entry_offset )
{
return ( ctx ? ( RomFileSystemDirectoryEntry * ) romfsGetEntryByOffset ( ctx , ctx - > dir_table , ctx - > dir_table_size , sizeof ( RomFileSystemDirectoryEntry ) , dir_entry_offset ) : NULL ) ;
}
NX_INLINE RomFileSystemDirectoryEntry * romfsGetCurrentDirectoryEntry ( RomFileSystemContext * ctx )
{
return ( ctx ? romfsGetDirectoryEntryByOffset ( ctx , ctx - > cur_dir_offset ) : NULL ) ;
}
NX_INLINE RomFileSystemFileEntry * romfsGetFileEntryByOffset ( RomFileSystemContext * ctx , u64 file_entry_offset )
{
return ( ctx ? ( RomFileSystemFileEntry * ) romfsGetEntryByOffset ( ctx , ctx - > file_table , ctx - > file_table_size , sizeof ( RomFileSystemFileEntry ) , file_entry_offset ) : NULL ) ;
}
NX_INLINE RomFileSystemFileEntry * romfsGetCurrentFileEntry ( RomFileSystemContext * ctx )
{
return ( ctx ? romfsGetFileEntryByOffset ( ctx , ctx - > cur_file_offset ) : NULL ) ;
}
/// Functions to check if it's possible to move to the next directory/file entry based on the current directory/file entry offset.
NX_INLINE bool romfsCanMoveToNextEntry ( RomFileSystemContext * ctx , void * entry_table , u64 entry_table_size , u64 entry_size , u64 entry_offset )
{
if ( ! romfsIsValidContext ( ctx ) | | ! entry_table | | ! entry_table_size | | entry_size < 4 | | ( entry_offset + entry_size ) > entry_table_size ) return false ;
u32 name_length = * ( ( u32 * ) ( ( u8 * ) entry_table + entry_offset + entry_size - 4 ) ) ;
return ( ( entry_offset + ALIGN_UP ( entry_size + name_length , ROMFS_TABLE_ENTRY_ALIGNMENT ) ) < = entry_table_size ) ;
}
NX_INLINE bool romfsCanMoveToNextDirectoryEntry ( RomFileSystemContext * ctx )
{
return ( ctx ? romfsCanMoveToNextEntry ( ctx , ctx - > dir_table , ctx - > dir_table_size , sizeof ( RomFileSystemDirectoryEntry ) , ctx - > cur_dir_offset ) : false ) ;
}
NX_INLINE bool romfsCanMoveToNextFileEntry ( RomFileSystemContext * ctx )
{
return ( ctx ? romfsCanMoveToNextEntry ( ctx , ctx - > file_table , ctx - > file_table_size , sizeof ( RomFileSystemFileEntry ) , ctx - > cur_file_offset ) : false ) ;
}
/// Functions to update the current directory/file entry offset to make it point to the next directory/file entry.
NX_INLINE bool romfsMoveToNextEntry ( RomFileSystemContext * ctx , void * entry_table , u64 entry_table_size , u64 entry_size , u64 * entry_offset )
{
if ( ! romfsIsValidContext ( ctx ) | | ! entry_table | | ! entry_table_size | | entry_size < 4 | | ! entry_offset | | ( * entry_offset + entry_size ) > entry_table_size ) return false ;
u32 name_length = * ( ( u32 * ) ( ( u8 * ) entry_table + * entry_offset + entry_size - 4 ) ) ;
* entry_offset + = ALIGN_UP ( entry_size + name_length , ROMFS_TABLE_ENTRY_ALIGNMENT ) ;
return true ;
}
NX_INLINE bool romfsMoveToNextDirectoryEntry ( RomFileSystemContext * ctx )
2020-04-26 09:35:01 +01:00
{
2022-07-06 10:57:31 +01:00
return ( ctx ? romfsMoveToNextEntry ( ctx , ctx - > dir_table , ctx - > dir_table_size , sizeof ( RomFileSystemDirectoryEntry ) , & ( ctx - > cur_dir_offset ) ) : false ) ;
2020-04-26 09:35:01 +01:00
}
2022-07-06 10:57:31 +01:00
NX_INLINE bool romfsMoveToNextFileEntry ( RomFileSystemContext * ctx )
2020-04-26 09:35:01 +01:00
{
2022-07-06 10:57:31 +01:00
return ( ctx ? romfsMoveToNextEntry ( ctx , ctx - > file_table , ctx - > file_table_size , sizeof ( RomFileSystemFileEntry ) , & ( ctx - > cur_file_offset ) ) : false ) ;
2020-04-26 09:35:01 +01:00
}
2022-07-06 10:57:31 +01:00
/// NCA patch management functions.
2020-07-23 01:37:02 +01:00
NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer ( RomFileSystemContext * ctx , RomFileSystemFileEntryPatch * patch , void * buf , u64 buf_size , u64 buf_offset )
2020-07-22 21:35:23 +01:00
{
2022-07-06 10:57:31 +01:00
if ( ! romfsIsValidContext ( ctx ) | | ctx - > is_patch | | ctx - > default_storage_ctx - > base_storage_type ! = NcaStorageBaseStorageType_Regular | | ! patch | | \
( ! patch - > use_old_format_patch & & ctx - > default_storage_ctx - > nca_fs_ctx - > section_type ! = NcaFsSectionType_RomFs ) | | \
2022-07-04 13:30:48 +01:00
( patch - > use_old_format_patch & & ctx - > default_storage_ctx - > nca_fs_ctx - > section_type ! = NcaFsSectionType_Nca0RomFs ) ) return ;
2022-07-05 02:04:28 +01:00
2022-09-12 19:19:10 +01:00
NcaContext * nca_ctx = ctx - > default_storage_ctx - > nca_fs_ctx - > nca_ctx ;
2022-07-05 02:04:28 +01:00
2020-07-22 21:35:23 +01:00
if ( patch - > use_old_format_patch )
{
2022-07-04 01:01:12 +01:00
ncaWriteHierarchicalSha256PatchToMemoryBuffer ( nca_ctx , & ( patch - > old_format_patch ) , buf , buf_size , buf_offset ) ;
2020-10-28 22:48:46 +00:00
patch - > written = patch - > old_format_patch . written ;
2020-07-22 21:35:23 +01:00
} else {
2022-07-04 01:01:12 +01:00
ncaWriteHierarchicalIntegrityPatchToMemoryBuffer ( nca_ctx , & ( patch - > cur_format_patch ) , buf , buf_size , buf_offset ) ;
2020-10-28 22:48:46 +00:00
patch - > written = patch - > cur_format_patch . written ;
2020-07-22 21:35:23 +01:00
}
}
NX_INLINE void romfsFreeFileEntryPatch ( RomFileSystemFileEntryPatch * patch )
{
if ( ! patch ) return ;
ncaFreeHierarchicalSha256Patch ( & ( patch - > old_format_patch ) ) ;
ncaFreeHierarchicalIntegrityPatch ( & ( patch - > cur_format_patch ) ) ;
2020-10-28 22:48:46 +00:00
memset ( patch , 0 , sizeof ( RomFileSystemFileEntryPatch ) ) ;
2020-07-22 21:35:23 +01:00
}
2021-03-23 14:06:52 +00:00
# ifdef __cplusplus
}
2021-03-24 17:25:19 +00:00
# endif
# endif /* __ROMFS_H__ */