#include #include #include #include #include #include #include #include ntd::n3ds::CciFsShapshotGenerator::CciFsShapshotGenerator(const std::shared_ptr& stream) : FileSystemSnapshot(), mBaseStream(stream), mCurDir(0) { // validate stream properties if (mBaseStream == nullptr) { throw tc::ObjectDisposedException("ntd::n3ds::CciFsShapshotGenerator", "Failed to open input stream."); } if (mBaseStream->canRead() == false || mBaseStream->canSeek() == false) { throw tc::NotSupportedException("ntd::n3ds::CciFsShapshotGenerator", "Input stream requires read/seek permissions."); } // validate and read CCI header ntd::n3ds::CciHeader hdr; if (mBaseStream->length() < sizeof(ntd::n3ds::CciHeader)) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "Input stream is too small."); } mBaseStream->seek(0, tc::io::SeekOrigin::Begin); mBaseStream->read((byte_t*)(&hdr), sizeof(ntd::n3ds::CciHeader)); if (hdr.ncsd_header.struct_magic.unwrap() != hdr.ncsd_header.kStructMagic) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "CCI header had invalid struct magic."); } if (hdr.ncsd_header.flags.media_type != hdr.ncsd_header.MediaType_Card1 && hdr.ncsd_header.flags.media_type != hdr.ncsd_header.MediaType_Card2) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "CCI header had unexpected media type."); } if (hdr.ncsd_header.flags.block_size_log != 0) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "CCI header had unexpected block size."); } if (hdr.ncsd_header.flags.media_platform.test(hdr.ncsd_header.MediaPlatform_CTR) == false) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "CCI header had unexpected supported platforms."); } // parse header partitions struct PartitionInformation { int64_t offset; int64_t size; uint64_t title_id; }; std::array partition; int64_t used_image_size = 0; for (size_t i = 0; i < partition.size(); i++) { if (hdr.ncsd_header.partition_fs_type[i] != hdr.ncsd_header.PartitionFsType_None) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "CCI partition had unexpected fs type."); } if (hdr.ncsd_header.partition_crypto_type[i] != hdr.ncsd_header.PartitionCryptoType_None) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "CCI partition had unexpected crypto type."); } partition[i].offset = int64_t(hdr.ncsd_header.partition_offsetsize[i].blk_offset.unwrap()) << 9; partition[i].size = int64_t(hdr.ncsd_header.partition_offsetsize[i].blk_size.unwrap()) << 9; partition[i].title_id = hdr.ncsd_header.card_ext.partition_id[i].unwrap(); used_image_size = std::max((partition[i].offset + partition[i].size), used_image_size); } if (stream->length() < used_image_size) { throw tc::ArgumentOutOfRangeException("ntd::n3ds::CciFsShapshotGenerator", "Input stream is too small, given calculated CCI partition geometry."); } // Add root directory dir_entries.push_back(DirEntry()); mCurDir = dir_entries.size() - 1; dir_entries[mCurDir].dir_listing.abs_path = tc::io::Path("/"); dir_entry_path_map[tc::io::Path("/")] = mCurDir; // populate virtual filesystem // initial data //addFile("initial_data.bin", sizeof(ntd::n3ds::NcsdCommonHeader) + 0xe00, sizeof(ntd::n3ds::CardInfoHeader::InitialData)); // NCCH partitions for (size_t i = 0; i < partition.size(); i++) { if (partition[i].size != 0) { std::stringstream ss; ss << std::hex << std::setfill('0') << std::setw(2) << i; ss << "_"; ss << std::hex << std::setfill('0') << std::setw(16) << partition[i].title_id; ss << ".app"; addFile(ss.str(), partition[i].offset, partition[i].size); } } } void ntd::n3ds::CciFsShapshotGenerator::addFile(const std::string& name, int64_t offset, int64_t size) { FileEntry tmp; tmp.stream = std::make_shared(tc::io::SubStream(mBaseStream, offset, size)); // create file path tc::io::Path file_path = dir_entries[mCurDir].dir_listing.abs_path + std::string(name); // add file entry to list file_entries.push_back(std::move(tmp)); // add file entry to map file_entry_path_map[file_path] = file_entries.size()-1; // add name to parent directory listing dir_entries[mCurDir].dir_listing.file_list.push_back(name); }