mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-28 13:37:12 +00:00
Add support for WUHB file format (#1190)
This commit is contained in:
parent
f28043e0e9
commit
dc480ac00b
15 changed files with 617 additions and 3 deletions
|
|
@ -1,9 +1,12 @@
|
|||
#include "TitleInfo.h"
|
||||
#include "Cafe/Filesystem/fscDeviceHostFS.h"
|
||||
#include "Cafe/Filesystem/WUHB/WUHBReader.h"
|
||||
#include "Cafe/Filesystem/FST/FST.h"
|
||||
#include "pugixml.hpp"
|
||||
#include "Common/FileStream.h"
|
||||
#include <zarchive/zarchivereader.h>
|
||||
#include "util/IniParser/IniParser.h"
|
||||
#include "util/crypto/crc32.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
|
||||
|
|
@ -97,6 +100,7 @@ TitleInfo::TitleInfo(const TitleInfo::CachedInfo& cachedInfo)
|
|||
m_isValid = false;
|
||||
if (cachedInfo.titleDataFormat != TitleDataFormat::HOST_FS &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::WIIU_ARCHIVE &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::WUHB &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::WUD &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::NUS &&
|
||||
cachedInfo.titleDataFormat != TitleDataFormat::INVALID_STRUCTURE)
|
||||
|
|
@ -245,6 +249,16 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF
|
|||
delete zar;
|
||||
return foundBase;
|
||||
}
|
||||
else if (boost::iends_with(filenameStr, ".wuhb"))
|
||||
{
|
||||
std::unique_ptr<WUHBReader> reader{WUHBReader::FromPath(path)};
|
||||
if(reader)
|
||||
{
|
||||
formatOut = TitleDataFormat::WUHB;
|
||||
pathOut = path;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// note: Since a Wii U archive file (.wua) contains multiple titles we shouldn't auto-detect them here
|
||||
// instead TitleInfo has a second constructor which takes a subpath
|
||||
// unable to determine type by extension, check contents
|
||||
|
|
@ -436,6 +450,23 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_titleFormat == TitleDataFormat::WUHB)
|
||||
{
|
||||
if (!m_wuhbreader)
|
||||
{
|
||||
m_wuhbreader = WUHBReader::FromPath(m_fullPath);
|
||||
if (!m_wuhbreader)
|
||||
return false;
|
||||
}
|
||||
bool r = FSCDeviceWUHB_Mount(virtualPath, subfolder, m_wuhbreader, mountPriority);
|
||||
if (!r)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to mount {} to {}", virtualPath, subfolder);
|
||||
delete m_wuhbreader;
|
||||
m_wuhbreader = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
|
|
@ -467,6 +498,12 @@ void TitleInfo::Unmount(std::string_view virtualPath)
|
|||
if (m_mountpoints.empty())
|
||||
m_zarchive = nullptr;
|
||||
}
|
||||
if (m_wuhbreader)
|
||||
{
|
||||
cemu_assert_debug(m_titleFormat == TitleDataFormat::WUHB);
|
||||
delete m_wuhbreader;
|
||||
m_wuhbreader = nullptr;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -502,6 +539,20 @@ bool TitleInfo::ParseXmlInfo()
|
|||
auto xmlData = fsc_extractFile(fmt::format("{}meta/meta.xml", mountPath).c_str());
|
||||
if(xmlData)
|
||||
m_parsedMetaXml = ParsedMetaXml::Parse(xmlData->data(), xmlData->size());
|
||||
|
||||
if(!m_parsedMetaXml)
|
||||
{
|
||||
// meta/meta.ini (WUHB)
|
||||
auto iniData = fsc_extractFile(fmt::format("{}meta/meta.ini", mountPath).c_str());
|
||||
if (iniData)
|
||||
m_parsedMetaXml = ParseAromaIni(*iniData);
|
||||
if(m_parsedMetaXml)
|
||||
{
|
||||
m_parsedCosXml = new ParsedCosXml{.argstr = "root.rpx"};
|
||||
m_parsedAppXml = new ParsedAppXml{m_parsedMetaXml->m_title_id, 0, 0, 0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
// code/app.xml
|
||||
xmlData = fsc_extractFile(fmt::format("{}code/app.xml", mountPath).c_str());
|
||||
if(xmlData)
|
||||
|
|
@ -539,6 +590,34 @@ bool TitleInfo::ParseXmlInfo()
|
|||
return true;
|
||||
}
|
||||
|
||||
ParsedMetaXml* TitleInfo::ParseAromaIni(std::span<unsigned char> content)
|
||||
{
|
||||
IniParser parser{content};
|
||||
while (parser.NextSection() && parser.GetCurrentSectionName() != "menu")
|
||||
continue;
|
||||
if (parser.GetCurrentSectionName() != "menu")
|
||||
return nullptr;
|
||||
|
||||
auto parsed = std::make_unique<ParsedMetaXml>();
|
||||
|
||||
const auto author = parser.FindOption("author");
|
||||
if (author)
|
||||
parsed->m_publisher[(size_t)CafeConsoleLanguage::EN] = *author;
|
||||
|
||||
const auto longName = parser.FindOption("longname");
|
||||
if (longName)
|
||||
parsed->m_long_name[(size_t)CafeConsoleLanguage::EN] = *longName;
|
||||
|
||||
const auto shortName = parser.FindOption("shortname");
|
||||
if (shortName)
|
||||
parsed->m_short_name[(size_t)CafeConsoleLanguage::EN] = *shortName;
|
||||
|
||||
auto checksumInput = std::string{*author}.append(*longName).append(*shortName);
|
||||
parsed->m_title_id = (0x0005000Full<<32) | crc32_calc(checksumInput.data(), checksumInput.length());
|
||||
|
||||
return parsed.release();
|
||||
}
|
||||
|
||||
bool TitleInfo::ParseAppXml(std::vector<uint8>& appXmlData)
|
||||
{
|
||||
pugi::xml_document app_doc;
|
||||
|
|
@ -695,6 +774,9 @@ std::string TitleInfo::GetPrintPath() const
|
|||
case TitleDataFormat::WIIU_ARCHIVE:
|
||||
tmp.append(" [WUA]");
|
||||
break;
|
||||
case TitleDataFormat::WUHB:
|
||||
tmp.append(" [WUHB]");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ public:
|
|||
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
|
||||
WUHB = 5,
|
||||
// error
|
||||
INVALID_STRUCTURE = 0,
|
||||
};
|
||||
|
|
@ -265,6 +266,7 @@ private:
|
|||
bool DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataFormat& formatOut);
|
||||
void CalcUID();
|
||||
void SetInvalidReason(InvalidReason reason);
|
||||
ParsedMetaXml* ParseAromaIni(std::span<unsigned char> content);
|
||||
bool ParseAppXml(std::vector<uint8>& appXmlData);
|
||||
|
||||
bool m_isValid{ false };
|
||||
|
|
@ -277,6 +279,7 @@ private:
|
|||
std::vector<std::pair<sint32, std::string>> m_mountpoints;
|
||||
class FSTVolume* m_wudVolume{};
|
||||
class ZArchiveReader* m_zarchive{};
|
||||
class WUHBReader* m_wuhbreader{};
|
||||
// xml info
|
||||
bool m_hasParsedXmlFiles{ false };
|
||||
ParsedMetaXml* m_parsedMetaXml{};
|
||||
|
|
|
|||
|
|
@ -342,7 +342,8 @@ bool _IsKnownFileNameOrExtension(const fs::path& path)
|
|||
fileExtension == ".wud" ||
|
||||
fileExtension == ".wux" ||
|
||||
fileExtension == ".iso" ||
|
||||
fileExtension == ".wua";
|
||||
fileExtension == ".wua" ||
|
||||
fileExtension == ".wuhb";
|
||||
// note: To detect extracted titles with RPX we rely on the presence of the content,code,meta directory structure
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue