From 0947e941b83b23b701edb31345c119f14e5ad56f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 8 Jul 2023 16:17:30 -0400 Subject: Modest cleanups for DSi_NAND (#1714) * Add a definition for TMD files * Wrap TitleMetadata in a namespace * Add a comment * Remove TitleMetadataCertificate - melonDS ignores it anyway * Refactor the use of title metadata - Move bitwise operations on the title ID into helper methods - Use TitleMetadata objects instead of pointers to raw data * Slight cleanup in DSi_NAND - Replace some constants with sizeof - Use an NDSHeader object instead of a raw array of bytes * Add a DSi_NAND::ImportFile overload that loads a file from memory * Split most of ImportTitle into InitTitleFileStructure - It will be reused in the next commit * Add ability to import title from memory * Fix another potential issue * Fix broken DSiWare installation - The bytes of the title ID/category were being swapped in most places, but not all * Add some logging calls * Declare array sizes in DSi_TMD in decimal, not hex * Add a space after the #endif - To adhere to the style guide * Assert the size of TitleMetadataContent * Change the type of SignatureName * Don't mark the TMD structs as packed * Remove extraneous comments * Cut down some newlines --- src/DSi_TMD.h | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/DSi_TMD.h (limited to 'src/DSi_TMD.h') diff --git a/src/DSi_TMD.h b/src/DSi_TMD.h new file mode 100644 index 0000000..12226c5 --- /dev/null +++ b/src/DSi_TMD.h @@ -0,0 +1,119 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS 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. + + melonDS 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. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef DSI_TMD_H +#define DSI_TMD_H + +#include "types.h" +#include + +namespace DSi_TMD +{ + +struct TitleMetadataContent { + u8 ContentId[4]; /// Content ID (00,00,00,vv) ;lowercase/hex ;"0000000vv.app" + u8 ContentIndex[2]; /// Content Index (00,00) + u8 ContentType[2]; /// Content Type (00,01) ;aka DSi .app + u8 ContentSize[8]; /// Content Size (00,00,00,00,00,19,E4,00) + u8 ContentSha1Hash[20]; /// Content SHA1 (on decrypted ".app" file) + + [[nodiscard]] u32 GetVersion() const noexcept + { + return (ContentId[0] << 24) | (ContentId[1] << 16) | (ContentId[2] << 8) | ContentId[3]; + } +}; + +static_assert(sizeof(TitleMetadataContent) == 36, "TitleMetadataContent is not 36 bytes!"); + +/// Metadata for a DSiWare title. +/// Used to install DSiWare titles to NAND. +/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcdsiwareticketsandtitlemetadata +struct TitleMetadata +{ + u32 SignatureType; + u8 Signature[256]; + u8 SignatureAlignment[60]; + char SignatureName[64]; + + u8 TmdVersion; + u8 CaCrlVersion; + u8 SignerCrlVersion; + u8 Padding0; + + u8 SystemVersion[8]; + u8 TitleId[8]; + u32 TitleType; + u8 GroupId[2]; + u8 PublicSaveSize[4]; + u8 PrivateSaveSize[4]; + u8 Padding1[4]; + + u8 SrlFlag; + u8 Padding2[3]; + + u8 AgeRatings[16]; + u8 Padding3[30]; + + u32 AccessRights; + u16 TitleVersion; + + u16 NumberOfContents; /// There's always one or zero content entries in practice + u16 BootContentIndex; + u8 Padding4[2]; + + TitleMetadataContent Contents; + + [[nodiscard]] bool HasPublicSaveData() const noexcept { return GetPublicSaveSize() != 0; } + [[nodiscard]] bool HasPrivateSaveData() const noexcept { return GetPrivateSaveSize() != 0; } + + [[nodiscard]] u32 GetPublicSaveSize() const noexcept + { + return (PublicSaveSize[0] << 24) | (PublicSaveSize[1] << 16) | (PublicSaveSize[2] << 8) | PublicSaveSize[3]; + } + + [[nodiscard]] u32 GetPrivateSaveSize() const noexcept + { + return (PrivateSaveSize[0] << 24) | (PrivateSaveSize[1] << 16) | (PrivateSaveSize[2] << 8) | PrivateSaveSize[3]; + } + + [[nodiscard]] u32 GetCategory() const noexcept + { + return (TitleId[0] << 24) | (TitleId[1] << 16) | (TitleId[2] << 8) | TitleId[3]; + } + + [[nodiscard]] u32 GetCategoryNoByteswap() const noexcept + { + return reinterpret_cast(TitleId); + } + + [[nodiscard]] u32 GetID() const noexcept + { + return (TitleId[4] << 24) | (TitleId[5] << 16) | (TitleId[6] << 8) | TitleId[7]; + } + + [[nodiscard]] u32 GetIDNoByteswap() const noexcept + { + return *reinterpret_cast(&TitleId[4]); + } +}; + +static_assert(sizeof(TitleMetadata) == 520, "TitleMetadata is not 520 bytes!"); + +} + +#endif // DSI_TMD_H -- cgit v1.2.3