mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-04 22:11:18 +12:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
612
src/Cafe/IOSU/legacy/iosu_acp.cpp
Normal file
612
src/Cafe/IOSU/legacy/iosu_acp.cpp
Normal file
|
@ -0,0 +1,612 @@
|
|||
#include "iosu_ioctl.h"
|
||||
#include "iosu_acp.h"
|
||||
#include "Cafe/OS/libs/nn_common.h"
|
||||
#include "util/tinyxml2/tinyxml2.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/OS/libs/nn_save/nn_save.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "Cafe/OS/libs/nn_acp/nn_acp.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_FS.h"
|
||||
#include "Cafe/Filesystem/fsc.h"
|
||||
#include "Cafe/HW/Espresso/PPCState.h"
|
||||
|
||||
static_assert(sizeof(acpMetaXml_t) == 0x3440);
|
||||
static_assert(offsetof(acpMetaXml_t, title_id) == 0x0000);
|
||||
static_assert(offsetof(acpMetaXml_t, boss_id) == 0x0008);
|
||||
static_assert(offsetof(acpMetaXml_t, os_version) == 0x0010);
|
||||
static_assert(offsetof(acpMetaXml_t, app_size) == 0x0018);
|
||||
static_assert(offsetof(acpMetaXml_t, common_save_size) == 0x0020);
|
||||
|
||||
static_assert(offsetof(acpMetaXml_t, version) == 0x0048);
|
||||
static_assert(offsetof(acpMetaXml_t, product_code) == 0x004C);
|
||||
static_assert(offsetof(acpMetaXml_t, logo_type) == 0x00B4);
|
||||
static_assert(offsetof(acpMetaXml_t, pc_cero) == 0x0100);
|
||||
|
||||
static_assert(offsetof(acpMetaXml_t, longname_ja) == 0x038C);
|
||||
static_assert(offsetof(acpMetaXml_t, shortname_ja) == 0x1B8C);
|
||||
static_assert(offsetof(acpMetaXml_t, publisher_ja) == 0x278C);
|
||||
|
||||
static_assert(sizeof(acpMetaData_t) == 0x1AB00);
|
||||
static_assert(offsetof(acpMetaData_t, bootMovie) == 0);
|
||||
static_assert(offsetof(acpMetaData_t, bootLogoTex) == 0x13B38);
|
||||
|
||||
namespace iosu
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
bool isInitialized;
|
||||
}iosuAcp = { 0 };
|
||||
|
||||
void _xml_parseU32(tinyxml2::XMLElement* xmlElement, const char* name, uint32be* v)
|
||||
{
|
||||
tinyxml2::XMLElement* subElement = xmlElement->FirstChildElement(name);
|
||||
*v = 0;
|
||||
if (subElement == nullptr)
|
||||
return;
|
||||
const char* text = subElement->GetText();
|
||||
uint32 value;
|
||||
if (sscanf(text, "%u", &value) == 0)
|
||||
return;
|
||||
*v = value;
|
||||
}
|
||||
|
||||
void _xml_parseHex16(tinyxml2::XMLElement* xmlElement, const char* name, uint16be* v)
|
||||
{
|
||||
tinyxml2::XMLElement* subElement = xmlElement->FirstChildElement(name);
|
||||
*v = 0;
|
||||
if (subElement == nullptr)
|
||||
return;
|
||||
const char* text = subElement->GetText();
|
||||
uint32 value;
|
||||
if (sscanf(text, "%x", &value) == 0)
|
||||
return;
|
||||
*v = value;
|
||||
}
|
||||
|
||||
void _xml_parseHex32(tinyxml2::XMLElement* xmlElement, const char* name, uint32be* v)
|
||||
{
|
||||
tinyxml2::XMLElement* subElement = xmlElement->FirstChildElement(name);
|
||||
*v = 0;
|
||||
if (subElement == nullptr)
|
||||
return;
|
||||
const char* text = subElement->GetText();
|
||||
uint32 value;
|
||||
if (sscanf(text, "%x", &value) == 0)
|
||||
return;
|
||||
*v = value;
|
||||
}
|
||||
|
||||
void _xml_parseHex64(tinyxml2::XMLElement* xmlElement, const char* name, uint64* v)
|
||||
{
|
||||
tinyxml2::XMLElement* subElement = xmlElement->FirstChildElement(name);
|
||||
*v = 0;
|
||||
if (subElement == nullptr)
|
||||
return;
|
||||
const char* text = subElement->GetText();
|
||||
uint64 value;
|
||||
if (sscanf(text, "%llx", &value) == 0)
|
||||
return;
|
||||
*v = _swapEndianU64(value);
|
||||
}
|
||||
|
||||
void _xml_parseString_(tinyxml2::XMLElement* xmlElement, const char* name, char* output, sint32 maxLength)
|
||||
{
|
||||
tinyxml2::XMLElement* subElement = xmlElement->FirstChildElement(name);
|
||||
output[0] = '\0';
|
||||
if (subElement == nullptr)
|
||||
return;
|
||||
const char* text = subElement->GetText();
|
||||
if (text == nullptr)
|
||||
{
|
||||
output[0] = '\0';
|
||||
return;
|
||||
}
|
||||
strncpy(output, text, maxLength - 1);
|
||||
output[maxLength - 1] = '\0';
|
||||
}
|
||||
|
||||
#define _metaXml_parseString(__xmlElement, __name, __output) _xml_parseString_(__xmlElement, __name, __output, sizeof(__output));
|
||||
|
||||
void parseSaveMetaXml(uint8* metaXmlData, sint32 metaXmlLength, acpMetaXml_t* metaXml)
|
||||
{
|
||||
memset(metaXml, 0, sizeof(acpMetaXml_t));
|
||||
tinyxml2::XMLDocument appXml;
|
||||
appXml.Parse((const char*)metaXmlData, metaXmlLength);
|
||||
uint32 titleVersion = 0xFFFFFFFF;
|
||||
tinyxml2::XMLElement* menuElement = appXml.FirstChildElement("menu");
|
||||
if (menuElement)
|
||||
{
|
||||
_xml_parseHex64(menuElement, "title_id", &metaXml->title_id);
|
||||
_xml_parseHex64(menuElement, "boss_id", &metaXml->boss_id);
|
||||
_xml_parseHex64(menuElement, "os_version", &metaXml->os_version);
|
||||
_xml_parseHex64(menuElement, "app_size", &metaXml->app_size);
|
||||
_xml_parseHex64(menuElement, "common_save_size", &metaXml->common_save_size);
|
||||
_xml_parseHex64(menuElement, "account_save_size", &metaXml->account_save_size);
|
||||
_xml_parseHex64(menuElement, "common_boss_size", &metaXml->common_boss_size);
|
||||
_xml_parseHex64(menuElement, "account_boss_size", &metaXml->account_boss_size);
|
||||
_xml_parseHex64(menuElement, "join_game_mode_mask", &metaXml->join_game_mode_mask);
|
||||
_xml_parseU32(menuElement, "version", &metaXml->version);
|
||||
_metaXml_parseString(menuElement, "product_code", metaXml->product_code);
|
||||
_metaXml_parseString(menuElement, "content_platform", metaXml->content_platform);
|
||||
_metaXml_parseString(menuElement, "company_code", metaXml->company_code);
|
||||
_metaXml_parseString(menuElement, "mastering_date", metaXml->mastering_date);
|
||||
_xml_parseU32(menuElement, "logo_type", &metaXml->logo_type);
|
||||
|
||||
_xml_parseU32(menuElement, "app_launch_type", &metaXml->app_launch_type);
|
||||
_xml_parseU32(menuElement, "invisible_flag", &metaXml->invisible_flag);
|
||||
_xml_parseU32(menuElement, "no_managed_flag", &metaXml->no_managed_flag);
|
||||
_xml_parseU32(menuElement, "no_event_log", &metaXml->no_event_log);
|
||||
_xml_parseU32(menuElement, "no_icon_database", &metaXml->no_icon_database);
|
||||
_xml_parseU32(menuElement, "launching_flag", &metaXml->launching_flag);
|
||||
_xml_parseU32(menuElement, "install_flag", &metaXml->install_flag);
|
||||
_xml_parseU32(menuElement, "closing_msg", &metaXml->closing_msg);
|
||||
_xml_parseU32(menuElement, "title_version", &metaXml->title_version);
|
||||
_xml_parseHex32(menuElement, "group_id", &metaXml->group_id);
|
||||
_xml_parseU32(menuElement, "save_no_rollback", &metaXml->save_no_rollback);
|
||||
_xml_parseU32(menuElement, "bg_daemon_enable", &metaXml->bg_daemon_enable);
|
||||
_xml_parseHex32(menuElement, "join_game_id", &metaXml->join_game_id);
|
||||
|
||||
_xml_parseU32(menuElement, "olv_accesskey", &metaXml->olv_accesskey);
|
||||
_xml_parseU32(menuElement, "wood_tin", &metaXml->wood_tin);
|
||||
_xml_parseU32(menuElement, "e_manual", &metaXml->e_manual);
|
||||
_xml_parseU32(menuElement, "e_manual_version", &metaXml->e_manual_version);
|
||||
_xml_parseHex32(menuElement, "region", &metaXml->region);
|
||||
|
||||
_xml_parseU32(menuElement, "pc_cero", &metaXml->pc_cero);
|
||||
_xml_parseU32(menuElement, "pc_esrb", &metaXml->pc_esrb);
|
||||
_xml_parseU32(menuElement, "pc_bbfc", &metaXml->pc_bbfc);
|
||||
_xml_parseU32(menuElement, "pc_usk", &metaXml->pc_usk);
|
||||
_xml_parseU32(menuElement, "pc_pegi_gen", &metaXml->pc_pegi_gen);
|
||||
_xml_parseU32(menuElement, "pc_pegi_fin", &metaXml->pc_pegi_fin);
|
||||
_xml_parseU32(menuElement, "pc_pegi_prt", &metaXml->pc_pegi_prt);
|
||||
_xml_parseU32(menuElement, "pc_pegi_bbfc", &metaXml->pc_pegi_bbfc);
|
||||
|
||||
_xml_parseU32(menuElement, "pc_cob", &metaXml->pc_cob);
|
||||
_xml_parseU32(menuElement, "pc_grb", &metaXml->pc_grb);
|
||||
_xml_parseU32(menuElement, "pc_cgsrr", &metaXml->pc_cgsrr);
|
||||
_xml_parseU32(menuElement, "pc_oflc", &metaXml->pc_oflc);
|
||||
|
||||
_xml_parseU32(menuElement, "pc_reserved0", &metaXml->pc_reserved0);
|
||||
_xml_parseU32(menuElement, "pc_reserved1", &metaXml->pc_reserved1);
|
||||
_xml_parseU32(menuElement, "pc_reserved2", &metaXml->pc_reserved2);
|
||||
_xml_parseU32(menuElement, "pc_reserved3", &metaXml->pc_reserved3);
|
||||
|
||||
_xml_parseU32(menuElement, "ext_dev_nunchaku", &metaXml->ext_dev_nunchaku);
|
||||
_xml_parseU32(menuElement, "ext_dev_classic", &metaXml->ext_dev_classic);
|
||||
_xml_parseU32(menuElement, "ext_dev_urcc", &metaXml->ext_dev_urcc);
|
||||
_xml_parseU32(menuElement, "ext_dev_board", &metaXml->ext_dev_board);
|
||||
_xml_parseU32(menuElement, "ext_dev_usb_keyboard", &metaXml->ext_dev_usb_keyboard);
|
||||
_xml_parseU32(menuElement, "ext_dev_etc", &metaXml->ext_dev_etc);
|
||||
|
||||
_metaXml_parseString(menuElement, "ext_dev_etc_name", metaXml->ext_dev_etc_name);
|
||||
|
||||
_xml_parseU32(menuElement, "eula_version", &metaXml->eula_version);
|
||||
_xml_parseU32(menuElement, "drc_use", &metaXml->drc_use);
|
||||
_xml_parseU32(menuElement, "network_use", &metaXml->network_use);
|
||||
_xml_parseU32(menuElement, "online_account_use", &metaXml->online_account_use);
|
||||
_xml_parseU32(menuElement, "direct_boot", &metaXml->direct_boot);
|
||||
_xml_parseU32(menuElement, "reserved_flag0", &(metaXml->reserved_flag[0]));
|
||||
_xml_parseU32(menuElement, "reserved_flag1", &(metaXml->reserved_flag[1]));
|
||||
_xml_parseU32(menuElement, "reserved_flag2", &(metaXml->reserved_flag[2]));
|
||||
_xml_parseU32(menuElement, "reserved_flag3", &(metaXml->reserved_flag[3]));
|
||||
_xml_parseU32(menuElement, "reserved_flag4", &(metaXml->reserved_flag[4]));
|
||||
_xml_parseU32(menuElement, "reserved_flag5", &(metaXml->reserved_flag[5]));
|
||||
_xml_parseU32(menuElement, "reserved_flag6", &(metaXml->reserved_flag[6]));
|
||||
_xml_parseU32(menuElement, "reserved_flag7", &(metaXml->reserved_flag[7]));
|
||||
|
||||
_metaXml_parseString(menuElement, "longname_ja", metaXml->longname_ja);
|
||||
_metaXml_parseString(menuElement, "longname_en", metaXml->longname_en);
|
||||
_metaXml_parseString(menuElement, "longname_fr", metaXml->longname_fr);
|
||||
_metaXml_parseString(menuElement, "longname_de", metaXml->longname_de);
|
||||
_metaXml_parseString(menuElement, "longname_it", metaXml->longname_it);
|
||||
_metaXml_parseString(menuElement, "longname_es", metaXml->longname_es);
|
||||
_metaXml_parseString(menuElement, "longname_zhs", metaXml->longname_zhs);
|
||||
_metaXml_parseString(menuElement, "longname_ko", metaXml->longname_ko);
|
||||
_metaXml_parseString(menuElement, "longname_nl", metaXml->longname_nl);
|
||||
_metaXml_parseString(menuElement, "longname_pt", metaXml->longname_pt);
|
||||
_metaXml_parseString(menuElement, "longname_ru", metaXml->longname_ru);
|
||||
_metaXml_parseString(menuElement, "longname_zht", metaXml->longname_zht);
|
||||
|
||||
_metaXml_parseString(menuElement, "shortname_ja", metaXml->shortname_ja);
|
||||
_metaXml_parseString(menuElement, "shortname_en", metaXml->shortname_en);
|
||||
_metaXml_parseString(menuElement, "shortname_fr", metaXml->shortname_fr);
|
||||
_metaXml_parseString(menuElement, "shortname_de", metaXml->shortname_de);
|
||||
_metaXml_parseString(menuElement, "shortname_it", metaXml->shortname_it);
|
||||
_metaXml_parseString(menuElement, "shortname_es", metaXml->shortname_es);
|
||||
_metaXml_parseString(menuElement, "shortname_zhs", metaXml->shortname_zhs);
|
||||
_metaXml_parseString(menuElement, "shortname_ko", metaXml->shortname_ko);
|
||||
_metaXml_parseString(menuElement, "shortname_nl", metaXml->shortname_nl);
|
||||
_metaXml_parseString(menuElement, "shortname_pt", metaXml->shortname_pt);
|
||||
_metaXml_parseString(menuElement, "shortname_ru", metaXml->shortname_ru);
|
||||
_metaXml_parseString(menuElement, "shortname_zht", metaXml->shortname_zht);
|
||||
|
||||
_metaXml_parseString(menuElement, "publisher_ja", metaXml->publisher_ja);
|
||||
_metaXml_parseString(menuElement, "publisher_en", metaXml->publisher_en);
|
||||
_metaXml_parseString(menuElement, "publisher_fr", metaXml->publisher_fr);
|
||||
_metaXml_parseString(menuElement, "publisher_de", metaXml->publisher_de);
|
||||
_metaXml_parseString(menuElement, "publisher_it", metaXml->publisher_it);
|
||||
_metaXml_parseString(menuElement, "publisher_es", metaXml->publisher_es);
|
||||
_metaXml_parseString(menuElement, "publisher_zhs", metaXml->publisher_zhs);
|
||||
_metaXml_parseString(menuElement, "publisher_ko", metaXml->publisher_ko);
|
||||
_metaXml_parseString(menuElement, "publisher_nl", metaXml->publisher_nl);
|
||||
_metaXml_parseString(menuElement, "publisher_pt", metaXml->publisher_pt);
|
||||
_metaXml_parseString(menuElement, "publisher_ru", metaXml->publisher_ru);
|
||||
_metaXml_parseString(menuElement, "publisher_zht", metaXml->publisher_zht);
|
||||
|
||||
for (sint32 i = 0; i < 32; i++)
|
||||
{
|
||||
char tempStr[256];
|
||||
sprintf(tempStr, "add_on_unique_id%d", i);
|
||||
_xml_parseU32(menuElement, tempStr, &(metaXml->add_on_unique_id[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _is8DigitHex(const char* str)
|
||||
{
|
||||
if (strlen(str) != 8)
|
||||
return false;
|
||||
for (sint32 f = 0; f < 8; f++)
|
||||
{
|
||||
if (str[f] >= '0' && str[f] <= '9')
|
||||
continue;
|
||||
if (str[f] >= 'a' && str[f] <= 'f')
|
||||
continue;
|
||||
if (str[f] >= 'A' && str[f] <= 'F')
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sint32 ACPGetSaveDataTitleIdList(uint32 storageDeviceGuessed, acpTitleId_t* titleIdList, sint32 maxCount, uint32be* countOut)
|
||||
{
|
||||
sint32 count = 0;
|
||||
|
||||
const char* devicePath = "/vol/storage_mlc01/";
|
||||
if (storageDeviceGuessed != 3)
|
||||
cemu_assert_unimplemented();
|
||||
char searchPath[FSA_CMD_PATH_MAX_LENGTH];
|
||||
sprintf(searchPath, "%susr/save/", devicePath);
|
||||
sint32 fscStatus = 0;
|
||||
FSCVirtualFile* fscDirIteratorTitleIdHigh = fsc_openDirIterator(searchPath, &fscStatus);
|
||||
FSCDirEntry dirEntryTitleIdHigh;
|
||||
FSCDirEntry dirEntryTitleIdLow;
|
||||
if(fscDirIteratorTitleIdHigh)
|
||||
{
|
||||
while (fsc_nextDir(fscDirIteratorTitleIdHigh, &dirEntryTitleIdHigh))
|
||||
{
|
||||
// is 8-digit hex?
|
||||
if(_is8DigitHex(dirEntryTitleIdHigh.path) == false)
|
||||
continue;
|
||||
uint32 titleIdHigh;
|
||||
sscanf(dirEntryTitleIdHigh.path, "%x", &titleIdHigh);
|
||||
sprintf(searchPath, "%susr/save/%08x/", devicePath, titleIdHigh);
|
||||
FSCVirtualFile* fscDirIteratorTitleIdLow = fsc_openDirIterator(searchPath, &fscStatus);
|
||||
if (fscDirIteratorTitleIdLow)
|
||||
{
|
||||
while (fsc_nextDir(fscDirIteratorTitleIdLow, &dirEntryTitleIdLow))
|
||||
{
|
||||
// is 8-digit hex?
|
||||
if (_is8DigitHex(dirEntryTitleIdLow.path) == false)
|
||||
continue;
|
||||
uint32 titleIdLow;
|
||||
sscanf(dirEntryTitleIdLow.path, "%x", &titleIdLow);
|
||||
// check if /meta/meta.xml exists
|
||||
char tempPath[FSA_CMD_PATH_MAX_LENGTH];
|
||||
sprintf(tempPath, "%susr/save/%08x/%08x/meta/meta.xml", devicePath, titleIdHigh, titleIdLow);
|
||||
if (fsc_doesFileExist(tempPath))
|
||||
{
|
||||
if (count < maxCount)
|
||||
{
|
||||
titleIdList[count].titleIdHigh = titleIdHigh;
|
||||
titleIdList[count].titleIdLow = titleIdLow;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
forceLogDebug_printf("ACPGetSaveDataTitleIdList(): Missing meta.xml for save %08x-%08x", titleIdHigh, titleIdLow);
|
||||
}
|
||||
}
|
||||
fsc_close(fscDirIteratorTitleIdLow);
|
||||
}
|
||||
}
|
||||
fsc_close(fscDirIteratorTitleIdHigh);
|
||||
}
|
||||
*countOut = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sint32 ACPGetTitleSaveMetaXml(uint64 titleId, acpMetaXml_t* acpMetaXml, sint32 uknType)
|
||||
{
|
||||
// uknType is probably the storage device?
|
||||
if (uknType != 3) // mlc01 ?
|
||||
assert_dbg();
|
||||
|
||||
char xmlPath[FSA_CMD_PATH_MAX_LENGTH];
|
||||
sprintf(xmlPath, "%susr/save/%08x/%08x/meta/meta.xml", "/vol/storage_mlc01/", (uint32)(titleId>>32), (uint32)(titleId&0xFFFFFFFF));
|
||||
|
||||
uint32 saveMetaXmlSize = 0;
|
||||
uint8* saveMetaXmlData = fsc_extractFile(xmlPath, &saveMetaXmlSize);
|
||||
if (saveMetaXmlData)
|
||||
{
|
||||
parseSaveMetaXml(saveMetaXmlData, saveMetaXmlSize, acpMetaXml);
|
||||
free(saveMetaXmlData);
|
||||
}
|
||||
else
|
||||
{
|
||||
forceLog_printf("ACPGetTitleSaveMetaXml(): Meta file \"%s\" does not exist", xmlPath);
|
||||
memset(acpMetaXml, 0, sizeof(acpMetaXml_t));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sint32 ACPGetTitleMetaData(uint64 titleId, acpMetaData_t* acpMetaData)
|
||||
{
|
||||
memset(acpMetaData, 0, sizeof(acpMetaData_t));
|
||||
|
||||
char titlePath[1024];
|
||||
|
||||
if (((titleId >> 32) & 0x10) != 0)
|
||||
{
|
||||
sprintf(titlePath, "/vol/storage_mlc01/sys/title/%08x/%08x/", (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(titlePath, "/vol/storage_mlc01/usr/title/%08x/%08x/", (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
|
||||
char filePath[FSA_CMD_PATH_MAX_LENGTH];
|
||||
sprintf(filePath, "%smeta/bootMovie.h264", titlePath);
|
||||
|
||||
// bootMovie.h264
|
||||
uint32 metaBootMovieSize = 0;
|
||||
uint8* metaBootMovieData = fsc_extractFile(filePath, &metaBootMovieSize);
|
||||
if (metaBootMovieData)
|
||||
{
|
||||
memcpy(acpMetaData->bootMovie, metaBootMovieData, std::min<uint32>(metaBootMovieSize, sizeof(acpMetaData->bootMovie)));
|
||||
free(metaBootMovieData);
|
||||
}
|
||||
else
|
||||
forceLog_printf("ACPGetTitleMetaData(): Unable to load \"%s\"", filePath);
|
||||
// bootLogoTex.tga
|
||||
sprintf(filePath, "%smeta/bootLogoTex.tga", titlePath);
|
||||
uint32 metaBootLogoSize = 0;
|
||||
uint8* metaBootLogoData = fsc_extractFile(filePath, &metaBootLogoSize);
|
||||
if (metaBootLogoData)
|
||||
{
|
||||
memcpy(acpMetaData->bootLogoTex, metaBootLogoData, std::min<uint32>(metaBootLogoSize, sizeof(acpMetaData->bootLogoTex)));
|
||||
free(metaBootLogoData);
|
||||
}
|
||||
else
|
||||
forceLog_printf("ACPGetTitleMetaData(): Unable to load \"%s\"", filePath);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sint32 ACPGetTitleMetaXml(uint64 titleId, acpMetaXml_t* acpMetaXml)
|
||||
{
|
||||
memset(acpMetaXml, 0, sizeof(acpMetaXml_t));
|
||||
|
||||
char titlePath[1024];
|
||||
|
||||
if (((titleId >> 32) & 0x10) != 0)
|
||||
{
|
||||
sprintf(titlePath, "/vol/storage_mlc01/sys/title/%08x/%08x/", (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(titlePath, "/vol/storage_mlc01/usr/title/%08x/%08x/", (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
|
||||
char filePath[FSA_CMD_PATH_MAX_LENGTH];
|
||||
sprintf(filePath, "%smeta/meta.xml", titlePath);
|
||||
|
||||
uint32 metaXmlSize = 0;
|
||||
uint8* metaXmlData = fsc_extractFile(filePath, &metaXmlSize);
|
||||
if (metaXmlData)
|
||||
{
|
||||
parseSaveMetaXml(metaXmlData, metaXmlSize, acpMetaXml);
|
||||
free(metaXmlData);
|
||||
}
|
||||
else
|
||||
{
|
||||
forceLog_printf("ACPGetTitleMetaXml(): Meta file \"%s\" does not exist", filePath);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sint32 ACPGetTitleSaveDirEx(uint64 titleId, uint32 storageDeviceGuessed, acpSaveDirInfo_t* saveDirInfo, sint32 maxCount, uint32be* countOut)
|
||||
{
|
||||
sint32 count = 0;
|
||||
|
||||
const char* devicePath = "/vol/storage_mlc01/";
|
||||
if (storageDeviceGuessed != 3)
|
||||
cemu_assert_unimplemented();
|
||||
|
||||
char searchPath[FSA_CMD_PATH_MAX_LENGTH];
|
||||
char tempPath[FSA_CMD_PATH_MAX_LENGTH];
|
||||
|
||||
sint32 fscStatus = 0;
|
||||
// add common dir
|
||||
sprintf(searchPath, "%susr/save/%08x/%08x/user/common/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
if (fsc_doesDirectoryExist(searchPath))
|
||||
{
|
||||
acpSaveDirInfo_t* entry = saveDirInfo + count;
|
||||
if (count < maxCount)
|
||||
{
|
||||
// get dir size
|
||||
sprintf(tempPath, "%susr/save/%08x/%08x/user/common/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
FSCVirtualFile* fscDir = fsc_open(tempPath, FSC_ACCESS_FLAG::OPEN_DIR, &fscStatus);
|
||||
uint64 dirSize = 0;
|
||||
if (fscDir)
|
||||
{
|
||||
dirSize = fsc_getFileSize(fscDir);
|
||||
fsc_close(fscDir);
|
||||
}
|
||||
|
||||
memset(entry, 0, sizeof(acpSaveDirInfo_t));
|
||||
entry->ukn00 = (uint32)(titleId>>32);
|
||||
entry->ukn04 = (uint32)(titleId&0xFFFFFFFF);
|
||||
entry->persistentId = 0; // 0 -> common save
|
||||
entry->ukn0C = 0;
|
||||
entry->sizeA = _swapEndianU64(0); // ukn
|
||||
entry->sizeB = _swapEndianU64(dirSize);
|
||||
entry->time = _swapEndianU64((coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK));
|
||||
sprintf(entry->path, "%susr/save/%08x/%08x/meta/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// add user directories
|
||||
sprintf(searchPath, "%susr/save/%08x/%08x/user/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
FSCVirtualFile* fscDirIterator = fsc_openDirIterator(searchPath, &fscStatus);
|
||||
if (fscDirIterator == nullptr)
|
||||
{
|
||||
forceLog_printf("ACPGetTitleSaveDirEx(): Failed to iterate directories in \"%s\"", searchPath);
|
||||
*countOut = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
FSCDirEntry dirEntry;
|
||||
while( fsc_nextDir(fscDirIterator, &dirEntry) )
|
||||
{
|
||||
if(dirEntry.isDirectory == false)
|
||||
continue;
|
||||
// is 8-digit hex name? (persistent id)
|
||||
if(_is8DigitHex(dirEntry.path) == false )
|
||||
continue;
|
||||
uint32 persistentId = 0;
|
||||
sscanf(dirEntry.path, "%x", &persistentId);
|
||||
acpSaveDirInfo_t* entry = saveDirInfo + count;
|
||||
if (count < maxCount)
|
||||
{
|
||||
memset(entry, 0, sizeof(acpSaveDirInfo_t));
|
||||
entry->ukn00 = (uint32)(titleId >> 32); // titleId?
|
||||
entry->ukn04 = (uint32)(titleId & 0xFFFFFFFF); // titleId?
|
||||
entry->persistentId = persistentId; // 0 -> common save
|
||||
entry->ukn0C = 0;
|
||||
entry->sizeA = _swapEndianU64(0);
|
||||
entry->sizeB = _swapEndianU64(0);
|
||||
entry->time = _swapEndianU64((coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK));
|
||||
sprintf(entry->path, "%susr/save/%08x/%08x/meta/", devicePath, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
fsc_close(fscDirIterator);
|
||||
}
|
||||
*countOut = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sint32 ACPCreateSaveDirEx(uint8 accountSlot, uint64 titleId)
|
||||
{
|
||||
uint32 persistentId = 0;
|
||||
nn::save::GetPersistentIdEx(accountSlot, &persistentId);
|
||||
|
||||
uint32 high = GetTitleIdHigh(titleId) & (~0xC);
|
||||
uint32 low = GetTitleIdLow(titleId);
|
||||
|
||||
sint32 fscStatus = FSC_STATUS_FILE_NOT_FOUND;
|
||||
char path[256];
|
||||
|
||||
sprintf(path, "%susr/boss/", "/vol/storage_mlc01/");
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/boss/%08x/", "/vol/storage_mlc01/", high);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/boss/%08x/%08x/", "/vol/storage_mlc01/", high, low);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/boss/%08x/%08x/user/", "/vol/storage_mlc01/", high, low);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/boss/%08x/%08x/user/common", "/vol/storage_mlc01/", high, low);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/boss/%08x/%08x/user/%08x/", "/vol/storage_mlc01/", high, low, persistentId == 0 ? 0x80000001 : persistentId);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
|
||||
sprintf(path, "%susr/save/%08x/", "/vol/storage_mlc01/", high);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/save/%08x/%08x/", "/vol/storage_mlc01/", high, low);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/save/%08x/%08x/meta/", "/vol/storage_mlc01/", high, low);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/save/%08x/%08x/user/", "/vol/storage_mlc01/", high, low);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/save/%08x/%08x/user/common", "/vol/storage_mlc01/", high, low);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
sprintf(path, "%susr/save/%08x/%08x/user/%08x", "/vol/storage_mlc01/", high, low, persistentId == 0 ? 0x80000001 : persistentId);
|
||||
fsc_createDir(path, &fscStatus);
|
||||
|
||||
// copy xml meta files
|
||||
nn::acp::CreateSaveMetaFiles(persistentId, titleId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iosuAcp_thread()
|
||||
{
|
||||
SetThreadName("iosuAcp_thread");
|
||||
while (true)
|
||||
{
|
||||
uint32 returnValue = 0; // Ioctl return value
|
||||
ioQueueEntry_t* ioQueueEntry = iosuIoctl_getNextWithWait(IOS_DEVICE_ACP_MAIN);
|
||||
if (ioQueueEntry->request == IOSU_ACP_REQUEST_CEMU)
|
||||
{
|
||||
iosuAcpCemuRequest_t* acpCemuRequest = (iosuAcpCemuRequest_t*)ioQueueEntry->bufferVectors[0].buffer.GetPtr();
|
||||
if (acpCemuRequest->requestCode == IOSU_ACP_GET_SAVE_DATA_TITLE_ID_LIST)
|
||||
{
|
||||
uint32be count = 0;
|
||||
acpCemuRequest->returnCode = ACPGetSaveDataTitleIdList(acpCemuRequest->type, (acpTitleId_t*)acpCemuRequest->ptr.GetPtr(), acpCemuRequest->maxCount, &count);
|
||||
acpCemuRequest->resultU32.u32 = count;
|
||||
}
|
||||
else if (acpCemuRequest->requestCode == IOSU_ACP_GET_TITLE_SAVE_META_XML)
|
||||
{
|
||||
acpCemuRequest->returnCode = ACPGetTitleSaveMetaXml(acpCemuRequest->titleId, (acpMetaXml_t*)acpCemuRequest->ptr.GetPtr(), acpCemuRequest->type);
|
||||
}
|
||||
else if (acpCemuRequest->requestCode == IOSU_ACP_GET_TITLE_SAVE_DIR)
|
||||
{
|
||||
uint32be count = 0;
|
||||
acpCemuRequest->returnCode = ACPGetTitleSaveDirEx(acpCemuRequest->titleId, acpCemuRequest->type, (acpSaveDirInfo_t*)acpCemuRequest->ptr.GetPtr(), acpCemuRequest->maxCount, &count);
|
||||
acpCemuRequest->resultU32.u32 = count;
|
||||
}
|
||||
else if (acpCemuRequest->requestCode == IOSU_ACP_GET_TITLE_META_DATA)
|
||||
{
|
||||
acpCemuRequest->returnCode = ACPGetTitleMetaData(acpCemuRequest->titleId, (acpMetaData_t*)acpCemuRequest->ptr.GetPtr());
|
||||
}
|
||||
else if (acpCemuRequest->requestCode == IOSU_ACP_GET_TITLE_META_XML)
|
||||
{
|
||||
acpCemuRequest->returnCode = ACPGetTitleMetaXml(acpCemuRequest->titleId, (acpMetaXml_t*)acpCemuRequest->ptr.GetPtr());
|
||||
}
|
||||
else if (acpCemuRequest->requestCode == IOSU_ACP_CREATE_SAVE_DIR_EX)
|
||||
{
|
||||
acpCemuRequest->returnCode = ACPCreateSaveDirEx(acpCemuRequest->accountSlot, acpCemuRequest->titleId);
|
||||
}
|
||||
else
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
else
|
||||
cemu_assert_unimplemented();
|
||||
iosuIoctl_completeRequest(ioQueueEntry, returnValue);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iosuAcp_init()
|
||||
{
|
||||
if (iosuAcp.isInitialized)
|
||||
return;
|
||||
std::thread t(iosuAcp_thread);
|
||||
t.detach();
|
||||
iosuAcp.isInitialized = true;
|
||||
}
|
||||
|
||||
bool iosuAcp_isInitialized()
|
||||
{
|
||||
return iosuAcp.isInitialized;
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue