WiiFlow_Lite/source/menu/menu_nandemu.cpp
overjoy.psm 37787dbcd9 NAND Emulation:
- Fixed Nand Emulation from SD-card.
- Fixed "CheckSave" functions.
- Fixed feature to flash a save file from emulated NAND back to real NAND.
- Some minor code cleanups and bug fixes.

neek2o: (neek2o r93+ only)
- Fixed return to channel function for titles on emulated NAND
- Added an option in wiiflow.ini: launchwiiflow in "NEEK2O" domain
  If this option is set to "no" the "exit to" option in home menu 
  will launch neek2o SM instead of the "DWFA" channel.
- If the return to channel option is set.. neek2o will return to 
  that specific title if that title exists on the emulated nand, even
  if the title was launched from real NAND.
2012-09-09 22:38:42 +00:00

907 lines
34 KiB
C++

#include "menu.hpp"
#include "defines.h"
#include "lockMutex.hpp"
#include "channel/nand.hpp"
#include "fileOps/fileOps.h"
#include "loader/cios.h"
#include "loader/nk.h"
#include "loader/sys.h"
// NandEmulation menu
u16 m_nandemuLblTitle;
u16 m_nandemuBtnBack;
u16 m_nandemuLblEmulationVal;
u16 m_nandemuLblEmulation;
u16 m_nandemuBtnEmulationM;
u16 m_nandemuBtnEmulationP;
u16 m_nandemuLblSaveDump;
u16 m_nandemuBtnAll;
u16 m_nandemuBtnMissing;
u16 m_nandemuLblNandDump;
u16 m_nandemuBtnNandDump;
u16 m_nandfileLblMessage;
u16 m_nandemuLblMessage;
u16 m_nandfileLblDialog;
u16 m_nandfinLblDialog;
u16 m_nandemuLblDialog;
u16 m_nandfilePBar;
u16 m_nandemuPBar;
u16 m_nandemuBtnExtract;
u16 m_nandemuBtnDisable;
u16 m_nandemuBtnPartition;
u16 m_nandemuLblInit;
u16 m_nandemuLblUser[4];
STexture m_nandemuBg;
bool m_nandext;
bool m_fulldump;
bool m_sgdump;
bool m_saveall;
static inline int loopNum(int i, int s)
{
return i < 0 ? (s - (-i % s)) % s : i % s;
}
static bool _saveExists(const char *path)
{
DIR *d;
d = opendir(path);
if(!d)
{
return false;
}
else
{
closedir(d);
return true;
}
}
bool CMenu::_TestEmuNand(int epart, const char *path, bool indept)
{
char basepath[64];
char testpath[MAX_FAT_PATH];
snprintf(basepath, sizeof(basepath), "%s:%s", DeviceName[epart], path);
DIR *d;
d = opendir(basepath);
if(!d)
return false;
else
closedir(d);
if(indept)
{
// Check Wiimotes && Region
snprintf(testpath, sizeof(testpath), "%s:%s/shared2/sys/SYSCONF", DeviceName[epart], path);
if(!fsop_FileExist(testpath))
return false;
snprintf(testpath, sizeof(testpath), "%s:%s/title/00000001/00000002/data/setting.txt", DeviceName[epart], path);
if(!fsop_FileExist(testpath))
return false;
// Check Mii's
snprintf(testpath, sizeof(testpath), "%s:%s/shared2/menu/FaceLib/RFL_DB.dat", DeviceName[epart], path);
if(!fsop_FileExist(testpath))
return false;
}
return true;
}
int CMenu::_FindEmuPart(string *emuPath, int part, bool searchvalid)
{
Nand::Instance()->Disable_Emu();
int emuPartition = -1;
string tmpPath;
if(m_current_view == COVERFLOW_CHANNEL)
{
emuPartition = m_cfg.getInt("NAND", "partition", 0);
tmpPath = m_cfg.getString("NAND", "path", "");
if(tmpPath.size() == 0)
{
m_cfg.setString("NAND", "path", STDEMU_DIR);
tmpPath = m_cfg.getString("NAND", "path", STDEMU_DIR);
}
}
else if(m_current_view == COVERFLOW_USB)
{
emuPartition = m_cfg.getInt("GAMES", "savepartition", -1);
if(emuPartition == -1)
emuPartition = m_cfg.getInt("NAND", "partition", 0);
tmpPath = m_cfg.getString("GAMES", "savepath", m_cfg.getString("NAND", "path", ""));
if(tmpPath.size() == 0)
{
m_cfg.setString("GAMES", "savepath", STDEMU_DIR);
tmpPath = m_cfg.getString("GAMES", "savepath", STDEMU_DIR);
}
}
if(!DeviceHandler::Instance()->IsInserted(emuPartition))
DeviceHandler::Instance()->Mount(emuPartition);
if(_TestEmuNand(emuPartition, tmpPath.c_str(), true) && DeviceHandler::Instance()->PartitionUsableForNandEmu(emuPartition))
{
*emuPath = tmpPath;
return emuPartition;
}
else
{
bool fllscn = emuPartition == -1;
for(u8 i = part; i <= USB8; ++i)
{
if(!DeviceHandler::Instance()->IsInserted(i))
DeviceHandler::Instance()->Mount(i);
if(!DeviceHandler::Instance()->PartitionUsableForNandEmu(i))
continue;
if(_TestEmuNand(i, tmpPath.c_str(), true) || searchvalid)
{
if(m_current_view == COVERFLOW_CHANNEL)
m_cfg.setInt("NAND", "partition", i);
else if(m_current_view == COVERFLOW_USB)
m_cfg.setInt("GAMES", "savepartition", i);
*emuPath = tmpPath;
m_cfg.save();
return i;
}
if(i == USB8 && !fllscn)
{
i = -1;
fllscn = true;
}
}
}
return -1;
}
bool CMenu::_checkSave(string id, bool nand)
{
int savePath = id.c_str()[0] << 24 | id.c_str()[1] << 16 | id.c_str()[2] << 8 | id.c_str()[3];
if(nand)
{
u32 temp = 0;
if(ISFS_ReadDir(sfmt("/title/00010000/%08x", savePath).c_str(), NULL, &temp) < 0)
if(ISFS_ReadDir(sfmt("/title/00010004/%08x", savePath).c_str(), NULL, &temp) < 0)
return false;
}
else
{
int emuPartition = m_cfg.getInt("GAMES", "savepartition", -1);
string emuPath = m_cfg.getString("GAMES", "savepath", "");
if(emuPartition < 0 || emuPath.size() == 0)
return false;
struct stat fstat;
if((stat(sfmt("%s:%s/title/00010000/%08x", DeviceName[emuPartition], emuPath.c_str(), savePath).c_str(), &fstat) != 0 )
&& (stat(sfmt("%s:%s/title/00010004/%08x", DeviceName[emuPartition], emuPath.c_str(), savePath).c_str(), &fstat) != 0))
return false;
}
return true;
}
void CMenu::_enableNandEmu(bool fromconfig)
{
_cfNeedsUpdate();
bool disable = (m_cfg.getBool("NAND", "disable", true) || neek2o()) && m_current_view == COVERFLOW_CHANNEL && !m_tempView;
if(!disable)
{
bool isD2XnewerThanV6 = (CurrentIOS.Type == IOS_TYPE_NEEK2O);
if(CurrentIOS.Revision > 6 && CurrentIOS.Type == IOS_TYPE_D2X)
isD2XnewerThanV6 = true;
if(m_current_view == COVERFLOW_CHANNEL && !m_cfg.getBool("NAND", "disable", true) && !neek2o() && !m_tempView)
Nand::Instance()->Enable_Emu();
u8 limiter = 0;
s8 direction = m_btnMgr.selected(m_configBtnPartitionP) ? 1 : -1;
if (!fromconfig)
direction = 0;
currentPartition = loopNum(currentPartition + direction, (int)USB8);
while(!DeviceHandler::Instance()->IsInserted(currentPartition) ||
(m_current_view == COVERFLOW_CHANNEL && (DeviceHandler::Instance()->GetFSType(currentPartition) != PART_FS_FAT ||
(!isD2XnewerThanV6 && DeviceHandler::Instance()->PathToDriveType(m_appDir.c_str()) == currentPartition) ||
(!isD2XnewerThanV6 && DeviceHandler::Instance()->PathToDriveType(m_dataDir.c_str()) == currentPartition))) ||
((m_current_view == COVERFLOW_HOMEBREW || m_current_view == COVERFLOW_DML) && DeviceHandler::Instance()->GetFSType(currentPartition) == PART_FS_WBFS))
{
currentPartition = loopNum(currentPartition + direction, (int)USB8);
if (limiter > 10) break;
limiter++;
}
gprintf("Next item: %s\n", DeviceName[currentPartition]);
if(m_tempView)
m_cfg.setInt("GAMES", "savepartition", currentPartition);
else
m_cfg.setInt(_domainFromView(), "partition", currentPartition);
}
}
void CMenu::_setDumpMsg(const wstringEx &msg, float totprog, float fileprog)
{
if(m_thrdStop) return;
if(msg != L"...") m_thrdMessage = msg;
m_thrdMessageAdded = true;
m_thrdProgress = totprog;
m_fileProgress = fileprog;
}
void CMenu::_ShowProgress(int dumpstat, int dumpprog, int filesize, int fileprog, int files, int folders, char *tmess, void *user_data)
{
CMenu &m = *(CMenu *)user_data;
m.m_progress = dumpprog == 0 ? 0.f : (float)dumpstat / (float)dumpprog;
m.m_fprogress = filesize == 0 ? 0.f : (float)fileprog / (float)filesize;
m.m_fileprog = fileprog;
m.m_filesize = filesize;
m.m_filesdone = files;
m.m_foldersdone = folders;
LWP_MutexLock(m.m_mutex);
if(m_nandext)
m._setDumpMsg(wfmt(m._fmt("cfgne9", L"Current file: %s"), tmess), m.m_progress, m.m_fprogress);
else
m._setDumpMsg(L"...", m.m_progress, m.m_fprogress);
LWP_MutexUnlock(m.m_mutex);
}
void CMenu::_hideNandEmu(bool instant)
{
m_btnMgr.hide(m_nandemuLblTitle, instant);
m_btnMgr.hide(m_nandemuBtnBack, instant);
m_btnMgr.hide(m_nandfilePBar, instant);
m_btnMgr.hide(m_nandemuPBar, instant);
m_btnMgr.hide(m_nandfileLblMessage, instant);
m_btnMgr.hide(m_nandemuLblMessage, instant);
m_btnMgr.hide(m_nandfileLblDialog, instant);
m_btnMgr.hide(m_nandemuLblDialog, instant);
m_btnMgr.hide(m_nandfinLblDialog, instant);
m_btnMgr.hide(m_nandemuLblEmulationVal, instant);
m_btnMgr.hide(m_nandemuLblEmulation, instant);
m_btnMgr.hide(m_nandemuBtnEmulationP, instant);
m_btnMgr.hide(m_nandemuBtnEmulationM, instant);
m_btnMgr.hide(m_nandemuLblSaveDump, instant);
m_btnMgr.hide(m_nandemuBtnAll, instant);
m_btnMgr.hide(m_nandemuBtnMissing, instant);
m_btnMgr.hide(m_nandemuLblNandDump, instant);
m_btnMgr.hide(m_nandemuBtnNandDump, instant);
m_btnMgr.hide(m_nandemuBtnExtract, instant);
m_btnMgr.hide(m_nandemuBtnPartition, instant);
m_btnMgr.hide(m_nandemuBtnDisable, instant);
m_btnMgr.hide(m_nandemuLblInit, instant);
}
void CMenu::_showNandEmu(void)
{
_setBg(m_nandemuBg, m_nandemuBg);
m_btnMgr.setText(m_nandemuLblTitle, _t("cfgne10", L"NAND Emulation Settings"));
m_btnMgr.show(m_nandemuLblTitle);
m_btnMgr.show(m_nandemuBtnBack);
int i;
if(((m_current_view == COVERFLOW_CHANNEL && !m_cfg.getBool("NAND", "disable", true)) || m_current_view == COVERFLOW_USB) && !m_locked)
{
m_btnMgr.show(m_nandemuLblEmulation);
m_btnMgr.show(m_nandemuLblEmulationVal);
m_btnMgr.show(m_nandemuBtnEmulationP);
m_btnMgr.show(m_nandemuBtnEmulationM);
}
if((m_current_view == COVERFLOW_CHANNEL || m_current_view == COVERFLOW_USB) && !m_locked)
{
m_btnMgr.show(m_nandemuLblSaveDump);
m_btnMgr.show(m_nandemuBtnAll);
m_btnMgr.show(m_nandemuBtnMissing);
m_btnMgr.show(m_nandemuLblNandDump);
m_btnMgr.show(m_nandemuBtnNandDump);
if (m_current_view == COVERFLOW_CHANNEL)
{
i = min(max(0, m_cfg.getInt("NAND", "emulation", 0)), (int)ARRAY_SIZE(CMenu::_NandEmu) - 1);
m_btnMgr.setText(m_nandemuLblEmulationVal, _t(CMenu::_NandEmu[i].id, CMenu::_NandEmu[i].text));
}
else if (m_current_view == COVERFLOW_USB)
{
i = min(max(0, m_cfg.getInt("GAMES", "save_emulation", 0)), (int)ARRAY_SIZE(CMenu::_GlobalSaveEmu) - 1);
m_btnMgr.setText(m_nandemuLblEmulationVal, _t(CMenu::_GlobalSaveEmu[i].id, CMenu::_GlobalSaveEmu[i].text));
}
}
}
int CMenu::_NandEmuCfg(void)
{
lwp_t thread = 0;
SetupInput();
_showNandEmu();
m_thrdStop = false;
m_thrdMessageAdded = false;
m_nandext = false;
while(!m_exit)
{
_mainLoopCommon();
if((BTN_HOME_PRESSED || BTN_B_PRESSED) && !m_thrdWorking)
break;
else if(BTN_UP_PRESSED)
m_btnMgr.up();
else if(BTN_DOWN_PRESSED)
m_btnMgr.down();
else if (BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnEmulationP) || m_btnMgr.selected(m_nandemuBtnEmulationM)))
{
s8 direction = m_btnMgr.selected(m_nandemuBtnEmulationP) ? 1 : -1;
if(m_current_view == COVERFLOW_CHANNEL)
m_cfg.setInt("NAND", "emulation", (int)loopNum((u32)m_cfg.getInt("NAND", "emulation", 0) + direction, ARRAY_SIZE(CMenu::_NandEmu)));
else if(m_current_view == COVERFLOW_USB)
m_cfg.setInt("GAMES", "save_emulation", (int)loopNum((u32)m_cfg.getInt("GAMES", "save_emulation", 0) + direction, ARRAY_SIZE(CMenu::_GlobalSaveEmu)));
_showNandEmu();
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnNandDump) || m_btnMgr.selected(m_nandemuBtnAll) || m_btnMgr.selected(m_nandemuBtnMissing)))
{
m_fulldump = m_btnMgr.selected(m_nandemuBtnNandDump) ? true : false;
m_saveall = m_btnMgr.selected(m_nandemuBtnAll) ? true : false;
m_btnMgr.hide(m_nandemuBtnBack);
m_btnMgr.hide(m_nandemuLblEmulationVal);
m_btnMgr.hide(m_nandemuLblEmulation);
m_btnMgr.hide(m_nandemuBtnEmulationP);
m_btnMgr.hide(m_nandemuBtnEmulationM);
m_btnMgr.hide(m_nandemuLblSaveDump);
m_btnMgr.hide(m_nandemuBtnAll);
m_btnMgr.hide(m_nandemuBtnMissing);
m_btnMgr.hide(m_nandemuLblNandDump);
m_btnMgr.hide(m_nandemuBtnNandDump);
m_btnMgr.show(m_nandfilePBar);
m_btnMgr.show(m_nandemuPBar);
m_btnMgr.show(m_nandfileLblMessage);
m_btnMgr.show(m_nandemuLblMessage);
m_btnMgr.show(m_nandfileLblDialog);
m_btnMgr.show(m_nandemuLblDialog);
m_btnMgr.setText(m_nandemuLblMessage, L"");
m_btnMgr.setText(m_nandfileLblMessage, L"");
m_btnMgr.setText(m_nandemuLblDialog, _t("cfgne11", L"Overall Progress:"));
if(m_fulldump)
m_btnMgr.setText(m_nandemuLblTitle, _t("cfgne12", L"NAND Extractor"));
else
m_btnMgr.setText(m_nandemuLblTitle, _t("cfgne13", L"Game Save Extractor"));
m_thrdStop = false;
m_thrdProgress = 0.f;
m_thrdWorking = true;
LWP_CreateThread(&thread, (void *(*)(void *))CMenu::_NandDumper, (void *)this, 0, 32768, 40);
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnBack)))
{
m_cfg.save();
break;
}
if (m_thrdMessageAdded)
{
LockMutex lock(m_mutex);
m_thrdMessageAdded = false;
if (!m_thrdMessage.empty())
m_btnMgr.setText(m_nandfileLblDialog, m_thrdMessage);
m_btnMgr.setProgress(m_nandfilePBar, m_fileProgress);
m_btnMgr.setProgress(m_nandemuPBar, m_thrdProgress);
m_btnMgr.setText(m_nandfileLblMessage, wfmt(_fmt("fileprogress", L"%d / %dKB"), m_fileprog/0x400, m_filesize/0x400));
m_btnMgr.setText(m_nandemuLblMessage, wfmt(_fmt("dumpprogress", L"%i%%"), (int)(m_thrdProgress*100.f)));
if (!m_thrdWorking)
{
if(m_sgdump)
m_btnMgr.setText(m_nandfinLblDialog, wfmt(_fmt("cfgne14", L"Extracted: %d saves / %d files / %d folders"), m_nandexentry, m_filesdone, m_foldersdone));
else
m_btnMgr.setText(m_nandfinLblDialog, wfmt(_fmt("cfgne15", L"Extracted: %d files / %d folders"), m_filesdone, m_foldersdone));
if(m_dumpsize/0x400 > 0x270f)
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne16", L"Total size: %uMB (%d blocks)"), (m_dumpsize/0x100000), (m_dumpsize/0x8000)>>2));
else
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne17", L"Total size: %uKB (%d blocks)"), (m_dumpsize/0x400), (m_dumpsize/0x8000)>>2));
m_btnMgr.show(m_nandemuBtnBack);
m_btnMgr.show(m_nandfinLblDialog);
}
}
}
_hideNandEmu();
return 0;
}
int CMenu::_FlashSave(string gameId)
{
int emuPartition = m_cfg.getInt("GAMES", "savepartition", m_cfg.getInt("NAND", "partition", 0));
char basepath[MAX_FAT_PATH];
snprintf(basepath, sizeof(basepath), "%s:%s", DeviceName[emuPartition], m_cfg.getString("GAMES", "savepath", m_cfg.getString("NAND", "path", "")).c_str());
if(!_checkSave(gameId, false))
return 0;
lwp_t thread = 0;
SetupInput();
m_thrdStop = false;
m_thrdMessageAdded = false;
m_nandext = false;
m_saveExtGameId = gameId;
while(!m_exit)
{
_mainLoopCommon();
if(m_forceext)
{
m_forceext = false;
m_btnMgr.hide(m_nandemuLblInit);
m_btnMgr.show(m_nandemuLblTitle);
m_btnMgr.show(m_nandfilePBar);
m_btnMgr.show(m_nandemuPBar);
m_btnMgr.show(m_nandfileLblMessage);
m_btnMgr.show(m_nandemuLblMessage);
m_btnMgr.show(m_nandfileLblDialog);
m_btnMgr.show(m_nandemuLblDialog);
m_btnMgr.setText(m_nandemuLblMessage, L"");
m_btnMgr.setText(m_nandfileLblMessage, L"");
m_btnMgr.setText(m_nandemuLblDialog, _t("cfgne11", L"Overall Progress:"));
m_btnMgr.setText(m_nandemuLblTitle, _t("cfgne28", L"Game Save Flasher"));
m_thrdStop = false;
m_thrdProgress = 0.f;
m_thrdWorking = true;
LWP_CreateThread(&thread, (void *(*)(void *))CMenu::_NandFlasher, (void *)this, 0, 32768, 40);
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnBack)))
{
m_cfg.save();
_hideNandEmu();
return 1;
}
if(m_thrdMessageAdded)
{
LockMutex lock(m_mutex);
m_thrdMessageAdded = false;
if (!m_thrdMessage.empty())
m_btnMgr.setText(m_nandfileLblDialog, m_thrdMessage);
m_btnMgr.setProgress(m_nandfilePBar, m_fileProgress);
m_btnMgr.setProgress(m_nandemuPBar, m_thrdProgress);
m_btnMgr.setText(m_nandfileLblMessage, wfmt(_fmt("fileprogress", L"%d / %dKB"), m_fileprog/0x400, m_filesize/0x400));
m_btnMgr.setText(m_nandemuLblMessage, wfmt(_fmt("dumpprogress", L"%i%%"), (int)(m_thrdProgress*100.f)));
if (!m_thrdWorking)
{
m_btnMgr.setText(m_nandfinLblDialog, wfmt(_fmt("cfgne29", L"Flashed: %d saves / %d files / %d folders"), m_nandexentry, m_filesdone, m_foldersdone));
if(m_dumpsize/0x400 > 0x270f)
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne16", L"Total size: %uMB (%d blocks)"), (m_dumpsize/0x100000), (m_dumpsize/0x8000)>>2));
else
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne17", L"Total size: %uKB (%d blocks)"), (m_dumpsize/0x400), (m_dumpsize/0x8000)>>2));
m_btnMgr.show(m_nandemuBtnBack);
m_btnMgr.show(m_nandfinLblDialog);
}
}
}
_hideNandEmu();
return 0;
}
int CMenu::_AutoExtractSave(string gameId)
{
string emuPath;
if(m_current_view == COVERFLOW_CHANNEL)
m_partRequest = m_cfg.getInt("NAND", "partition", -1);
else if(m_current_view == COVERFLOW_USB)
m_partRequest = m_cfg.getInt("GAMES", "savepartition", -1);
int emuPartition = _FindEmuPart(&emuPath, m_partRequest, false);
if(emuPartition < 0)
emuPartition = _FindEmuPart(&emuPath, m_partRequest, true);
if(!_checkSave(gameId, true))
return 1;
if(!m_forceext && _checkSave(gameId, false))
return 1;
lwp_t thread = 0;
SetupInput();
m_thrdStop = false;
m_thrdMessageAdded = false;
m_nandext = false;
if(!m_forceext)
{
m_btnMgr.setText(m_nandemuBtnExtract, _t("cfgne24", L"Extract save"));
m_btnMgr.setText(m_nandemuBtnDisable, _t("cfgne25", L"Create new save"));
m_btnMgr.setText(m_nandemuLblInit, _t("cfgne26", L"A save file for this game was created on real NAND. Extract existing save file from real NAND or create new file for NAND Emulation?"));
m_btnMgr.show(m_nandemuBtnExtract);
m_btnMgr.show(m_nandemuBtnDisable);
m_btnMgr.show(m_nandemuLblInit);
}
m_saveExtGameId = gameId;
while(!m_exit)
{
_mainLoopCommon();
if((BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnExtract))) || m_forceext)
{
m_forceext = false;
m_fulldump = false;
m_btnMgr.hide(m_nandemuBtnExtract);
m_btnMgr.hide(m_nandemuBtnDisable);
m_btnMgr.hide(m_nandemuLblInit);
m_btnMgr.show(m_nandemuLblTitle);
m_btnMgr.show(m_nandfilePBar);
m_btnMgr.show(m_nandemuPBar);
m_btnMgr.show(m_nandfileLblMessage);
m_btnMgr.show(m_nandemuLblMessage);
m_btnMgr.show(m_nandfileLblDialog);
m_btnMgr.show(m_nandemuLblDialog);
m_btnMgr.setText(m_nandemuLblMessage, L"");
m_btnMgr.setText(m_nandfileLblMessage, L"");
m_btnMgr.setText(m_nandemuLblDialog, _t("cfgne11", L"Overall Progress:"));
m_btnMgr.setText(m_nandemuLblTitle, _t("cfgne13", L"Game Save Extractor"));
m_thrdStop = false;
m_thrdProgress = 0.f;
m_thrdWorking = true;
LWP_CreateThread(&thread, (void *(*)(void *))CMenu::_NandDumper, (void *)this, 0, 32768, 40);
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnDisable)))
{
char basepath[MAX_FAT_PATH];
snprintf(basepath, sizeof(basepath), "%s:%s", DeviceName[emuPartition], emuPath.c_str());
Nand::Instance()->CreatePath("%s/import", basepath);
Nand::Instance()->CreatePath("%s/meta", basepath);
Nand::Instance()->CreatePath("%s/shared1", basepath);
Nand::Instance()->CreatePath("%s/shared2", basepath);
Nand::Instance()->CreatePath("%s/sys", basepath);
Nand::Instance()->CreatePath("%s/title", basepath);
Nand::Instance()->CreatePath("%s/ticket", basepath);
Nand::Instance()->CreatePath("%s/tmp", basepath);
_hideNandEmu();
return 0;
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnBack)))
{
m_cfg.save();
_hideNandEmu();
return 1;
}
if(m_thrdMessageAdded)
{
LockMutex lock(m_mutex);
m_thrdMessageAdded = false;
if (!m_thrdMessage.empty())
m_btnMgr.setText(m_nandfileLblDialog, m_thrdMessage);
m_btnMgr.setProgress(m_nandfilePBar, m_fileProgress);
m_btnMgr.setProgress(m_nandemuPBar, m_thrdProgress);
m_btnMgr.setText(m_nandfileLblMessage, wfmt(_fmt("fileprogress", L"%d / %dKB"), m_fileprog/0x400, m_filesize/0x400));
m_btnMgr.setText(m_nandemuLblMessage, wfmt(_fmt("dumpprogress", L"%i%%"), (int)(m_thrdProgress*100.f)));
if (!m_thrdWorking)
{
m_btnMgr.setText(m_nandfinLblDialog, wfmt(_fmt("cfgne14", L"Extracted: %d saves / %d files / %d folders"), m_nandexentry, m_filesdone, m_foldersdone));
if(m_dumpsize/0x400 > 0x270f)
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne16", L"Total size: %uMB (%d blocks)"), (m_dumpsize/0x100000), (m_dumpsize/0x8000)>>2));
else
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne17", L"Total size: %uKB (%d blocks)"), (m_dumpsize/0x400), (m_dumpsize/0x8000)>>2));
m_btnMgr.show(m_nandemuBtnBack);
m_btnMgr.show(m_nandfinLblDialog);
}
}
}
_hideNandEmu();
return 0;
}
int CMenu::_AutoCreateNand(void)
{
lwp_t thread = 0;
SetupInput();
string emuPath;
m_thrdStop = false;
m_thrdMessageAdded = false;
m_nandext = false;
m_tempView = false;
m_btnMgr.setText(m_nandemuBtnExtract, _t("cfgne5", L"Extract NAND"));
m_btnMgr.setText(m_nandemuBtnDisable, _t("cfgne22", L"Disable NAND Emulation"));
m_btnMgr.setText(m_nandemuBtnPartition, _t("cfgne31", L"Select Partition"));
m_btnMgr.setText(m_nandemuLblInit, _t("cfgne23", L"Welcome to WiiFlow. I have not found a valid NAND for NAND Emulation. Click Extract to extract your NAND, or click disable to disable NAND Emulation."));
m_btnMgr.show(m_nandemuBtnExtract);
m_btnMgr.show(m_nandemuBtnDisable);
m_btnMgr.show(m_nandemuBtnPartition);
m_btnMgr.show(m_nandemuLblInit);
while(!m_exit)
{
_mainLoopCommon();
if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnExtract)))
{
m_fulldump = true;
m_btnMgr.hide(m_nandemuBtnExtract);
m_btnMgr.hide(m_nandemuBtnDisable);
m_btnMgr.hide(m_nandemuBtnPartition);
m_btnMgr.hide(m_nandemuLblInit);
m_btnMgr.show(m_nandemuLblTitle);
m_btnMgr.show(m_nandfilePBar);
m_btnMgr.show(m_nandemuPBar);
m_btnMgr.show(m_nandfileLblMessage);
m_btnMgr.show(m_nandemuLblMessage);
m_btnMgr.show(m_nandfileLblDialog);
m_btnMgr.show(m_nandemuLblDialog);
m_btnMgr.setText(m_nandemuLblMessage, L"");
m_btnMgr.setText(m_nandfileLblMessage, L"");
m_btnMgr.setText(m_nandemuLblDialog, _t("cfgne11", L"Overall Progress:"));
m_btnMgr.setText(m_nandemuLblTitle, _t("cfgne12", L"NAND Extractor"));
m_thrdStop = false;
m_thrdProgress = 0.f;
m_thrdWorking = true;
LWP_CreateThread(&thread, (void *(*)(void *))CMenu::_NandDumper, (void *)this, 0, 32768, 40);
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnDisable)))
{
_hideNandEmu();
return 0;
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnPartition)))
{
if(m_current_view == COVERFLOW_USB)
{
m_tempView = true;
m_current_view = COVERFLOW_CHANNEL;
}
_hideNandEmu();
_config(1);
if(m_tempView)
{
m_current_view = COVERFLOW_USB;
m_tempView = false;
return 0;
}
return 1;
}
else if(BTN_A_PRESSED && (m_btnMgr.selected(m_nandemuBtnBack)))
{
m_cfg.save();
_hideNandEmu();
return 1;
}
if(m_thrdMessageAdded)
{
LockMutex lock(m_mutex);
m_thrdMessageAdded = false;
if (!m_thrdMessage.empty())
m_btnMgr.setText(m_nandfileLblDialog, m_thrdMessage);
m_btnMgr.setProgress(m_nandfilePBar, m_fileProgress);
m_btnMgr.setProgress(m_nandemuPBar, m_thrdProgress);
m_btnMgr.setText(m_nandfileLblMessage, wfmt(_fmt("fileprogress", L"%d / %dKB"), m_fileprog/0x400, m_filesize/0x400));
m_btnMgr.setText(m_nandemuLblMessage, wfmt(_fmt("dumpprogress", L"%i%%"), (int)(m_thrdProgress*100.f)));
if (!m_thrdWorking)
{
m_btnMgr.setText(m_nandfinLblDialog, wfmt(_fmt("cfgne15", L"Extracted: %d files / %d folders"), m_filesdone, m_foldersdone));
if(m_dumpsize/0x400 > 0x270f)
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne16", L"Total size: %uMB (%d blocks)"), (m_dumpsize/0x100000), (m_dumpsize/0x8000)>>2));
else
m_btnMgr.setText(m_nandemuLblDialog, wfmt(_fmt("cfgne17", L"Total size: %uKB (%d blocks)"), (m_dumpsize/0x400), (m_dumpsize/0x8000)>>2));
m_btnMgr.show(m_nandemuBtnBack);
m_btnMgr.show(m_nandfinLblDialog);
}
}
}
_hideNandEmu();
return 0;
}
int CMenu::_NandFlasher(void *obj)
{
CMenu &m = *(CMenu *)obj;
string emuPath;
char source[MAX_FAT_PATH];
char dest[ISFS_MAXPATH];
if(m.m_current_view == COVERFLOW_CHANNEL)
m.m_partRequest = m.m_cfg.getInt("NAND", "partition", -1);
else if(m.m_current_view == COVERFLOW_USB)
m.m_partRequest = m.m_cfg.getInt("GAMES", "savepartition", -1);
int emuPartition = m._FindEmuPart(&emuPath, m.m_partRequest, false);
int flashID = m.m_saveExtGameId.c_str()[0] << 24 | m.m_saveExtGameId.c_str()[1] << 16 | m.m_saveExtGameId.c_str()[2] << 8 | m.m_saveExtGameId.c_str()[3];
if(_saveExists(sfmt("%s:%s/title/00010000/%08x", DeviceName[emuPartition], emuPath.c_str(), flashID).c_str()))
{
snprintf(source, sizeof(source), "%s:%s/title/00010000/%08x", DeviceName[emuPartition], emuPath.c_str(), flashID);
snprintf(dest, sizeof(dest), "/title/00010000/%08x", flashID);
}
else if(_saveExists(sfmt("%s:%s/title/00010004/%08x", DeviceName[emuPartition], emuPath.c_str(), flashID).c_str()))
{
snprintf(source, sizeof(source), "%s:%s/title/00010004/%08x", DeviceName[emuPartition], emuPath.c_str(), flashID);
snprintf(dest, sizeof(dest), "/title/00010004/%08x", flashID);
}
Nand::Instance()->ResetCounters();
m.m_nandexentry = 1;
m.m_dumpsize = Nand::Instance()->CalcFlashSize(source, CMenu::_ShowProgress, obj);
m_nandext = true;
Nand::Instance()->FlashToNAND(source, dest, CMenu::_ShowProgress, obj);
m.m_thrdWorking = false;
LWP_MutexLock(m.m_mutex);
m.m_btnMgr.hide(m_nandfilePBar);
m.m_btnMgr.hide(m_nandfileLblMessage);
m._setDumpMsg(m._t("cfgne30", L"Flashing save files finished!"), 1.f, 1.f);
LWP_MutexUnlock(m.m_mutex);
return 0;
}
int CMenu::_NandDumper(void *obj)
{
CMenu &m = *(CMenu *)obj;
string emuPath;
int emuPartition = -1;
m_nandext = false;
m_sgdump = false;
m.m_dumpsize = 0;
m.m_filesdone = 0;
m.m_foldersdone = 0;
Nand::Instance()->ResetCounters();
if(m.m_current_view == COVERFLOW_CHANNEL)
m.m_partRequest = m.m_cfg.getInt("NAND", "partition", -1);
else if(m.m_current_view == COVERFLOW_USB)
m.m_partRequest = m.m_cfg.getInt("GAMES", "savepartition", -1);
emuPartition = m._FindEmuPart(&emuPath, m.m_partRequest, true);
if(emuPartition < 0)
{
m.error(m._t("cfgne8", L"No valid FAT partition found for NAND Emulation!"));
return 0;
}
char basepath[64];
snprintf(basepath, sizeof(basepath), "%s:%s", DeviceName[emuPartition], emuPath.c_str());
LWP_MutexLock(m.m_mutex);
m._setDumpMsg(m._t("cfgne27", L"Calculating space needed for extraction..."), 0.f, 0.f);
LWP_MutexUnlock(m.m_mutex);
if(m_fulldump)
{
m.m_dumpsize = Nand::Instance()->CalcDumpSpace("/", CMenu::_ShowProgress, obj);
m_nandext = true;
Nand::Instance()->DoNandDump("/", basepath, CMenu::_ShowProgress, obj);
}
else
{
bool missingOnly = !m_saveall;
vector<string> saveList;
m_sgdump = true;
if(m.m_saveExtGameId.empty())
{
m.m_nandexentry = 0;
saveList.reserve(m.m_gameList.size());
for(u32 i = 0; i < m.m_gameList.size() && !m.m_thrdStop; ++i)
{
LWP_MutexLock(m.m_mutex);
m._setDumpMsg(m._t("cfgne18", L"Listing game saves to extract..."), 0.f, 0.f);
LWP_MutexUnlock(m.m_mutex);
string id((const char *)m.m_gameList[i].id, 4);
if(!missingOnly || !m._checkSave(id, false))
{
if(m._checkSave(id, true))
{
m.m_nandexentry++;
saveList.push_back(id);
}
}
}
}
else
{
m.m_nandexentry = 1;
saveList.push_back(m.m_saveExtGameId);
}
for(u32 i = 0; i < saveList.size() && !m.m_thrdStop; ++i)
{
char source[ISFS_MAXPATH];
int savePath = saveList[i].c_str()[0] << 24 | saveList[i].c_str()[1] << 16 | saveList[i].c_str()[2] << 8 | saveList[i].c_str()[3];
snprintf(source, sizeof(source), "/title/00010000/%08x", savePath);
if(!m._checkSave(saveList[i], true))
snprintf(source, sizeof(source), "/title/00010004/%08x", savePath);
m.m_dumpsize = Nand::Instance()->CalcDumpSpace(source, CMenu::_ShowProgress, obj);
}
for(u32 i = 0; i < saveList.size() && !m.m_thrdStop; ++i)
{
char source[ISFS_MAXPATH];
int savePath = saveList[i].c_str()[0] << 24 | saveList[i].c_str()[1] << 16 | saveList[i].c_str()[2] << 8 | saveList[i].c_str()[3];
snprintf(source, sizeof(source), "/title/00010000/%08x", savePath);
if(!m._checkSave(saveList[i], true))
snprintf(source, sizeof(source), "/title/00010004/%08x", savePath);
m_nandext = true;
Nand::Instance()->DoNandDump(source, basepath, CMenu::_ShowProgress, obj);
}
}
m.m_thrdWorking = false;
LWP_MutexLock(m.m_mutex);
m.m_btnMgr.hide(m_nandfilePBar);
m.m_btnMgr.hide(m_nandfileLblMessage);
m._setDumpMsg(m._t("cfgne19", L"Extraction finished!"), 1.f, 1.f);
LWP_MutexUnlock(m.m_mutex);
return 0;
}
void CMenu::_initNandEmuMenu(CMenu::SThemeData &theme)
{
_addUserLabels(theme, m_nandemuLblUser, ARRAY_SIZE(m_nandemuLblUser), "NANDEMU");
m_nandemuBg = _texture(theme.texSet, "NANDEMU/BG", "texture", theme.bg);
m_nandemuLblTitle = _addTitle(theme, "NANDEMU/TITLE", theme.titleFont, L"", 20, 30, 600, 60, theme.titleFontColor, FTGX_JUSTIFY_CENTER | FTGX_ALIGN_MIDDLE);
m_nandfileLblMessage = _addLabel(theme, "NANDEMU/FMESSAGE", theme.lblFont, L"", 40, 230, 560, 100, theme.lblFontColor, FTGX_JUSTIFY_CENTER | FTGX_ALIGN_TOP);
m_nandemuLblMessage = _addLabel(theme, "NANDEMU/MESSAGE", theme.lblFont, L"", 40, 350, 560, 100, theme.lblFontColor, FTGX_JUSTIFY_CENTER | FTGX_ALIGN_TOP);
m_nandfileLblDialog = _addLabel(theme, "NANDEMU/FDIALOG", theme.lblFont, L"", 40, 60, 560, 200, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE);
m_nandfinLblDialog = _addLabel(theme, "NANDEMU/FINDIALOG", theme.lblFont, L"", 40, 120, 560, 200, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE);
m_nandemuLblDialog = _addLabel(theme, "NANDEMU/DIALOG", theme.lblFont, L"", 40, 180, 560, 200, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE);
m_nandfilePBar = _addProgressBar(theme, "NANDEMU/FILEPROGRESS_BAR", 40, 200, 560, 20);
m_nandemuPBar = _addProgressBar(theme, "NANDEMU/PROGRESS_BAR", 40, 320, 560, 20);
m_nandemuLblEmulation = _addLabel(theme, "NANDEMU/EMU_SAVE", theme.lblFont, L"", 40, 130, 340, 56, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE);
m_nandemuLblEmulationVal = _addLabel(theme, "NANDEMU/EMU_SAVE_BTN_GLOBAL", theme.btnFont, L"", 400, 130, 144, 56, theme.btnFontColor, FTGX_JUSTIFY_CENTER | FTGX_ALIGN_MIDDLE, theme.btnTexC);
m_nandemuBtnEmulationM = _addPicButton(theme, "NANDEMU/EMU_SAVE_MINUS", theme.btnTexMinus, theme.btnTexMinusS, 344, 130, 56, 56);
m_nandemuBtnEmulationP = _addPicButton(theme, "NANDEMU/EMU_SAVE_PLUS", theme.btnTexPlus, theme.btnTexPlusS, 544, 130, 56, 56);
m_nandemuLblSaveDump = _addLabel(theme, "NANDEMU/SAVE_DUMP", theme.lblFont, L"", 40, 190, 340, 56, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE);
m_nandemuBtnAll = _addButton(theme, "NANDEMU/ALL_BTN", theme.btnFont, L"", 350, 190, 250, 56, theme.btnFontColor);
m_nandemuBtnMissing = _addButton(theme, "NANDEMU/MISSING_BTN", theme.btnFont, L"", 350, 250, 250, 56, theme.btnFontColor);
m_nandemuLblNandDump = _addLabel(theme, "NANDEMU/NAND_DUMP", theme.lblFont, L"", 40, 310, 340, 56, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE);
m_nandemuBtnNandDump = _addButton(theme, "NANDEMU/NAND_DUMP_BTN", theme.btnFont, L"", 350, 310, 250, 56, theme.btnFontColor);
m_nandemuBtnBack = _addButton(theme, "NANDEMU/BACK_BTN", theme.btnFont, L"", 420, 400, 200, 56, theme.btnFontColor);
m_nandemuBtnExtract = _addButton(theme, "NANDEMU/EXTRACT", theme.titleFont, L"", 72, 180, 496, 56, theme.titleFontColor);
m_nandemuBtnDisable = _addButton(theme, "NANDEMU/DISABLE", theme.titleFont, L"", 72, 270, 496, 56, theme.titleFontColor);
m_nandemuBtnPartition = _addButton(theme, "NANDEMU/PARTITION", theme.titleFont, L"", 72, 360, 496, 56, theme.titleFontColor);
m_nandemuLblInit = _addLabel(theme, "NANDEMU/INIT", theme.lblFont, L"", 40, 40, 560, 140, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE);
_setHideAnim(m_nandemuLblTitle, "NANDEMU/TITLE", 0, -100, 0.f, 0.f);
_setHideAnim(m_nandfileLblMessage, "NANDEMU/FMESSAGE", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuLblMessage, "NANDEMU/MESSAGE", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandfileLblDialog, "NANDEMU/FDIALOG", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandfinLblDialog, "NANDEMU/FINDIALOG", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuLblDialog, "NANDEMU/DIALOG", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandfilePBar, "NANDEMU/FILEPROGRESS_BAR", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuPBar, "NANDEMU/PROGRESS_BAR", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuLblEmulation, "NANDEMU/EMU_SAVE", 100, 0, -2.f, 0.f);
_setHideAnim(m_nandemuLblEmulationVal, "NANDEMU/EMU_SAVE_BTN_GLOBAL", 0, 0, 1.f, -1.f);
_setHideAnim(m_nandemuBtnEmulationM, "NANDEMU/EMU_SAVE_MINUS", 0, 0, 1.f, -1.f);
_setHideAnim(m_nandemuBtnEmulationP, "NANDEMU/EMU_SAVE_PLUS", 0, 0, 1.f, -1.f);
_setHideAnim(m_nandemuLblSaveDump, "NANDEMU/SAVE_DUMP", 100, 0, -2.f, 0.f);
_setHideAnim(m_nandemuBtnAll, "NANDEMU/ALL_BTN", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuBtnMissing, "NANDEMU/MISSING_BTN", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuLblNandDump, "NANDEMU/NAND_DUMP", 100, 0, -2.f, 0.f);
_setHideAnim(m_nandemuBtnNandDump, "NANDEMU/NAND_DUMP_BTN", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuBtnBack, "NANDEMU/BACK_BTN", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuBtnExtract, "NANDEMU/EXTRACT", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuBtnPartition, "NANDEMU/PARTITION", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuBtnDisable, "NANDEMU/DISABLE", 0, 0, -2.f, 0.f);
_setHideAnim(m_nandemuLblInit, "NANDEMU/INIT", 100, 0, -2.f, 0.f);
_hideNandEmu(true);
_textNandEmu();
}
void CMenu::_textNandEmu(void)
{
m_btnMgr.setText(m_nandemuLblEmulation, _t("cfgne1", L"NAND Emulation"));
m_btnMgr.setText(m_nandemuLblSaveDump, _t("cfgne2", L"Extract Game Saves"));
m_btnMgr.setText(m_nandemuBtnAll, _t("cfgne3", L"All"));
m_btnMgr.setText(m_nandemuBtnMissing, _t("cfgne4", L"Missing"));
m_btnMgr.setText(m_nandemuLblNandDump, _t("cfgne5", L"Extract NAND"));
m_btnMgr.setText(m_nandemuBtnNandDump, _t("cfgne6", L"Start"));
m_btnMgr.setText(m_nandemuBtnBack, _t("cfgne7", L"Back"));
}