1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-12-28 19:36:03 +00:00
nxdumptool/include/core/romfs.h

259 lines
13 KiB
C
Raw Normal View History

2020-04-26 09:35:01 +01:00
/*
* romfs.h
2020-04-26 09:35:01 +01:00
*
2024-04-12 10:47:36 +01:00
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* 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
*
* 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
* 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
#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 bucket offset.
u32 directory_bucket_size; ///< Directory bucket size.
2020-04-26 09:35:01 +01:00
u32 directory_entry_offset; ///< Directory entries table offset.
u32 directory_entry_size; ///< Directory entries table size.
u32 file_bucket_offset; ///< File bucket offset.
u32 file_bucket_size; ///< File bucket size.
2020-04-26 09:35:01 +01:00
u32 file_entry_offset; ///< File entries table offset.
u32 file_entry_size; ///< File entries table size.
u32 body_offset; ///< File data body offset.
} RomFileSystemInformationOld;
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 bucket offset.
u64 directory_bucket_size; ///< Directory bucket size.
2020-04-26 09:35:01 +01:00
u64 directory_entry_offset; ///< Directory entries table offset.
u64 directory_entry_size; ///< Directory entries table size.
u64 file_bucket_offset; ///< File bucket offset.
u64 file_bucket_size; ///< File bucket size.
2020-04-26 09:35:01 +01:00
u64 file_entry_offset; ///< File entries table offset.
u64 file_entry_size; ///< File entries table size.
u64 body_offset; ///< File data body offset.
} RomFileSystemInformation;
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;
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;
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;
NXDT_ASSERT(RomFileSystemFileEntry, 0x20);
2020-04-26 09:35:01 +01:00
typedef struct {
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_bucket_size; ///< RomFS directory bucket size.
u32 *dir_bucket; ///< RomFS directory bucket.
u64 dir_table_size; ///< RomFS directory entries table size.
RomFileSystemDirectoryEntry *dir_table; ///< RomFS directory entries table.
u64 file_bucket_size; ///< RomFS file bucket size.
u32 *file_bucket; ///< RomFS file bucket.
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).
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.
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,
*FormattedStringToBuffer: relax input validation Affects both utilsAppendFormattedStringToBuffer() and logWriteFormattedStringToBuffer(). Fixes logging issues within both the exception handler and memory debugging code. Other changes include: * bfttf: rename BfttfFontType_Total -> BfttfFontType_Count. * config: rename "append_authoringtool_data" -> "generate_authoringtool_data". * fs_ext: update FsGameCardCertificate struct * gamecard: fix gamecardReadLotusAsicFirmwareBlob() not returning false if FS .data segment memory couldn't be retrieved; update GameCardInfo struct to reflect a recently discovered area that's not zeroed out. * mem: expand MemoryProgramSegmentType enum; define extra macros for PID buffer size and memory page type checks; force empty memory page attribute and R/X permission checks while looking for the last FS .text segment; make memory page filtering code more readable. * npdm: rename KernelCapability enums; rename entry_value -> bitmask in all KernelCapability descriptor structs. * tik: force byte-for-byte memory lookup while dumping volatile tickets. * libs: update libusbhsfs to latest commit. * codebase: add missing comments to some enums; add missing "None" and "Count" elements to some enums. "Count" entries from enums with bitmasks will only reflect the number of available bitmask values. Enums with hex values remain unchanged. * PoC builds: use EXIT_SUCCESS and EXIT_FAILURE as return values. * nxdt_rw_poc: move "end" jump label within main() to avoid a crash if utilsInitializeResources() fails; reflect changes in config.c.
2023-07-17 00:03:05 +01:00
RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly = 2,
RomFileSystemPathIllegalCharReplaceType_Count = 3 ///< Total values supported by this enum.
2020-05-10 17:40:12 +01:00
} RomFileSystemPathIllegalCharReplaceType;
/// Initializes a RomFS or Patch RomFS context.
Fix building with latest libnx + QoL changes. libnx now implements fsDeviceOperatorGetGameCardIdSet(), so I got rid of my own implementation. Other changes include: * cnmt: add cnmtVerifyContentHash(). * defines: add SHA256_HASH_STR_SIZE. * fs_ext: add FsCardId1MakerCode, FsCardId1MemoryType and FsCardId2CardType enums. * fs_ext: update FsCardId* structs. * gamecard: change all package_id definitions from u64 -> u8[0x8]. * gamecard: fix misleading struct member names in GameCardHeader. * gamecard: rename gamecardGetIdSet() -> gamecardGetCardIdSet(). * gamecard_tab: fix Package ID printing. * gamecard_tab: add Card ID Set printing. * host: add executable flag to Python scripts. * keys: detect if we're dealing with a wiped eTicket RSA device key (e.g. via set:cal blanking). If so, the application will still launch, but all operations related to personalized titlekey crypto are disabled. * pfs: rename PartitionFileSystemFileContext -> PartitionFileSystemImageContext and propagate the change throughout the codebase. * pfs: rename PFS_FULL_HEADER_ALIGNMENT -> PFS_HEADER_PADDING_ALIGNMENT and update pfsWriteImageContextHeaderToMemoryBuffer() accordingly. * poc: print certain button prompts with reversed colors, in the hopes of getting the user's attention. * poc: NSP, Ticket and NCA submenus for updates and DLC updates now display the highest available title by default. * poc: simplified output path generation for extracted NCA FS section dumps. * poc: handle edge cases where a specific NCA from an update has no matching equivalent by type/ID offset in its base title (e.g. Fall Guys' HtmlDocument NCA). * poc: implement NCA checksum validation while generating NSP dumps. * romfs: update romfsInitializeContext() to allow its base_nca_fs_ctx argument to be NULL. * usb: use USB_BOS_SIZE only once. * workflow: update commit hash referenced by "rewrite-prerelease" tag on update.
2023-11-03 01:22:47 +00:00
/// 'base_nca_fs_ctx' shall be NULL *only* if a NCA from an update has no matching equivalent available in its base title.
/// '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
/// Reads raw filesystem data using a RomFS context.
/// Input offset must be relative to the start of the RomFS.
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.
/// 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.
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size);
/// 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);
/// 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
/// 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);
/// 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().
/// 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.
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
2020-04-28 09:58:17 +01:00
{
if (!ctx) return;
ncaStorageFreeContext(&(ctx->storage_ctx[0]));
ncaStorageFreeContext(&(ctx->storage_ctx[1]));
if (ctx->dir_bucket) free(ctx->dir_bucket);
if (ctx->dir_table) free(ctx->dir_table);
if (ctx->file_bucket) free(ctx->file_bucket);
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
/// Checks if the provided RomFileSystemContext is valid.
NX_INLINE bool romfsIsValidContext(RomFileSystemContext *ctx)
{
return (ctx && ncaStorageIsValidContext(ctx->default_storage_ctx) && ctx->size && ctx->dir_bucket_size && ctx->dir_bucket && ctx->dir_table_size && ctx->dir_table && \
ctx->file_bucket_size && ctx->file_bucket && ctx->file_table_size && ctx->file_table && ctx->body_offset >= ctx->header.old_format.header_size && \
ctx->body_offset < ctx->size);
2022-07-06 10:57:31 +01:00
}
/// Functions to retrieve a directory/file entry.
I'm a terrible person and an even worse developer. And I don't need anyone to tell me so, thank you very much. * PoC: remove gc_dumper and nsp_dumper PoC; create nxdt_rw_poc with all gc_dumper and nsp_dumper capabilities + standalone ticket dumping + raw NCA dumping; use ftruncate() to set output file sizes whenever possible. PoC code is a mess, as always. Expect the features from the rest of the PoCs to be implemented into nxdt_rw_poc soon. * workflow: temporarily disable borealis build generation; comment out manual installation of up-to-date packages from Leseratte's mirrors because the latest devkitA64 Docker image has them all. * borealis: update to fix building issues with latest devkitA64. * bfttf: error out on invalid NCA signatures. * config: save configuration to the current working directory; parse and validate new "gamecard/write_raw_hfs_partition" flag. * defines: remove CONFIG_PATH macro; rename CONFIG_FILE_NAME. * gamecard: rename fs_ctx -> hfs_ctx everywhere; use HFS function calls to retrieve partition names. * hfs: move GameCardHashFileSystemPartitionType enum from gamecard.h and rename it to HashFileSystemPartitionType; add hfsIsValidContext(); add hfsGetPartitionNameString(). * nca/npdm: update comments to reflect latest HOS version. * nxdt_bfsar: always generate absolute SD card paths with the device name; error out on an invalid NCA signature. * nxdt_includes: include dirent.h; refactor Version struct to make it a union of all known *Version structs. * nxdt_log: don't write session separator if the logfile is empty. * nxdt_utils: log appletIsGamePlayRecordingSupported() errors; add utilsDeleteDirectoryRecursively(). * rsa: provide clearer function descriptions in header file. * services: handle usb:ds initialization. * tik: update tikConvertPersonalizedTicketToCommonTicket() to allow NULL input pointers as raw certificate chain arguments (much needed for standalone ticket dumping). * title: add titleGetApplicationIdByMetaKey(). * usb: refactor interface (de)initialization code; slightly improve ABI usage (console-side only); redefine ABI version field in StartSession command blocks; upgrade ABI to v1.1. * FatFs: rename DIR -> FDIR to avoid conflicts with definitions from stdlib's dirent.h. * gamecard_tab: display package ID from the inserted gamecard; fix displayed version numbers from bundled system updates below 3.0.0. * todo: add notes about creating devoptab devices for HFS/PFS/RomFS file tree dumping.
2023-05-24 20:05:34 +01:00
2022-07-06 10:57:31 +01:00
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 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);
}
/// NCA patch management functions.
I'm a terrible person and an even worse developer. And I don't need anyone to tell me so, thank you very much. * PoC: remove gc_dumper and nsp_dumper PoC; create nxdt_rw_poc with all gc_dumper and nsp_dumper capabilities + standalone ticket dumping + raw NCA dumping; use ftruncate() to set output file sizes whenever possible. PoC code is a mess, as always. Expect the features from the rest of the PoCs to be implemented into nxdt_rw_poc soon. * workflow: temporarily disable borealis build generation; comment out manual installation of up-to-date packages from Leseratte's mirrors because the latest devkitA64 Docker image has them all. * borealis: update to fix building issues with latest devkitA64. * bfttf: error out on invalid NCA signatures. * config: save configuration to the current working directory; parse and validate new "gamecard/write_raw_hfs_partition" flag. * defines: remove CONFIG_PATH macro; rename CONFIG_FILE_NAME. * gamecard: rename fs_ctx -> hfs_ctx everywhere; use HFS function calls to retrieve partition names. * hfs: move GameCardHashFileSystemPartitionType enum from gamecard.h and rename it to HashFileSystemPartitionType; add hfsIsValidContext(); add hfsGetPartitionNameString(). * nca/npdm: update comments to reflect latest HOS version. * nxdt_bfsar: always generate absolute SD card paths with the device name; error out on an invalid NCA signature. * nxdt_includes: include dirent.h; refactor Version struct to make it a union of all known *Version structs. * nxdt_log: don't write session separator if the logfile is empty. * nxdt_utils: log appletIsGamePlayRecordingSupported() errors; add utilsDeleteDirectoryRecursively(). * rsa: provide clearer function descriptions in header file. * services: handle usb:ds initialization. * tik: update tikConvertPersonalizedTicketToCommonTicket() to allow NULL input pointers as raw certificate chain arguments (much needed for standalone ticket dumping). * title: add titleGetApplicationIdByMetaKey(). * usb: refactor interface (de)initialization code; slightly improve ABI usage (console-side only); redefine ABI version field in StartSession command blocks; upgrade ABI to v1.1. * FatFs: rename DIR -> FDIR to avoid conflicts with definitions from stdlib's dirent.h. * gamecard_tab: display package ID from the inserted gamecard; fix displayed version numbers from bundled system updates below 3.0.0. * todo: add notes about creating devoptab devices for HFS/PFS/RomFS file tree dumping.
2023-05-24 20:05:34 +01:00
NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
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) || \
(patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;
NcaContext *nca_ctx = ctx->default_storage_ctx->nca_fs_ctx->nca_ctx;
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);
patch->written = patch->old_format_patch.written;
} else {
2022-07-04 01:01:12 +01:00
ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(nca_ctx, &(patch->cur_format_patch), buf, buf_size, buf_offset);
patch->written = patch->cur_format_patch.written;
}
}
NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch)
{
if (!patch) return;
ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch));
ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch));
memset(patch, 0, sizeof(RomFileSystemFileEntryPatch));
}
2021-03-23 14:06:52 +00:00
#ifdef __cplusplus
}
#endif
#endif /* __ROMFS_H__ */