CNMT: Difference between revisions

Magic (talk | contribs)
No edit summary
No edit summary
 
(37 intermediate revisions by 11 users not shown)
Line 1: Line 1:
For the content of the NCA FS which can be [[Filesystem_services|mounted]], see [[NCA_Content_FS|here]].
This is the metadata file that ends in <code>".cnmt{.nca}"</code> or <code>"meta0.ncd"</code>. This seems to replace the TMD format.


= Metadata file =
The official name for CNMT is "PackagedContentMeta".
This is the file that ends in <code>".cnmt{.nca}"</code> or <code>"meta0.ncd"</code>. This seems to replace the TMD format.


There's at least 3 different filenames used for ".cnmt":
= PackagedContentMetaHeader =
* "Application_{lower-case hex titleID}.cnmt"
This is "nn::ncm::PackagedContentMetaHeader".
* "Patch_{lower-case hex titleID}.cnmt"
* "SystemUpdate_{hex titleID}.cnmt"


It starts with a header:
{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
Line 17: Line 13:
|-
|-
| 0x0
| 0x0
| 8
| 0x8
| Title ID
| Id
|-
|-
| 0x8
| 0x8
| 4
| 0x4
| u32 [[Title_list|title-version]]
| Version
|-
|-
| 0xC
| 0xC
| 1
| 0x1
| Type (see [[Content_Manager_services#Title_Types|Title Types]])
| [[NCM_services#ContentMetaType|ContentMetaType]]
|-
|-
| 0xD
| 0xD
| 1
| 0x1
|
| [17.0.0+] [[NCM_services#ContentMetaPlatform|ContentMetaPlatform]] ([1.0.0-16.1.0] Reserved)
|-
|-
| 0xE
| 0xE
| 2
| 0x2
| Offset to table relative to the end of this 0x20-byte header.
| ExtendedHeaderSize
|-
|-
| 0x10
| 0x10
| 2
| 0x2
| Number of content entries
| ContentCount
|-
|-
| 0x12
| 0x12
| 2
| 0x2
| Number of meta entries
| ContentMetaCount
|-
|-
| 0x14
| 0x14
| 12
| 0x1
|
| [[NCM_services#ContentMetaAttributes|ContentMetaAttributes]]
|-
| 0x15
| 0x3
| Reserved
|-
| 0x18
| 0x4
| RequiredDownloadSystemVersion
|-
| 0x1C
| 0x4
| Reserved
|}
|}


With SystemUpdate, the 4-bytes at offset 0xE are zero, with the entry-count field located at offset 0x12 instead(header size is the same).
= SystemUpdateMetaExtendedHeader =
This is "nn::ncm::SystemUpdateMetaExtendedHeader".


An optional header can follow, depending on the title type.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x4
| ExtendedDataSize
|}


At the end of the file following the entries is a 0x20-byte block, presumably a hash.
= ApplicationMetaExtendedHeader =
This is "nn::ncm::ApplicationMetaExtendedHeader".


With Patch-format, there's additional data after the end of the entries specified in the header and before the ending hash.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| PatchId
|-
| 0x8
| 0x4
| RequiredSystemVersion
|-
| 0xC
| 0x4
| RequiredApplicationVersion
|}
 
= PatchMetaExtendedHeader =
This is "nn::ncm::PatchMetaExtendedHeader".


== Application header ==
{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
Line 64: Line 102:
! Description
! Description
|-
|-
| 0x20
| 0x0
| 8
| 0x8
| Patch title ID
| ApplicationId
|-
| 0x8
| 0x4
| RequiredSystemVersion
|-
| 0xC
| 0x4
| ExtendedDataSize
|-
|-
| 0x28
| 0x10
| 8
| 0x8
|
| Reserved
|}
|}


== Patch header ==
= AddOnContentMetaExtendedHeader =
This is "nn::ncm::AddOnContentMetaExtendedHeader".
 
{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
Line 80: Line 128:
! Description
! Description
|-
|-
| 0x20
| 0x0
| 8
| 0x8
| Original title ID
| ApplicationId
|-
| 0x8
| 0x4
| RequiredApplicationVersion
|-
| 0xC
| 0x1
| [15.0.0+] ContentAccessibilities ([1.0.0-14.1.2] Reserved)
|-
| 0xD
| 0x3
| Reserved
|-
|-
| 0x28
| 0x10
| 8
| 0x8
|
| [15.0.0+] DataPatchId
|}
|}


== Add-on content header ==
= DeltaMetaExtendedHeader =
This is "nn::ncm::DeltaMetaExtendedHeader".
 
{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
Line 96: Line 158:
! Description
! Description
|-
|-
| 0x20
| 0x0
| 8
| 0x8
| Application title ID
| ApplicationId
|-
| 0x8
| 0x4
| ExtendedDataSize
|-
|-
| 0x28
| 0xC
| 8
| 0x4
|
| Reserved
|}
|}


== Content records ==
= DataPatchMetaExtendedHeader =
This is "nn::ncm::DataPatchMetaExtendedHeader".
 
This was added with [15.0.0+].
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| DataId
|-
| 0x8
| 0x8
| ApplicationId
|-
| 0x10
| 0x4
| RequiredApplicationVersion
|-
| 0x14
| 0x4
| ExtendedDataSize
|-
| 0x18
| 0x8
| Reserved
|}


Each entry is 0x38 bytes:
= PackagedContentInfo =
This is "nn::ncm::PackagedContentInfo".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 116: Line 213:
|-
|-
| 0x0
| 0x0
| 32
| 0x20
| Hash
| Hash (SHA256 of the referenced content)
|-
|-
| 0x20
| 0x20
| 16
| 0x10
| NcaId [same as first 16-bytes of hash]
| ContentId
|-
|-
| 0x30
| 0x30
| 6
| [15.0.0+] 0x5 ([1.0.0-14.1.2] 0x6)
| Size, same as the output from [[Content_Manager_services#GetEntrySize]].
| Size
|-
| 0x35
| 0x1
| [15.0.0+] ContentAttributes
|-
|-
| 0x36
| 0x36
| 1
| 0x1
| Type (0=meta, 1=program, 2=data, 3=control, 4=offline-manual [[Internet_Browser|html]], 5=legal [[Internet_Browser|html]])
| [[NCM_services#ContentType|ContentType]]
|-
|-
| 0x37
| 0x37
| 1
| 0x1
|
| IdOffset
|}
|}


== Meta records ==
= ContentMetaInfo =
 
This is "nn::ncm::ContentMetaInfo".
Each entry is 0x10 bytes:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 147: Line 247:
|-
|-
| 0x0
| 0x0
| 8
| 0x8
| Title ID
| Id
|-
|-
| 0x8
| 0x8
| 4
| 0x4
| Title version
| Version
|-
|-
| 0xC
| 0xC
| 1
| 0x1
| Type (see [[Content_Manager_services#Title_Types|Title Types]])
| [[NCM_services#ContentMetaType|ContentMetaType]]
|-
|-
| 0xD
| 0xD
| 1
| 0x1
| ? bit0 set = don't install?
| [[NCM_services#ContentMetaAttributes|ContentMetaAttributes]]
|-
|-
| 0xE
| 0xE
| 2
| 0x2
| Unused?
| Reserved
|}
 
This is used for SystemUpdate, see here: [[NCM_services#ReadEntryMetaRecords]].
 
= SystemUpdateMetaExtendedData =
This is "nn::ncm::SystemUpdateMetaExtendedData".
 
If the Version field is 1, the format is:
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x4
| Version
|-
| 0x4
| 0x4
| VariationCount
|-
| 0x8
| 0x20 * VariationCount
| [[#FirmwareVariationInfo|FirmwareVariationInfo]] (Version 1)
|}
 
If the Version field is 2, the format is:
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x4
| Version
|-
| 0x4
| 0x4
| VariationCount
|-
| 0x8
| 0x4 * VariationCount
| FirmwareVariationId
|-
| Variable
| 0x20 * VariationCount
| [[#FirmwareVariationInfo|FirmwareVariationInfo]] (Version 2)
|-
| Variable
| 0x10 * MetaCount (from [[#FirmwareVariationInfo|FirmwareVariationInfo]] (Version 2))
| [[#ContentMetaInfo|ContentMetaInfo]] (if ReferToBase is False)
|}
 
== FirmwareVariationInfo ==
This is "FirmwareVariationInfo ".
 
If the Version field is 1, the format is:
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x4
| FirmwareVariationId
|-
| 0x4
| 0x1C
| Reserved
|}
 
If the Version field is 2, the format is:
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| ReferToBase (0=False, 1=True)
|-
| 0x1
| 0x3
| Reserved
|-
| 0x4
| 0x4
| MetaCount
|-
| 0x8
| 0x18
| Reserved
|}
 
= PatchMetaExtendedData =
This is "nn::ncm::PatchMetaExtendedData".
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x4
| PatchHistoryHeaderCount
|-
| 0x4
| 0x4
| PatchDeltaHistoryCount
|-
| 0x8
| 0x4
| PatchDeltaHeaderCount
|-
| 0xC
| 0x4
| FragmentSetCount
|-
| 0x10
| 0x4
| PatchHistoryContentInfoCount
|-
| 0x14
| 0x4
| PatchDeltaPackagedContentInfoCount
|-
| 0x18
| 0x4
| Reserved
|-
| 0x1C
| 0x38 * PatchHistoryHeaderCount
| [[#PatchHistoryHeader|PatchHistoryHeader]]
|-
| Variable
| 0x28 * PatchDeltaHistoryCount
| [[#PatchDeltaHistory|PatchDeltaHistory]]
|-
| Variable
| 0x28 * PatchDeltaHeaderCount
| [[#PatchDeltaHeader|PatchDeltaHeader]]
|-
| Variable
| 0x34 * FragmentSetCount
| [[#FragmentSet|FragmentSet]]
|-
| Variable
| 0x18 * PatchHistoryContentInfoCount
| [[#PackagedContentInfo|PatchHistoryContentInfo]] ([[#PackagedContentInfo|PackagedContentInfo]] without the Hash entry)
|-
| Variable
| 0x38 * PatchDeltaPackagedContentInfoCount
| [[#PackagedContentInfo|PatchDeltaPackagedContentInfo]]
|-
| Variable
| 0x4 * FragmentIndicatorCount (from [[#FragmentSet|FragmentSet]])
| [[#FragmentIndicator|FragmentIndicator]]
|}
 
== PatchHistoryHeader ==
This is "nn::ncm::PatchHistoryHeader".
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x10
| ContentMetaKey
|-
| 0x10
| 0x20
| Digest
|-
| 0x30
| 0x2
| ContentInfoCount
|-
| 0x32
| 0x6
| Reserved
|}
 
== PatchDeltaHistory ==
This is "nn::ncm::PatchDeltaHistory".
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| SourcePatchId
|-
| 0x8
| 0x8
| DestinationPatchId
|-
| 0x10
| 0x4
| SourceVersion
|-
| 0x14
| 0x4
| DestinationVersion
|-
| 0x18
| 0x8
| DownloadSize
|-
| 0x20
| 0x8
| Reserved
|}
 
== PatchDeltaHeader ==
This is "nn::ncm::PatchDeltaHeader".
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| SourcePatchId
|-
| 0x8
| 8
| DestinationPatchId
|-
| 0x10
| 0x4
| SourceVersion
|-
| 0x14
| 0x4
| DestinationVersion
|-
| 0x18
| 0x2
| FragmentSetCount
|-
| 0x1A
| 0x6
| Reserved
|-
| 0x20
| 0x2
| ContentInfoCount
|-
| 0x22
| 0x6
| Reserved
|}
 
== FragmentSet ==
This is "nn::ncm::FragmentSet".
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x10
| SourceContentId
|-
| 0x10
| 0x10
| DestinationContentId
|-
| 0x20
| 0x6
| SourceSize
|-
| 0x26
| 0x6
| DestinationSize
|-
| 0x2C
| 0x2
| FragmentIndicatorCount
|-
| 0x2E
| 0x1
| [[NCM_services#ContentMetaType|FragmentTargetContentType]]
|-
| 0x2F
| 0x1
| UpdateType (0=ApplyAsDelta, 1=Overwrite, 2=Create)
|-
| 0x30
| 0x4
| Reserved
|}
 
== FragmentIndicator ==
This is "nn::ncm::FragmentIndicator".
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x2
| ContentInfoIndex
|-
| 0x2
| 0x2
| FragmentIndex
|}
 
= DeltaMetaExtendedData =
This is "nn::ncm::DeltaMetaExtendedData".
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| SourcePatchId
|-
| 0x8
| 0x8
| DestinationPatchId
|-
| 0x10
| 0x4
| SourceVersion
|-
| 0x14
| 0x4
| DestinationVersion
|-
| 0x18
| 0x2
| FragmentSetCount
|-
| 0x1A
| 0x6
| Reserved
|-
| 0x20
| 0x34 * FragmentSetCount
| [[#FragmentSet|FragmentSet]]
|-
| Variable
| 0x4 * FragmentIndicatorCount (from [[#FragmentSet|FragmentSet]])
| [[#FragmentIndicator|FragmentIndicator]]
|}
 
= Digest =
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x20
| Digest
|}
|}


This is used for SystemUpdate, see here: [[Content_Manager_services#GetUpdateTitleList]].
This is a SHA-256 hash always found at the end of the file. The hash is calculated over the CNMT file's contents, but this is only done for the development version of the file which results in its production version counterpart sharing the same hash value.