2019-11-01 20:00:28 +00:00
/*
Copyright ( c ) 2017 - 2018 Adubbz
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the " Software " ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE .
*/
2019-10-18 03:59:34 +00:00
# include "install/install.hpp"
# include <switch.h>
# include <cstring>
# include <memory>
2019-10-22 22:14:37 +00:00
# include "util/error.hpp"
2019-10-18 03:59:34 +00:00
# include "nx/ncm.hpp"
# include "util/title_util.hpp"
// TODO: Check NCA files are present
// TODO: Check tik/cert is present
namespace tin : : install
{
2019-11-14 19:04:40 +00:00
Install : : Install ( NcmStorageId destStorageId , bool ignoreReqFirmVersion ) :
2019-10-18 03:59:34 +00:00
m_destStorageId ( destStorageId ) , m_ignoreReqFirmVersion ( ignoreReqFirmVersion ) , m_contentMeta ( )
{
appletSetMediaPlaybackState ( true ) ;
}
Install : : ~ Install ( )
{
appletSetMediaPlaybackState ( false ) ;
}
// TODO: Implement RAII on NcmContentMetaDatabase
2019-11-29 08:23:11 +00:00
void Install : : InstallContentMetaRecords ( tin : : data : : ByteBuffer & installContentMetaBuf , int i )
2019-10-18 03:59:34 +00:00
{
NcmContentMetaDatabase contentMetaDatabase ;
2019-11-29 08:23:11 +00:00
NcmContentMetaKey contentMetaKey = m_contentMeta [ i ] . GetContentMetaKey ( ) ;
2019-10-18 03:59:34 +00:00
try
{
ASSERT_OK ( ncmOpenContentMetaDatabase ( & contentMetaDatabase , m_destStorageId ) , " Failed to open content meta database " ) ;
ASSERT_OK ( ncmContentMetaDatabaseSet ( & contentMetaDatabase , & contentMetaKey , ( NcmContentMetaHeader * ) installContentMetaBuf . GetData ( ) , installContentMetaBuf . GetSize ( ) ) , " Failed to set content records " ) ;
ASSERT_OK ( ncmContentMetaDatabaseCommit ( & contentMetaDatabase ) , " Failed to commit content records " ) ;
}
catch ( std : : runtime_error & e )
{
serviceClose ( & contentMetaDatabase . s ) ;
throw e ;
}
2019-11-03 18:18:42 +00:00
2019-10-18 03:59:34 +00:00
serviceClose ( & contentMetaDatabase . s ) ;
}
2019-11-29 08:23:11 +00:00
void Install : : InstallApplicationRecord ( int i )
2019-10-18 03:59:34 +00:00
{
Result rc = 0 ;
std : : vector < ContentStorageRecord > storageRecords ;
2019-11-29 08:23:11 +00:00
u64 baseTitleId = tin : : util : : GetBaseTitleId ( this - > GetTitleId ( i ) , this - > GetContentMetaType ( i ) ) ;
2019-10-18 03:59:34 +00:00
u32 contentMetaCount = 0 ;
printf ( " Base title Id: 0x%lx " , baseTitleId ) ;
// TODO: Make custom error with result code field
// 0x410: The record doesn't already exist
if ( R_FAILED ( rc = nsCountApplicationContentMeta ( baseTitleId , & contentMetaCount ) ) & & rc ! = 0x410 )
{
throw std : : runtime_error ( " Failed to count application content meta " ) ;
}
rc = 0 ;
printf ( " Content meta count: %u \n " , contentMetaCount ) ;
// Obtain any existing app record content meta and append it to our vector
if ( contentMetaCount > 0 )
{
storageRecords . resize ( contentMetaCount ) ;
size_t contentStorageBufSize = contentMetaCount * sizeof ( ContentStorageRecord ) ;
auto contentStorageBuf = std : : make_unique < ContentStorageRecord [ ] > ( contentMetaCount ) ;
u32 entriesRead ;
ASSERT_OK ( nsListApplicationRecordContentMeta ( 0 , baseTitleId , contentStorageBuf . get ( ) , contentStorageBufSize , & entriesRead ) , " Failed to list application record content meta " ) ;
if ( entriesRead ! = contentMetaCount )
{
throw std : : runtime_error ( " Mismatch between entries read and content meta count " ) ;
}
memcpy ( storageRecords . data ( ) , contentStorageBuf . get ( ) , contentStorageBufSize ) ;
}
// Add our new content meta
ContentStorageRecord storageRecord ;
2019-11-29 08:23:11 +00:00
storageRecord . metaRecord = m_contentMeta [ i ] . GetContentMetaKey ( ) ;
2019-10-18 03:59:34 +00:00
storageRecord . storageId = m_destStorageId ;
storageRecords . push_back ( storageRecord ) ;
// Replace the existing application records with our own
try
{
nsDeleteApplicationRecord ( baseTitleId ) ;
}
catch ( . . . ) { }
printf ( " Pushing application record... \n " ) ;
ASSERT_OK ( nsPushApplicationRecord ( baseTitleId , 0x3 , storageRecords . data ( ) , storageRecords . size ( ) * sizeof ( ContentStorageRecord ) ) , " Failed to push application record " ) ;
}
// Validate and obtain all data needed for install
void Install : : Prepare ( )
{
tin : : data : : ByteBuffer cnmtBuf ;
2019-11-29 08:23:11 +00:00
std : : vector < std : : tuple < nx : : ncm : : ContentMeta , NcmContentInfo > > tupelList = this - > ReadCNMT ( ) ;
for ( size_t i = 0 ; i < tupelList . size ( ) ; i + + ) {
std : : tuple < nx : : ncm : : ContentMeta , NcmContentInfo > cnmtTuple = tupelList [ i ] ;
m_contentMeta . push_back ( std : : get < 0 > ( cnmtTuple ) ) ;
NcmContentInfo cnmtContentRecord = std : : get < 1 > ( cnmtTuple ) ;
2019-10-18 03:59:34 +00:00
2019-11-29 08:23:11 +00:00
nx : : ncm : : ContentStorage contentStorage ( m_destStorageId ) ;
2019-10-18 03:59:34 +00:00
2019-11-29 08:23:11 +00:00
if ( ! contentStorage . Has ( cnmtContentRecord . content_id ) )
{
printf ( " Installing CNMT NCA... \n " ) ;
this - > InstallNCA ( cnmtContentRecord . content_id ) ;
}
else
{
printf ( " CNMT NCA already installed. Proceeding... \n " ) ;
}
2019-10-18 03:59:34 +00:00
2019-11-29 08:23:11 +00:00
// Parse data and create install content meta
if ( m_ignoreReqFirmVersion )
printf ( " WARNING: Required system firmware version is being IGNORED! \n " ) ;
2019-10-18 03:59:34 +00:00
2019-11-29 08:23:11 +00:00
tin : : data : : ByteBuffer installContentMetaBuf ;
m_contentMeta [ i ] . GetInstallContentMeta ( installContentMetaBuf , cnmtContentRecord , m_ignoreReqFirmVersion ) ;
this - > InstallContentMetaRecords ( installContentMetaBuf , i ) ;
this - > InstallApplicationRecord ( i ) ;
}
}
2019-10-18 03:59:34 +00:00
2019-11-29 08:23:11 +00:00
void Install : : Begin ( )
{
2019-10-18 03:59:34 +00:00
printf ( " Installing ticket and cert... \n " ) ;
try
{
this - > InstallTicketCert ( ) ;
}
catch ( std : : runtime_error & e )
{
printf ( " WARNING: Ticket installation failed! This may not be an issue, depending on your use case. \n Proceed with caution! \n " ) ;
}
2019-11-29 08:23:11 +00:00
for ( nx : : ncm : : ContentMeta contentMeta : m_contentMeta ) {
printf ( " Installing NCAs... \n " ) ;
for ( auto & record : contentMeta . GetContentInfos ( ) )
{
printf ( " Installing from %s \n " , tin : : util : : GetNcaIdString ( record . content_id ) . c_str ( ) ) ;
this - > InstallNCA ( record . content_id ) ;
}
2019-10-18 03:59:34 +00:00
2019-11-29 08:23:11 +00:00
printf ( " Post Install Records: \n " ) ;
}
2019-10-18 03:59:34 +00:00
}
2019-11-29 08:23:11 +00:00
u64 Install : : GetTitleId ( int i )
2019-10-18 03:59:34 +00:00
{
2019-11-29 08:23:11 +00:00
return m_contentMeta [ i ] . GetContentMetaKey ( ) . id ;
2019-10-18 03:59:34 +00:00
}
2019-11-29 08:23:11 +00:00
NcmContentMetaType Install : : GetContentMetaType ( int i )
2019-10-18 03:59:34 +00:00
{
2019-11-29 08:23:11 +00:00
return static_cast < NcmContentMetaType > ( m_contentMeta [ i ] . GetContentMetaKey ( ) . type ) ;
2019-10-18 03:59:34 +00:00
}
void Install : : DebugPrintInstallData ( )
{
2019-11-29 08:23:11 +00:00
/*
2019-10-18 03:59:34 +00:00
# ifdef NXLINK_DEBUG
NcmContentMetaDatabase contentMetaDatabase ;
NcmContentMetaKey metaRecord = m_contentMeta . GetContentMetaKey ( ) ;
2019-11-09 14:09:31 +00:00
u64 baseTitleId = tin : : util : : GetBaseTitleId ( metaRecord . id , static_cast < NcmContentMetaType > ( metaRecord . type ) ) ;
2019-10-18 03:59:34 +00:00
u64 updateTitleId = baseTitleId ^ 0x800 ;
bool hasUpdate = true ;
try
{
NcmContentMetaKey latestApplicationContentMetaKey ;
NcmContentMetaKey latestPatchContentMetaKey ;
ASSERT_OK ( ncmOpenContentMetaDatabase ( & contentMetaDatabase , m_destStorageId ) , " Failed to open content meta database " ) ;
ASSERT_OK ( ncmContentMetaDatabaseGetLatestContentMetaKey ( & contentMetaDatabase , & latestApplicationContentMetaKey , baseTitleId ) , " Failed to get latest application content meta key " ) ;
2019-11-03 18:18:42 +00:00
2019-10-18 03:59:34 +00:00
try
{
ASSERT_OK ( ncmContentMetaDatabaseGetLatestContentMetaKey ( & contentMetaDatabase , & latestPatchContentMetaKey , updateTitleId ) , " Failed to get latest patch content meta key " ) ;
}
catch ( std : : exception & e )
{
hasUpdate = false ;
}
u64 appContentRecordSize ;
u64 appContentRecordSizeRead ;
ASSERT_OK ( ncmContentMetaDatabaseGetSize ( & contentMetaDatabase , & appContentRecordSize , & latestApplicationContentMetaKey ) , " Failed to get application content record size " ) ;
2019-11-03 18:18:42 +00:00
2019-10-18 03:59:34 +00:00
auto appContentRecordBuf = std : : make_unique < u8 [ ] > ( appContentRecordSize ) ;
ASSERT_OK ( ncmContentMetaDatabaseGet ( & contentMetaDatabase , & latestApplicationContentMetaKey , & appContentRecordSizeRead , ( NcmContentMetaHeader * ) appContentRecordBuf . get ( ) , appContentRecordSizeRead ) , " Failed to get app content record size " ) ;
if ( appContentRecordSize ! = appContentRecordSizeRead )
{
throw std : : runtime_error ( " Mismatch between app content record size and content record size read " ) ;
}
printf ( " Application content meta key: \n " ) ;
2019-11-17 15:01:06 +00:00
printBytes ( ( u8 * ) & latestApplicationContentMetaKey , sizeof ( NcmContentMetaKey ) , true ) ;
2019-10-18 03:59:34 +00:00
printf ( " Application content meta: \n " ) ;
2019-11-17 15:01:06 +00:00
printBytes ( appContentRecordBuf . get ( ) , appContentRecordSize , true ) ;
2019-10-18 03:59:34 +00:00
if ( hasUpdate )
{
u64 patchContentRecordsSize ;
u64 patchContentRecordSizeRead ;
ASSERT_OK ( ncmContentMetaDatabaseGetSize ( & contentMetaDatabase , & patchContentRecordsSize , & latestPatchContentMetaKey ) , " Failed to get patch content record size " ) ;
2019-11-03 18:18:42 +00:00
2019-10-18 03:59:34 +00:00
auto patchContentRecordBuf = std : : make_unique < u8 [ ] > ( patchContentRecordsSize ) ;
ASSERT_OK ( ncmContentMetaDatabaseGet ( & contentMetaDatabase , & latestPatchContentMetaKey , & patchContentRecordSizeRead , ( NcmContentMetaHeader * ) patchContentRecordBuf . get ( ) , patchContentRecordsSize ) , " Failed to get patch content record size " ) ;
2019-11-03 18:18:42 +00:00
2019-10-18 03:59:34 +00:00
if ( patchContentRecordsSize ! = patchContentRecordSizeRead )
{
throw std : : runtime_error ( " Mismatch between app content record size and content record size read " ) ;
}
printf ( " Patch content meta key: \n " ) ;
2019-11-17 15:01:06 +00:00
printBytes ( ( u8 * ) & latestPatchContentMetaKey , sizeof ( NcmContentMetaKey ) , true ) ;
2019-10-18 03:59:34 +00:00
printf ( " Patch content meta: \n " ) ;
2019-11-17 15:01:06 +00:00
printBytes ( patchContentRecordBuf . get ( ) , patchContentRecordsSize , true ) ;
2019-10-18 03:59:34 +00:00
}
else
{
printf ( " No update records found, or an error occurred. \n " ) ;
}
auto appRecordBuf = std : : make_unique < u8 [ ] > ( 0x100 ) ;
u32 numEntriesRead ;
ASSERT_OK ( nsListApplicationRecordContentMeta ( 0 , baseTitleId , appRecordBuf . get ( ) , 0x100 , & numEntriesRead ) , " Failed to list application record content meta " ) ;
printf ( " Application record content meta: \n " ) ;
2019-11-17 15:01:06 +00:00
printBytes ( appRecordBuf . get ( ) , 0x100 , true ) ;
2019-10-18 03:59:34 +00:00
}
catch ( std : : runtime_error & e )
{
serviceClose ( & contentMetaDatabase . s ) ;
printf ( " Failed to log install data. Error: %s " , e . what ( ) ) ;
}
# endif
2019-11-29 08:23:11 +00:00
*/
2019-10-18 03:59:34 +00:00
}
}