mirror of
https://github.com/wiiu-env/wudd.git
synced 2024-11-22 01:49:15 +01:00
Refactor menus and overall structure. NTFS only
This commit is contained in:
parent
eb74e76edd
commit
47cca1165d
2
Makefile
2
Makefile
@ -43,7 +43,7 @@ INCLUDES := include source
|
||||
#-------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#-------------------------------------------------------------------------------
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
CFLAGS := -g -Wall -O0 -ffunction-sections \
|
||||
$(MACHDEP)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||
|
@ -1,858 +0,0 @@
|
||||
#include "ApplicationState.h"
|
||||
#include "utils/WiiUScreen.h"
|
||||
#include "utils/ScreenUtils.h"
|
||||
#include "common/common.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/StringTools.h"
|
||||
#include "utils/rijndael.h"
|
||||
#include "fs/FSUtils.h"
|
||||
#include <sysapp/launch.h>
|
||||
#include <coreinit/ios.h>
|
||||
#include <iosuhax.h>
|
||||
#include <malloc.h>
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <cstdarg>
|
||||
#include <ntfs.h>
|
||||
#include <WUD/DiscReaderDiscDrive.h>
|
||||
#include <whb/proc.h>
|
||||
#include <coreinit/debug.h>
|
||||
#include <WUD/content/partitions/WiiUGMPartition.h>
|
||||
#include <WUD/header/WiiUDiscHeader.h>
|
||||
#include <WUD/entities/TMD/Content.h>
|
||||
#include <WUD/entities/TMD/TitleMetaData.h>
|
||||
#include <WUD/NUSDataProviderWUD.h>
|
||||
#include <WUD/NUSTitle.h>
|
||||
#include <memory>
|
||||
|
||||
extern ntfs_md *ntfs_mounts;
|
||||
extern int ntfs_mount_count;
|
||||
|
||||
|
||||
unsigned int swap_uint32(unsigned int val) {
|
||||
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
|
||||
return (val << 16) | (val >> 16);
|
||||
}
|
||||
|
||||
unsigned long long swap_uint64(unsigned long long val) {
|
||||
val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);
|
||||
val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);
|
||||
return (val << 32) | (val >> 32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash function used to create a hash of each sector
|
||||
* The hashes are then compared to find duplicate sectors
|
||||
*/
|
||||
void calculateHash256(unsigned char *data, unsigned int length, unsigned char *hashOut) {
|
||||
// cheap and simple hash implementation
|
||||
// you can replace this part with your favorite hash method
|
||||
memset(hashOut, 0x00, 32);
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
hashOut[i % 32] ^= data[i];
|
||||
hashOut[(i + 7) % 32] += data[i];
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationState::ApplicationState() : log("fs:/vol/external01/wudump.log", CFile::WriteOnly) {
|
||||
this->log.fwrite("Started wudump\n");
|
||||
this->state = STATE_WELCOME_SCREEN;
|
||||
}
|
||||
|
||||
void ApplicationState::printHeader() {
|
||||
WiiUScreen::drawLine("Wudump");
|
||||
WiiUScreen::drawLine("==================");
|
||||
WiiUScreen::drawLine("");
|
||||
}
|
||||
|
||||
void ApplicationState::render() {
|
||||
WiiUScreen::clearScreen();
|
||||
|
||||
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 to the Wii U Menu.");
|
||||
} else if (this->state == STATE_WELCOME_SCREEN) {
|
||||
WiiUScreen::drawLine("Welcome to Wudump");
|
||||
WiiUScreen::drawLine("Press A to dump the currently inserted Disc");
|
||||
WiiUScreen::drawLine("");
|
||||
if (this->selectedOption == 0) {
|
||||
WiiUScreen::drawLine("> Dump as WUX Dump as WUD Dump as .app Exit");
|
||||
} else if (this->selectedOption == 1) {
|
||||
WiiUScreen::drawLine(" Dump as WUX > Dump as WUD Dump as .app Exit");
|
||||
} else if (this->selectedOption == 2) {
|
||||
WiiUScreen::drawLine(" Dump as WUX Dump as WUD > Dump as .app Exit");
|
||||
} else if (this->selectedOption == 3) {
|
||||
WiiUScreen::drawLine(" Dump as WUX Dump as WUD Dump as .app > Exit");
|
||||
}
|
||||
} else if (this->state == STATE_CHOOSE_TARGET) {
|
||||
printHeader();
|
||||
WiiUScreen::drawLine("Please choose your target:");
|
||||
std::vector<std::string> options;
|
||||
int32_t targetCount = 0;
|
||||
if (this->dumpFormat == DUMP_AS_APP) {
|
||||
options.emplace_back("SD");
|
||||
targetCount++;
|
||||
}
|
||||
for (int i = 0; i < ntfs_mount_count; i++) {
|
||||
options.emplace_back(ntfs_mounts[i].name);
|
||||
targetCount++;
|
||||
}
|
||||
|
||||
std::string curLine = "";
|
||||
if (this->selectedOption == 0) {
|
||||
curLine = "> Back\t";
|
||||
} else {
|
||||
curLine = " Back\t";
|
||||
}
|
||||
|
||||
if (targetCount == 0) {
|
||||
WiiUScreen::drawLine("Please insert a NTFS formatted USB drive and restart wudump\n");
|
||||
} else {
|
||||
for (int32_t i = 0; i < targetCount; i++) {
|
||||
if (this->selectedOption - 1 == i) {
|
||||
curLine += "> " + options[i];
|
||||
} else {
|
||||
curLine += " " + options[i];
|
||||
}
|
||||
curLine += "\t";
|
||||
}
|
||||
}
|
||||
WiiUScreen::drawLine(curLine.c_str());
|
||||
} else if (this->state == STATE_OPEN_ODD1) {
|
||||
WiiUScreen::drawLine("Open /dev/odd01");
|
||||
} else if (this->state == STATE_READ_DISC_INFO) {
|
||||
WiiUScreen::drawLine("Read disc information");
|
||||
} else if (this->state == STATE_READ_DISC_INFO_DONE) {
|
||||
WiiUScreen::drawLinef("Dumping: %s", this->discId);
|
||||
} else if (this->state == STATE_DUMP_DISC_KEY) {
|
||||
WiiUScreen::drawLinef("Dumping game.key");
|
||||
} else if (this->state == STATE_DUMP_DISC_START || this->state == STATE_DUMP_DISC || this->state == STATE_WAIT_USER_ERROR_CONFIRM) {
|
||||
WiiUScreen::drawLinef("Dumping: %s", this->discId);
|
||||
|
||||
float percent = this->currentSector / (WUD_FILE_SIZE / READ_SECTOR_SIZE * 1.0f) * 100.0f;
|
||||
WiiUScreen::drawLinef("Progress: %0.2f MiB / %5.2f MiB (%2.1f %%)", this->currentSector * (READ_SECTOR_SIZE / 1024.0f / 1024.0f), WUD_FILE_SIZE / 1024.0f / 1024.0f, percent);
|
||||
if (doWUX) {
|
||||
WiiUScreen::drawLinef("Written %0.2f MiB. Compression ratio 1:%0.2f", this->hashMap.size() * (READ_SECTOR_SIZE / 1024.0f / 1024.0f),
|
||||
1.0f / (this->hashMap.size() / (float) this->currentSector));
|
||||
}
|
||||
|
||||
if (this->readResult < 0 || this->oddFd < 0) {
|
||||
WiiUScreen::drawLine();
|
||||
|
||||
if (this->oddFd < 0) {
|
||||
WiiUScreen::drawLine("Failed to open disc, try again.");
|
||||
} else {
|
||||
WiiUScreen::drawLinef("Error: Failed to read sector - Error %d", this->readResult);
|
||||
}
|
||||
WiiUScreen::drawLine();
|
||||
WiiUScreen::drawLine("Press A to skip this sector (will be replaced by 0's)");
|
||||
WiiUScreen::drawLine("Press B to try again");
|
||||
} else {
|
||||
OSTime curTime = OSGetTime();
|
||||
float remaining = (WUD_FILE_SIZE - (READ_SECTOR_SIZE * this->currentSector)) / 1024.0f / 1024.0f;
|
||||
float curSpeed = READ_SECTOR_SIZE * ((this->readSectors / 1000.0f) / OSTicksToMilliseconds(curTime - startTime));
|
||||
int32_t remainingSec = remaining / curSpeed;
|
||||
int32_t minutes = (remainingSec / 60) % 60;
|
||||
int32_t seconds = remainingSec % 60;
|
||||
int32_t hours = remainingSec / 3600;
|
||||
|
||||
WiiUScreen::drawLinef("Speed: %.2f MiB/s ETA: %02dh %02dm %02ds", curSpeed, remaining, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
WiiUScreen::drawLine();
|
||||
if (!this->skippedSectors.empty()) {
|
||||
WiiUScreen::drawLinef("Skipped dumping %d sectors", this->skippedSectors.size());
|
||||
}
|
||||
} else if (this->state == STATE_DUMP_DISC_DONE) {
|
||||
if (!flushWriteCache()) {
|
||||
setError(ERROR_WRITE_FAILED);
|
||||
}
|
||||
WiiUScreen::drawLinef("Dumping done! Press A to continue");
|
||||
} else if (this->state == STATE_DUMP_APP_FILES_DONE) {
|
||||
WiiUScreen::drawLinef("Dumping done! Press A to continue");
|
||||
} else if (this->state == STATE_PLEASE_INSERT_DISC) {
|
||||
WiiUScreen::drawLinef("Please insert a Disc. Press A to continue");
|
||||
}
|
||||
printFooter();
|
||||
WiiUScreen::flipBuffers();
|
||||
}
|
||||
|
||||
void ApplicationState::update(Input *input) {
|
||||
if (this->state == STATE_ERROR) {
|
||||
OSEnableHomeButtonMenu(true);
|
||||
if (entrySelected(input)) {
|
||||
SYSLaunchMenu();
|
||||
}
|
||||
} else if (this->state == STATE_WELCOME_SCREEN) {
|
||||
proccessMenuNavigation(input, 4);
|
||||
if (entrySelected(input)) {
|
||||
if (this->selectedOption == 0) {
|
||||
this->retryCount = 10;
|
||||
this->state = STATE_CHOOSE_TARGET;
|
||||
this->dumpFormat = DUMP_AS_WUX;
|
||||
} else if (this->selectedOption == 1) {
|
||||
this->retryCount = 10;
|
||||
this->state = STATE_CHOOSE_TARGET;
|
||||
this->dumpFormat = DUMP_AS_WUD;
|
||||
} else if (this->selectedOption == 2) {
|
||||
this->retryCount = 10;
|
||||
this->state = STATE_CHOOSE_TARGET;
|
||||
this->dumpFormat = DUMP_AS_APP;
|
||||
} else {
|
||||
SYSLaunchMenu();
|
||||
}
|
||||
this->selectedOption = 0;
|
||||
return;
|
||||
}
|
||||
} else if (this->state == STATE_CHOOSE_TARGET) {
|
||||
WiiUScreen::drawLine("Please choose your target");
|
||||
std::vector<std::string> options;
|
||||
uint32_t targetCount = 0;
|
||||
|
||||
if (this->dumpFormat == DUMP_AS_APP) {
|
||||
options.emplace_back("fs:/vol/external01/");
|
||||
targetCount++;
|
||||
}
|
||||
if (ntfs_mount_count > 0) {
|
||||
|
||||
for (int i = 0; i < ntfs_mount_count; i++) {
|
||||
options.emplace_back(std::string(ntfs_mounts[i].name) + ":/");
|
||||
targetCount++;
|
||||
}
|
||||
}
|
||||
|
||||
proccessMenuNavigation(input, targetCount + 1);
|
||||
if (entrySelected(input)) {
|
||||
if (this->selectedOption == 0) {
|
||||
this->state = STATE_WELCOME_SCREEN;
|
||||
} else if (targetCount > 0) {
|
||||
target = options[selectedOption - 1];
|
||||
this->state = STATE_OPEN_ODD1;
|
||||
}
|
||||
this->selectedOption = 0;
|
||||
}
|
||||
} else if (this->state == STATE_OPEN_ODD1) {
|
||||
if (this->readSectors > 0) {
|
||||
auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &(this->oddFd));
|
||||
if (ret >= 0) {
|
||||
// continue!
|
||||
this->state = STATE_DUMP_DISC;
|
||||
} else {
|
||||
this->oddFd = -1;
|
||||
this->state = STATE_WAIT_USER_ERROR_CONFIRM;
|
||||
}
|
||||
return;
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("STATE_OPEN_ODD1");
|
||||
if (this->retryCount-- <= 0) {
|
||||
this->state = STATE_PLEASE_INSERT_DISC;
|
||||
return;
|
||||
}
|
||||
auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &(this->oddFd));
|
||||
if (ret >= 0) {
|
||||
if (this->sectorBuf == nullptr) {
|
||||
this->sectorBuf = (void *) memalign(0x100, this->sectorBufSize);
|
||||
if (this->sectorBuf == nullptr) {
|
||||
this->setError(ERROR_MALLOC_FAILED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Opened /dev/odd01 %d", this->oddFd);
|
||||
this->state = STATE_READ_DISC_INFO;
|
||||
}
|
||||
} else if (this->state == STATE_READ_DISC_INFO) {
|
||||
DEBUG_FUNCTION_LINE("STATE_READ_DISC_INFO");
|
||||
if (IOSUHAX_FSA_RawRead(gFSAfd, this->sectorBuf, READ_SECTOR_SIZE, 1, 0, this->oddFd) >= 0) {
|
||||
this->discId[10] = '\0';
|
||||
memcpy(this->discId, sectorBuf, 10);
|
||||
if (this->discId[0] == 0) {
|
||||
setError(ERROR_NO_DISC_ID);
|
||||
return;
|
||||
}
|
||||
this->state = STATE_READ_DISC_INFO_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
this->setError(ERROR_READ_FIRST_SECTOR);
|
||||
return;
|
||||
} else if (this->state == STATE_READ_DISC_INFO_DONE) {
|
||||
DEBUG_FUNCTION_LINE("STATE_READ_DISC_INFO_DONE");
|
||||
this->state = STATE_DUMP_DISC_KEY;
|
||||
} else if (this->state == STATE_DUMP_DISC_KEY) {
|
||||
DEBUG_FUNCTION_LINE("STATE_DUMP_DISC_KEY");
|
||||
|
||||
auto res = IOSUHAX_FSA_RawRead(gFSAfd, this->sectorBuf, READ_SECTOR_SIZE, 1, 3, this->oddFd);
|
||||
uint8_t discKey[16];
|
||||
bool hasDiscKey = false;
|
||||
if (res >= 0) {
|
||||
if (((uint32_t *) this->sectorBuf)[0] != 0xCCA6E67B) {
|
||||
auto discKeyRes = IOSUHAX_ODM_GetDiscKey(discKey);
|
||||
if (discKeyRes >= 0) {
|
||||
hasDiscKey = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDiscKey) {
|
||||
if (!FSUtils::CreateSubfolder(StringTools::fmt("%swudump/%s", target.c_str(), discId))) {
|
||||
setError(ERROR_WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
if (!FSUtils::saveBufferToFile(StringTools::fmt("%swudump/%s/game.key", target.c_str(), discId), discKey, 16)) {
|
||||
setError(ERROR_WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this->dumpFormat == DUMP_AS_WUX || this->dumpFormat == DUMP_AS_WUD) {
|
||||
if (this->dumpFormat == DUMP_AS_WUX) {
|
||||
this->doWUX = true;
|
||||
}
|
||||
this->state = STATE_DUMP_DISC_START;
|
||||
} else {
|
||||
this->state = STATE_DUMP_APP_FILES;
|
||||
}
|
||||
} else if (this->state == STATE_DUMP_APP_FILES) {
|
||||
ApplicationState::dumpAppFiles();
|
||||
if (this->state != STATE_ERROR) {
|
||||
this->state = STATE_DUMP_APP_FILES_DONE;
|
||||
}
|
||||
} else if (this->state == STATE_DUMP_APP_FILES_DONE) {
|
||||
if (entrySelected(input)) {
|
||||
this->state = STATE_WELCOME_SCREEN;
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd);
|
||||
this->oddFd = -1;
|
||||
}
|
||||
this->currentSector = 0;
|
||||
this->readSectors = 0;
|
||||
}
|
||||
} else if (this->state == STATE_DUMP_DISC_START) {
|
||||
ApplicationState::clearWriteCache();
|
||||
DEBUG_FUNCTION_LINE("STATE_DUMP_DISC_START");
|
||||
if (!FSUtils::CreateSubfolder(StringTools::fmt("%swudump/%s", target.c_str(), discId))) {
|
||||
setError(ERROR_WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
this->fileHandle = new CFile(StringTools::fmt("%swudump/%s/game.%s", target.c_str(), discId, doWUX ? "wux" : "wud"), CFile::WriteOnly);
|
||||
|
||||
this->totalSectorCount = WUD_FILE_SIZE / SECTOR_SIZE;
|
||||
|
||||
if (!this->fileHandle->isOpen()) {
|
||||
DEBUG_FUNCTION_LINE("Failed to open file");
|
||||
this->setError(ERROR_FILE_OPEN_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (doWUX) {
|
||||
wuxHeader_t wuxHeader = {0};
|
||||
wuxHeader.magic0 = WUX_MAGIC_0;
|
||||
wuxHeader.magic1 = WUX_MAGIC_1;
|
||||
wuxHeader.sectorSize = swap_uint32(SECTOR_SIZE);
|
||||
wuxHeader.uncompressedSize = swap_uint64(WUD_FILE_SIZE);
|
||||
wuxHeader.flags = 0;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Write header");
|
||||
this->fileHandle->write((uint8_t *) &wuxHeader, sizeof(wuxHeader_t));
|
||||
this->sectorTableStart = this->fileHandle->tell();
|
||||
|
||||
this->sectorIndexTable = (void *) malloc(totalSectorCount * 4);
|
||||
if (sectorIndexTable == nullptr) {
|
||||
this->setError(ERROR_MALLOC_FAILED);
|
||||
return;
|
||||
}
|
||||
memset(this->sectorIndexTable, 0, totalSectorCount * 4);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Write empty sectorIndexTable");
|
||||
this->fileHandle->write((uint8_t *) this->sectorIndexTable, totalSectorCount * 4);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Get sector table end");
|
||||
this->sectorTableEnd = this->fileHandle->tell();
|
||||
uint64_t tableEnd = this->sectorTableEnd;
|
||||
|
||||
this->sectorTableEnd += SECTOR_SIZE - 1;
|
||||
this->sectorTableEnd -= (this->sectorTableEnd % SECTOR_SIZE);
|
||||
|
||||
uint64_t padding = this->sectorTableEnd - tableEnd;
|
||||
auto *paddingData = (uint8_t *) malloc(padding);
|
||||
memset(paddingData, 0, padding);
|
||||
this->fileHandle->write(reinterpret_cast<const uint8_t *>(paddingData), padding);
|
||||
free(paddingData);
|
||||
this->hashMap.clear();
|
||||
}
|
||||
|
||||
this->writeBufferSize = READ_SECTOR_SIZE * WRITE_BUFFER_NUM_SECTORS;
|
||||
this->writeBuffer = (void *) memalign(0x1000, this->writeBufferSize);
|
||||
if (this->writeBuffer == nullptr) {
|
||||
this->setError(ERROR_MALLOC_FAILED);
|
||||
return;
|
||||
}
|
||||
this->writeBufferPos = 0;
|
||||
|
||||
this->startTime = OSGetTime();
|
||||
|
||||
this->state = STATE_DUMP_DISC;
|
||||
this->currentSector = 0;
|
||||
this->retryCount = 10;
|
||||
this->selectedOption = 0;
|
||||
this->readSectors = 0;
|
||||
} else if (this->state == STATE_DUMP_DISC) {
|
||||
//DEBUG_FUNCTION_LINE("STATE_DUMP_DISC");
|
||||
int32_t numSectors = this->currentSector + READ_NUM_SECTORS > this->totalSectorCount ? this->totalSectorCount - this->currentSector : READ_NUM_SECTORS;
|
||||
if ((this->readResult = IOSUHAX_FSA_RawRead(gFSAfd, sectorBuf, READ_SECTOR_SIZE, numSectors, this->currentSector, this->oddFd)) >= 0) {
|
||||
if (!writeDataToFile(this->sectorBuf, numSectors)) {
|
||||
this->setError(ERROR_WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
//DEBUG_FUNCTION_LINE("Read done %lld %lld", this->currentSector, this->totalSectorCount);
|
||||
this->retryCount = 10;
|
||||
if (this->currentSector >= this->totalSectorCount) {
|
||||
this->state = STATE_DUMP_DISC_DONE;
|
||||
|
||||
if (this->fileHandle->isOpen()) {
|
||||
if (!this->flushWriteCache()) {
|
||||
this->setError(ERROR_WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
if (doWUX) {
|
||||
this->writeSectorIndexTable();
|
||||
}
|
||||
this->fileHandle->close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this->state = STATE_WAIT_USER_ERROR_CONFIRM;
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd);
|
||||
this->oddFd = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (this->state == STATE_WAIT_USER_ERROR_CONFIRM) {
|
||||
if (this->autoSkip) {
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd);
|
||||
this->oddFd = -1;
|
||||
}
|
||||
}
|
||||
if (this->autoSkip || (input->data.buttons_d & Input::BUTTON_A)) {
|
||||
this->log.fwrite("Skipped sector %d : 0x%ll016X-0x%ll016X, filled with 0's\n", this->currentSector, this->currentSector * READ_SECTOR_SIZE, (this->currentSector + 1) * READ_SECTOR_SIZE);
|
||||
this->state = STATE_OPEN_ODD1;
|
||||
this->skippedSectors.push_back(this->currentSector);
|
||||
// We can't use seek because we may have cached values.
|
||||
|
||||
if (this->emptySector == nullptr) {
|
||||
this->emptySector = memalign(0x100, READ_SECTOR_SIZE);
|
||||
if (this->emptySector == nullptr) {
|
||||
this->setError(ERROR_MALLOC_FAILED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->writeCached(reinterpret_cast<uint32_t>(emptySector), READ_SECTOR_SIZE)) {
|
||||
this->setError(ERROR_WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
this->currentSector += 1;
|
||||
this->readResult = 0;
|
||||
} else if (input->data.buttons_d & Input::BUTTON_B) {
|
||||
this->state = STATE_OPEN_ODD1;
|
||||
this->readResult = 0;
|
||||
} else if (input->data.buttons_d & Input::BUTTON_Y) {
|
||||
this->autoSkip = true;
|
||||
}
|
||||
} else if (this->state == STATE_DUMP_DISC_DONE) {
|
||||
if (entrySelected(input)) {
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd);
|
||||
this->oddFd = -1;
|
||||
}
|
||||
this->state = STATE_WELCOME_SCREEN;
|
||||
this->selectedOption = 0;
|
||||
this->currentSector = 0;
|
||||
this->readSectors = 0;
|
||||
this->writtenSector = 0;
|
||||
return;
|
||||
}
|
||||
} else if (this->state == STATE_PLEASE_INSERT_DISC) {
|
||||
if (entrySelected(input)) {
|
||||
this->state = STATE_WELCOME_SCREEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ApplicationState::ErrorMessage() {
|
||||
if (this->error == ERROR_NONE) {
|
||||
return "NONE";
|
||||
} else if (this->error == ERROR_IOSUHAX_FAILED) {
|
||||
return "ERROR_IOSUHAX_FAILED";
|
||||
} else if (this->error == ERROR_MALLOC_FAILED) {
|
||||
return "ERROR_MALLOC_FAILED";
|
||||
} else if (this->error == ERROR_FILE_OPEN_FAILED) {
|
||||
return "ERROR_FILE_OPEN_FAILED";
|
||||
} else if (this->error == ERROR_NO_DISC_ID) {
|
||||
return "ERROR_NO_DISC_ID";
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Error: %d", this->error);
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
||||
|
||||
std::string ApplicationState::ErrorDescription() {
|
||||
if (this->error == ERROR_NONE) {
|
||||
return "-";
|
||||
} else if (this->error == ERROR_IOSUHAX_FAILED) {
|
||||
return "Failed to init IOSUHAX.";
|
||||
} else if (this->error == ERROR_MALLOC_FAILED) {
|
||||
return "Failed to allocate data.";
|
||||
} else if (this->error == ERROR_FILE_OPEN_FAILED) {
|
||||
return "Failed to create file";
|
||||
} else if (this->error == ERROR_NO_DISC_ID) {
|
||||
return "Failed to get the disc id";
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Error: %d", this->error);
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
||||
|
||||
void ApplicationState::setError(eErrorState err) {
|
||||
this->state = STATE_ERROR;
|
||||
this->error = err;
|
||||
//OSEnableHomeButtonMenu(true);
|
||||
}
|
||||
|
||||
void ApplicationState::printFooter() {
|
||||
ScreenUtils::printTextOnScreen(CONSOLE_SCREEN_TV, 0, 27, "By Maschell");
|
||||
ScreenUtils::printTextOnScreen(CONSOLE_SCREEN_DRC, 0, 17, "By Maschell");
|
||||
}
|
||||
|
||||
void ApplicationState::proccessMenuNavigation(Input *input, int maxOptionValue) {
|
||||
if (input->data.buttons_d & Input::BUTTON_LEFT) {
|
||||
this->selectedOption--;
|
||||
} else if (input->data.buttons_d & Input::BUTTON_RIGHT) {
|
||||
this->selectedOption++;
|
||||
}
|
||||
if (this->selectedOption < 0) {
|
||||
this->selectedOption = maxOptionValue;
|
||||
} else if (this->selectedOption >= maxOptionValue) {
|
||||
this->selectedOption = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplicationState::entrySelected(Input *input) {
|
||||
return input->data.buttons_d & Input::BUTTON_A;
|
||||
}
|
||||
|
||||
ApplicationState::~ApplicationState() {
|
||||
this->log.close();
|
||||
if (this->fileHandle->isOpen()) {
|
||||
if (!this->flushWriteCache()) {
|
||||
|
||||
}
|
||||
if (doWUX) {
|
||||
this->writeSectorIndexTable();
|
||||
}
|
||||
this->fileHandle->close();
|
||||
}
|
||||
if (this->emptySector != nullptr) {
|
||||
free(this->emptySector);
|
||||
this->emptySector = nullptr;
|
||||
}
|
||||
if (this->writeBuffer != nullptr) {
|
||||
free(this->writeBuffer);
|
||||
this->writeBuffer = nullptr;
|
||||
}
|
||||
if (this->sectorIndexTable != nullptr) {
|
||||
free(this->sectorIndexTable);
|
||||
this->sectorIndexTable = nullptr;
|
||||
}
|
||||
if (this->sectorBuf != nullptr) {
|
||||
free(this->sectorBuf);
|
||||
this->sectorBuf = nullptr;
|
||||
}
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd);
|
||||
this->oddFd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplicationState::writeDataToFile(void *buffer, int numberOfSectors) {
|
||||
if (!doWUX) {
|
||||
if (!writeCached(reinterpret_cast<uint32_t>(buffer), numberOfSectors * READ_SECTOR_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
this->currentSector += numberOfSectors;
|
||||
this->readSectors += numberOfSectors;
|
||||
} else {
|
||||
char hashOut[32];
|
||||
for (int i = 0; i < numberOfSectors; i++) {
|
||||
uint32_t addr = ((uint32_t) buffer) + (i * READ_SECTOR_SIZE);
|
||||
calculateHash256(reinterpret_cast<unsigned char *>(addr), READ_SECTOR_SIZE, reinterpret_cast<unsigned char *>(hashOut));
|
||||
char tmp[34];
|
||||
auto *test = (uint32_t *) hashOut;
|
||||
snprintf(tmp, 33, "%08X%08X%08X%08X", test[0], test[1], test[2], test[3]);
|
||||
std::string hash(tmp);
|
||||
|
||||
auto *indexTable = (uint32_t *) this->sectorIndexTable;
|
||||
|
||||
auto it = hashMap.find(hash);
|
||||
if (it != hashMap.end()) {
|
||||
indexTable[this->currentSector] = swap_uint32(this->hashMap[hash]);
|
||||
} else {
|
||||
indexTable[this->currentSector] = swap_uint32(this->writtenSector);
|
||||
hashMap[hash] = this->writtenSector;
|
||||
if (this->fileHandle->isOpen()) {
|
||||
if (!writeCached(addr, READ_SECTOR_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->writtenSector++;
|
||||
}
|
||||
this->currentSector++;
|
||||
this->readSectors++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApplicationState::writeCached(uint32_t addr, uint32_t writeSize) {
|
||||
if (writeSize == this->writeBufferSize) {
|
||||
if (!this->flushWriteCache()) {
|
||||
return false;
|
||||
}
|
||||
int32_t res = this->fileHandle->write(reinterpret_cast<const uint8_t *>(addr), writeSize);
|
||||
return res >= 0;
|
||||
}
|
||||
|
||||
uint32_t toWrite = writeSize;
|
||||
if (toWrite == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t written = 0;
|
||||
|
||||
do {
|
||||
uint32_t curWrite = toWrite;
|
||||
|
||||
if (this->writeBufferPos + curWrite > this->writeBufferSize) {
|
||||
curWrite = this->writeBufferSize - this->writeBufferPos;
|
||||
}
|
||||
OSBlockMove((void *) (((uint32_t) this->writeBuffer) + this->writeBufferPos), (void *) (addr + written), curWrite, 1);
|
||||
this->writeBufferPos += curWrite;
|
||||
|
||||
if (this->writeBufferPos == this->writeBufferSize) {
|
||||
if (!flushWriteCache()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
toWrite -= curWrite;
|
||||
written += curWrite;
|
||||
} while (toWrite > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApplicationState::flushWriteCache() {
|
||||
if (this->writeBufferPos > 0) {
|
||||
int32_t res = this->fileHandle->write(static_cast<const uint8_t *>(this->writeBuffer), this->writeBufferPos);
|
||||
if (res < 0) {
|
||||
return false;
|
||||
}
|
||||
this->writeBufferPos = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApplicationState::clearWriteCache() {
|
||||
this->writeBufferPos = 0;
|
||||
}
|
||||
|
||||
void ApplicationState::writeSectorIndexTable() {
|
||||
if (this->fileHandle->isOpen() && doWUX) {
|
||||
this->fileHandle->seek(this->sectorTableStart, SEEK_SET);
|
||||
this->fileHandle->write((uint8_t *) this->sectorIndexTable, totalSectorCount * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationState::printDumpState(const char *fmt, ...) {
|
||||
WiiUScreen::clearScreen();
|
||||
ApplicationState::printHeader();
|
||||
char *buf = (char *) MEMAllocFromDefaultHeapEx(PRINTF_BUFFER_LENGTH, 4);
|
||||
va_list va;
|
||||
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(va, fmt);
|
||||
vsnprintf(buf, PRINTF_BUFFER_LENGTH, fmt, va);
|
||||
|
||||
WiiUScreen::drawLine(buf);
|
||||
|
||||
MEMFreeToDefaultHeap(buf);
|
||||
va_end(va);
|
||||
ApplicationState::printFooter();
|
||||
WiiUScreen::flipBuffers();
|
||||
}
|
||||
|
||||
void ApplicationState::dumpAppFiles() {
|
||||
uint8_t opt[0x400];
|
||||
IOSUHAX_read_otp(opt, 0x400);
|
||||
std::array<uint8_t, 0x10> cKey{};
|
||||
memcpy(cKey.data(), opt + 0xE0, 0x10);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Reading Partitions");
|
||||
|
||||
printDumpState("Reading Partitions...");
|
||||
|
||||
auto discReader = std::make_shared<DiscReaderDiscDrive>();
|
||||
if (!discReader->IsReady()) {
|
||||
DEBUG_FUNCTION_LINE("!IsReady");
|
||||
this->setError(ERROR_OPEN_ODD1);
|
||||
return;
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Read DiscHeader");
|
||||
auto discHeader = WiiUDiscHeader::make_unique(discReader);
|
||||
if (!discHeader.has_value()) {
|
||||
DEBUG_FUNCTION_LINE("Failed to read discheader");
|
||||
return;
|
||||
}
|
||||
bool forceExit = false;
|
||||
for (auto &partition: discHeader.value()->wiiUContentsInformation->partitions->partitions) {
|
||||
auto gmPartition = std::dynamic_pointer_cast<WiiUGMPartition>(partition);
|
||||
if (gmPartition != nullptr) {
|
||||
auto nusTitleOpt = NUSTitle::loadTitleFromGMPartition(gmPartition, discReader, cKey);
|
||||
if (!nusTitleOpt.has_value()) {
|
||||
DEBUG_FUNCTION_LINE("nusTitle was null");
|
||||
continue;
|
||||
}
|
||||
auto nusTitle = nusTitleOpt.value();
|
||||
auto dataProvider = nusTitle->dataProcessor->getDataProvider();
|
||||
|
||||
uint64_t partitionSize = 0;
|
||||
uint64_t partitionSizeWritten = 0;
|
||||
for (auto &content: nusTitle->tmd->contentList) {
|
||||
partitionSize += ROUNDUP(content->encryptedFileSize, 16);
|
||||
}
|
||||
|
||||
auto partitionDumpInfo = StringTools::strfmt("Partition: %s\n\tProgress: %.2f MiB / %.2f MiB\n", partition->getVolumeId().c_str(), partitionSizeWritten / 1024.0f / 1024.0f,
|
||||
partitionSize / 1024.0f / 1024.0f);
|
||||
printDumpState("%s", partitionDumpInfo.c_str());
|
||||
|
||||
char buffer[512];
|
||||
snprintf(buffer, 500, "%swudump/%s/%s", target.c_str(), this->discId, gmPartition->getVolumeId().c_str());
|
||||
FSUtils::CreateSubfolder(buffer);
|
||||
|
||||
std::vector<uint8_t> wBuffer;
|
||||
if (dataProvider->getRawTMD(wBuffer)) {
|
||||
std::string fileName = std::string(buffer).append("/").append(WUD_TMD_FILENAME);
|
||||
printDumpState("%s\nSaving %s", partitionDumpInfo.c_str(), WUD_TMD_FILENAME);
|
||||
FSUtils::saveBufferToFile(fileName.c_str(), wBuffer.data(), wBuffer.size());
|
||||
wBuffer.clear();
|
||||
}
|
||||
|
||||
if (dataProvider->getRawTicket(wBuffer)) {
|
||||
std::string fileName = std::string(buffer).append("/").append(WUD_TICKET_FILENAME);
|
||||
printDumpState("%s\nSaving %s", partitionDumpInfo.c_str(), WUD_TICKET_FILENAME);
|
||||
FSUtils::saveBufferToFile(fileName.c_str(), wBuffer.data(), wBuffer.size());
|
||||
wBuffer.clear();
|
||||
}
|
||||
|
||||
if (dataProvider->getRawCert(wBuffer)) {
|
||||
std::string fileName = std::string(buffer).append("/").append(WUD_TICKET_FILENAME);
|
||||
printDumpState("%s\nSaving %s", partitionDumpInfo.c_str(), WUD_CERT_FILENAME);
|
||||
FSUtils::saveBufferToFile(fileName.c_str(), wBuffer.data(), wBuffer.size());
|
||||
wBuffer.clear();
|
||||
}
|
||||
|
||||
auto contentCount = nusTitle->tmd->contentList.size();
|
||||
auto contentI = 1;
|
||||
|
||||
for (auto &content: nusTitle->tmd->contentList) {
|
||||
char bufApp[32];
|
||||
snprintf(bufApp, 31, "%08X.app", content->ID);
|
||||
std::string appFileName = std::string(buffer) + "/" + bufApp;
|
||||
|
||||
partitionDumpInfo = StringTools::strfmt("Partition: %s\n\tProgress: %.2f MiB / %.2f MiB\n", partition->getVolumeId().c_str(), partitionSizeWritten / 1024.0f / 1024.0f,
|
||||
partitionSize / 1024.0f / 1024.0f);
|
||||
auto contentDumpInfo = StringTools::strfmt("Saving %s (Content %02d/%02d)\n", bufApp, contentI, contentCount);
|
||||
|
||||
printDumpState("%s\n%s", partitionDumpInfo.c_str(), contentDumpInfo.c_str());
|
||||
|
||||
uint32_t bufferSize = READ_NUM_SECTORS * READ_SECTOR_SIZE * 2;
|
||||
|
||||
auto *readBuffer = (uint8_t *) malloc(bufferSize);
|
||||
if (readBuffer == nullptr) {
|
||||
DEBUG_FUNCTION_LINE("Failed to alloc buffer");
|
||||
continue;
|
||||
}
|
||||
|
||||
CFile file(appFileName, CFile::WriteOnly);
|
||||
if (!file.isOpen()) {
|
||||
free(readBuffer);
|
||||
continue;
|
||||
}
|
||||
uint32_t readSoFar = 0;
|
||||
uint64_t curOffset = 0;
|
||||
uint32_t size = ROUNDUP(content->encryptedFileSize, 16);
|
||||
OSTime startTimeApp = OSGetTime();
|
||||
do {
|
||||
if (!WHBProcIsRunning()) {
|
||||
forceExit = true;
|
||||
break;
|
||||
}
|
||||
startTimeApp = OSGetTime();
|
||||
WiiUScreen::clearScreen();
|
||||
|
||||
uint32_t toRead = size - readSoFar;
|
||||
if (toRead > bufferSize) {
|
||||
toRead = bufferSize;
|
||||
}
|
||||
|
||||
if (!dataProvider->readRawContent(content, readBuffer, curOffset, toRead)) {
|
||||
DEBUG_FUNCTION_LINE("Failed to read content");
|
||||
this->setError(ERROR_WRITE_FAILED);
|
||||
forceExit = true;
|
||||
break;
|
||||
}
|
||||
if (file.write((const uint8_t *) readBuffer, toRead) != (int32_t) toRead) {
|
||||
DEBUG_FUNCTION_LINE("Failed to write");
|
||||
break;
|
||||
}
|
||||
|
||||
OSTime curTime = OSGetTime();
|
||||
auto curSpeed = (float) toRead / (float) OSTicksToMilliseconds(curTime - startTimeApp);
|
||||
|
||||
readSoFar += toRead;
|
||||
curOffset += toRead;
|
||||
|
||||
partitionSizeWritten += toRead;
|
||||
|
||||
partitionDumpInfo = StringTools::strfmt("Partition: %s\n\tProgress: %.2f MiB / %.2f MiB\n", partition->getVolumeId().c_str(), partitionSizeWritten / 1024.0f / 1024.0f,
|
||||
partitionSize / 1024.0f / 1024.0f);
|
||||
printDumpState("%s\n%s\tProgress: %.2f MiB / %.2f MiB (%0.2f%%)\n\tSpeed: %0.2f MiB/s", partitionDumpInfo.c_str(), contentDumpInfo.c_str(), readSoFar / 1024.0f / 1024.0f,
|
||||
size / 1024.0f / 1024.0f, ((readSoFar * 1.0f) / size) * 100.0f, curSpeed / 1024.0f);
|
||||
} while (readSoFar < size);
|
||||
|
||||
file.close();
|
||||
|
||||
if (forceExit) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> h3Data;
|
||||
if (dataProvider->getContentH3Hash(content, h3Data)) {
|
||||
char bufh3[32];
|
||||
snprintf(bufh3, 31, "%08X.h3", content->ID);
|
||||
std::string h3FileName = std::string(buffer) + "/" + bufh3;
|
||||
printDumpState("%s\n%s", partitionDumpInfo.c_str(), contentDumpInfo.c_str());
|
||||
FSUtils::saveBufferToFile(h3FileName.c_str(), h3Data.data(), h3Data.size());
|
||||
}
|
||||
contentI++;
|
||||
}
|
||||
if (forceExit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,140 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <coreinit/time.h>
|
||||
#include "input/Input.h"
|
||||
#include "fs/CFile.hpp"
|
||||
|
||||
#define SECTOR_SIZE 0x8000
|
||||
#define READ_SECTOR_SIZE SECTOR_SIZE
|
||||
#define READ_NUM_SECTORS 128
|
||||
#define WRITE_BUFFER_NUM_SECTORS 128
|
||||
#define WUD_FILE_SIZE 0x5D3A00000L
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic0;
|
||||
unsigned int magic1;
|
||||
unsigned int sectorSize;
|
||||
unsigned long long uncompressedSize;
|
||||
unsigned int flags;
|
||||
} wuxHeader_t;
|
||||
|
||||
#define WUX_MAGIC_0 0x57555830
|
||||
#define WUX_MAGIC_1 swap_uint32(0x1099d02e)
|
||||
#include <utils/WiiUScreen.h>
|
||||
#include <utils/ScreenUtils.h>
|
||||
|
||||
class ApplicationState {
|
||||
public:
|
||||
|
||||
enum eDumpTargetFormat {
|
||||
DUMP_AS_WUX,
|
||||
DUMP_AS_WUD,
|
||||
DUMP_AS_APP,
|
||||
enum eSubState {
|
||||
SUBSTATE_RUNNING,
|
||||
SUBSTATE_RETURN,
|
||||
};
|
||||
|
||||
virtual ~ApplicationState() = default;
|
||||
|
||||
enum eErrorState {
|
||||
ERROR_NONE,
|
||||
ERROR_IOSUHAX_FAILED,
|
||||
ERROR_OPEN_ODD1,
|
||||
ERROR_READ_FIRST_SECTOR,
|
||||
ERROR_FILE_OPEN_FAILED,
|
||||
ERROR_MALLOC_FAILED,
|
||||
ERROR_NO_DISC_ID,
|
||||
ERROR_SECTOR_SIZE,
|
||||
ERROR_MAGIC_NUMBER_WRONG,
|
||||
ERROR_WRITE_FAILED,
|
||||
};
|
||||
virtual void render() = 0;
|
||||
|
||||
enum eGameState {
|
||||
STATE_ERROR,
|
||||
STATE_WELCOME_SCREEN,
|
||||
STATE_CHOOSE_TARGET,
|
||||
STATE_OPEN_ODD1,
|
||||
STATE_PLEASE_INSERT_DISC,
|
||||
STATE_DUMP_APP_FILES,
|
||||
STATE_DUMP_APP_FILES_DONE,
|
||||
STATE_READ_DISC_INFO,
|
||||
STATE_READ_DISC_INFO_DONE,
|
||||
STATE_DUMP_DISC_KEY,
|
||||
STATE_DUMP_DISC_START,
|
||||
STATE_DUMP_DISC_DONE,
|
||||
STATE_WAIT_USER_ERROR_CONFIRM,
|
||||
STATE_DUMP_DISC,
|
||||
};
|
||||
virtual eSubState update(Input *input) = 0;
|
||||
|
||||
ApplicationState();
|
||||
virtual void proccessMenuNavigation(Input *input, int32_t maxOptionValue) {
|
||||
if (input->data.buttons_d & Input::BUTTON_UP) {
|
||||
this->selectedOption--;
|
||||
} else if (input->data.buttons_d & Input::BUTTON_DOWN) {
|
||||
this->selectedOption++;
|
||||
}
|
||||
if (this->selectedOption < 0) {
|
||||
this->selectedOption = maxOptionValue;
|
||||
} else if (this->selectedOption >= maxOptionValue) {
|
||||
this->selectedOption = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~ApplicationState();
|
||||
virtual bool entrySelected(Input *input) {
|
||||
return input->data.buttons_d & Input::BUTTON_A;
|
||||
}
|
||||
|
||||
void setError(eErrorState error);
|
||||
virtual void printHeader() {
|
||||
WiiUScreen::drawLine("Wudump");
|
||||
WiiUScreen::drawLine("==================");
|
||||
WiiUScreen::drawLine("");
|
||||
}
|
||||
|
||||
void render();
|
||||
virtual void printFooter() {
|
||||
ScreenUtils::printTextOnScreen(CONSOLE_SCREEN_TV, 0, 27, "By Maschell");
|
||||
ScreenUtils::printTextOnScreen(CONSOLE_SCREEN_DRC, 0, 17, "By Maschell");
|
||||
}
|
||||
|
||||
void update(Input *input);
|
||||
|
||||
std::string ErrorMessage();
|
||||
|
||||
std::string ErrorDescription();
|
||||
|
||||
int selectedOption;
|
||||
|
||||
static void printFooter();
|
||||
|
||||
void proccessMenuNavigation(Input *input, int maxOptionValue);
|
||||
|
||||
static bool entrySelected(Input *input);
|
||||
|
||||
private:
|
||||
static void printHeader();
|
||||
|
||||
CFile log;
|
||||
eGameState state;
|
||||
eDumpTargetFormat dumpFormat;
|
||||
eErrorState error = ERROR_NONE;
|
||||
std::string target = "fs:/vol/external01/";
|
||||
int oddFd = -1;
|
||||
int retryCount = 0;
|
||||
void *sectorBuf = nullptr;
|
||||
int sectorBufSize = READ_NUM_SECTORS * READ_SECTOR_SIZE;
|
||||
char discId[11];
|
||||
uint64_t currentSector = 0;
|
||||
std::vector<uint64_t> skippedSectors;
|
||||
int readResult = 0;
|
||||
|
||||
[[nodiscard]] bool writeDataToFile(void *buffer, int numberOfSection);
|
||||
|
||||
uint64_t totalSectorCount = 0;
|
||||
|
||||
std::map<std::string, int32_t> hashMap;
|
||||
CFile *fileHandle;
|
||||
OSTime startTime;
|
||||
void *writeBuffer = nullptr;
|
||||
uint32_t writeBufferPos = 0;
|
||||
uint32_t writeBufferSize = 0;
|
||||
|
||||
[[nodiscard]] bool writeCached(uint32_t addr, uint32_t writeSize);
|
||||
|
||||
void clearWriteCache();
|
||||
|
||||
[[nodiscard]] bool flushWriteCache();
|
||||
|
||||
uint32_t readSectors = 0;
|
||||
uint64_t sectorTableStart = 0;
|
||||
void *sectorIndexTable = nullptr;
|
||||
uint64_t sectorTableEnd = 0;
|
||||
|
||||
void writeSectorIndexTable();
|
||||
|
||||
void *emptySector = nullptr;
|
||||
bool doWUX = false;
|
||||
uint32_t writtenSector = 0;
|
||||
bool autoSkip = false;
|
||||
|
||||
void dumpAppFiles();
|
||||
|
||||
static void printDumpState(const char *fmt, ...);
|
||||
int selectedOption = 0;
|
||||
};
|
495
source/GMPartitionsDumperState.cpp
Normal file
495
source/GMPartitionsDumperState.cpp
Normal file
@ -0,0 +1,495 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#include <common/common.h>
|
||||
#include <iosuhax.h>
|
||||
#include <memory>
|
||||
#include <WUD/DiscReaderDiscDrive.h>
|
||||
#include <WUD/header/WiiUDiscHeader.h>
|
||||
#include <fs/FSUtils.h>
|
||||
#include <WUD/content/partitions/WiiUGMPartition.h>
|
||||
#include <WUD/NUSTitle.h>
|
||||
#include "GMPartitionsDumperState.h"
|
||||
#include <utils/StringTools.h>
|
||||
|
||||
#define READ_BUFFER_SIZE (SECTOR_SIZE * 128)
|
||||
|
||||
GMPartitionsDumperState::GMPartitionsDumperState() {
|
||||
this->sectorBufSize = SECTOR_SIZE;
|
||||
this->state = STATE_OPEN_ODD1;
|
||||
}
|
||||
|
||||
GMPartitionsDumperState::~GMPartitionsDumperState() {
|
||||
free(this->sectorBuf);
|
||||
this->sectorBuf = nullptr;
|
||||
free(this->readBuffer);
|
||||
this->readBuffer = nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
for (auto &partitionPair: gmPartitionPairs) {
|
||||
uint32_t size = 0;
|
||||
for (auto &content: partitionPair.second->tmd->contentList) {
|
||||
size += ROUNDUP(content->encryptedFileSize, 16);
|
||||
}
|
||||
WiiUScreen::drawLinef("%s %s (~%0.2f MiB)", index == (uint32_t) selectedOption ? ">" : " ", partitionPair.first->getVolumeId().c_str(), (float) size / 1024.0f / 1024.0f);
|
||||
index++;
|
||||
}
|
||||
WiiUScreen::drawLine();
|
||||
WiiUScreen::drawLinef("%s Back", index == (uint32_t) selectedOption ? ">" : " ");
|
||||
}
|
||||
} 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());
|
||||
} else {
|
||||
WiiUScreen::drawLine("Dumping Partition");
|
||||
}
|
||||
|
||||
uint64_t partitionSize = 0;
|
||||
uint64_t partitionOffset = curOffsetInContent;
|
||||
if (curNUSTitle != nullptr) {
|
||||
for (auto &content: curNUSTitle->tmd->contentList) {
|
||||
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;
|
||||
auto size = curContent != nullptr ? ROUNDUP(curContent->encryptedFileSize, 16) : 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} else if (this->state == STATE_DUMP_DONE) {
|
||||
WiiUScreen::drawLine("Dumping done. Press A to return.");
|
||||
}
|
||||
|
||||
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) {
|
||||
auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &(this->oddFd));
|
||||
if (ret >= 0) {
|
||||
if (this->sectorBuf == nullptr) {
|
||||
this->sectorBuf = (void *) memalign(0x100, this->sectorBufSize);
|
||||
if (this->sectorBuf == nullptr) {
|
||||
DEBUG_FUNCTION_LINE("ERROR_MALLOC_FAILED");
|
||||
this->setError(ERROR_MALLOC_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Opened /dev/odd01 %d", this->oddFd);
|
||||
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) {
|
||||
if (IOSUHAX_FSA_RawRead(gFSAfd, this->sectorBuf, READ_SECTOR_SIZE, 1, 0, this->oddFd) >= 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
uint8_t opt[0x400];
|
||||
IOSUHAX_read_otp(opt, 0x400);
|
||||
memcpy(cKey.data(), opt + 0xE0, 0x10);
|
||||
this->state = STATE_CREATE_DISC_READER;
|
||||
} else if (this->state == STATE_CREATE_DISC_READER) {
|
||||
this->discReader = std::make_shared<DiscReaderDiscDrive>();
|
||||
if (!discReader->IsReady()) {
|
||||
this->setError(ERROR_OPEN_ODD1);
|
||||
return SUBSTATE_RUNNING;
|
||||
}
|
||||
this->state = STATE_PARSE_DISC_HEADER;
|
||||
} else if (this->state == STATE_PARSE_DISC_HEADER) {
|
||||
auto discHeaderOpt = WiiUDiscHeader::make_unique(discReader);
|
||||
if (!discHeaderOpt.has_value()) {
|
||||
DEBUG_FUNCTION_LINE("Failed to read DiscHeader");
|
||||
this->setError(ERROR_PARSE_DISCHEADER);
|
||||
return SUBSTATE_RUNNING;
|
||||
}
|
||||
this->discHeader = std::move(discHeaderOpt.value());
|
||||
this->state = STATE_PROCESS_GM_PARTITIONS;
|
||||
} else if (this->state == STATE_PROCESS_GM_PARTITIONS) {
|
||||
this->gmPartitionPairs.clear();
|
||||
for (auto &partition: discHeader->wiiUContentsInformation->partitions->partitions) {
|
||||
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;
|
||||
}
|
||||
|
||||
this->gmPartitionPairs.emplace_back(gmPartition, nusTitleOpt.value());
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
proccessMenuNavigation(input, (int32_t) gmPartitionPairs.size() + 1);
|
||||
if (entrySelected(input)) {
|
||||
if (selectedOption >= (int32_t) gmPartitionPairs.size()) {
|
||||
return SUBSTATE_RETURN;
|
||||
}
|
||||
auto gmPartitionPair = gmPartitionPairs[selectedOption];
|
||||
if (gmPartitionPair.first != nullptr) {
|
||||
this->curPartition = gmPartitionPair.first;
|
||||
this->curNUSTitle = gmPartitionPair.second;
|
||||
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;
|
||||
}
|
||||
|
||||
this->targetPath = StringTools::strfmt("%swudump/%s/%s", "ntfs0:/", this->discId, curPartition->getVolumeId().c_str());
|
||||
FSUtils::CreateSubfolder(targetPath.c_str());
|
||||
|
||||
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;
|
||||
this->state = STATE_DUMP_PARTITION_CONTENTS;
|
||||
} else if (this->state == STATE_DUMP_PARTITION_CONTENTS) {
|
||||
// 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) {
|
||||
readBuffer = (uint8_t *) malloc(READ_BUFFER_SIZE);
|
||||
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;
|
||||
} else if (state == STATE_DUMP_DONE) {
|
||||
if (entrySelected(input)) {
|
||||
return ApplicationState::SUBSTATE_RETURN;
|
||||
}
|
||||
}
|
||||
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
|
||||
void GMPartitionsDumperState::setError(GMPartitionsDumperState::eErrorState err) {
|
||||
this->state = STATE_ERROR;
|
||||
this->errorState = err;
|
||||
//OSEnableHomeButtonMenu(true);
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
||||
|
110
source/GMPartitionsDumperState.h
Normal file
110
source/GMPartitionsDumperState.h
Normal file
@ -0,0 +1,110 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <input/Input.h>
|
||||
#include "ApplicationState.h"
|
||||
#include "fs/WriteOnlyFileWithCache.h"
|
||||
#include "fs/WUXFileWriter.h"
|
||||
#include <WUD/entities/TMD/Content.h>
|
||||
#include <WUD/NUSTitle.h>
|
||||
#include <WUD/header/WiiUDiscHeader.h>
|
||||
|
||||
class GMPartitionsDumperState : public ApplicationState {
|
||||
public:
|
||||
enum eDumpState {
|
||||
STATE_ERROR,
|
||||
STATE_RETURN,
|
||||
STATE_OPEN_ODD1,
|
||||
STATE_READ_DISC_INFO,
|
||||
STATE_PLEASE_INSERT_DISC,
|
||||
STATE_READ_DISC_INFO_DONE,
|
||||
STATE_READ_COMMON_KEY,
|
||||
STATE_CREATE_DISC_READER,
|
||||
STATE_PARSE_DISC_HEADER,
|
||||
STATE_PROCESS_GM_PARTITIONS,
|
||||
STATE_CHOOSE_PARTITION_TO_DUMP,
|
||||
STATE_CREATE_DATA_PROVIDER,
|
||||
STATE_DUMP_PARTITION_TMD,
|
||||
STATE_DUMP_PARTITION_TICKET,
|
||||
STATE_DUMP_PARTITION_CERT,
|
||||
STATE_DUMP_PARTITION_CONTENTS,
|
||||
STATE_DUMP_DONE
|
||||
};
|
||||
|
||||
enum eErrorState {
|
||||
ERROR_NONE,
|
||||
ERROR_MALLOC_FAILED,
|
||||
ERROR_NO_DISC_ID,
|
||||
ERROR_READ_FIRST_SECTOR,
|
||||
ERROR_OPEN_ODD1,
|
||||
ERROR_PARSE_DISCHEADER,
|
||||
ERROR_NO_GM_PARTITION,
|
||||
ERROR_FAILED_TO_GET_NUSTITLE,
|
||||
ERROR_FAILED_WRITE_TMD,
|
||||
ERROR_FAILED_WRITE_TICKET,
|
||||
ERROR_FAILED_WRITE_CERT,
|
||||
ERROR_FIND_CONTENT_BY_INDEX,
|
||||
ERROR_FAILED_CREATE_FILE,
|
||||
ERROR_FAILED_WRITE_H3,
|
||||
ERROR_READ_CONTENT,
|
||||
ERROR_WRITE_CONTENT
|
||||
};
|
||||
|
||||
explicit GMPartitionsDumperState();
|
||||
|
||||
~GMPartitionsDumperState() override;
|
||||
|
||||
eDumpState state;
|
||||
eErrorState errorState = ERROR_NONE;
|
||||
|
||||
void render() override;
|
||||
|
||||
eSubState update(Input *input) override;
|
||||
|
||||
void setError(eErrorState err);
|
||||
|
||||
std::array<uint8_t, 11> discId{};
|
||||
std::array<uint8_t, 0x10> cKey{};
|
||||
|
||||
int32_t oddFd = -1;
|
||||
void *sectorBuf = nullptr;
|
||||
uint32_t sectorBufSize;
|
||||
std::shared_ptr<DiscReaderDiscDrive> discReader = nullptr;
|
||||
std::unique_ptr<WiiUDiscHeader> discHeader = nullptr;
|
||||
|
||||
std::shared_ptr<WiiUGMPartition> curPartition = nullptr;
|
||||
std::shared_ptr<NUSDataProvider> dataProvider = nullptr;
|
||||
std::string targetPath;
|
||||
std::shared_ptr<NUSTitle> curNUSTitle = nullptr;
|
||||
|
||||
uint16_t curContentIndex = 0;
|
||||
std::unique_ptr<CFile> contentFile = nullptr;
|
||||
std::shared_ptr<Content> curContent = nullptr;
|
||||
uint8_t *readBuffer = nullptr;
|
||||
uint32_t curOffsetInContent = 0;
|
||||
|
||||
[[nodiscard]] std::string ErrorMessage() const;
|
||||
|
||||
[[nodiscard]] std::string ErrorDescription() const;
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<WiiUGMPartition>, std::shared_ptr<NUSTitle>>> gmPartitionPairs;
|
||||
};
|
83
source/MainApplicationState.cpp
Normal file
83
source/MainApplicationState.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#include "MainApplicationState.h"
|
||||
#include "WUDDumperState.h"
|
||||
#include "GMPartitionsDumperState.h"
|
||||
#include <sysapp/launch.h>
|
||||
|
||||
MainApplicationState::MainApplicationState() {
|
||||
DEBUG_FUNCTION_LINE("Welcome!");
|
||||
this->state = STATE_WELCOME_SCREEN;
|
||||
}
|
||||
|
||||
MainApplicationState::~MainApplicationState() = default;;
|
||||
|
||||
void MainApplicationState::render() {
|
||||
if (this->state == STATE_DO_SUBSTATE) {
|
||||
if (this->subState == nullptr) {
|
||||
OSFatal("SubState was null");
|
||||
}
|
||||
this->subState->render();
|
||||
return;
|
||||
}
|
||||
WiiUScreen::clearScreen();
|
||||
printHeader();
|
||||
|
||||
if (this->state == STATE_WELCOME_SCREEN) {
|
||||
WiiUScreen::drawLine("Welcome to Wudump");
|
||||
WiiUScreen::drawLine("");
|
||||
WiiUScreen::drawLinef("%s Dump as WUX", this->selectedOption == 0 ? ">" : " ");
|
||||
WiiUScreen::drawLinef("%s Dump as WUD", this->selectedOption == 1 ? ">" : " ");
|
||||
WiiUScreen::drawLinef("%s Dump partition as .app", this->selectedOption == 2 ? ">" : " ");
|
||||
WiiUScreen::drawLinef("%s Exit", this->selectedOption == 3 ? ">" : " ");
|
||||
}
|
||||
|
||||
printFooter();
|
||||
WiiUScreen::flipBuffers();
|
||||
}
|
||||
|
||||
ApplicationState::eSubState MainApplicationState::update(Input *input) {
|
||||
if (this->state == STATE_WELCOME_SCREEN) {
|
||||
proccessMenuNavigation(input, 4);
|
||||
if (entrySelected(input)) {
|
||||
if (this->selectedOption == 0) {
|
||||
this->state = STATE_DO_SUBSTATE;
|
||||
this->subState = std::make_unique<WUDDumperState>(WUDDumperState::DUMP_AS_WUX);
|
||||
} else if (this->selectedOption == 1) {
|
||||
this->state = STATE_DO_SUBSTATE;
|
||||
this->subState = std::make_unique<WUDDumperState>(WUDDumperState::DUMP_AS_WUD);
|
||||
} else if (this->selectedOption == 2) {
|
||||
this->state = STATE_DO_SUBSTATE;
|
||||
this->subState = std::make_unique<GMPartitionsDumperState>();
|
||||
} else {
|
||||
SYSLaunchMenu();
|
||||
}
|
||||
this->selectedOption = 0;
|
||||
}
|
||||
} else if (this->state == STATE_DO_SUBSTATE) {
|
||||
auto retSubState = this->subState->update(input);
|
||||
if (retSubState == SUBSTATE_RUNNING) {
|
||||
// keep running.
|
||||
return SUBSTATE_RUNNING;
|
||||
} else if (retSubState == SUBSTATE_RETURN) {
|
||||
DEBUG_FUNCTION_LINE("Delete sub state");
|
||||
this->subState.reset();
|
||||
this->state = STATE_WELCOME_SCREEN;
|
||||
}
|
||||
}
|
||||
return SUBSTATE_RUNNING;
|
||||
}
|
49
source/MainApplicationState.h
Normal file
49
source/MainApplicationState.h
Normal file
@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <ctime>
|
||||
#include "input/Input.h"
|
||||
#include "fs/CFile.hpp"
|
||||
#include "ApplicationState.h"
|
||||
|
||||
|
||||
class MainApplicationState : public ApplicationState {
|
||||
public:
|
||||
enum eGameState {
|
||||
STATE_WELCOME_SCREEN,
|
||||
STATE_DO_SUBSTATE,
|
||||
};
|
||||
|
||||
MainApplicationState();
|
||||
|
||||
~MainApplicationState() override;
|
||||
|
||||
void render() override;
|
||||
|
||||
ApplicationState::eSubState update(Input *input) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ApplicationState> subState{};
|
||||
|
||||
eGameState state = STATE_WELCOME_SCREEN;
|
||||
};
|
@ -16,11 +16,10 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include <WUD/content/WiiUDiscContentsHeader.h>
|
||||
#include <ApplicationState.h>
|
||||
#include <utils/logger.h>
|
||||
#include <MainApplicationState.h>
|
||||
#include <utils/rijndael.h>
|
||||
#include <coreinit/debug.h>
|
||||
#include "DiscReader.h"
|
||||
#include <common/common.h>
|
||||
|
||||
bool DiscReader::readDecryptedChunk(uint64_t readOffset, uint8_t *out_buffer, uint8_t *key, uint8_t *IV) const {
|
||||
int CHUNK_SIZE = 0x10000;
|
||||
|
@ -16,7 +16,7 @@
|
||||
****************************************************************************/
|
||||
#include <WUD/content/WiiUDiscContentsHeader.h>
|
||||
#include <common/common.h>
|
||||
#include <ApplicationState.h>
|
||||
#include <MainApplicationState.h>
|
||||
#include <iosuhax.h>
|
||||
#include <utils/rijndael.h>
|
||||
#include <utils/logger.h>
|
||||
|
@ -17,6 +17,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include "DiscReader.h"
|
||||
|
||||
class DiscReaderDiscDrive : public DiscReader {
|
||||
|
326
source/WUDDumperState.cpp
Normal file
326
source/WUDDumperState.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#include <malloc.h>
|
||||
#include <utils/WiiUScreen.h>
|
||||
#include <common/common.h>
|
||||
#include <iosuhax.h>
|
||||
#include <fs/FSUtils.h>
|
||||
#include <utils/StringTools.h>
|
||||
#include <utils/utils.h>
|
||||
#include <WUD/content/WiiUDiscContentsHeader.h>
|
||||
#include "WUDDumperState.h"
|
||||
|
||||
WUDDumperState::WUDDumperState(WUDDumperState::eDumpTargetFormat pTarget)
|
||||
: target(pTarget) {
|
||||
this->sectorBufSize = READ_SECTOR_SIZE * READ_NUM_SECTORS;
|
||||
this->state = STATE_OPEN_ODD1;
|
||||
}
|
||||
|
||||
WUDDumperState::~WUDDumperState() {
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, oddFd);
|
||||
}
|
||||
free(sectorBuf);
|
||||
free(emptySector);
|
||||
}
|
||||
|
||||
|
||||
ApplicationState::eSubState WUDDumperState::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) {
|
||||
if (this->currentSector > 0) {
|
||||
auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &(this->oddFd));
|
||||
if (ret >= 0) {
|
||||
// continue!
|
||||
this->state = STATE_DUMP_DISC;
|
||||
} else {
|
||||
this->oddFd = -1;
|
||||
this->state = STATE_WAIT_USER_ERROR_CONFIRM;
|
||||
}
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
if (this->retryCount-- <= 0) {
|
||||
this->state = STATE_PLEASE_INSERT_DISC;
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &(this->oddFd));
|
||||
if (ret >= 0) {
|
||||
if (this->sectorBuf == nullptr) {
|
||||
this->sectorBuf = (void *) memalign(0x100, this->sectorBufSize);
|
||||
if (this->sectorBuf == nullptr) {
|
||||
DEBUG_FUNCTION_LINE("ERROR_MALLOC_FAILED");
|
||||
this->setError(ERROR_MALLOC_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Opened /dev/odd01 %d", this->oddFd);
|
||||
this->state = STATE_READ_DISC_INFO;
|
||||
}
|
||||
} else if (this->state == STATE_PLEASE_INSERT_DISC) {
|
||||
if (entrySelected(input)) {
|
||||
this->state = STATE_RETURN;
|
||||
}
|
||||
} else if (this->state == STATE_READ_DISC_INFO) {
|
||||
if (IOSUHAX_FSA_RawRead(gFSAfd, this->sectorBuf, READ_SECTOR_SIZE, 1, 0, this->oddFd) >= 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
this->setError(ERROR_READ_FIRST_SECTOR);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
} else if (this->state == STATE_READ_DISC_INFO_DONE) {
|
||||
this->state = STATE_DUMP_DISC_KEY;
|
||||
} else if (this->state == STATE_DUMP_DISC_KEY) {
|
||||
// Read the WiiUDiscContentsHeader to determine if we need disckey and if it's the correct one.
|
||||
auto res = IOSUHAX_FSA_RawRead(gFSAfd, this->sectorBuf, READ_SECTOR_SIZE, 1, 3, this->oddFd);
|
||||
uint8_t discKey[16];
|
||||
bool hasDiscKey = false;
|
||||
if (res >= 0) {
|
||||
if (((uint32_t *) this->sectorBuf)[0] != WiiUDiscContentsHeader::MAGIC) {
|
||||
auto discKeyRes = IOSUHAX_ODM_GetDiscKey(discKey);
|
||||
if (discKeyRes >= 0) {
|
||||
hasDiscKey = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDiscKey) {
|
||||
if (!FSUtils::CreateSubfolder(StringTools::fmt("%swudump/%s", "ntfs0:/", discId))) {
|
||||
setError(ERROR_WRITE_FAILED);
|
||||
return SUBSTATE_RUNNING;
|
||||
}
|
||||
if (!FSUtils::saveBufferToFile(StringTools::fmt("%swudump/%s/game.key", "ntfs0:/", discId), discKey, 16)) {
|
||||
setError(ERROR_WRITE_FAILED);
|
||||
return SUBSTATE_RUNNING;
|
||||
}
|
||||
}
|
||||
this->state = STATE_DUMP_DISC_START;
|
||||
} else if (this->state == STATE_DUMP_DISC_START) {
|
||||
if (!FSUtils::CreateSubfolder(StringTools::fmt("%swudump/%s", "ntfs0:/", discId))) {
|
||||
setError(ERROR_WRITE_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
if (target == DUMP_AS_WUX) {
|
||||
this->fileHandle = std::make_unique<WUXFileWriter>(StringTools::fmt("%swudump/%s/game.wux", "ntfs0:/", discId), CFile::WriteOnly, READ_SECTOR_SIZE * WRITE_BUFFER_NUM_SECTORS, SECTOR_SIZE);
|
||||
} else {
|
||||
this->fileHandle = std::make_unique<WUXFileWriter>(StringTools::fmt("%swudump/%s/game.wud", "ntfs0:/", discId), CFile::WriteOnly, READ_SECTOR_SIZE * WRITE_BUFFER_NUM_SECTORS, SECTOR_SIZE);
|
||||
}
|
||||
if (!this->fileHandle->isOpen()) {
|
||||
DEBUG_FUNCTION_LINE("Failed to open file");
|
||||
this->setError(ERROR_FILE_OPEN_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
|
||||
this->startTime = OSGetTime();
|
||||
|
||||
this->state = STATE_DUMP_DISC;
|
||||
this->totalSectorCount = (WUD_FILE_SIZE / SECTOR_SIZE);
|
||||
this->currentSector = 0;
|
||||
this->writtenSectors = 0;
|
||||
this->retryCount = 10;
|
||||
} else if (this->state == STATE_DUMP_DISC) {
|
||||
size_t numSectors = this->currentSector + READ_NUM_SECTORS > this->totalSectorCount ? this->totalSectorCount - this->currentSector : READ_NUM_SECTORS;
|
||||
if ((this->readResult = IOSUHAX_FSA_RawRead(gFSAfd, sectorBuf, READ_SECTOR_SIZE, numSectors, this->currentSector, this->oddFd)) >= 0) {
|
||||
auto curWrittenSectors = fileHandle->writeSector((const uint8_t *) this->sectorBuf, numSectors);
|
||||
if (curWrittenSectors < 0) {
|
||||
this->setError(ERROR_WRITE_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
currentSector += numSectors;
|
||||
this->writtenSectors += curWrittenSectors;
|
||||
|
||||
this->retryCount = 10;
|
||||
if (this->currentSector >= this->totalSectorCount) {
|
||||
this->state = STATE_DUMP_DISC_DONE;
|
||||
if (this->fileHandle->isOpen()) {
|
||||
if (!this->fileHandle->flush()) {
|
||||
DEBUG_FUNCTION_LINE("Flush failed");
|
||||
this->setError(ERROR_WRITE_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
this->fileHandle->close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this->state = STATE_WAIT_USER_ERROR_CONFIRM;
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd);
|
||||
this->oddFd = -1;
|
||||
}
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
} else if (this->state == STATE_WAIT_USER_ERROR_CONFIRM) {
|
||||
if (this->autoSkipOnError) {
|
||||
if (this->oddFd >= 0) {
|
||||
IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd);
|
||||
this->oddFd = -1;
|
||||
}
|
||||
}
|
||||
if (this->autoSkipOnError || (input->data.buttons_d & Input::BUTTON_A)) {
|
||||
// this->log.fwrite("Skipped sector %d : 0x%ll016X-0x%ll016X, filled with 0's\n", this->currentSector, this->currentSector * READ_SECTOR_SIZE, (this->currentSector + 1) * READ_SECTOR_SIZE);
|
||||
this->state = STATE_OPEN_ODD1;
|
||||
this->skippedSectors.push_back(this->currentSector);
|
||||
// We can't use seek because we may have cached values.
|
||||
|
||||
if (this->emptySector == nullptr) {
|
||||
this->emptySector = memalign(0x100, READ_SECTOR_SIZE);
|
||||
if (this->emptySector == nullptr) {
|
||||
this->setError(ERROR_MALLOC_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
auto curWrittenSectors = fileHandle->writeSector((uint8_t *) emptySector, 1);
|
||||
if (curWrittenSectors < 0) {
|
||||
this->setError(ERROR_WRITE_FAILED);
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
|
||||
this->currentSector += 1;
|
||||
this->writtenSectors += curWrittenSectors;
|
||||
this->readResult = 0;
|
||||
} else if (input->data.buttons_d & Input::BUTTON_B) {
|
||||
this->state = STATE_OPEN_ODD1;
|
||||
this->readResult = 0;
|
||||
} else if (input->data.buttons_d & Input::BUTTON_Y) {
|
||||
this->autoSkipOnError = true;
|
||||
}
|
||||
} else if (this->state == STATE_DUMP_DISC_DONE) {
|
||||
WiiUScreen::drawLinef("Dumping done! Press A to continue");
|
||||
if (entrySelected(input)) {
|
||||
this->state = STATE_RETURN;
|
||||
}
|
||||
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
return ApplicationState::SUBSTATE_RUNNING;
|
||||
}
|
||||
|
||||
|
||||
void WUDDumperState::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::drawLinef("Dumping: %s", this->discId);
|
||||
} else if (this->state == STATE_DUMP_DISC_START || this->state == STATE_DUMP_DISC || this->state == STATE_WAIT_USER_ERROR_CONFIRM) {
|
||||
WiiUScreen::drawLinef("Dumping: %s", this->discId);
|
||||
|
||||
float percent = this->currentSector / (WUD_FILE_SIZE / READ_SECTOR_SIZE * 1.0f) * 100.0f;
|
||||
WiiUScreen::drawLinef("Progress: %0.2f MiB / %5.2f MiB (%2.1f %%)", this->currentSector * (READ_SECTOR_SIZE / 1024.0f / 1024.0f), WUD_FILE_SIZE / 1024.0f / 1024.0f, percent);
|
||||
if (target == DUMP_AS_WUX) {
|
||||
WiiUScreen::drawLinef("Written %0.2f MiB. Compression ratio 1:%0.2f", writtenSectors * (READ_SECTOR_SIZE / 1024.0f / 1024.0f),
|
||||
1.0f / (writtenSectors / (float) this->currentSector));
|
||||
}
|
||||
|
||||
if (this->readResult < 0 || this->oddFd < 0) {
|
||||
WiiUScreen::drawLine();
|
||||
|
||||
if (this->oddFd < 0) {
|
||||
WiiUScreen::drawLine("Failed to open disc, try again.");
|
||||
} else {
|
||||
WiiUScreen::drawLinef("Error: Failed to read sector - Error %d", this->readResult);
|
||||
}
|
||||
WiiUScreen::drawLine();
|
||||
WiiUScreen::drawLine("Press A to skip this sector (will be replaced by 0's)");
|
||||
WiiUScreen::drawLine("Press B to try again");
|
||||
} else {
|
||||
OSTime curTime = OSGetTime();
|
||||
float remaining = (WUD_FILE_SIZE - (READ_SECTOR_SIZE * this->currentSector)) / 1024.0f / 1024.0f;
|
||||
float curSpeed = READ_SECTOR_SIZE * ((this->currentSector / 1000.0f) / OSTicksToMilliseconds(curTime - startTime));
|
||||
int32_t remainingSec = remaining / curSpeed;
|
||||
int32_t minutes = (remainingSec / 60) % 60;
|
||||
int32_t seconds = remainingSec % 60;
|
||||
int32_t hours = remainingSec / 3600;
|
||||
|
||||
WiiUScreen::drawLinef("Speed: %.2f MiB/s ETA: %02dh %02dm %02ds", curSpeed, remaining, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
WiiUScreen::drawLine();
|
||||
if (!this->skippedSectors.empty()) {
|
||||
WiiUScreen::drawLinef("Skipped dumping %d sectors", this->skippedSectors.size());
|
||||
}
|
||||
} else if (this->state == STATE_DUMP_DISC_DONE) {
|
||||
WiiUScreen::drawLinef("Dumping done! Press A to continue");
|
||||
} else if (this->state == STATE_RETURN) {
|
||||
WiiUScreen::drawLinef("Returning");
|
||||
}
|
||||
|
||||
ApplicationState::printFooter();
|
||||
WiiUScreen::flipBuffers();
|
||||
}
|
||||
|
||||
void WUDDumperState::setError(WUDDumperState::eErrorState err) {
|
||||
this->state = STATE_ERROR;
|
||||
this->errorState = err;
|
||||
//OSEnableHomeButtonMenu(true);
|
||||
}
|
||||
|
||||
std::string WUDDumperState::ErrorMessage() const {
|
||||
if (this->errorState == ERROR_READ_FIRST_SECTOR) {
|
||||
return "ERROR_READ_FIRST_SECTOR";
|
||||
} else if (this->errorState == ERROR_FILE_OPEN_FAILED) {
|
||||
return "ERROR_FILE_OPEN_FAILED";
|
||||
} else if (this->errorState == ERROR_MALLOC_FAILED) {
|
||||
return "ERROR_MALLOC_FAILED";
|
||||
} else if (this->errorState == ERROR_NO_DISC_ID) {
|
||||
return "ERROR_NO_DISC_ID";
|
||||
} else if (this->errorState == ERROR_WRITE_FAILED) {
|
||||
return "ERROR_WRITE_FAILED";
|
||||
}
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
||||
|
||||
std::string WUDDumperState::ErrorDescription() const {
|
||||
if (this->errorState == ERROR_READ_FIRST_SECTOR) {
|
||||
return "Failed to read first sector.";
|
||||
} else if (this->errorState == ERROR_MALLOC_FAILED) {
|
||||
return "Failed to allocate data.";
|
||||
} else if (this->errorState == ERROR_FILE_OPEN_FAILED) {
|
||||
return "Failed to create file";
|
||||
} else if (this->errorState == ERROR_NO_DISC_ID) {
|
||||
return "Failed to get the disc id";
|
||||
} else if (this->errorState == ERROR_WRITE_FAILED) {
|
||||
return "Failed to write the file. \nMake sure to have enough space on the storage";
|
||||
}
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
108
source/WUDDumperState.h
Normal file
108
source/WUDDumperState.h
Normal file
@ -0,0 +1,108 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <input/Input.h>
|
||||
#include "ApplicationState.h"
|
||||
#include "fs/WriteOnlyFileWithCache.h"
|
||||
#include "fs/WUXFileWriter.h"
|
||||
|
||||
#define READ_NUM_SECTORS 128
|
||||
#define WRITE_BUFFER_NUM_SECTORS 128
|
||||
#define WUD_FILE_SIZE 0x5D3A00000L
|
||||
|
||||
class WUDDumperState : public ApplicationState {
|
||||
public:
|
||||
|
||||
enum eDumpTargetFormat {
|
||||
DUMP_AS_WUX,
|
||||
DUMP_AS_WUD,
|
||||
};
|
||||
|
||||
enum eDumpState {
|
||||
STATE_ERROR,
|
||||
STATE_RETURN,
|
||||
STATE_OPEN_ODD1,
|
||||
STATE_PLEASE_INSERT_DISC,
|
||||
STATE_READ_DISC_INFO,
|
||||
STATE_READ_DISC_INFO_DONE,
|
||||
STATE_DUMP_DISC_KEY,
|
||||
STATE_DUMP_DISC_START,
|
||||
STATE_DUMP_DISC_DONE,
|
||||
STATE_WAIT_USER_ERROR_CONFIRM,
|
||||
STATE_DUMP_DISC,
|
||||
};
|
||||
|
||||
enum eErrorState {
|
||||
ERROR_NONE,
|
||||
ERROR_READ_FIRST_SECTOR,
|
||||
ERROR_FILE_OPEN_FAILED,
|
||||
ERROR_MALLOC_FAILED,
|
||||
ERROR_NO_DISC_ID,
|
||||
ERROR_WRITE_FAILED,
|
||||
ERROR_NO_DISC_FOUND
|
||||
};
|
||||
|
||||
explicit WUDDumperState(eDumpTargetFormat pTarget);
|
||||
|
||||
~WUDDumperState() override;
|
||||
|
||||
eDumpState state;
|
||||
eDumpTargetFormat target;
|
||||
WUDDumperState::eErrorState errorState = ERROR_NONE;
|
||||
|
||||
void render() override;
|
||||
|
||||
eSubState update(Input *input) override;
|
||||
|
||||
void setError(eErrorState err);
|
||||
|
||||
[[nodiscard]] std::string ErrorMessage() const;
|
||||
|
||||
[[nodiscard]] std::string ErrorDescription() const;
|
||||
|
||||
void *sectorBuf = nullptr;
|
||||
|
||||
int readResult = 0;
|
||||
int oddFd = -1;
|
||||
int retryCount = 10;
|
||||
|
||||
OSTime startTime{};
|
||||
|
||||
std::unique_ptr<WUDFileWriter> fileHandle = {};
|
||||
|
||||
std::array<uint8_t, 11> discId{};
|
||||
|
||||
uint32_t sectorBufSize{};
|
||||
|
||||
//
|
||||
uint64_t currentSector{};
|
||||
uint64_t totalSectorCount{};
|
||||
|
||||
//
|
||||
std::vector<uint64_t> skippedSectors;
|
||||
bool autoSkipOnError = false;
|
||||
|
||||
// We need this to calculate the compression ratio.
|
||||
int32_t writtenSectors{};
|
||||
|
||||
void *emptySector = nullptr;
|
||||
};
|
@ -3,3 +3,6 @@
|
||||
#include <cstdint>
|
||||
|
||||
extern int32_t gFSAfd;
|
||||
|
||||
#define SECTOR_SIZE 0x8000
|
||||
#define READ_SECTOR_SIZE SECTOR_SIZE
|
@ -1,11 +1,12 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <strings.h>
|
||||
#include <utils/logger.h>
|
||||
#include "CFile.hpp"
|
||||
|
||||
CFile::CFile() {
|
||||
iFd = -1;
|
||||
mem_file = NULL;
|
||||
mem_file = nullptr;
|
||||
filesize = 0;
|
||||
pos = 0;
|
||||
}
|
||||
@ -21,7 +22,7 @@ CFile::CFile(const uint8_t *mem, int32_t size) {
|
||||
}
|
||||
|
||||
CFile::~CFile() {
|
||||
this->close();
|
||||
CFile::close();
|
||||
}
|
||||
|
||||
int32_t CFile::open(const std::string &filepath, eOpenTypes mode) {
|
||||
@ -76,7 +77,7 @@ void CFile::close() {
|
||||
::close(iFd);
|
||||
|
||||
iFd = -1;
|
||||
mem_file = NULL;
|
||||
mem_file = nullptr;
|
||||
filesize = 0;
|
||||
pos = 0;
|
||||
}
|
||||
@ -89,18 +90,18 @@ int32_t CFile::read(uint8_t *ptr, size_t size) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t readsize = size;
|
||||
size_t readsize = size;
|
||||
|
||||
if (readsize > (int64_t) (filesize - pos))
|
||||
readsize = filesize - pos;
|
||||
|
||||
if (readsize <= 0)
|
||||
return readsize;
|
||||
return (int32_t) readsize;
|
||||
|
||||
if (mem_file != NULL) {
|
||||
memcpy(ptr, mem_file + pos, readsize);
|
||||
pos += readsize;
|
||||
return readsize;
|
||||
return (int32_t) readsize;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -111,20 +112,23 @@ int32_t CFile::write(const uint8_t *ptr, size_t size) {
|
||||
size_t done = 0;
|
||||
while (done < size) {
|
||||
int32_t ret = ::write(iFd, ptr, size - done);
|
||||
if (ret <= 0)
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptr += ret;
|
||||
done += ret;
|
||||
pos += ret;
|
||||
}
|
||||
return done;
|
||||
if (pos > filesize) {
|
||||
filesize = pos;
|
||||
}
|
||||
return (int32_t) done;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t CFile::seek(long int offset, int32_t origin) {
|
||||
int32_t CFile::seek(int64_t offset, int32_t origin) {
|
||||
int32_t ret = 0;
|
||||
int64_t newPos = pos;
|
||||
|
||||
@ -145,7 +149,7 @@ int32_t CFile::seek(long int offset, int32_t origin) {
|
||||
if (iFd >= 0)
|
||||
ret = ::lseek(iFd, pos, SEEK_SET);
|
||||
|
||||
if (mem_file != NULL) {
|
||||
if (mem_file != nullptr) {
|
||||
if (pos > filesize) {
|
||||
pos = filesize;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
#ifndef CFILE_HPP_
|
||||
#define CFILE_HPP_
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <wut_types.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
class CFile {
|
||||
public:
|
||||
@ -29,31 +29,32 @@ public:
|
||||
|
||||
int32_t open(const uint8_t *memory, int32_t memsize);
|
||||
|
||||
BOOL isOpen() const {
|
||||
if (iFd >= 0)
|
||||
[[nodiscard]] BOOL isOpen() const {
|
||||
if (iFd >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_file)
|
||||
if (mem_file) {
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void close();
|
||||
virtual void close();
|
||||
|
||||
int32_t read(uint8_t *ptr, size_t size);
|
||||
virtual int32_t read(uint8_t *ptr, size_t size);
|
||||
|
||||
int32_t write(const uint8_t *ptr, size_t size);
|
||||
virtual int32_t write(const uint8_t *ptr, size_t size);
|
||||
|
||||
int32_t fwrite(const char *format, ...);
|
||||
|
||||
int32_t seek(long int offset, int32_t origin);
|
||||
virtual int32_t seek(int64_t offset, int32_t origin);
|
||||
|
||||
uint64_t tell() {
|
||||
[[nodiscard]] uint64_t tell() const {
|
||||
return pos;
|
||||
};
|
||||
|
||||
uint64_t size() {
|
||||
[[nodiscard]] uint64_t size() const {
|
||||
return filesize;
|
||||
};
|
||||
|
||||
@ -63,9 +64,7 @@ public:
|
||||
|
||||
protected:
|
||||
int32_t iFd;
|
||||
const uint8_t *mem_file;
|
||||
uint64_t filesize;
|
||||
uint64_t pos;
|
||||
const uint8_t *mem_file{};
|
||||
uint64_t filesize{};
|
||||
uint64_t pos{};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
35
source/fs/WUDFileWriter.cpp
Normal file
35
source/fs/WUDFileWriter.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#include <utils/logger.h>
|
||||
#include "WUDFileWriter.h"
|
||||
|
||||
WUDFileWriter::WUDFileWriter(const char *path, eOpenTypes mode, int32_t cacheSize, int32_t pSectorSize) :
|
||||
WriteOnlyFileWithCache(path, mode, cacheSize),
|
||||
sectorSize(pSectorSize) {
|
||||
}
|
||||
|
||||
int32_t WUDFileWriter::writeSector(const uint8_t *buffer, uint32_t numberOfSectors) {
|
||||
auto result = write(buffer, numberOfSectors * this->sectorSize);
|
||||
if (result == (int32_t) (numberOfSectors * this->sectorSize)) {
|
||||
return (int32_t) numberOfSectors;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
WUDFileWriter::~WUDFileWriter() {
|
||||
WUDFileWriter::close();
|
||||
}
|
31
source/fs/WUDFileWriter.h
Normal file
31
source/fs/WUDFileWriter.h
Normal file
@ -0,0 +1,31 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "WriteOnlyFileWithCache.h"
|
||||
|
||||
class WUDFileWriter : public WriteOnlyFileWithCache {
|
||||
public:
|
||||
WUDFileWriter(const char *string, eOpenTypes types, int32_t cacheSize, int32_t sectorSize);
|
||||
|
||||
~WUDFileWriter() override;
|
||||
|
||||
virtual int32_t writeSector(const uint8_t *buffer, uint32_t numberOfSectors);
|
||||
|
||||
protected:
|
||||
int32_t sectorSize;
|
||||
};
|
113
source/fs/WUXFileWriter.cpp
Normal file
113
source/fs/WUXFileWriter.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#include <utils/logger.h>
|
||||
#include "WUXFileWriter.h"
|
||||
#include "WUDDumperState.h"
|
||||
|
||||
WUXFileWriter::WUXFileWriter(const char *path, CFile::eOpenTypes mode, int32_t cacheSize, int32_t sectorSize) :
|
||||
WUDFileWriter(path, mode, cacheSize, sectorSize) {
|
||||
wuxHeader_t wuxHeader = {0};
|
||||
wuxHeader.magic0 = WUX_MAGIC_0;
|
||||
wuxHeader.magic1 = WUX_MAGIC_1;
|
||||
wuxHeader.sectorSize = swap_uint32(this->sectorSize);
|
||||
wuxHeader.uncompressedSize = swap_uint64(WUD_FILE_SIZE);
|
||||
wuxHeader.flags = 0;
|
||||
|
||||
this->write((uint8_t *) &wuxHeader, sizeof(wuxHeader_t));
|
||||
this->flush();
|
||||
this->sectorTableStart = this->tell();
|
||||
this->totalSectorCount = WUD_FILE_SIZE / this->sectorSize;
|
||||
|
||||
this->sectorIndexTable = (void *) malloc(totalSectorCount * 4);
|
||||
if (sectorIndexTable == nullptr) {
|
||||
DEBUG_FUNCTION_LINE("Failed to alloc");
|
||||
WUDFileWriter::close();
|
||||
return;
|
||||
}
|
||||
memset(this->sectorIndexTable, 0, totalSectorCount * 4);
|
||||
|
||||
if (this->write((uint8_t *) this->sectorIndexTable, totalSectorCount * 4) < 0) {
|
||||
WUDFileWriter::close();
|
||||
return;
|
||||
}
|
||||
this->flush();
|
||||
|
||||
this->sectorTableEnd = this->tell();
|
||||
uint64_t tableEnd = this->sectorTableEnd;
|
||||
|
||||
this->sectorTableEnd += this->sectorSize - 1;
|
||||
this->sectorTableEnd -= (this->sectorTableEnd % this->sectorSize);
|
||||
|
||||
uint64_t padding = this->sectorTableEnd - tableEnd;
|
||||
auto *paddingData = (uint8_t *) malloc(padding);
|
||||
memset(paddingData, 0, padding);
|
||||
this->write(reinterpret_cast<const uint8_t *>(paddingData), padding);
|
||||
this->flush();
|
||||
free(paddingData);
|
||||
this->hashMap.clear();
|
||||
}
|
||||
|
||||
int32_t WUXFileWriter::writeSector(const uint8_t *buffer, uint32_t numberOfSectors) {
|
||||
char hashOut[32];
|
||||
int32_t curWrittenSectors = 0;
|
||||
for (uint32_t i = 0; i < numberOfSectors; i++) {
|
||||
uint32_t addr = ((uint32_t) buffer) + (i * this->sectorSize);
|
||||
calculateHash256(reinterpret_cast<unsigned char *>(addr), this->sectorSize, reinterpret_cast<unsigned char *>(hashOut));
|
||||
char tmp[34];
|
||||
auto *test = (uint32_t *) hashOut;
|
||||
snprintf(tmp, 33, "%08X%08X%08X%08X", test[0], test[1], test[2], test[3]);
|
||||
std::string hash(tmp);
|
||||
|
||||
auto *indexTable = (uint32_t *) this->sectorIndexTable;
|
||||
|
||||
auto it = hashMap.find(hash);
|
||||
if (it != hashMap.end()) {
|
||||
indexTable[this->currentSector] = swap_uint32(this->hashMap[hash]);
|
||||
} else {
|
||||
indexTable[this->currentSector] = swap_uint32(this->writtenSector);
|
||||
hashMap[hash] = writtenSector;
|
||||
if (isOpen()) {
|
||||
if (!write((uint8_t *) addr, this->sectorSize)) {
|
||||
DEBUG_FUNCTION_LINE("Write failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
this->writtenSector++;
|
||||
curWrittenSectors++;
|
||||
}
|
||||
this->currentSector++;
|
||||
}
|
||||
return curWrittenSectors;
|
||||
}
|
||||
|
||||
void WUXFileWriter::writeSectorIndexTable() {
|
||||
if (this->isOpen()) {
|
||||
// We need to make sure to call CFile::seek!
|
||||
CFile::seek((int64_t) sectorTableStart, SEEK_SET);
|
||||
CFile::write((uint8_t *) sectorIndexTable, totalSectorCount * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void WUXFileWriter::close() {
|
||||
writeSectorIndexTable();
|
||||
CFile::close();
|
||||
}
|
||||
|
||||
WUXFileWriter::~WUXFileWriter() {
|
||||
WUXFileWriter::close();
|
||||
free(sectorIndexTable);
|
||||
}
|
56
source/fs/WUXFileWriter.h
Normal file
56
source/fs/WUXFileWriter.h
Normal file
@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "utils/utils.h"
|
||||
#include "WUDFileWriter.h"
|
||||
#include <map>
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic0;
|
||||
unsigned int magic1;
|
||||
unsigned int sectorSize;
|
||||
unsigned long long uncompressedSize;
|
||||
unsigned int flags;
|
||||
} wuxHeader_t;
|
||||
|
||||
#define WUX_MAGIC_0 0x57555830
|
||||
#define WUX_MAGIC_1 swap_uint32(0x1099d02e)
|
||||
|
||||
class WUXFileWriter : public WUDFileWriter {
|
||||
public:
|
||||
WUXFileWriter(const char *string, eOpenTypes types, int32_t cacheSize, int32_t pSectorSize);
|
||||
|
||||
~WUXFileWriter() override;
|
||||
|
||||
int32_t writeSector(const uint8_t *buffer, uint32_t numberOfSectors) override;
|
||||
|
||||
void close() override;
|
||||
|
||||
private:
|
||||
void writeSectorIndexTable();
|
||||
|
||||
uint64_t totalSectorCount;
|
||||
|
||||
uint64_t sectorTableStart;
|
||||
uint64_t sectorTableEnd;
|
||||
void *sectorIndexTable = nullptr;
|
||||
|
||||
std::map<std::string, uint32_t> hashMap;
|
||||
uint32_t currentSector = 0;
|
||||
uint32_t writtenSector = 0;
|
||||
};
|
96
source/fs/WriteOnlyFileWithCache.cpp
Normal file
96
source/fs/WriteOnlyFileWithCache.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#include <malloc.h>
|
||||
#include <coreinit/memory.h>
|
||||
#include <utils/logger.h>
|
||||
#include "WriteOnlyFileWithCache.h"
|
||||
|
||||
WriteOnlyFileWithCache::WriteOnlyFileWithCache(const char *path, CFile::eOpenTypes mode, int32_t cacheSize) : CFile(path, mode) {
|
||||
if (!this->isOpen()) {
|
||||
return;
|
||||
}
|
||||
this->writeBufferSize = cacheSize;
|
||||
this->writeBuffer = (void *) memalign(0x1000, this->writeBufferSize);
|
||||
if (this->writeBuffer == nullptr) {
|
||||
this->close();
|
||||
return;
|
||||
}
|
||||
this->writeBufferPos = 0;
|
||||
}
|
||||
|
||||
WriteOnlyFileWithCache::~WriteOnlyFileWithCache() {
|
||||
WriteOnlyFileWithCache::close();
|
||||
free(writeBuffer);
|
||||
}
|
||||
|
||||
bool WriteOnlyFileWithCache::flush() {
|
||||
if (this->writeBufferPos > 0) {
|
||||
int32_t res = CFile::write(static_cast<const uint8_t *>(this->writeBuffer), this->writeBufferPos);
|
||||
if (res < 0) {
|
||||
DEBUG_FUNCTION_LINE("Failed to flush cache, write failed: %d", res);
|
||||
return false;
|
||||
}
|
||||
this->writeBufferPos = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t WriteOnlyFileWithCache::write(const uint8_t *addr, size_t writeSize) {
|
||||
if (writeSize == this->writeBufferSize) {
|
||||
if (!this->flush()) {
|
||||
DEBUG_FUNCTION_LINE("Flush failed");
|
||||
return -1;
|
||||
}
|
||||
return CFile::write(reinterpret_cast<const uint8_t *>(addr), writeSize);
|
||||
}
|
||||
|
||||
auto toWrite = (int32_t) writeSize;
|
||||
if (toWrite == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t written = 0;
|
||||
|
||||
do {
|
||||
int32_t curWrite = toWrite;
|
||||
|
||||
if (this->writeBufferPos + curWrite > this->writeBufferSize) {
|
||||
curWrite = this->writeBufferSize - this->writeBufferPos;
|
||||
}
|
||||
OSBlockMove((void *) (((uint32_t) this->writeBuffer) + this->writeBufferPos), (void *) (addr + written), curWrite, 1);
|
||||
this->writeBufferPos += curWrite;
|
||||
|
||||
if (this->writeBufferPos == this->writeBufferSize) {
|
||||
if (!this->flush()) {
|
||||
DEBUG_FUNCTION_LINE("Flush failed");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
toWrite -= curWrite;
|
||||
written += curWrite;
|
||||
} while (toWrite > 0);
|
||||
return written;
|
||||
}
|
||||
|
||||
int32_t WriteOnlyFileWithCache::seek(int64_t offset, int32_t origin) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t WriteOnlyFileWithCache::read(uint8_t *ptr, size_t size) {
|
||||
return -1;
|
||||
}
|
38
source/fs/WriteOnlyFileWithCache.h
Normal file
38
source/fs/WriteOnlyFileWithCache.h
Normal file
@ -0,0 +1,38 @@
|
||||
/****************************************************************************
|
||||
* 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/>.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <fs/CFile.hpp>
|
||||
|
||||
class WriteOnlyFileWithCache : public CFile {
|
||||
public:
|
||||
WriteOnlyFileWithCache(const char *string, eOpenTypes types, int32_t cacheSize);
|
||||
|
||||
~WriteOnlyFileWithCache() override;
|
||||
|
||||
int32_t write(const uint8_t *data, size_t size) override;
|
||||
|
||||
int32_t seek(int64_t offset, int32_t origin) override;
|
||||
|
||||
int32_t read(uint8_t *ptr, size_t size) override;
|
||||
|
||||
bool flush();
|
||||
|
||||
void *writeBuffer = nullptr;
|
||||
size_t writeBufferSize;
|
||||
size_t writeBufferPos;
|
||||
};
|
@ -6,13 +6,13 @@
|
||||
|
||||
#include <iosuhax.h>
|
||||
#include <ntfs.h>
|
||||
#include <coreinit/debug.h>
|
||||
|
||||
#include "utils/logger.h"
|
||||
#include "utils/WiiUScreen.h"
|
||||
#include "input/VPADInput.h"
|
||||
#include "ApplicationState.h"
|
||||
#include "MainApplicationState.h"
|
||||
#include "common/common.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
void initIOSUHax();
|
||||
|
||||
@ -63,18 +63,19 @@ int main(int argc, char **argv) {
|
||||
|
||||
void main_loop() {
|
||||
DEBUG_FUNCTION_LINE("Creating state");
|
||||
ApplicationState state;
|
||||
std::unique_ptr<MainApplicationState> state = std::make_unique<MainApplicationState>();
|
||||
VPadInput input;
|
||||
|
||||
if (gFSAfd < 0 || !sIosuhaxMount) {
|
||||
state.setError(ApplicationState::eErrorState::ERROR_IOSUHAX_FAILED);
|
||||
// state.setError(MainApplicationState::eErrorState::ERROR_IOSUHAX_FAILED);
|
||||
OSFatal("IOSUHAX Failed");
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Entering main loop");
|
||||
while (WHBProcIsRunning()) {
|
||||
input.update(1280, 720);
|
||||
state.update(&input);
|
||||
state.render();
|
||||
state->update(&input);
|
||||
state->render();
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,8 +90,8 @@ void initIOSUHax() {
|
||||
if (gFSAfd < 0) {
|
||||
DEBUG_FUNCTION_LINE("IOSUHAX_FSA_Open failed");
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("IOSUHAX done");
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("IOSUHAX done");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,3 +62,28 @@ std::string Utils::hashFile(const std::string &path) {
|
||||
free(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned int swap_uint32(unsigned int val) {
|
||||
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
|
||||
return (val << 16) | (val >> 16);
|
||||
}
|
||||
|
||||
unsigned long long swap_uint64(unsigned long long val) {
|
||||
val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);
|
||||
val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);
|
||||
return (val << 32) | (val >> 32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash function used to create a hash of each sector
|
||||
* The hashes are then compared to find duplicate sectors
|
||||
*/
|
||||
void calculateHash256(unsigned char *data, unsigned int length, unsigned char *hashOut) {
|
||||
// cheap and simple hash implementation
|
||||
// you can replace this part with your favorite hash method
|
||||
memset(hashOut, 0x00, 32);
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
hashOut[i % 32] ^= data[i];
|
||||
hashOut[(i + 7) % 32] += data[i];
|
||||
}
|
||||
}
|
@ -30,6 +30,12 @@ extern "C" {
|
||||
#define le32(i) ((((uint32_t)le16((i) & 0xFFFF)) << 16) | ((uint32_t)le16(((i) & 0xFFFF0000) >> 16)))
|
||||
#define le64(i) ((((uint64_t)le32((i) & 0xFFFFFFFFLL)) << 32) | ((uint64_t)le32(((i) & 0xFFFFFFFF00000000LL) >> 32)))
|
||||
|
||||
unsigned int swap_uint32(unsigned int val);
|
||||
|
||||
unsigned long long swap_uint64(unsigned long long val);
|
||||
|
||||
void calculateHash256(unsigned char *data, unsigned int length, unsigned char *hashOut);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user