/* * gamecard_dump_tasks.cpp * * Copyright (c) 2020-2024, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). * * nxdumptool 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. * * nxdumptool 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 this program. If not, see . */ #include #include #include #include namespace i18n = brls::i18n; /* For getStr(). */ using namespace i18n::literals; /* For _i18n. */ #define BLOCK_SIZE 0x800000 namespace nxdt::tasks { GameCardDumpTaskError GameCardImageDumpTask::DoInBackground(const std::string& output_path, const bool& prepend_key_area, const bool& keep_certificate, const bool& trim_dump, const bool& calculate_checksum) { std::scoped_lock lock(this->crc_mtx); GameCardKeyArea gc_key_area{}; GameCardSecurityInformation gc_security_information{}; u32 gc_key_area_crc = 0; size_t gc_img_size = 0; nxdt::utils::FileWriter *file = nullptr; void *buf = nullptr; DataTransferProgress progress{}; this->calculate_checksum = calculate_checksum; LOG_MSG_DEBUG("Starting dump with parameters:\n- Output path: \"%s\".\n- Prepend key area: %u.\n- Keep certificate: %u.\n- Trim dump: %u.\n- Calculate checksum: %u.", \ output_path.c_str(), prepend_key_area, keep_certificate, trim_dump, calculate_checksum); /* Retrieve gamecard image size. */ if ((!trim_dump && !gamecardGetTotalSize(&gc_img_size)) || (trim_dump && !gamecardGetTrimmedSize(&gc_img_size)) || !gc_img_size) return "tasks/gamecard/image/get_size_failed"_i18n; /* Check if we're supposed to prepend the key area to the gamecard image. */ if (prepend_key_area) { /* Update gamecard image size. */ gc_img_size += sizeof(GameCardKeyArea); /* Retrieve the GameCardSecurityInformation area. */ if (!gamecardGetSecurityInformation(&gc_security_information)) return "tasks/gamecard/image/get_security_info_failed"_i18n; /* Copy the GameCardInitialData area from the GameCardSecurityInformation area to our GameCardKeyArea object. */ memcpy(&(gc_key_area.initial_data), &(gc_security_information.initial_data), sizeof(GameCardInitialData)); if (calculate_checksum) { /* Update gamecard image checksum if we're prepending the key area to it. */ gc_key_area_crc = crc32Calculate(&gc_key_area, sizeof(GameCardKeyArea)); this->full_gc_img_crc = gc_key_area_crc; } } /* Open output file. */ try { file = new nxdt::utils::FileWriter(output_path, gc_img_size); } catch(const std::string& msg) { return msg; } ON_SCOPE_EXIT { delete file; }; /* Push progress onto the class. */ progress.total_size = gc_img_size; this->PublishProgress(progress); if (prepend_key_area) { /* Write GameCardKeyArea object. */ if (!file->Write(&gc_key_area, sizeof(GameCardKeyArea))) return "tasks/gamecard/image/write_key_area_failed"_i18n; /* Push progress onto the class. */ progress.xfer_size = sizeof(GameCardKeyArea); this->PublishProgress(progress); } /* Allocate memory buffer for the dump process. */ buf = usbAllocatePageAlignedBuffer(BLOCK_SIZE); if (!buf) return "generic/mem_alloc_failed"_i18n; ON_SCOPE_EXIT { free(buf); }; /* Dump gamecard image. */ for(size_t offset = 0, blksize = BLOCK_SIZE; offset < gc_img_size; offset += blksize) { /* Don't proceed if the task has been cancelled. */ if (this->IsCancelled()) return "generic/process_cancelled"_i18n; /* Adjust current block size, if needed. */ if (blksize > (gc_img_size - offset)) blksize = (gc_img_size - offset); /* Read current block. */ if (!gamecardReadStorage(buf, blksize, offset)) return i18n::getStr("tasks/gamecard/image/io_failed", "generic/read"_i18n, blksize, offset); /* Remove certificate, if needed. */ if (!keep_certificate && offset == 0) memset(static_cast(buf) + GAMECARD_CERT_OFFSET, 0xFF, sizeof(FsGameCardCertificate)); /* Update image checksum. */ if (calculate_checksum) { this->gc_img_crc = crc32CalculateWithSeed(this->gc_img_crc, buf, blksize); if (prepend_key_area) this->full_gc_img_crc = crc32CalculateWithSeed(this->full_gc_img_crc, buf, blksize); } /* Write current block. */ if (!file->Write(buf, blksize)) return i18n::getStr("tasks/gamecard/image/io_failed", "generic/write"_i18n, blksize, offset); /* Push progress onto the class. */ progress.xfer_size += blksize; progress.percentage = static_cast((progress.xfer_size * 100) / progress.total_size); this->PublishProgress(progress); } return {}; } }