mirror of
https://github.com/DarkStore-3DS/Project_CTR.git
synced 2026-07-03 00:39:14 +00:00
697 lines
19 KiB
C
697 lines
19 KiB
C
#include "lib.h"
|
|
|
|
#include "ncch_read.h"
|
|
#include "exheader_read.h"
|
|
#include "tik_read.h"
|
|
#include "tmd_read.h"
|
|
#include "cia_read.h"
|
|
|
|
#include "ncsd_build.h"
|
|
#include "cardinfo.h"
|
|
#include "titleid.h"
|
|
|
|
|
|
const int NCCH0_OFFSET = 0x4000;
|
|
const int CCI_BLOCK_SIZE = 0x200;
|
|
|
|
void ImportCciSettings(cci_settings *set, user_settings *usrset);
|
|
void FreeCciSettings(cci_settings *set);
|
|
int ImportCciNcch(cci_settings *set);
|
|
int ProcessNcchForCci(cci_settings *set);
|
|
int GenCciHdr(cci_settings *set);
|
|
int CheckRomConfig(cci_settings *set);
|
|
void WriteCciDataToOutput(cci_settings *set);
|
|
|
|
int build_CCI(user_settings *usrset)
|
|
{
|
|
int result = 0;
|
|
cci_settings *set = calloc(1,sizeof(cci_settings));
|
|
if(!set){
|
|
fprintf(stderr,"[CCI ERROR] Not enough memory\n");
|
|
return MEM_ERROR;
|
|
}
|
|
ImportCciSettings(set,usrset);
|
|
|
|
if(ImportCciNcch(set)){
|
|
result = FAILED_TO_IMPORT_FILE;
|
|
goto finish;
|
|
}
|
|
|
|
if(ProcessNcchForCci(set)){
|
|
result = FAILED_TO_IMPORT_FILE;
|
|
goto finish;
|
|
}
|
|
|
|
if(GenCciHdr(set)){
|
|
result = GEN_HDR_FAIL;
|
|
goto finish;
|
|
}
|
|
|
|
if(CheckRomConfig(set)){
|
|
result = CCI_CONFIG_FAIL;
|
|
goto finish;
|
|
}
|
|
|
|
if(GenCardInfoHdr(set)){
|
|
result = GEN_HDR_FAIL;
|
|
goto finish;
|
|
}
|
|
|
|
set->out = fopen(usrset->common.outFileName,"wb");
|
|
if(!set->out){
|
|
fprintf(stderr,"[CCI ERROR] Failed to create '%s'\n",usrset->common.outFileName);
|
|
result = FAILED_TO_CREATE_OUTFILE;
|
|
goto finish;
|
|
}
|
|
|
|
WriteCciDataToOutput(set);
|
|
|
|
finish:
|
|
FreeCciSettings(set);
|
|
return result;
|
|
}
|
|
|
|
void ImportCciSettings(cci_settings *set, user_settings *usrset)
|
|
{
|
|
set->keys = &usrset->common.keys;
|
|
set->rsf = &usrset->common.rsfSet;
|
|
|
|
set->content.data = usrset->common.workingFile.buffer;
|
|
set->content.dataLen = usrset->common.workingFile.size;
|
|
set->content.dataType = usrset->common.workingFileType;
|
|
|
|
set->content.path = usrset->common.contentPath;
|
|
set->content.dSize = usrset->common.contentSize;
|
|
|
|
usrset->common.workingFile.buffer = NULL;
|
|
usrset->common.workingFile.size = 0;
|
|
|
|
set->options.verbose = usrset->common.verbose;
|
|
set->options.padCci = set->rsf->Option.MediaFootPadding;
|
|
set->options.noModTid = usrset->cci.dontModifyNcchTitleID;
|
|
set->options.useExternalSdkCardInfo = usrset->cci.useSDKStockData;
|
|
set->options.closeAlignWR = usrset->cci.closeAlignWritableRegion;
|
|
|
|
set->options.cverDataType = usrset->cci.cverDataType;
|
|
set->options.cverDataPath = usrset->cci.cverDataPath;
|
|
|
|
set->romInfo.blockSize = CCI_BLOCK_SIZE;
|
|
set->romInfo.saveSize = 0;
|
|
}
|
|
|
|
void FreeCciSettings(cci_settings *set)
|
|
{
|
|
free(set->options.tmdHdr);
|
|
free(set->content.data);
|
|
free(set->headers.ccihdr.buffer);
|
|
free(set->headers.cardinfohdr.buffer);
|
|
if(set->out)
|
|
fclose(set->out);
|
|
free(set);
|
|
}
|
|
|
|
int ImportNcchForCci(cci_settings *set)
|
|
{
|
|
for(int i = 0; i < CCI_MAX_CONTENT; i++){
|
|
if(i == 0){
|
|
set->content.active[i] = true;
|
|
set->content.dSize[i] = set->content.dataLen;
|
|
set->content.dOffset[i] = 0;
|
|
}
|
|
else if(set->content.dSize[i] && set->content.path[i]){
|
|
set->content.active[i] = true;
|
|
set->content.dOffset[i] = set->content.dataLen;
|
|
set->content.dataLen += set->content.dSize[i];
|
|
}
|
|
else
|
|
set->content.active[i] = false;
|
|
}
|
|
|
|
set->content.data = realloc(set->content.data,set->content.dataLen);
|
|
if(!set->content.data){
|
|
fprintf(stderr,"[CCI ERROR] Not enough memory\n");
|
|
return MEM_ERROR;
|
|
}
|
|
|
|
FILE *ncch;
|
|
for(int i = 1; i < CCI_MAX_CONTENT; i++){
|
|
if(!set->content.active[i])
|
|
continue;
|
|
|
|
u8 *ncchpos = (u8*)(set->content.data+set->content.dOffset[i]);
|
|
|
|
ncch = fopen(set->content.path[i],"rb");
|
|
|
|
ReadFile64(ncchpos, set->content.dSize[i], 0, ncch);
|
|
|
|
fclose(ncch);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool CanCiaBeCci(u16 cat, u16 count, tmd_content_chunk *content)
|
|
{
|
|
if(cat != PROGRAM_ID_CATEGORY_APPLICATION && cat != PROGRAM_ID_CATEGORY_SYSTEM_APPLICATION)
|
|
return false;
|
|
|
|
if(count > CCI_MAX_CONTENT)
|
|
return false;
|
|
|
|
for(int i = 0; i < count; i++){
|
|
if(GetTmdContentIndex(content[i]) >= CCI_MAX_CONTENT)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GenRsfInputFromTmd(tmd_hdr *tmd, tmd_content_chunk *info, cci_settings *set)
|
|
{
|
|
if(!set->rsf->CardInfo.MediaSize){
|
|
set->rsf->CardInfo.MediaSize = calloc(20,sizeof(char));
|
|
u64 contentSize = NCCH0_OFFSET;
|
|
u16 contentNum = GetTmdContentCount(tmd);
|
|
for(int i = 0; i < contentNum; i++)
|
|
contentSize += GetTmdContentSize(info[i]);
|
|
|
|
if(contentSize < (u64)128*MB)
|
|
strcpy(set->rsf->CardInfo.MediaSize,"128MB");
|
|
else if(contentSize < (u64)256*MB)
|
|
strcpy(set->rsf->CardInfo.MediaSize,"256MB");
|
|
else if(contentSize < (u64)512*MB)
|
|
strcpy(set->rsf->CardInfo.MediaSize,"512MB");
|
|
else if(contentSize < (u64)1*GB)
|
|
strcpy(set->rsf->CardInfo.MediaSize,"1GB");
|
|
else if(contentSize < (u64)2*GB)
|
|
strcpy(set->rsf->CardInfo.MediaSize,"2GB");
|
|
else if(contentSize < (u64)4*GB)
|
|
strcpy(set->rsf->CardInfo.MediaSize,"4GB");
|
|
else if(contentSize < (u64)8*GB)
|
|
strcpy(set->rsf->CardInfo.MediaSize,"8GB");
|
|
else{
|
|
free(set->rsf->CardInfo.MediaSize);
|
|
set->rsf->CardInfo.MediaSize = NULL;
|
|
}
|
|
|
|
if(set->options.verbose && set->rsf->CardInfo.MediaSize)
|
|
printf("[CCI] Auto generating RSF setting \"CardInfo/MediaSize: %s\" \n",set->rsf->CardInfo.MediaSize);
|
|
}
|
|
|
|
if(!set->rsf->CardInfo.MediaType){
|
|
set->rsf->CardInfo.MediaType = calloc(20,sizeof(char));
|
|
if(set->romInfo.saveSize < (u64)1*MB)
|
|
strcpy(set->rsf->CardInfo.MediaType,"Card1");
|
|
else
|
|
strcpy(set->rsf->CardInfo.MediaType,"Card2");
|
|
|
|
if(set->options.verbose)
|
|
printf("[CCI] Auto generating RSF setting \"CardInfo/MediaType: %s\" \n",set->rsf->CardInfo.MediaType);
|
|
}
|
|
|
|
if(!set->rsf->CardInfo.CardDevice){
|
|
set->rsf->CardInfo.CardDevice = calloc(20,sizeof(char));
|
|
if(set->romInfo.saveSize < (u64)1*MB && set->romInfo.saveSize > 0)
|
|
strcpy(set->rsf->CardInfo.CardDevice,"NorFlash");
|
|
else
|
|
strcpy(set->rsf->CardInfo.CardDevice,"None");
|
|
|
|
if(set->options.verbose)
|
|
printf("[CCI] Auto generating RSF setting \"CardInfo/CardDevice: %s\" \n",set->rsf->CardInfo.CardDevice);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int ProcessCiaForCci(cci_settings *set)
|
|
{
|
|
if(!IsCia(set->content.data)){
|
|
fprintf(stderr,"[CCI ERROR] CIA is corrupt\n");
|
|
return FAILED_TO_IMPORT_FILE;
|
|
}
|
|
|
|
tik_hdr *tik = GetTikHdr(GetCiaTik(set->content.data));
|
|
tmd_hdr *tmd = GetTmdHdr(GetCiaTmd(set->content.data));
|
|
tmd_content_chunk *contentInfo = GetTmdContentInfo(GetCiaTmd(set->content.data));
|
|
u64 contentOffset = GetCiaContentOffset((cia_hdr*)set->content.data);
|
|
|
|
u16 titleCat = (GetTmdTitleId(tmd) >> 32) & 0xffff;
|
|
u16 contentCount = GetTmdContentCount(tmd);
|
|
set->romInfo.saveSize = GetTmdSaveSize(tmd);
|
|
if(set->romInfo.saveSize > 0 && set->romInfo.saveSize < (u64)(128*KB))
|
|
set->romInfo.saveSize = (u64)(128*KB);
|
|
else if(set->romInfo.saveSize > (u64)(128*KB) && set->romInfo.saveSize < (u64)(512*KB))
|
|
set->romInfo.saveSize = (u64)(512*KB);
|
|
else if(set->romInfo.saveSize > (u64)(512*KB))
|
|
set->romInfo.saveSize = align(set->romInfo.saveSize,MB);
|
|
|
|
if(!CanCiaBeCci(titleCat,contentCount,contentInfo)){
|
|
fprintf(stderr,"[CCI ERROR] This CIA cannot be converted to CCI\n");
|
|
return INCOMPAT_CIA;
|
|
}
|
|
|
|
GenRsfInputFromTmd(tmd,contentInfo,set);
|
|
|
|
bool canDecrypt;
|
|
u8 titleKey[AES_128_KEY_SIZE];
|
|
canDecrypt = GetTikTitleKey(titleKey,tik,set->keys);
|
|
if(set->options.verbose){
|
|
if(canDecrypt)
|
|
memdump(stdout,"[CCI] CIA title key: ",titleKey,AES_128_KEY_SIZE);
|
|
else
|
|
fprintf(stdout,"[CCI] CIA title key could not be decrypted\n");
|
|
}
|
|
|
|
for(u16 i = 0; i < contentCount; i++){
|
|
u16 index = GetTmdContentIndex(contentInfo[i]);
|
|
set->content.active[index] = true;
|
|
set->content.dOffset[index] = contentOffset;
|
|
set->content.dSize[index] = GetTmdContentSize(contentInfo[i]);
|
|
u8 *content = set->content.data + contentOffset;
|
|
if(IsTmdContentEncrypted(contentInfo[i])){
|
|
if(canDecrypt){
|
|
CryptContent(content,content,set->content.dSize[index],titleKey,i,DEC);
|
|
}
|
|
else{
|
|
fprintf(stderr,"[CCI ERROR] Failed to decrypt CIA content: 0x%08x\n",GetTmdContentId(contentInfo[i]));
|
|
return INCOMPAT_CIA;
|
|
}
|
|
}
|
|
if(!ValidateTmdContent(content,contentInfo[i])){
|
|
fprintf(stderr,"[CCI ERROR] CIA content: 0x%08x is corrupt\n",GetTmdContentId(contentInfo[i]));
|
|
return NCSD_INVALID_NCCH;
|
|
}
|
|
|
|
contentOffset += set->content.dSize[index];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ImportCciNcch(cci_settings *set)
|
|
{
|
|
if(set->content.dataType == infile_ncch)
|
|
return ImportNcchForCci(set);
|
|
else if(set->content.dataType == infile_cia)
|
|
return ProcessCiaForCci(set);
|
|
else
|
|
fprintf(stderr,"[CCI ERROR] Unrecognised input data type\n");
|
|
|
|
if(set->rsf->SystemControlInfo.SaveDataSize)
|
|
GetSaveDataSizeFromString(&set->romInfo.saveSize,set->rsf->SystemControlInfo.SaveDataSize,"CCI");
|
|
|
|
return FAILED_TO_IMPORT_FILE;
|
|
}
|
|
|
|
int ProcessCverDataForCci(cci_settings *set)
|
|
{
|
|
u64 tmdSize,tmdOffset;
|
|
|
|
u64 dataSize = GetFileSize64(set->options.cverDataPath);
|
|
FILE *data = fopen(set->options.cverDataPath,"rb");
|
|
|
|
|
|
if(set->options.cverDataType == CVER_DTYPE_CIA){
|
|
cia_hdr *ciaHdr = calloc(1,sizeof(cia_hdr));
|
|
ReadFile64(ciaHdr,sizeof(cia_hdr),0,data);
|
|
|
|
tmdSize = GetCiaTmdSize(ciaHdr);
|
|
tmdOffset = GetCiaTmdOffset(ciaHdr);
|
|
|
|
free(ciaHdr);
|
|
}
|
|
else{
|
|
tmdSize = dataSize;
|
|
tmdOffset = 0;
|
|
}
|
|
|
|
u8 *tmd = calloc(1,tmdSize);
|
|
|
|
ReadFile64(tmd,tmdSize,tmdOffset,data);
|
|
fclose(data);
|
|
|
|
tmd_hdr *tmdHdr = GetTmdHdr(tmd);
|
|
if(!tmdHdr){
|
|
fprintf(stderr,"[CCI ERROR] Corrupt cver TMD\n");
|
|
free(tmd);
|
|
return FAILED_TO_IMPORT_FILE;
|
|
}
|
|
|
|
set->options.tmdHdr = calloc(1,sizeof(tmd_hdr));
|
|
memcpy(set->options.tmdHdr,tmdHdr,sizeof(tmd_hdr));
|
|
|
|
free(tmd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GetNewNcchIdForCci(u8 *newTid, u8 *srcTid, u8 index, tmd_hdr *tmdHdr)
|
|
{
|
|
u64 titleId = u8_to_u64(srcTid,LE) & 0xffffffffffff;
|
|
if(tmdHdr && index == 7)
|
|
titleId |= (u64)(GetTmdVersion(tmdHdr)) << 48;
|
|
else
|
|
titleId |= (u64)(index+4) << 48;
|
|
|
|
u64_to_u8(newTid,titleId,LE);
|
|
}
|
|
|
|
int ProcessNcchForCci(cci_settings *set)
|
|
{
|
|
u8 *ncch;
|
|
ncch_hdr *hdr;
|
|
|
|
u8 titleId[8];
|
|
u8 srcId[8];
|
|
|
|
if(set->options.cverDataPath && set->content.active[7]){
|
|
if(ProcessCverDataForCci(set))
|
|
return FAILED_TO_IMPORT_FILE;
|
|
}
|
|
|
|
for(int i = 0; i < CCI_MAX_CONTENT; i++){
|
|
if(set->content.active[i]){
|
|
ncch = set->content.data + set->content.dOffset[i];
|
|
if(!IsNcch(NULL,ncch)){
|
|
fprintf(stderr,"[CCI ERROR] NCCH %d is corrupt\n",i);
|
|
return NCSD_INVALID_NCCH;
|
|
}
|
|
hdr = (ncch_hdr*)ncch;
|
|
if(i > 0 && !set->options.noModTid){
|
|
if(set->options.verbose){
|
|
printf("[CCI] Modifying NCCH %d IDs\n",i);
|
|
printf("[Old Ids]\n");
|
|
memdump(stdout," > TitleId: 0x",hdr->titleId,8);
|
|
memdump(stdout," > ProgramId: 0x",hdr->programId,8);
|
|
}
|
|
GetNewNcchIdForCci(titleId,srcId,i,set->options.tmdHdr);
|
|
if(ModifyNcchIds(ncch, titleId, srcId, set->keys))
|
|
return -1;
|
|
if(set->options.verbose){
|
|
printf("[New Ids]\n");
|
|
memdump(stdout," > TitleId: 0x",hdr->titleId,8);
|
|
memdump(stdout," > ProgramId: 0x",hdr->programId,8);
|
|
}
|
|
}
|
|
set->content.titleId[i] = u8_to_u64(hdr->titleId,LE);
|
|
if(i == 0)
|
|
memcpy(srcId,hdr->titleId,8);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SetMediaSize(u8 *mediaSize, cci_settings *set)
|
|
{
|
|
char *str = set->rsf->CardInfo.MediaSize;
|
|
if(!str)
|
|
set->romInfo.mediaSize = (u64)GB*2;
|
|
else{
|
|
if(strcasecmp(str,"128MB") == 0) set->romInfo.mediaSize = (u64)MB*128;
|
|
else if(strcasecmp(str,"256MB") == 0) set->romInfo.mediaSize = (u64)MB*256;
|
|
else if(strcasecmp(str,"512MB") == 0) set->romInfo.mediaSize = (u64)MB*512;
|
|
else if(strcasecmp(str,"1GB") == 0) set->romInfo.mediaSize = (u64)GB*1;
|
|
else if(strcasecmp(str,"2GB") == 0) set->romInfo.mediaSize = (u64)GB*2;
|
|
else if(strcasecmp(str,"4GB") == 0) set->romInfo.mediaSize = (u64)GB*4;
|
|
else if(strcasecmp(str,"8GB") == 0) set->romInfo.mediaSize = (u64)GB*8;
|
|
//else if(strcasecmp(str,"16GB") == 0) set->romInfo.mediaSize = (u64)GB*16;
|
|
//else if(strcasecmp(str,"32GB") == 0) set->romInfo.mediaSize = (u64)GB*32;
|
|
else {
|
|
fprintf(stderr,"[CCI ERROR] Invalid MediaSize: %s\n",str);
|
|
return INVALID_RSF_OPT;
|
|
}
|
|
}
|
|
|
|
u32_to_u8(mediaSize,(set->romInfo.mediaSize/set->romInfo.blockSize),LE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SetBackupWriteWaitTime(u8 *flag, rsf_settings *rsf)
|
|
{
|
|
char *str = rsf->CardInfo.BackupWriteWaitTime;
|
|
if(!str)
|
|
*flag = 0;
|
|
else{
|
|
u32 waitTime = strtoul(str,NULL,0);
|
|
if(waitTime > 255){
|
|
fprintf(stderr,"[CCI ERROR] Invalid Card BackupWriteWaitTime (%d) : must 0-255\n",waitTime);
|
|
return INVALID_RSF_OPT;
|
|
}
|
|
*flag = (u8)waitTime;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SetMediaType(u8 *flag, cci_settings *set)
|
|
{
|
|
char *str = set->rsf->CardInfo.MediaType;
|
|
|
|
if(!str)
|
|
*flag = mediatype_CARD1;
|
|
else{
|
|
if(strcasecmp(str,"Card1") == 0)
|
|
*flag = mediatype_CARD1;
|
|
else if(strcasecmp(str,"Card2") == 0)
|
|
*flag = mediatype_CARD2;
|
|
else {
|
|
fprintf(stderr,"[CCI ERROR] Invalid MediaType: %s\n",str);
|
|
return INVALID_RSF_OPT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SetCardDevice(u8 *flags, u64 saveSize, rsf_settings *rsf)
|
|
{
|
|
u8 saveCrypto;
|
|
|
|
if(!rsf->CardInfo.SaveCrypto)
|
|
saveCrypto = 3;
|
|
else{
|
|
if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw1") == 0 || strcasecmp(rsf->CardInfo.SaveCrypto,"ctr fail") == 0 ) saveCrypto = 1;
|
|
else if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw2") == 0) saveCrypto = 2;
|
|
else if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw3") == 0) saveCrypto = 3;
|
|
else if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw6") == 0) saveCrypto = 6;
|
|
else {
|
|
fprintf(stderr,"[CCI ERROR] Invalid SaveCrypto: %s\n",rsf->CardInfo.SaveCrypto);
|
|
return INVALID_RSF_OPT;
|
|
}
|
|
}
|
|
|
|
|
|
/* FW6x SaveCrypto */
|
|
if(saveCrypto == 6)
|
|
flags[cciflag_FW6_SAVE_CRYPTO] = 1;
|
|
else
|
|
flags[cciflag_FW6_SAVE_CRYPTO] = 0;
|
|
|
|
/* CardDevice */
|
|
u8 cardDevice = 0;
|
|
if(!rsf->CardInfo.CardDevice)
|
|
cardDevice = carddevice_NONE;
|
|
else{
|
|
if(strcmp(rsf->CardInfo.CardDevice,"NorFlash") == 0)
|
|
cardDevice = carddevice_NOR_FLASH;
|
|
else if(strcmp(rsf->CardInfo.CardDevice,"None") == 0)
|
|
cardDevice = carddevice_NONE;
|
|
else if(strcmp(rsf->CardInfo.CardDevice,"BT") == 0)
|
|
cardDevice = carddevice_BT;
|
|
else {
|
|
fprintf(stderr,"[CCI ERROR] Invalid CardDevice: %s\n",rsf->CardInfo.CardDevice);
|
|
return INVALID_RSF_OPT;
|
|
}
|
|
}
|
|
|
|
if(flags[cciflag_MEDIA_TYPE] == mediatype_CARD1){
|
|
if(saveSize != (u64)(128*KB) && saveSize != (u64)(512*KB) && cardDevice == carddevice_NOR_FLASH){
|
|
fprintf(stderr,"[CCI ERROR] 'CardDevice: NorFlash' can only be used with save-data sizes: 128K & 512K\n");
|
|
return INVALID_RSF_OPT;
|
|
}
|
|
}
|
|
if(flags[cciflag_MEDIA_TYPE] == mediatype_CARD2){
|
|
if(cardDevice == carddevice_NOR_FLASH){
|
|
fprintf(stderr,"[CCI WARNING] 'CardDevice: NorFlash' is invalid for Card2\n");
|
|
cardDevice = carddevice_NONE;
|
|
}
|
|
}
|
|
|
|
if(saveCrypto > 1)
|
|
flags[saveCrypto == 2? cciflag_CARD_DEVICE_OLD : cciflag_CARD_DEVICE] = cardDevice;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SetCciFlags(u8 *flags, cci_settings *set)
|
|
{
|
|
// Backup Write Wait Time
|
|
if(SetBackupWriteWaitTime(&flags[cciflag_BACKUP_WRITE_WAIT_TIME], set->rsf))
|
|
return INVALID_RSF_OPT;
|
|
// Platform
|
|
flags[cciflag_MEDIA_PLATFORM] = cciplatform_CTR;
|
|
// Card Type
|
|
if(SetMediaType(&flags[cciflag_MEDIA_TYPE], set))
|
|
return INVALID_RSF_OPT;
|
|
// Media Unit
|
|
flags[cciflag_MEDIA_BLOCK_SIZE] = GetCtrBlockSizeFlag(set->romInfo.blockSize);
|
|
// Card Device
|
|
if(SetCardDevice(flags, set->romInfo.saveSize, set->rsf))
|
|
return INVALID_RSF_OPT;
|
|
|
|
set->romInfo.mediaType = flags[cciflag_MEDIA_TYPE];
|
|
set->romInfo.cardDevice = flags[cciflag_CARD_DEVICE] | flags[cciflag_CARD_DEVICE_OLD];
|
|
|
|
return 0;
|
|
}
|
|
|
|
void SetCciNcchInfo(cci_hdr *hdr, cci_settings *set)
|
|
{
|
|
u64 ncchSize,ncchOffset;
|
|
|
|
ncchOffset = NCCH0_OFFSET;
|
|
|
|
for(int i = 0; i < CCI_MAX_CONTENT; i++){
|
|
if(set->content.active[i]){
|
|
set->content.cOffset[i] = ncchOffset;
|
|
ncchSize = align(set->content.dSize[i],set->romInfo.blockSize);
|
|
|
|
u32_to_u8(hdr->offset_sizeTable[i].offset,(ncchOffset/set->romInfo.blockSize),LE);
|
|
u32_to_u8(hdr->offset_sizeTable[i].size,(ncchSize/set->romInfo.blockSize),LE);
|
|
u64_to_u8(hdr->ncchIdTable[i],set->content.titleId[i],LE);
|
|
|
|
ncchOffset += ncchSize;
|
|
}
|
|
}
|
|
|
|
set->romInfo.usedSize = ncchOffset;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
int GenCciHdr(cci_settings *set)
|
|
{
|
|
set->headers.ccihdr.size = sizeof(cci_hdr);
|
|
set->headers.ccihdr.buffer = calloc(1,set->headers.ccihdr.size);
|
|
if(!set->headers.ccihdr.buffer){
|
|
set->headers.ccihdr.size = 0;
|
|
fprintf(stderr,"[CCI ERROR] Not enough memory\n");
|
|
return MEM_ERROR;
|
|
}
|
|
|
|
cci_hdr *hdr = (cci_hdr*)set->headers.ccihdr.buffer;
|
|
|
|
// Magic & TitleId
|
|
memcpy(hdr->magic,"NCSD",4);
|
|
u64_to_u8(hdr->titleId,set->content.titleId[0],LE);
|
|
|
|
|
|
if(SetMediaSize(hdr->mediaSize,set))
|
|
return GEN_HDR_FAIL;
|
|
if(SetCciFlags(hdr->flags,set))
|
|
return GEN_HDR_FAIL;
|
|
SetCciNcchInfo(hdr,set);
|
|
|
|
// Sign Header
|
|
ctr_sig(&hdr->magic,sizeof(cci_hdr)-RSA_2048_KEY_SIZE,hdr->signature,set->keys->rsa.cciCfaPub,set->keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CheckRomConfig(cci_settings *set)
|
|
{
|
|
u64 cciUsedSize;
|
|
if(set->romInfo.mediaType == mediatype_CARD2)
|
|
cciUsedSize = set->romInfo.card2SaveOffset + set->romInfo.saveSize;
|
|
else
|
|
cciUsedSize = set->romInfo.usedSize;
|
|
|
|
if(cciUsedSize > set->romInfo.mediaSize){
|
|
char *str = set->rsf->CardInfo.MediaSize;
|
|
if(!str)
|
|
str = "2GB";
|
|
|
|
fprintf(stderr,"[CCI ERROR] MediaSize '%s' is insufficient for the CCI data\n",str);
|
|
return CCI_CONFIG_FAIL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void WriteCciDataToOutput(cci_settings *set)
|
|
{
|
|
// NCSD Header
|
|
WriteBuffer(set->headers.ccihdr.buffer, set->headers.ccihdr.size, 0, set->out);
|
|
// Card Info Header
|
|
WriteBuffer(set->headers.cardinfohdr.buffer, set->headers.cardinfohdr.size, set->headers.ccihdr.size, set->out);
|
|
|
|
// Dummy data between header and first NCCH
|
|
u64 len = set->content.cOffset[0] - (set->headers.ccihdr.size + set->headers.cardinfohdr.size);
|
|
u8 *dummy_data = malloc(len);
|
|
if(set->headers.cardinfohdr.size > sizeof(cardinfo_hdr)) // additional debug header data exists
|
|
memset(dummy_data, 0x00, len);
|
|
else // normal production cci image
|
|
memset(dummy_data, 0xff, len);
|
|
WriteBuffer(dummy_data, len, (set->headers.ccihdr.size + set->headers.cardinfohdr.size),set->out);
|
|
free(dummy_data);
|
|
|
|
// NCCH Partitions
|
|
u8 *ncch;
|
|
for(int i = 0; i < CCI_MAX_CONTENT; i++){
|
|
if(set->content.active[i]){
|
|
ncch = set->content.data + set->content.dOffset[i];
|
|
WriteBuffer(ncch, set->content.dSize[i], set->content.cOffset[i], set->out);
|
|
}
|
|
}
|
|
|
|
// Cci Padding
|
|
if(set->options.padCci){
|
|
fseek_64(set->out,set->romInfo.usedSize);
|
|
|
|
// Determining Size of Padding
|
|
u64 len = set->romInfo.mediaSize - set->romInfo.usedSize;
|
|
|
|
// Create Padding chunk
|
|
u8 *pad = malloc(set->romInfo.blockSize);
|
|
memset(pad,0xff,set->romInfo.blockSize);
|
|
|
|
// Writing Dummy Bytes to file
|
|
for(u64 i = 0; i < len; i += set->romInfo.blockSize)
|
|
fwrite(pad,set->romInfo.blockSize,1,set->out);
|
|
|
|
free(pad);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool IsCci(u8 *ncsd)
|
|
{
|
|
cci_hdr *hdr = (cci_hdr*)ncsd;
|
|
if(!hdr) return false;
|
|
if(memcmp(hdr->magic,"NCSD",4)!=0) return false;
|
|
if(hdr->flags[cciflag_MEDIA_PLATFORM] != cciplatform_CTR) return false;
|
|
if(hdr->flags[cciflag_MEDIA_TYPE] != mediatype_CARD1 && hdr->flags[cciflag_MEDIA_TYPE] != mediatype_CARD2) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
u64 GetPartitionOffset(u8 *ncsd, u8 index)
|
|
{
|
|
cci_hdr *hdr = (cci_hdr*)ncsd;
|
|
u32 offset = ;
|
|
return (u64)u8_to_u64(hdr->offset_sizeTable[index].offset,LE) * (u64)GetCtrBlockSize(hdr->flags[cciflag_MEDIA_BLOCK_SIZE]);
|
|
}
|
|
|
|
u64 GetPartitionSize(u8 *ncsd, u8 index)
|
|
{
|
|
cci_hdr *hdr = (cci_hdr*)ncsd;
|
|
return (u64)u8_to_u64(hdr->offset_sizeTable[index].size,LE) * (u64)GetCtrBlockSize(hdr->flags[cciflag_MEDIA_BLOCK_SIZE]);
|
|
}
|
|
|
|
u8* GetPartition(u8 *ncsd, u8 index)
|
|
{
|
|
return ncsd + GetPartitionOffset(ncsd,index);
|
|
} |