2021-10-15 15:41:16 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Copyright (C) 2021 Maschell
|
|
|
|
*
|
|
|
|
* This program 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.
|
|
|
|
*
|
|
|
|
* This program 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 <http://www.gnu.org/licenses/>.
|
|
|
|
****************************************************************************/
|
2022-07-26 08:16:27 +02:00
|
|
|
#include "GMPartitionsDumperState.h"
|
2021-10-15 15:41:16 +02:00
|
|
|
#include <WUD/DiscReaderDiscDrive.h>
|
2022-07-26 08:16:27 +02:00
|
|
|
#include <WUD/NUSTitle.h>
|
|
|
|
#include <WUD/content/partitions/WiiUGMPartition.h>
|
2021-10-15 15:41:16 +02:00
|
|
|
#include <WUD/header/WiiUDiscHeader.h>
|
2022-07-26 08:16:27 +02:00
|
|
|
#include <common/common.h>
|
2021-10-15 15:41:16 +02:00
|
|
|
#include <fs/FSUtils.h>
|
2022-07-26 08:16:27 +02:00
|
|
|
#include <memory>
|
2022-07-26 09:24:06 +02:00
|
|
|
#include <mocha/fsa.h>
|
|
|
|
#include <mocha/mocha.h>
|
2021-10-15 15:41:16 +02:00
|
|
|
#include <utils/StringTools.h>
|
|
|
|
|
|
|
|
#define READ_BUFFER_SIZE (SECTOR_SIZE * 128)
|
|
|
|
|
2021-10-16 13:03:32 +02:00
|
|
|
GMPartitionsDumperState::GMPartitionsDumperState(eDumpTarget pTargetDevice) : targetDevice(pTargetDevice) {
|
2021-10-15 15:41:16 +02:00
|
|
|
this->sectorBufSize = SECTOR_SIZE;
|
2022-07-26 08:16:27 +02:00
|
|
|
this->state = STATE_OPEN_ODD1;
|
2022-07-26 09:24:06 +02:00
|
|
|
gBlockHomeButton = true;
|
2021-10-15 15:41:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
GMPartitionsDumperState::~GMPartitionsDumperState() {
|
|
|
|
free(this->sectorBuf);
|
|
|
|
this->sectorBuf = nullptr;
|
|
|
|
free(this->readBuffer);
|
|
|
|
this->readBuffer = nullptr;
|
2022-07-26 09:24:06 +02:00
|
|
|
gBlockHomeButton = false;
|
2021-10-15 15:41:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void GMPartitionsDumperState::render() {
|
|
|
|
WiiUScreen::clearScreen();
|
|
|
|
ApplicationState::printHeader();
|
|
|
|
|
|
|
|
if (this->state == STATE_ERROR) {
|
|
|
|
WiiUScreen::drawLine();
|
|
|
|
WiiUScreen::drawLinef("Error: %s", ErrorMessage().c_str());
|
|
|
|
WiiUScreen::drawLinef("Description: %s", ErrorDescription().c_str());
|
|
|
|
WiiUScreen::drawLine();
|
|
|
|
WiiUScreen::drawLine("Press A to return.");
|
|
|
|
} else if (this->state == STATE_OPEN_ODD1) {
|
|
|
|
WiiUScreen::drawLine("Open /dev/odd01");
|
|
|
|
} else if (this->state == STATE_PLEASE_INSERT_DISC) {
|
|
|
|
WiiUScreen::drawLine("Please insert a Wii U disc and try again.\n\nPress A to return");
|
|
|
|
} else if (this->state == STATE_READ_DISC_INFO) {
|
|
|
|
WiiUScreen::drawLine("Read disc information");
|
|
|
|
} else if (this->state == STATE_READ_DISC_INFO_DONE) {
|
|
|
|
WiiUScreen::drawLine("Read disc information done");
|
|
|
|
} else if (this->state == STATE_READ_COMMON_KEY) {
|
|
|
|
WiiUScreen::drawLine("Read common key");
|
|
|
|
} else if (this->state == STATE_CREATE_DISC_READER) {
|
|
|
|
WiiUScreen::drawLine("Create disc reader");
|
|
|
|
} else if (this->state == STATE_PARSE_DISC_HEADER) {
|
|
|
|
WiiUScreen::drawLine("Parse disc header");
|
|
|
|
} else if (this->state == STATE_PROCESS_GM_PARTITIONS) {
|
|
|
|
WiiUScreen::drawLine("Process partitions");
|
|
|
|
} else if (this->state == STATE_CHOOSE_PARTITION_TO_DUMP) {
|
|
|
|
WiiUScreen::drawLine("Choose the partition to dump:");
|
|
|
|
WiiUScreen::drawLine();
|
|
|
|
if (gmPartitionPairs.empty()) {
|
|
|
|
WiiUScreen::drawLine("This disc has no dumpable GM partitions ");
|
|
|
|
} else {
|
|
|
|
uint32_t index = 0;
|
2022-07-26 08:16:27 +02:00
|
|
|
for (auto &partitionPair : gmPartitionPairs) {
|
2022-07-26 09:24:06 +02:00
|
|
|
uint64_t size = 0;
|
2022-07-26 08:16:27 +02:00
|
|
|
for (auto &content : partitionPair.second->tmd->contentList) {
|
2021-10-15 15:41:16 +02:00
|
|
|
size += ROUNDUP(content->encryptedFileSize, 16);
|
|
|
|
}
|
2022-07-26 09:24:06 +02:00
|
|
|
std::string titleId = partitionPair.first->getVolumeId().substr(2, 18);
|
|
|
|
std::string appType = "Other ";
|
|
|
|
if (titleId.starts_with("00050000")) {
|
|
|
|
appType = "Game ";
|
|
|
|
} else if (titleId.starts_with("0005000C")) {
|
|
|
|
appType = "DLC ";
|
|
|
|
} else if (titleId.starts_with("0005000E")) {
|
|
|
|
appType = "Update";
|
|
|
|
}
|
|
|
|
WiiUScreen::drawLinef("%s %s - %s (~%0.2f GiB) (%s)", index == (uint32_t) selectedOptionY ? ">" : " ", appType.c_str(),
|
|
|
|
partitionPair.second->getShortnameEn().c_str(),
|
|
|
|
(float) ((float) size / 1024.0f / 1024.0f / 1024.0f), titleId.c_str());
|
2021-10-15 15:41:16 +02:00
|
|
|
index++;
|
|
|
|
}
|
|
|
|
WiiUScreen::drawLine();
|
2021-10-16 13:03:32 +02:00
|
|
|
WiiUScreen::drawLinef("%s Back", index == (uint32_t) selectedOptionY ? ">" : " ");
|
2021-10-15 15:41:16 +02:00
|
|
|
}
|
|
|
|
} else if (this->state == STATE_CREATE_DATA_PROVIDER) {
|
|
|
|
WiiUScreen::drawLine("Create data provider from partition");
|
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_TMD) {
|
|
|
|
WiiUScreen::drawLine("Dump title.tmd");
|
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_TICKET) {
|
|
|
|
WiiUScreen::drawLine("Dump title.tik");
|
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_CERT) {
|
|
|
|
WiiUScreen::drawLine("Dump title.cert");
|
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_CONTENTS) {
|
|
|
|
if (curPartition != nullptr) {
|
|
|
|
WiiUScreen::drawLinef("Dumping Partition %s", curPartition->getVolumeId().c_str());
|
2022-07-26 09:24:06 +02:00
|
|
|
WiiUScreen::drawLinef("Name: %s", curNUSTitle->getLongnameEn().c_str(), curNUSTitle->tmd->titleId);
|
|
|
|
WiiUScreen::drawLinef("TitleID: %016llX", curNUSTitle->tmd->titleId);
|
2021-10-15 15:41:16 +02:00
|
|
|
} else {
|
|
|
|
WiiUScreen::drawLine("Dumping Partition");
|
|
|
|
}
|
|
|
|
|
2022-07-26 08:16:27 +02:00
|
|
|
uint64_t partitionSize = 0;
|
2021-10-15 15:41:16 +02:00
|
|
|
uint64_t partitionOffset = curOffsetInContent;
|
|
|
|
if (curNUSTitle != nullptr) {
|
2022-07-26 08:16:27 +02:00
|
|
|
for (auto &content : curNUSTitle->tmd->contentList) {
|
2021-10-15 15:41:16 +02:00
|
|
|
partitionSize += ROUNDUP(content->encryptedFileSize, 16);
|
|
|
|
if (content->index < curContentIndex) {
|
|
|
|
partitionOffset += ROUNDUP(content->encryptedFileSize, 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WiiUScreen::drawLinef("Total Progress: %.2f MiB / %.2f MiB (%0.2f%%)", partitionOffset / 1024.0f / 1024.0f,
|
|
|
|
partitionSize / 1024.0f / 1024.0f, ((partitionOffset * 1.0f) / partitionSize) * 100.0f);
|
|
|
|
WiiUScreen::drawLine();
|
|
|
|
} else {
|
|
|
|
WiiUScreen::drawLine();
|
|
|
|
WiiUScreen::drawLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curNUSTitle != nullptr) {
|
|
|
|
WiiUScreen::drawLinef("Dumping Content %d / %d", curContentIndex, curNUSTitle->tmd->contentList.size());
|
|
|
|
} else {
|
|
|
|
WiiUScreen::drawLine("Dumping Content ?? / ??");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto offset = curOffsetInContent;
|
2022-07-26 08:16:27 +02:00
|
|
|
auto size = curContent != nullptr ? ROUNDUP(curContent->encryptedFileSize, 16) : 0;
|
2021-10-15 15:41:16 +02:00
|
|
|
|
|
|
|
if (size > 0) {
|
|
|
|
WiiUScreen::drawLinef("Progress: %.2f MiB / %.2f MiB (%0.2f%%)", offset / 1024.0f / 1024.0f,
|
|
|
|
size / 1024.0f / 1024.0f, ((offset * 1.0f) / size) * 100.0f);
|
2022-07-26 09:24:06 +02:00
|
|
|
} else {
|
|
|
|
WiiUScreen::drawLine();
|
2021-10-15 15:41:16 +02:00
|
|
|
}
|
2022-07-26 09:24:06 +02:00
|
|
|
WiiUScreen::drawLine();
|
|
|
|
WiiUScreen::drawLine("Press B to abort the dumping");
|
2021-10-15 15:41:16 +02:00
|
|
|
|
|
|
|
} else if (this->state == STATE_DUMP_DONE) {
|
|
|
|
WiiUScreen::drawLine("Dumping done. Press A to return.");
|
2022-07-26 09:24:06 +02:00
|
|
|
} else if (this->state == STATE_ABORT_CONFIRMATION) {
|
|
|
|
WiiUScreen::drawLinef("Do you really want to abort the disc dumping?");
|
|
|
|
WiiUScreen::drawLinef("");
|
|
|
|
if (selectedOptionX == 0) {
|
|
|
|
WiiUScreen::drawLinef("> Continue dumping Abort dumping");
|
|
|
|
} else {
|
|
|
|
WiiUScreen::drawLinef(" Continue dumping > Abort dumping");
|
|
|
|
}
|
2021-10-15 15:41:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ApplicationState::printFooter();
|
|
|
|
WiiUScreen::flipBuffers();
|
|
|
|
}
|
|
|
|
|
|
|
|
ApplicationState::eSubState GMPartitionsDumperState::update(Input *input) {
|
|
|
|
if (this->state == STATE_RETURN) {
|
|
|
|
return ApplicationState::SUBSTATE_RETURN;
|
|
|
|
}
|
|
|
|
if (this->state == STATE_ERROR) {
|
|
|
|
if (entrySelected(input)) {
|
|
|
|
return ApplicationState::SUBSTATE_RETURN;
|
|
|
|
}
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->state == STATE_OPEN_ODD1) {
|
2022-07-26 09:24:06 +02:00
|
|
|
auto ret = FSAEx_RawOpen(__wut_devoptab_fs_client, "/dev/odd01", &(this->oddFd));
|
2021-10-15 15:41:16 +02:00
|
|
|
if (ret >= 0) {
|
|
|
|
if (this->sectorBuf == nullptr) {
|
|
|
|
this->sectorBuf = (void *) memalign(0x100, this->sectorBufSize);
|
|
|
|
if (this->sectorBuf == nullptr) {
|
|
|
|
this->setError(ERROR_MALLOC_FAILED);
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->state = STATE_READ_DISC_INFO;
|
|
|
|
} else {
|
|
|
|
this->state = STATE_PLEASE_INSERT_DISC;
|
|
|
|
}
|
|
|
|
} else if (this->state == STATE_PLEASE_INSERT_DISC) {
|
|
|
|
if (entrySelected(input)) {
|
|
|
|
return ApplicationState::SUBSTATE_RETURN;
|
|
|
|
}
|
|
|
|
} else if (this->state == STATE_READ_DISC_INFO) {
|
2022-07-26 09:24:06 +02:00
|
|
|
if (FSAEx_RawRead(__wut_devoptab_fs_client, this->sectorBuf, READ_SECTOR_SIZE, 1, 0, this->oddFd) >= 0) {
|
2021-10-15 15:41:16 +02:00
|
|
|
this->discId[10] = '\0';
|
|
|
|
memcpy(this->discId.data(), sectorBuf, 10);
|
|
|
|
if (this->discId[0] == 0) {
|
|
|
|
setError(ERROR_NO_DISC_ID);
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->state = STATE_READ_DISC_INFO_DONE;
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
}
|
2022-07-26 09:24:06 +02:00
|
|
|
FSAEx_RawClose(__wut_devoptab_fs_client, this->oddFd);
|
|
|
|
this->oddFd = -1;
|
2021-10-15 15:41:16 +02:00
|
|
|
|
|
|
|
this->setError(ERROR_READ_FIRST_SECTOR);
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
} else if (this->state == STATE_READ_DISC_INFO_DONE) {
|
|
|
|
this->state = STATE_READ_COMMON_KEY;
|
|
|
|
} else if (this->state == STATE_READ_COMMON_KEY) {
|
2022-07-26 09:24:06 +02:00
|
|
|
WiiUConsoleOTP otp;
|
|
|
|
Mocha_ReadOTP(&otp);
|
|
|
|
memcpy(cKey.data(), otp.wiiUBank.wiiUCommonKey, 0x10);
|
2021-10-15 15:41:16 +02:00
|
|
|
this->state = STATE_CREATE_DISC_READER;
|
|
|
|
} else if (this->state == STATE_CREATE_DISC_READER) {
|
2022-07-26 09:24:06 +02:00
|
|
|
auto discReaderOpt = DiscReaderDiscDrive::make_unique();
|
|
|
|
if (!discReaderOpt) {
|
2021-10-15 15:41:16 +02:00
|
|
|
this->setError(ERROR_OPEN_ODD1);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
2022-07-26 09:24:06 +02:00
|
|
|
this->discReader = std::move(discReaderOpt.value());
|
|
|
|
this->state = STATE_PARSE_DISC_HEADER;
|
2021-10-15 15:41:16 +02:00
|
|
|
} else if (this->state == STATE_PARSE_DISC_HEADER) {
|
|
|
|
auto discHeaderOpt = WiiUDiscHeader::make_unique(discReader);
|
|
|
|
if (!discHeaderOpt.has_value()) {
|
2022-07-26 09:24:06 +02:00
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to read DiscHeader");
|
2021-10-15 15:41:16 +02:00
|
|
|
this->setError(ERROR_PARSE_DISCHEADER);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
this->discHeader = std::move(discHeaderOpt.value());
|
2022-07-26 08:16:27 +02:00
|
|
|
this->state = STATE_PROCESS_GM_PARTITIONS;
|
2021-10-15 15:41:16 +02:00
|
|
|
} else if (this->state == STATE_PROCESS_GM_PARTITIONS) {
|
|
|
|
this->gmPartitionPairs.clear();
|
2022-07-26 08:16:27 +02:00
|
|
|
for (auto &partition : discHeader->wiiUContentsInformation->partitions->partitions) {
|
2021-10-15 15:41:16 +02:00
|
|
|
auto gmPartition = std::dynamic_pointer_cast<WiiUGMPartition>(partition);
|
|
|
|
if (gmPartition != nullptr) {
|
|
|
|
auto nusTitleOpt = NUSTitle::loadTitleFromGMPartition(gmPartition, discReader, cKey);
|
|
|
|
if (!nusTitleOpt.has_value()) {
|
|
|
|
this->setError(ERROR_FAILED_TO_GET_NUSTITLE);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
|
2022-07-26 09:24:06 +02:00
|
|
|
this->gmPartitionPairs.emplace_back(gmPartition, std::move(nusTitleOpt.value()));
|
2021-10-15 15:41:16 +02:00
|
|
|
}
|
|
|
|
}
|
2022-07-26 09:24:06 +02:00
|
|
|
|
2021-10-15 15:41:16 +02:00
|
|
|
this->state = STATE_CHOOSE_PARTITION_TO_DUMP;
|
|
|
|
} else if (this->state == STATE_CHOOSE_PARTITION_TO_DUMP) {
|
|
|
|
if (gmPartitionPairs.empty()) {
|
|
|
|
if (entrySelected(input)) {
|
|
|
|
return SUBSTATE_RETURN;
|
|
|
|
}
|
|
|
|
}
|
2022-07-26 09:24:06 +02:00
|
|
|
|
|
|
|
if (buttonPressed(input, Input::BUTTON_B)) {
|
|
|
|
return SUBSTATE_RETURN;
|
|
|
|
}
|
|
|
|
|
2021-10-16 13:03:32 +02:00
|
|
|
proccessMenuNavigationY(input, (int32_t) gmPartitionPairs.size() + 1);
|
2021-10-15 15:41:16 +02:00
|
|
|
if (entrySelected(input)) {
|
2021-10-16 13:03:32 +02:00
|
|
|
if (selectedOptionY >= (int32_t) gmPartitionPairs.size()) {
|
2021-10-15 15:41:16 +02:00
|
|
|
return SUBSTATE_RETURN;
|
|
|
|
}
|
2021-10-16 13:03:32 +02:00
|
|
|
auto gmPartitionPair = gmPartitionPairs[selectedOptionY];
|
2021-10-15 15:41:16 +02:00
|
|
|
if (gmPartitionPair.first != nullptr) {
|
|
|
|
this->curPartition = gmPartitionPair.first;
|
2022-07-26 08:16:27 +02:00
|
|
|
this->curNUSTitle = gmPartitionPair.second;
|
2021-10-15 15:41:16 +02:00
|
|
|
this->dataProvider = this->curNUSTitle->dataProcessor->getDataProvider();
|
|
|
|
} else {
|
|
|
|
DEBUG_FUNCTION_LINE("Failed to find a GM partition");
|
|
|
|
this->setError(ERROR_NO_GM_PARTITION);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
|
2022-07-26 09:24:06 +02:00
|
|
|
this->targetPath = string_format("%swudump/%s/%s", getPathForDevice(targetDevice).c_str(), this->discId, curPartition->getVolumeId().c_str());
|
2021-10-16 13:03:32 +02:00
|
|
|
if (!FSUtils::CreateSubfolder(targetPath.c_str())) {
|
|
|
|
this->setError(ERROR_CREATE_DIR);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
2021-10-15 15:41:16 +02:00
|
|
|
|
|
|
|
this->state = STATE_DUMP_PARTITION_TMD;
|
|
|
|
}
|
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_TMD) {
|
|
|
|
std::vector<uint8_t> wBuffer;
|
|
|
|
if (dataProvider->getRawTMD(wBuffer)) {
|
|
|
|
std::string fileName = targetPath + "/" + WUD_TMD_FILENAME;
|
|
|
|
if (FSUtils::saveBufferToFile(fileName.c_str(), wBuffer.data(), wBuffer.size()) != (int32_t) wBuffer.size()) {
|
|
|
|
this->setError(ERROR_FAILED_WRITE_TMD);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
wBuffer.clear();
|
|
|
|
}
|
|
|
|
this->state = STATE_DUMP_PARTITION_TICKET;
|
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_TICKET) {
|
|
|
|
std::vector<uint8_t> wBuffer;
|
|
|
|
if (dataProvider->getRawTicket(wBuffer)) {
|
|
|
|
std::string fileName = targetPath + "/" + WUD_TICKET_FILENAME;
|
|
|
|
if (FSUtils::saveBufferToFile(fileName.c_str(), wBuffer.data(), wBuffer.size()) != (int32_t) wBuffer.size()) {
|
|
|
|
this->setError(ERROR_FAILED_WRITE_TICKET);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
wBuffer.clear();
|
|
|
|
} else {
|
|
|
|
this->setError(ERROR_FAILED_WRITE_TICKET);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
this->state = STATE_DUMP_PARTITION_CERT;
|
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_CERT) {
|
|
|
|
std::vector<uint8_t> wBuffer;
|
|
|
|
if (dataProvider->getRawCert(wBuffer)) {
|
|
|
|
std::string fileName = targetPath + "/" + WUD_CERT_FILENAME;
|
|
|
|
if (FSUtils::saveBufferToFile(fileName.c_str(), wBuffer.data(), wBuffer.size()) != (int32_t) wBuffer.size()) {
|
|
|
|
this->setError(ERROR_FAILED_WRITE_CERT);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
wBuffer.clear();
|
|
|
|
} else {
|
|
|
|
this->setError(ERROR_FAILED_WRITE_TICKET);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
this->curContentIndex = 0;
|
2022-07-26 08:16:27 +02:00
|
|
|
this->state = STATE_DUMP_PARTITION_CONTENTS;
|
2021-10-15 15:41:16 +02:00
|
|
|
} else if (this->state == STATE_DUMP_PARTITION_CONTENTS) {
|
2022-07-26 09:24:06 +02:00
|
|
|
if (buttonPressed(input, Input::BUTTON_B)) {
|
|
|
|
this->state = STATE_ABORT_CONFIRMATION;
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
}
|
2021-10-15 15:41:16 +02:00
|
|
|
// Get current content by index.
|
|
|
|
if (curContent == nullptr) {
|
|
|
|
auto curContentOpt = curNUSTitle->tmd->getContentByIndex(curContentIndex);
|
|
|
|
if (!curContentOpt.has_value()) {
|
|
|
|
this->setError(ERROR_FIND_CONTENT_BY_INDEX);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
this->curContent = curContentOpt.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we're done reading the file.
|
|
|
|
uint32_t size = ROUNDUP(curContent->encryptedFileSize, 16);
|
|
|
|
if (curOffsetInContent >= size) {
|
|
|
|
// Close current file
|
|
|
|
this->contentFile->close();
|
|
|
|
// Free content
|
|
|
|
this->contentFile.reset();
|
|
|
|
this->curContent.reset();
|
|
|
|
|
|
|
|
curOffsetInContent = 0;
|
|
|
|
|
|
|
|
auto contentListSize = curNUSTitle->tmd->contentList.size();
|
|
|
|
if (curContentIndex + 1 < (int32_t) contentListSize) {
|
|
|
|
curContentIndex++;
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
} else {
|
|
|
|
this->state = STATE_DUMP_DONE;
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open the file if needed
|
|
|
|
if (this->contentFile == nullptr) {
|
|
|
|
char bufApp[32];
|
|
|
|
snprintf(bufApp, 31, "%08X.app", curContent->ID);
|
|
|
|
std::string appFileName = targetPath + "/" + bufApp;
|
|
|
|
|
|
|
|
this->contentFile = std::make_unique<CFile>(appFileName, CFile::WriteOnly);
|
|
|
|
if (!contentFile->isOpen()) {
|
|
|
|
this->contentFile.reset();
|
|
|
|
this->setError(ERROR_FAILED_CREATE_FILE);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
// dump .h3 file as well
|
|
|
|
std::vector<uint8_t> h3Data;
|
|
|
|
if (dataProvider->getContentH3Hash(this->curContent, h3Data)) {
|
|
|
|
snprintf(bufApp, 31, "%08X.h3", curContent->ID);
|
|
|
|
std::string h3FileName = targetPath + "/" + bufApp;
|
|
|
|
if (FSUtils::saveBufferToFile(h3FileName.c_str(), h3Data.data(), h3Data.size()) != (int32_t) h3Data.size()) {
|
|
|
|
this->setError(ERROR_FAILED_WRITE_H3);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// alloc readBuffer if needed
|
|
|
|
if (this->readBuffer == nullptr) {
|
2022-07-26 09:24:06 +02:00
|
|
|
readBuffer = (uint8_t *) memalign(0x40, ROUNDUP(READ_BUFFER_SIZE, 0x40));
|
2021-10-15 15:41:16 +02:00
|
|
|
if (readBuffer == nullptr) {
|
|
|
|
this->setError(ERROR_MALLOC_FAILED);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t toRead = size - curOffsetInContent;
|
|
|
|
if (toRead > READ_BUFFER_SIZE) {
|
|
|
|
toRead = READ_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dataProvider->readRawContent(curContent, readBuffer, curOffsetInContent, toRead)) {
|
|
|
|
this->setError(ERROR_READ_CONTENT);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (contentFile->write((const uint8_t *) readBuffer, toRead) != (int32_t) toRead) {
|
|
|
|
this->setError(ERROR_WRITE_CONTENT);
|
|
|
|
return SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
curOffsetInContent += toRead;
|
|
|
|
|
|
|
|
// Go on!
|
|
|
|
this->state = STATE_DUMP_PARTITION_CONTENTS;
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
2022-07-26 09:24:06 +02:00
|
|
|
} else if (this->state == STATE_ABORT_CONFIRMATION) {
|
|
|
|
if (buttonPressed(input, Input::BUTTON_B)) {
|
|
|
|
this->state = STATE_DUMP_PARTITION_CONTENTS;
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
proccessMenuNavigationX(input, 2);
|
|
|
|
if (buttonPressed(input, Input::BUTTON_A)) {
|
|
|
|
if (selectedOptionX == 0) {
|
|
|
|
this->state = STATE_DUMP_PARTITION_CONTENTS;
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
} else {
|
|
|
|
return ApplicationState::SUBSTATE_RETURN;
|
|
|
|
}
|
|
|
|
}
|
2021-10-15 15:41:16 +02:00
|
|
|
} else if (state == STATE_DUMP_DONE) {
|
|
|
|
if (entrySelected(input)) {
|
|
|
|
return ApplicationState::SUBSTATE_RETURN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ApplicationState::SUBSTATE_RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GMPartitionsDumperState::setError(GMPartitionsDumperState::eErrorState err) {
|
2022-07-26 08:16:27 +02:00
|
|
|
this->state = STATE_ERROR;
|
2021-10-15 15:41:16 +02:00
|
|
|
this->errorState = err;
|
|
|
|
//OSEnableHomeButtonMenu(true);
|
|
|
|
}
|
|
|
|
|
2021-10-16 13:03:32 +02:00
|
|
|
std::string GMPartitionsDumperState::getPathForDevice(eDumpTarget target) const {
|
2022-07-26 08:16:27 +02:00
|
|
|
if (target == TARGET_NTFS) {
|
2021-10-16 13:03:32 +02:00
|
|
|
return "ntfs0:/";
|
|
|
|
}
|
2021-10-16 14:11:28 +02:00
|
|
|
return "fs:/vol/external01/";
|
2021-10-16 13:03:32 +02:00
|
|
|
}
|
|
|
|
|
2021-10-15 15:41:16 +02:00
|
|
|
std::string GMPartitionsDumperState::ErrorMessage() const {
|
|
|
|
if (this->errorState == ERROR_MALLOC_FAILED) {
|
|
|
|
return "ERROR_MALLOC_FAILED";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_NO_DISC_ID) {
|
|
|
|
return "ERROR_NO_DISC_ID";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_READ_FIRST_SECTOR) {
|
|
|
|
return "ERROR_READ_FIRST_SECTOR";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_OPEN_ODD1) {
|
|
|
|
return "ERROR_OPEN_ODD1";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_PARSE_DISCHEADER) {
|
|
|
|
return "ERROR_PARSE_DISCHEADER";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_NO_GM_PARTITION) {
|
|
|
|
return "ERROR_NO_GM_PARTITION";
|
|
|
|
}
|
2021-10-16 13:03:32 +02:00
|
|
|
if (this->errorState == ERROR_CREATE_DIR) {
|
|
|
|
return "ERROR_CREATE_DIR";
|
|
|
|
}
|
2021-10-15 15:41:16 +02:00
|
|
|
if (this->errorState == ERROR_FAILED_TO_GET_NUSTITLE) {
|
|
|
|
return "ERROR_FAILED_TO_GET_NUSTITLE";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_TMD) {
|
|
|
|
return "ERROR_FAILED_WRITE_TMD";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_TICKET) {
|
|
|
|
return "ERROR_FAILED_WRITE_TICKET";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_CERT) {
|
|
|
|
return "ERROR_FAILED_WRITE_CERT";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FIND_CONTENT_BY_INDEX) {
|
|
|
|
return "ERROR_FIND_CONTENT_BY_INDEX";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_CREATE_FILE) {
|
|
|
|
return "ERROR_FAILED_CREATE_FILE";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_H3) {
|
|
|
|
return "ERROR_FAILED_WRITE_H3";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_READ_CONTENT) {
|
|
|
|
return "ERROR_READ_CONTENT";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_WRITE_CONTENT) {
|
|
|
|
return "ERROR_WRITE_CONTENT";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_MALLOC_FAILED) {
|
|
|
|
return "ERROR_MALLOC_FAILED";
|
|
|
|
}
|
|
|
|
return "UNKNOWN_ERROR";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GMPartitionsDumperState::ErrorDescription() const {
|
|
|
|
if (this->errorState == ERROR_MALLOC_FAILED) {
|
|
|
|
return "ERROR_MALLOC_FAILED";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_NO_DISC_ID) {
|
|
|
|
return "ERROR_NO_DISC_ID";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_READ_FIRST_SECTOR) {
|
|
|
|
return "ERROR_READ_FIRST_SECTOR";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_OPEN_ODD1) {
|
|
|
|
return "ERROR_OPEN_ODD1";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_PARSE_DISCHEADER) {
|
|
|
|
return "ERROR_PARSE_DISCHEADER";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_NO_GM_PARTITION) {
|
|
|
|
return "ERROR_NO_GM_PARTITION";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_TO_GET_NUSTITLE) {
|
|
|
|
return "ERROR_FAILED_TO_GET_NUSTITLE";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_TMD) {
|
|
|
|
return "ERROR_FAILED_WRITE_TMD";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_TICKET) {
|
|
|
|
return "ERROR_FAILED_WRITE_TICKET";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_CERT) {
|
|
|
|
return "ERROR_FAILED_WRITE_CERT";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FIND_CONTENT_BY_INDEX) {
|
|
|
|
return "ERROR_FIND_CONTENT_BY_INDEX";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_CREATE_FILE) {
|
|
|
|
return "ERROR_FAILED_CREATE_FILE";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_FAILED_WRITE_H3) {
|
|
|
|
return "ERROR_FAILED_WRITE_H3";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_READ_CONTENT) {
|
|
|
|
return "ERROR_READ_CONTENT";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_WRITE_CONTENT) {
|
|
|
|
return "ERROR_WRITE_CONTENT";
|
|
|
|
}
|
|
|
|
if (this->errorState == ERROR_MALLOC_FAILED) {
|
|
|
|
return "ERROR_MALLOC_FAILED";
|
|
|
|
}
|
|
|
|
return "UNKNOWN_ERROR";
|
|
|
|
}
|