Refactor menus and overall structure. NTFS only

This commit is contained in:
Maschell 2021-10-15 15:41:16 +02:00
parent eb74e76edd
commit 47cca1165d
24 changed files with 1657 additions and 1027 deletions

View File

@ -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__

View File

@ -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;
}
}
}
}

View File

@ -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;
};

View 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";
}

View 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;
};

View 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;
}

View 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;
};

View File

@ -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;

View File

@ -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>

View File

@ -17,6 +17,7 @@
#pragma once
#include <cstdint>
#include <optional>
#include "DiscReader.h"
class DiscReaderDiscDrive : public DiscReader {

326
source/WUDDumperState.cpp Normal file
View 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
View 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;
};

View File

@ -2,4 +2,7 @@
#include <cstdint>
extern int32_t gFSAfd;
extern int32_t gFSAfd;
#define SECTOR_SIZE 0x8000
#define READ_SECTOR_SIZE SECTOR_SIZE

View File

@ -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;
}

View File

@ -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;
};
#endif
const uint8_t *mem_file{};
uint64_t filesize{};
uint64_t pos{};
};

View 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
View 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
View 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
View 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;
};

View 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;
}

View 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;
};

View File

@ -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");
}
}

View File

@ -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];
}
}

View File

@ -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