#include "lib.h" #include "ncch.h" #include "exheader.h" #include "exefs.h" #include "certs.h" #include "cia.h" #include "tik.h" #include "tmd.h" #include "titleid.h" #include "srl.h" #include "ncsd.h" // Private Prototypes /* cia_settings tools */ void init_CIASettings(cia_settings *set); void free_CIASettings(cia_settings *set); int get_CIASettings(cia_settings *ciaset, user_settings *usrset); int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset); int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset); int GetCIADataFromNcch(cia_settings *ciaset, NCCH_Header *NcchHdr, ExtendedHeader_Struct *ExHeader); int GetMetaRegion(cia_settings *ciaset, ExtendedHeader_Struct *ExHeader, u8 *ExeFs); int GetContentFilePtrs(cia_settings *ciaset, user_settings *usrset); int GetSettingsFromSrl(cia_settings *ciaset); int GetSettingsFromCci(cia_settings *ciaset); u16 SetupVersion(u16 Major, u16 Minor, u16 Micro); int BuildCIA_CertChain(cia_settings *ciaset); int BuildCIA_Header(cia_settings *ciaset); int WriteCurrentSectionstoFile(cia_settings *ciaset); int WriteContentsToFile(cia_settings *ciaset, user_settings *usrset); int WriteTMDToFile(cia_settings *ciaset); int CryptContent(u8 *EncBuffer,u8 *DecBuffer,u64 size,u8 *title_key, u16 index, u8 mode); int build_CIA(user_settings *usrset) { int result = 0; // Init Settings cia_settings *ciaset = malloc(sizeof(cia_settings)); if(!ciaset) {fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); return MEM_ERROR;} init_CIASettings(ciaset); // Get Settings result = get_CIASettings(ciaset,usrset); if(result) goto finish; // Create Output File ciaset->out = fopen(usrset->outfile,"wb"); if(!ciaset->out){ fprintf(stderr,"[CIA ERROR] Failed to create '%s'\n",usrset->outfile); result = FAILED_TO_CREATE_OUTFILE; goto finish; } // Create CIA Sections /* Certificate Chain */ result = BuildCIA_CertChain(ciaset); if(result) goto finish; /* Ticket */ result = BuildTicket(ciaset); if(result) goto finish; /* CIA Header */ result = BuildCIA_Header(ciaset); if(result) goto finish; /* Write To File Current Sections to File */ /* Explanation : In order to conserve memory, only one Content is in memory at a time. This however has the limitation of only being able to generate TMD after all content has been processed (, encrypted) and written to file. */ result = WriteCurrentSectionstoFile(ciaset); if(result) goto finish; result = WriteContentsToFile(ciaset, usrset); if(result) goto finish; result = BuildTMD(ciaset); if(result) goto finish; result = WriteTMDToFile(ciaset); finish: if(result != FAILED_TO_CREATE_OUTFILE && ciaset->out) fclose(ciaset->out); free_CIASettings(ciaset); return result; } void init_CIASettings(cia_settings *set) { memset(set,0,sizeof(cia_settings)); } void free_CIASettings(cia_settings *set) { if(set->content.ContentFilePtrs){ for(u32 i = 1; i < set->content.ContentCount; i++){ fclose(set->content.ContentFilePtrs[i]); } free(set->content.ContentFilePtrs); } free(set->CIA_Sections.CertChain.buffer); free(set->CIA_Sections.Ticket.buffer); free(set->CIA_Sections.TitleMetaData.buffer); free(set->CIA_Sections.CXI_MetaData.buffer); memset(set,0,sizeof(cia_settings)); free(set); } int get_CIASettings(cia_settings *ciaset, user_settings *usrset) { int result = 0; // Transfering data from usrset result = GetSettingsFromUsrset(ciaset,usrset); if(usrset->Content0IsNcch){ result = GetSettingsFromNcch0(ciaset,0); if(result) return result; result = GetContentFilePtrs(ciaset,usrset); if(result) return result; } else if(usrset->Content0IsSrl){ result = GetSettingsFromSrl(ciaset); if(result) return result; } else if(usrset->ConvertCci){ result = GetSettingsFromCci(ciaset); if(result) return result; } return 0; } int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) { // General Stuff ciaset->keys = &usrset->keys; ciaset->content.content0 = usrset->Content0.buffer; ciaset->content.content0_FileLen = usrset->Content0.size; u32_to_u8(ciaset->Title_type,TYPE_CTR,BE); ciaset->content.EncryptContents = usrset->EncryptContents; if(ciaset->keys->aes.CommonKey[ciaset->keys->aes.CurrentCommonKey] == NULL && ciaset->content.EncryptContents){ fprintf(stderr,"[CIA WARNING] Common Key could not be loaded, CIA will not be encrypted\n"); ciaset->content.EncryptContents = false; } ciaset->cert.ca_crl_version = 0; ciaset->cert.signer_crl_version = 0; for(int i = 0; i < 3; i++){ ciaset->Version[i] = usrset->Version[i]; } // Random Number generator u8 hash[0x20]; ctr_sha(ciaset->content.content0,0x100,hash,CTR_SHA_256); // Ticket Data memcpy(ciaset->tik.TicketID,(hash+0x8),8); if(usrset->RandomTitleKey){ memcpy(ciaset->tik.TitleKey,(hash+0x10),16); } else{ memcpy(ciaset->tik.TitleKey,usrset->keys.aes.NormalKey,16); } ciaset->tik.ticket_format_ver = 1; int result = GenCertChildIssuer(ciaset->tik.TicketIssuer,usrset->keys.certs.tik_cert); if(result) return result; // Tmd Stuff if(usrset->ContentID[0] > 0xffffffff){ ciaset->content.ContentId[0] = u8_to_u32(hash,BE); } else ciaset->content.ContentId[0] = usrset->ContentID[0]; ciaset->tmd.tmd_format_ver = 1; result = GenCertChildIssuer(ciaset->tmd.TMDIssuer,usrset->keys.certs.tmd_cert); return 0; } int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) { /* Sanity Checks */ if(!ciaset->content.content0_FileLen) return CIA_NO_NCCH0; u8 *ncch0 = (u8*)(ciaset->content.content0+ncch0_offset); if(!IsNCCH(NULL,ncch0)){ fprintf(stderr,"[CIA ERROR] Content0 is not NCCH\n"); return CIA_INVALID_NCCH0; } /* Get Ncch0 Header */ NCCH_Header *hdr = NULL; hdr = GetNCCH_CommonHDR(hdr,NULL,ncch0); if(IsCfa(hdr)){ ciaset->content.IsCfa = true; } ciaset->content.ContentOffset[0] = 0; ciaset->content.ContentSize[0] = GetNCCH_MediaSize(hdr)*GetNCCH_MediaUnitSize(hdr); ciaset->content.TotalContentSize = ciaset->content.ContentSize[0]; /* Get Ncch0 Import Context */ NCCH_STRUCT *ncch_ctx = malloc(sizeof(NCCH_STRUCT)); if(!ncch_ctx){ fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); return MEM_ERROR; } memset(ncch_ctx,0x0,sizeof(NCCH_STRUCT)); GetCXIStruct(ncch_ctx,hdr); /* Verify Ncch0 (Sig&Hash Checks) */ int result = VerifyNCCH(ncch0,ciaset->keys,true); if(result == UNABLE_TO_LOAD_NCCH_KEY){ ciaset->content.KeyNotFound = true; if(!ciaset->content.IsCfa){ fprintf(stderr,"[CIA WARNING] CXI AES Key could not be loaded\n"); fprintf(stderr," Meta Region, SaveDataSize, Remaster Version cannot be obtained\n"); } } else if(result != 0){ fprintf(stderr,"[CIA ERROR] Content 0 Is Corrupt (res = %d)\n",result); return CIA_INVALID_NCCH0; } /* Gen Settings From Ncch0 */ endian_memcpy(ciaset->TitleID,hdr->title_id,8,LE); /* Getting ExeFs/ExHeader */ u8 *ExeFs = malloc(ncch_ctx->exefs_size); if(!ExeFs){ fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); return MEM_ERROR; } ExtendedHeader_Struct *ExHeader = malloc(ncch_ctx->exheader_size); if(!ExHeader){ fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); free(ExeFs); return MEM_ERROR; } if(!(ciaset->content.IsCfa||ciaset->content.KeyNotFound)) GetNCCHSection(ExeFs, ncch_ctx->exefs_size, 0, ncch0, ncch_ctx, ciaset->keys, ncch_exefs); if(!(ciaset->content.IsCfa||ciaset->content.KeyNotFound)) GetNCCHSection((u8*)ExHeader, ncch_ctx->exheader_size, 0, ncch0, ncch_ctx, ciaset->keys, ncch_ExHeader); result = GetCIADataFromNcch(ciaset,hdr,ExHeader); // Data For TMD if(result) goto finish; result = GetMetaRegion(ciaset,ExHeader,ExeFs); // Meta Region /* Finish */ finish: free(ExeFs); free(ExHeader); /* Return */ free(ncch_ctx); return result; } int GetCIADataFromNcch(cia_settings *ciaset, NCCH_Header *NcchHdr, ExtendedHeader_Struct *ExHeader) { u16 Category = u8_to_u16((ciaset->TitleID+2),BE); bool IsPatch = (Category == 0x000E); if(IsPatch||ciaset->content.IsCfa||ciaset->content.KeyNotFound) u32_to_u8(ciaset->tmd.SaveDataSize,0,LE); else u32_to_u8(ciaset->tmd.SaveDataSize,(u32)GetSaveDataSize_frm_exhdr(ExHeader),LE); if(ciaset->content.IsCfa||ciaset->content.KeyNotFound){ if(ciaset->Version[0] == 0xffff){ // '-major' wasn't set if(ciaset->content.IsCfa){ // Is a CFA and can be decrypted fprintf(stderr,"[CIA ERROR] Invalid major version. Use '-major' option.\n"); return CIA_BAD_VERSION; } else // CXI which cannot be decrypted ciaset->Version[0] = 0; } } else{ // Is a CXI and can be decrypted if(ciaset->Version[0] != 0xffff){ // '-major' was set fprintf(stderr,"[CIA ERROR] Option '-major' cannot be applied for cxi.\n"); return CIA_BAD_VERSION; } // Setting remaster ver ciaset->Version[0] = GetRemasterVersion_frm_exhdr(ExHeader); } SetupVersion(ciaset->Version[0],ciaset->Version[1],ciaset->Version[2]); u16 version = SetupVersion(ciaset->Version[0],ciaset->Version[1],ciaset->Version[2]); u16_to_u8(ciaset->tik.TicketVersion,version,BE); u16_to_u8(ciaset->tmd.TitleVersion,version,BE); return 0; } int GetMetaRegion(cia_settings *ciaset, ExtendedHeader_Struct *ExHeader, u8 *ExeFs) { if(ciaset->content.IsCfa || ciaset->content.KeyNotFound) return 0; ciaset->CIA_Sections.CXI_MetaData.size = sizeof(MetaData_Struct) + GetExeFsSectionSize("icon",ExeFs); ciaset->CIA_Sections.CXI_MetaData.buffer = malloc(ciaset->CIA_Sections.CXI_MetaData.size); if(!ciaset->CIA_Sections.CXI_MetaData.buffer){ fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); return MEM_ERROR; } MetaData_Struct *hdr = (MetaData_Struct*)ciaset->CIA_Sections.CXI_MetaData.buffer; memset(hdr,0,sizeof(MetaData_Struct)); GetDependancyList_frm_exhdr(hdr->DependancyList,ExHeader); GetCoreVersion_frm_exhdr(hdr->CoreVersion,ExHeader); if(DoesExeFsSectionExist("icon",ExeFs)){ u8 *IconDestPos = (ciaset->CIA_Sections.CXI_MetaData.buffer + sizeof(MetaData_Struct)); memcpy(IconDestPos,GetExeFsSection("icon",ExeFs),GetExeFsSectionSize("icon",ExeFs)); } return 0; } int GetContentFilePtrs(cia_settings *ciaset, user_settings *usrset) { ciaset->content.ContentFilePtrs = malloc(sizeof(FILE*)*CIA_MAX_CONTENT); if(!ciaset->content.ContentFilePtrs){ fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); return MEM_ERROR; } memset(ciaset->content.ContentFilePtrs,0,sizeof(FILE*)*CIA_MAX_CONTENT); int j = 1; NCCH_Header *hdr = malloc(sizeof(NCCH_Header)); for(int i = 1; i < CIA_MAX_CONTENT; i++){ if(usrset->ContentPath[i]){ ciaset->content.ContentFilePtrs[j] = fopen(usrset->ContentPath[i],"rb"); if(!ciaset->content.ContentFilePtrs[j]){ fprintf(stderr,"[CIA ERROR] Failed to open '%s'\n",usrset->ContentPath[i]); return FAILED_TO_OPEN_FILE; } if(usrset->ContentID[i] == 0x100000000){ u8 hash[0x20]; ctr_sha(usrset->ContentPath[i],strlen(usrset->ContentPath[i]),hash,CTR_SHA_256); ciaset->content.ContentId[j] = u8_to_u32(hash,BE); } else ciaset->content.ContentId[j] = (u32)usrset->ContentID[i]; ciaset->content.ContentIndex[j] = (u16)i; // Get Data from ncch HDR GetNCCH_CommonHDR(hdr,ciaset->content.ContentFilePtrs[j],NULL); // Get TitleID memcpy(ciaset->content.ContentTitleId[j],hdr->title_id,8); // Get Size ciaset->content.ContentSize[j] = GetNCCH_MediaSize(hdr)*GetNCCH_MediaUnitSize(hdr); ciaset->content.ContentOffset[j] = ciaset->content.TotalContentSize; ciaset->content.TotalContentSize += ciaset->content.ContentSize[j]; // Finish get next content j++; } } free(hdr); ciaset->content.ContentCount = j; // Check Conflicting IDs for(int i = 0; i < ciaset->content.ContentCount; i++){ for(j = i+1; j < ciaset->content.ContentCount; j++){ if(ciaset->content.ContentId[j] == ciaset->content.ContentId[i]){ fprintf(stderr,"[CIA ERROR] CIA Content %d and %d, have conflicting IDs\n",ciaset->content.ContentIndex[j],ciaset->content.ContentIndex[i]); return CIA_CONFILCTING_CONTENT_IDS; } } } return 0; } int GetSettingsFromSrl(cia_settings *ciaset) { SRL_Header *hdr = (SRL_Header*)ciaset->content.content0; if(!hdr || ciaset->content.content0_FileLen < sizeof(SRL_Header)) { fprintf(stderr,"[CIA ERROR] Invalid TWL SRL File\n"); return FAILED_TO_IMPORT_FILE; } // Check if TWL SRL File if(u8_to_u16(&hdr->title_id[6],LE) != 0x0003){ fprintf(stderr,"[CIA ERROR] Invalid TWL SRL File\n"); return FAILED_TO_IMPORT_FILE; } // Generate and store Converted TitleID u64_to_u8(ciaset->TitleID,ConvertTwlIdToCtrId(u8_to_u64(hdr->title_id,LE)),BE); //memdump(stdout,"SRL TID: ",ciaset->TitleID,8); // Get TWL Flag ciaset->tmd.twl_flag = ((hdr->reserved_flags[3] & 6) >> 1); // Get Remaster Version u16 version = SetupVersion(hdr->rom_version,ciaset->Version[1],0); u16_to_u8(ciaset->tik.TicketVersion,version,BE); u16_to_u8(ciaset->tmd.TitleVersion,version,BE); // Get SaveDataSize (Public and Private) memcpy(ciaset->tmd.SaveDataSize,hdr->pub_save_data_size,4); memcpy(ciaset->tmd.PrivSaveDataSize,hdr->priv_save_data_size,4); // Setting CIA Content Settings ciaset->content.ContentCount = 1; ciaset->content.ContentOffset[0] = 0; ciaset->content.ContentSize[0] = ciaset->content.content0_FileLen; ciaset->content.TotalContentSize = ciaset->content.content0_FileLen; return 0; } int GetSettingsFromCci(cia_settings *ciaset) { int result = 0; if(!IsCci(ciaset->content.content0)){ fprintf(stderr,"[CIA ERROR] Invalid CCI file\n"); return FAILED_TO_IMPORT_FILE; } u32 ncch0_offset = GetPartitionOffset(ciaset->content.content0,0); if(!ncch0_offset){ fprintf(stderr,"[CIA ERROR] Invalid CCI file (invalid ncch0 size)\n"); return FAILED_TO_IMPORT_FILE; } result = GetSettingsFromNcch0(ciaset, ncch0_offset); if(result){ fprintf(stderr,"Import of Ncch 0 failed(%d)\n",result); return result; } ciaset->content.ContentCount = 1; ciaset->content.CCIContentOffsets[0] = ncch0_offset; NCCH_Header *hdr = malloc(sizeof(NCCH_Header)); for(int i = 1; i < 8; i++){ if(GetPartitionSize(ciaset->content.content0,i)){ ciaset->content.CCIContentOffsets[ciaset->content.ContentCount] = GetPartitionOffset(ciaset->content.content0,i); // Get Data from ncch HDR GetNCCH_CommonHDR(hdr,NULL,GetPartition(ciaset->content.content0,i)); // Get Size ciaset->content.ContentSize[ciaset->content.ContentCount] = GetPartitionSize(ciaset->content.content0,i); ciaset->content.ContentOffset[ciaset->content.ContentCount] = ciaset->content.TotalContentSize; ciaset->content.TotalContentSize += ciaset->content.ContentSize[ciaset->content.ContentCount]; // Get ID u8 hash[0x20]; ctr_sha((u8*)hdr,0x200,hash,CTR_SHA_256); ciaset->content.ContentId[ciaset->content.ContentCount] = u8_to_u32(hash,BE); // Get Index ciaset->content.ContentIndex[ciaset->content.ContentCount] = i; // Increment Content Count ciaset->content.ContentCount++; } } free(hdr); return 0; } u16 SetupVersion(u16 Major, u16 Minor, u16 Micro) { return (((Major << 10) & 0xFC00) | ((Minor << 4) & 0x3F0) | (Micro & 0xf)); } int BuildCIA_CertChain(cia_settings *ciaset) { ciaset->CIA_Sections.CertChain.size = GetCertSize(ciaset->keys->certs.ca_cert) + GetCertSize(ciaset->keys->certs.tik_cert) + GetCertSize(ciaset->keys->certs.tmd_cert); ciaset->CIA_Sections.CertChain.buffer = malloc(ciaset->CIA_Sections.CertChain.size); if(!ciaset->CIA_Sections.CertChain.buffer) { fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); return MEM_ERROR; } memcpy(ciaset->CIA_Sections.CertChain.buffer,ciaset->keys->certs.ca_cert,GetCertSize(ciaset->keys->certs.ca_cert)); memcpy((ciaset->CIA_Sections.CertChain.buffer+GetCertSize(ciaset->keys->certs.ca_cert)),ciaset->keys->certs.tik_cert,GetCertSize(ciaset->keys->certs.tik_cert)); memcpy((ciaset->CIA_Sections.CertChain.buffer+GetCertSize(ciaset->keys->certs.ca_cert)+GetCertSize(ciaset->keys->certs.tik_cert)),ciaset->keys->certs.tmd_cert,GetCertSize(ciaset->keys->certs.tmd_cert)); return 0; } int BuildCIA_Header(cia_settings *ciaset) { // Allocating memory for header ciaset->CIA_Sections.Header.size = sizeof(CIA_Header); ciaset->CIA_Sections.Header.buffer = malloc(ciaset->CIA_Sections.Header.size); if(!ciaset->CIA_Sections.Header.buffer){ fprintf(stderr,"[CIA ERROR] MEM ERROR\n"); return MEM_ERROR; } CIA_Header *hdr = (CIA_Header*)ciaset->CIA_Sections.Header.buffer; // Clearing memset(hdr,0,sizeof(CIA_Header)); // Predict TMD Size ciaset->CIA_Sections.TitleMetaData.size = PredictTMDSize(ciaset->content.ContentCount); // Setting Data u32_to_u8(hdr->HdrSize,sizeof(CIA_Header),LE); u16_to_u8(hdr->Type,0x0,LE); u16_to_u8(hdr->Version,0x0,LE); u32_to_u8(hdr->CertChainSize,ciaset->CIA_Sections.CertChain.size,LE); u32_to_u8(hdr->TicketSize,ciaset->CIA_Sections.Ticket.size,LE); u32_to_u8(hdr->TitleMetaDataSize,ciaset->CIA_Sections.TitleMetaData.size,LE); u32_to_u8(hdr->CXI_MetaSize,ciaset->CIA_Sections.CXI_MetaData.size,LE); u64_to_u8(hdr->ContentSize,ciaset->content.TotalContentSize,LE); // Recording Offsets ciaset->CIA_Sections.CertChainOffset = align_value(sizeof(CIA_Header),0x40); ciaset->CIA_Sections.TicketOffset = align_value(ciaset->CIA_Sections.CertChainOffset+ciaset->CIA_Sections.CertChain.size,0x40); ciaset->CIA_Sections.TitleMetaDataOffset = align_value(ciaset->CIA_Sections.TicketOffset+ciaset->CIA_Sections.Ticket.size,0x40); ciaset->CIA_Sections.ContentOffset = align_value(ciaset->CIA_Sections.TitleMetaDataOffset+ciaset->CIA_Sections.TitleMetaData.size,0x40); ciaset->CIA_Sections.CXI_MetaDataOffset = align_value(ciaset->CIA_Sections.ContentOffset+ciaset->content.TotalContentSize,0x40); // SetCIAContentIndex, actually works for all index values now. CIA files generated can now hold, with // validity, 65536 contents. Or at least have a content with index value of 65535. for(int i = 0; i < ciaset->content.ContentCount; i++){ // This works by treating the 0x2000 byte index array as an array of 2048 u32 values // Used for determining which u32 chunk to write the value to u16 section = ciaset->content.ContentIndex[i]/32; // Calculating the value added to the u32 u32 value = 0x80000000/(1<content.ContentIndex[i]); // Retrieving current u32 block u32 cur_content_index_section = u8_to_u32(hdr->ContentIndex+(sizeof(u32)*section),BE); // Adding value to block cur_content_index_section += value; // Returning block u32_to_u8(hdr->ContentIndex+(sizeof(u32)*section),cur_content_index_section,BE); } return 0; } int WriteCurrentSectionstoFile(cia_settings *ciaset) { WriteBuffer(ciaset->CIA_Sections.Header.buffer,ciaset->CIA_Sections.Header.size,0,ciaset->out); WriteBuffer(ciaset->CIA_Sections.CertChain.buffer,ciaset->CIA_Sections.CertChain.size,ciaset->CIA_Sections.CertChainOffset,ciaset->out); WriteBuffer(ciaset->CIA_Sections.Ticket.buffer,ciaset->CIA_Sections.Ticket.size,ciaset->CIA_Sections.TicketOffset,ciaset->out); WriteBuffer(ciaset->CIA_Sections.CXI_MetaData.buffer,ciaset->CIA_Sections.CXI_MetaData.size,ciaset->CIA_Sections.CXI_MetaDataOffset,ciaset->out); return 0; } int WriteContentsToFile(cia_settings *ciaset, user_settings *usrset) { u8 *Content0 = ciaset->content.content0; if(usrset->ConvertCci) Content0 = (u8*)(ciaset->content.content0+ciaset->content.CCIContentOffsets[0]); ctr_sha(Content0,ciaset->content.ContentSize[0],ciaset->content.ContentHash[0],CTR_SHA_256); if(ciaset->content.EncryptContents) { ciaset->content.ContentType[0] |= Encrypted; CryptContent(Content0,Content0,ciaset->content.ContentSize[0],ciaset->tik.TitleKey,ciaset->content.ContentIndex[0],ENC); } WriteBuffer(Content0,ciaset->content.ContentSize[0],ciaset->content.ContentOffset[0]+ciaset->CIA_Sections.ContentOffset,ciaset->out); // Free Buffer if Not CCI if(!usrset->ConvertCci){ free(usrset->Content0.buffer); usrset->Content0.buffer = NULL; usrset->Content0.size = 0; } // Add additional contents, recreating them with their new TitleID if(usrset->Content0IsNcch){ u8 TitleId[8]; endian_memcpy(TitleId,ciaset->TitleID,8,LE); for(int i = 1; i < ciaset->content.ContentCount; i++){ u8 *ContentBuff = RetargetNCCH(ciaset->content.ContentFilePtrs[i],ciaset->content.ContentSize[i],ciaset->content.ContentTitleId[i],TitleId,ciaset->keys); if(!ContentBuff){ fprintf(stderr,"[CIA ERROR] Could not import content %d to CIA\n",i); return FAILED_TO_IMPORT_FILE; } ctr_sha(ContentBuff,ciaset->content.ContentSize[i],ciaset->content.ContentHash[i],CTR_SHA_256); if(ciaset->content.EncryptContents) { ciaset->content.ContentType[i] |= Encrypted; CryptContent(ContentBuff,ContentBuff,ciaset->content.ContentSize[i],ciaset->tik.TitleKey,ciaset->content.ContentIndex[i],ENC); } WriteBuffer(ContentBuff,ciaset->content.ContentSize[i],ciaset->content.ContentOffset[i]+ciaset->CIA_Sections.ContentOffset,ciaset->out); free(ContentBuff); } } else if(usrset->ConvertCci){ for(int i = 1; i < ciaset->content.ContentCount; i++){ u8 *ContentBuff = (u8*)(ciaset->content.content0+ciaset->content.CCIContentOffsets[i]); ctr_sha(ContentBuff,ciaset->content.ContentSize[i],ciaset->content.ContentHash[i],CTR_SHA_256); if(ciaset->content.EncryptContents) { ciaset->content.ContentType[i] |= Encrypted; CryptContent(ContentBuff,ContentBuff,ciaset->content.ContentSize[i],ciaset->tik.TitleKey,ciaset->content.ContentIndex[i],ENC); } WriteBuffer(ContentBuff,ciaset->content.ContentSize[i],ciaset->content.ContentOffset[i]+ciaset->CIA_Sections.ContentOffset,ciaset->out); } free(usrset->Content0.buffer); usrset->Content0.buffer = NULL; usrset->Content0.size = 0; } return 0; } int WriteTMDToFile(cia_settings *ciaset) { WriteBuffer(ciaset->CIA_Sections.TitleMetaData.buffer,ciaset->CIA_Sections.TitleMetaData.size,ciaset->CIA_Sections.TitleMetaDataOffset,ciaset->out); return 0; } int CryptContent(u8 *EncBuffer,u8 *DecBuffer,u64 size,u8 *title_key, u16 index, u8 mode) { //generating IV u8 iv[16]; memset(&iv,0x0,16); iv[0] = (index >> 8) & 0xff; iv[1] = index & 0xff; //Crypting content ctr_aes_context ctx; memset(&ctx,0x0,sizeof(ctr_aes_context)); ctr_init_aes_cbc(&ctx,title_key,iv,mode); if(mode == ENC) ctr_aes_cbc(&ctx,DecBuffer,EncBuffer,size,ENC); else ctr_aes_cbc(&ctx,EncBuffer,DecBuffer,size,DEC); return 0; }