mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-22 09:09:18 +01:00
Add support for games in NUS format (.app)
Requires title.tmd and title.tik in same directory
This commit is contained in:
parent
f9f6206929
commit
5ad57bb0c9
@ -78,13 +78,6 @@ struct RPLRegionMappingTable
|
||||
void RPLLoader_UnloadModule(RPLModule* rpl);
|
||||
void RPLLoader_RemoveDependency(const char* name);
|
||||
|
||||
char _ansiToLower(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c -= ('A' - 'a');
|
||||
return c;
|
||||
}
|
||||
|
||||
uint8* RPLLoader_AllocateTrampolineCodeSpace(RPLModule* rplLoaderContext, sint32 size)
|
||||
{
|
||||
if (rplLoaderContext)
|
||||
|
@ -99,6 +99,7 @@ TitleInfo::TitleInfo(const TitleInfo::CachedInfo& cachedInfo)
|
||||
if (cachedInfo.titleDataFormat != TitleDataFormat::HOST_FS &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::WIIU_ARCHIVE &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::WUD &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::NUS &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::INVALID_STRUCTURE)
|
||||
return;
|
||||
if (cachedInfo.path.empty())
|
||||
@ -204,6 +205,12 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF
|
||||
pathOut = path;
|
||||
return true;
|
||||
}
|
||||
else if (boost::iequals(filenameStr, "title.tmd"))
|
||||
{
|
||||
formatOut = TitleDataFormat::NUS;
|
||||
pathOut = path;
|
||||
return true;
|
||||
}
|
||||
else if (boost::iends_with(filenameStr, ".wua"))
|
||||
{
|
||||
formatOut = TitleDataFormat::WIIU_ARCHIVE;
|
||||
@ -378,12 +385,15 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_titleFormat == TitleDataFormat::WUD)
|
||||
else if (m_titleFormat == TitleDataFormat::WUD || m_titleFormat == TitleDataFormat::NUS)
|
||||
{
|
||||
if (m_mountpoints.empty())
|
||||
{
|
||||
cemu_assert_debug(!m_wudVolume);
|
||||
m_wudVolume = FSTVolume::OpenFromDiscImage(m_fullPath);
|
||||
if(m_titleFormat == TitleDataFormat::WUD)
|
||||
m_wudVolume = FSTVolume::OpenFromDiscImage(m_fullPath); // open wud/wux
|
||||
else
|
||||
m_wudVolume = FSTVolume::OpenFromContentFolder(m_fullPath.parent_path()); // open from .app files directory, the path points to /title.tmd
|
||||
}
|
||||
if (!m_wudVolume)
|
||||
return false;
|
||||
@ -433,7 +443,7 @@ void TitleInfo::Unmount(std::string_view virtualPath)
|
||||
{
|
||||
if (m_wudVolume)
|
||||
{
|
||||
cemu_assert_debug(m_titleFormat == TitleDataFormat::WUD);
|
||||
cemu_assert_debug(m_titleFormat == TitleDataFormat::WUD || m_titleFormat == TitleDataFormat::NUS);
|
||||
delete m_wudVolume;
|
||||
m_wudVolume = nullptr;
|
||||
}
|
||||
@ -664,6 +674,9 @@ std::string TitleInfo::GetPrintPath() const
|
||||
case TitleDataFormat::WUD:
|
||||
tmp.append(" [WUD]");
|
||||
break;
|
||||
case TitleDataFormat::NUS:
|
||||
tmp.append(" [NUS]");
|
||||
break;
|
||||
case TitleDataFormat::WIIU_ARCHIVE:
|
||||
tmp.append(" [WUA]");
|
||||
break;
|
||||
|
@ -60,6 +60,7 @@ public:
|
||||
HOST_FS = 1, // host filesystem directory (fullPath points to root with content/code/meta subfolders)
|
||||
WUD = 2, // WUD or WUX
|
||||
WIIU_ARCHIVE = 3, // Wii U compressed single-file archive (.wua)
|
||||
NUS = 4, // NUS format. Directory with .app files, title.tik and title.tmd
|
||||
// error
|
||||
INVALID_STRUCTURE = 0,
|
||||
};
|
||||
|
@ -324,17 +324,25 @@ bool CafeTitleList::RefreshWorkerThread()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _IsKnownFileExtension(std::string fileExtension)
|
||||
bool _IsKnownFileNameOrExtension(const fs::path& path)
|
||||
{
|
||||
std::string fileExtension = _pathToUtf8(path.extension());
|
||||
for (auto& it : fileExtension)
|
||||
if (it >= 'A' && it <= 'Z')
|
||||
it -= ('A' - 'a');
|
||||
it = _ansiToLower(it);
|
||||
if(fileExtension == ".tmd")
|
||||
{
|
||||
// must be "title.tmd"
|
||||
std::string fileName = _pathToUtf8(path.filename());
|
||||
for (auto& it : fileName)
|
||||
it = _ansiToLower(it);
|
||||
return fileName == "title.tmd";
|
||||
}
|
||||
return
|
||||
fileExtension == ".wud" ||
|
||||
fileExtension == ".wux" ||
|
||||
fileExtension == ".iso" ||
|
||||
fileExtension == ".wua";
|
||||
// note: To detect extracted titles with RPX we use the content/code/meta folder structure
|
||||
// note: To detect extracted titles with RPX we rely on the presence of the content,code,meta directory structure
|
||||
}
|
||||
|
||||
void CafeTitleList::ScanGamePath(const fs::path& path)
|
||||
@ -353,7 +361,6 @@ void CafeTitleList::ScanGamePath(const fs::path& path)
|
||||
else if (it.is_directory(ec))
|
||||
{
|
||||
dirsInDirectory.emplace_back(it.path());
|
||||
|
||||
std::string dirName = _pathToUtf8(it.path().filename());
|
||||
if (boost::iequals(dirName, "content"))
|
||||
hasContentFolder = true;
|
||||
@ -366,10 +373,10 @@ void CafeTitleList::ScanGamePath(const fs::path& path)
|
||||
// always check individual files
|
||||
for (auto& it : filesInDirectory)
|
||||
{
|
||||
// since checking files is slow, we only do it for known file extensions
|
||||
// since checking individual files is slow, we limit it to known file names or extensions
|
||||
if (!it.has_extension())
|
||||
continue;
|
||||
if (!_IsKnownFileExtension(_pathToUtf8(it.extension())))
|
||||
if (!_IsKnownFileNameOrExtension(it))
|
||||
continue;
|
||||
AddTitleFromPath(it);
|
||||
}
|
||||
|
@ -468,6 +468,14 @@ inline fs::path _utf8ToPath(std::string_view input)
|
||||
return fs::path(v);
|
||||
}
|
||||
|
||||
// locale-independent variant of tolower() which also matches Wii U behavior
|
||||
inline char _ansiToLower(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c -= ('A' - 'a');
|
||||
return c;
|
||||
}
|
||||
|
||||
class RunAtCemuBoot // -> replaces this with direct function calls. Linkers other than MSVC may optimize way object files entirely if they are not referenced from outside. So a source file self-registering using this would be causing issues
|
||||
{
|
||||
public:
|
||||
|
@ -639,13 +639,15 @@ void MainWindow::OnFileMenu(wxCommandEvent& event)
|
||||
if (menuId == MAINFRAME_MENU_ID_FILE_LOAD)
|
||||
{
|
||||
const auto wildcard = formatWxString(
|
||||
"{}|*.wud;*.wux;*.wua;*.iso;*.rpx;*.elf"
|
||||
"{}|*.wud;*.wux;*.wua;*.iso;*.rpx;*.elf;title.tmd"
|
||||
"|{}|*.wud;*.wux;*.iso"
|
||||
"|{}|title.tmd"
|
||||
"|{}|*.wua"
|
||||
"|{}|*.rpx;*.elf"
|
||||
"|{}|*",
|
||||
_("All Wii U files (*.wud, *.wux, *.wua, *.iso, *.rpx, *.elf)"),
|
||||
_("Wii U image (*.wud, *.wux, *.iso, *.wad)"),
|
||||
_("Wii U NUS content"),
|
||||
_("Wii U archive (*.wua)"),
|
||||
_("Wii U executable (*.rpx, *.elf)"),
|
||||
_("All files (*.*)")
|
||||
|
@ -941,6 +941,8 @@ wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColu
|
||||
return _("Folder");
|
||||
case wxTitleManagerList::EntryFormat::WUD:
|
||||
return _("WUD");
|
||||
case wxTitleManagerList::EntryFormat::NUS:
|
||||
return _("NUS");
|
||||
case wxTitleManagerList::EntryFormat::WUA:
|
||||
return _("WUA");
|
||||
}
|
||||
@ -1010,16 +1012,19 @@ void wxTitleManagerList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt
|
||||
wxTitleManagerList::EntryFormat entryFormat;
|
||||
switch (titleInfo.GetFormat())
|
||||
{
|
||||
case TitleInfo::TitleDataFormat::HOST_FS:
|
||||
default:
|
||||
entryFormat = EntryFormat::Folder;
|
||||
break;
|
||||
case TitleInfo::TitleDataFormat::WUD:
|
||||
entryFormat = EntryFormat::WUD;
|
||||
break;
|
||||
case TitleInfo::TitleDataFormat::NUS:
|
||||
entryFormat = EntryFormat::NUS;
|
||||
break;
|
||||
case TitleInfo::TitleDataFormat::WIIU_ARCHIVE:
|
||||
entryFormat = EntryFormat::WUA;
|
||||
break;
|
||||
case TitleInfo::TitleDataFormat::HOST_FS:
|
||||
default:
|
||||
entryFormat = EntryFormat::Folder;
|
||||
break;
|
||||
}
|
||||
|
||||
if (evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_DISCOVERED)
|
||||
|
@ -42,6 +42,7 @@ public:
|
||||
{
|
||||
Folder,
|
||||
WUD,
|
||||
NUS,
|
||||
WUA,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user