mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2025-12-16 12:08:49 +00:00
Merge 1dbbb51ebb into f7eaf13a4d
This commit is contained in:
commit
ef070703c7
@ -1242,6 +1242,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
|
||||
const auto scope = render_window->Acquire();
|
||||
|
||||
if (!UISettings::values.inserted_cartridge.GetValue().empty()) {
|
||||
system.InsertCartridge(UISettings::values.inserted_cartridge.GetValue());
|
||||
}
|
||||
|
||||
const Core::System::ResultStatus result{
|
||||
system.Load(*render_window, filename.toStdString(), secondary_window)};
|
||||
|
||||
@ -1526,6 +1530,8 @@ void GMainWindow::ShutdownGame() {
|
||||
emu_thread->wait();
|
||||
emu_thread = nullptr;
|
||||
|
||||
system.EjectCartridge();
|
||||
|
||||
OnCloseMovie();
|
||||
|
||||
discord_rpc->Update();
|
||||
@ -3807,6 +3813,9 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||
ShutdownGame();
|
||||
}
|
||||
|
||||
// Save settings in case they were changed from outside the configuration menu.
|
||||
config->Save();
|
||||
|
||||
render_window->close();
|
||||
secondary_window->close();
|
||||
multiplayer_state->Close();
|
||||
|
||||
@ -668,6 +668,11 @@ void QtConfig::ReadPathValues() {
|
||||
ReadSetting(QStringLiteral("last_artic_base_addr"), QString{}).toString();
|
||||
UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
|
||||
UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
|
||||
|
||||
ReadBasicSetting(UISettings::values.inserted_cartridge);
|
||||
if (!FileUtil::Exists(UISettings::values.inserted_cartridge.GetValue())) {
|
||||
UISettings::values.inserted_cartridge.SetValue("");
|
||||
}
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
@ -1199,6 +1204,7 @@ void QtConfig::SavePathValues() {
|
||||
UISettings::values.last_artic_base_addr, QString{});
|
||||
WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
|
||||
WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
|
||||
WriteBasicSetting(UISettings::values.inserted_cartridge);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
@ -533,7 +533,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||
selected.data(GameListItemPath::ProgramIdRole).toULongLong(),
|
||||
selected.data(GameListItemPath::ExtdataIdRole).toULongLong(),
|
||||
static_cast<Service::FS::MediaType>(
|
||||
selected.data(GameListItemPath::MediaTypeRole).toUInt()));
|
||||
selected.data(GameListItemPath::MediaTypeRole).toUInt()),
|
||||
selected.data(GameListItemPath::CanInsertRole).toUInt() != 0);
|
||||
break;
|
||||
case GameListItemType::CustomDir:
|
||||
AddPermDirPopup(context_menu, selected);
|
||||
@ -603,8 +604,16 @@ void ForEachOpenGLCacheFile(u64 program_id, auto func) {
|
||||
#endif
|
||||
|
||||
void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QString& name,
|
||||
u64 program_id, u64 extdata_id, Service::FS::MediaType media_type) {
|
||||
u64 program_id, u64 extdata_id, Service::FS::MediaType media_type,
|
||||
bool can_insert) {
|
||||
QAction* favorite = context_menu.addAction(tr("Favorite"));
|
||||
bool is_inserted =
|
||||
can_insert && UISettings::values.inserted_cartridge.GetValue() == path.toStdString();
|
||||
QAction* cartridge_insert = nullptr;
|
||||
if (can_insert) {
|
||||
cartridge_insert =
|
||||
context_menu.addAction(is_inserted ? tr("Eject Cartridge") : tr("Insert Cartridge"));
|
||||
}
|
||||
context_menu.addSeparator();
|
||||
QMenu* open_menu = context_menu.addMenu(tr("Open"));
|
||||
QAction* open_application_location = open_menu->addAction(tr("Application Location"));
|
||||
@ -718,6 +727,15 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
|
||||
connect(open_extdata_location, &QAction::triggered, this, [this, extdata_id] {
|
||||
emit OpenFolderRequested(extdata_id, GameListOpenTarget::EXT_DATA);
|
||||
});
|
||||
if (cartridge_insert) {
|
||||
connect(cartridge_insert, &QAction::triggered, this, [path, is_inserted] {
|
||||
if (is_inserted) {
|
||||
UISettings::values.inserted_cartridge.SetValue("");
|
||||
} else {
|
||||
UISettings::values.inserted_cartridge.SetValue(path.toStdString());
|
||||
}
|
||||
});
|
||||
}
|
||||
connect(open_application_location, &QAction::triggered, this, [this, program_id] {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::APPLICATION);
|
||||
});
|
||||
|
||||
@ -124,7 +124,7 @@ private:
|
||||
void PopupContextMenu(const QPoint& menu_location);
|
||||
void PopupHeaderContextMenu(const QPoint& menu_location);
|
||||
void AddGamePopup(QMenu& context_menu, const QString& path, const QString& name, u64 program_id,
|
||||
u64 extdata_id, Service::FS::MediaType media_type);
|
||||
u64 extdata_id, Service::FS::MediaType media_type, bool can_insert);
|
||||
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||
void AddFavoritesPopup(QMenu& context_menu);
|
||||
|
||||
@ -159,15 +159,18 @@ public:
|
||||
static constexpr int ExtdataIdRole = SortRole + 4;
|
||||
static constexpr int LongTitleRole = SortRole + 5;
|
||||
static constexpr int MediaTypeRole = SortRole + 6;
|
||||
static constexpr int CanInsertRole = SortRole + 7;
|
||||
|
||||
GameListItemPath() = default;
|
||||
GameListItemPath(const QString& game_path, std::span<const u8> smdh_data, u64 program_id,
|
||||
u64 extdata_id, Service::FS::MediaType media_type, bool is_encrypted) {
|
||||
u64 extdata_id, Service::FS::MediaType media_type, bool is_encrypted,
|
||||
bool can_insert) {
|
||||
setData(type(), TypeRole);
|
||||
setData(game_path, FullPathRole);
|
||||
setData(qulonglong(program_id), ProgramIdRole);
|
||||
setData(qulonglong(extdata_id), ExtdataIdRole);
|
||||
setData(quint32(media_type), MediaTypeRole);
|
||||
setData(quint32(can_insert), CanInsertRole);
|
||||
|
||||
if (UISettings::values.game_list_icon_size.GetValue() ==
|
||||
UISettings::GameListIconSize::NoIcon) {
|
||||
|
||||
@ -113,7 +113,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
{
|
||||
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id,
|
||||
extdata_id, media_type,
|
||||
res == Loader::ResultStatus::ErrorEncrypted),
|
||||
res == Loader::ResultStatus::ErrorEncrypted,
|
||||
loader->GetFileType() == Loader::FileType::CCI),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItemRegion(smdh),
|
||||
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(
|
||||
|
||||
@ -85,6 +85,8 @@ struct Values {
|
||||
Settings::Setting<bool> hide_mouse{false, "hideInactiveMouse"};
|
||||
Settings::Setting<bool> check_for_update_on_start{true, "check_for_update_on_start"};
|
||||
|
||||
Settings::Setting<std::string> inserted_cartridge{"", "inserted_cartridge"};
|
||||
|
||||
// Discord RPC
|
||||
Settings::Setting<bool> enable_discord_presence{true, "enable_discord_presence"};
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/dumping/backend.h"
|
||||
#include "core/file_sys/ncch_container.h"
|
||||
#include "core/frontend/image_interface.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/global.h"
|
||||
@ -752,6 +753,18 @@ void System::RegisterAppLoaderEarly(std::unique_ptr<Loader::AppLoader>& loader)
|
||||
early_app_loader = std::move(loader);
|
||||
}
|
||||
|
||||
void System::InsertCartridge(const std::string& path) {
|
||||
FileSys::NCCHContainer cartridge_container(path);
|
||||
if (cartridge_container.LoadHeader() == Loader::ResultStatus::Success &&
|
||||
cartridge_container.IsNCSD()) {
|
||||
inserted_cartridge = path;
|
||||
}
|
||||
}
|
||||
|
||||
void System::EjectCartridge() {
|
||||
inserted_cartridge.clear();
|
||||
}
|
||||
|
||||
bool System::IsInitialSetup() {
|
||||
return app_loader && app_loader->DoingInitialSetup();
|
||||
}
|
||||
|
||||
@ -374,6 +374,14 @@ public:
|
||||
|
||||
void RegisterAppLoaderEarly(std::unique_ptr<Loader::AppLoader>& loader);
|
||||
|
||||
void InsertCartridge(const std::string& path);
|
||||
|
||||
void EjectCartridge();
|
||||
|
||||
const std::string& GetCartridge() const {
|
||||
return inserted_cartridge;
|
||||
}
|
||||
|
||||
bool IsInitialSetup();
|
||||
|
||||
private:
|
||||
@ -399,6 +407,9 @@ private:
|
||||
// Temporary app loader passed from frontend
|
||||
std::unique_ptr<Loader::AppLoader> early_app_loader;
|
||||
|
||||
/// AppLoader for current inserted cartridge
|
||||
std::string inserted_cartridge;
|
||||
|
||||
/// ARM11 CPU core
|
||||
std::vector<std::shared_ptr<ARM_Interface>> cpu_cores;
|
||||
ARM_Interface* running_core = nullptr;
|
||||
|
||||
@ -88,14 +88,31 @@ ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path,
|
||||
std::memcpy(&openfile_path, binary.data(), sizeof(NCCHFilePath));
|
||||
|
||||
std::string file_path;
|
||||
if (Settings::values.is_new_3ds) {
|
||||
// Try the New 3DS specific variant first.
|
||||
file_path = Service::AM::GetTitleContentPath(media_type, title_id | 0x20000000,
|
||||
openfile_path.content_index);
|
||||
}
|
||||
if (!Settings::values.is_new_3ds || !FileUtil::Exists(file_path)) {
|
||||
file_path =
|
||||
Service::AM::GetTitleContentPath(media_type, title_id, openfile_path.content_index);
|
||||
|
||||
if (media_type == Service::FS::MediaType::GameCard) {
|
||||
const auto& cartridge = Core::System::GetInstance().GetCartridge();
|
||||
if (cartridge.empty()) {
|
||||
return ResultNotFound;
|
||||
}
|
||||
|
||||
u64 card_program_id;
|
||||
auto cartridge_loader = Loader::GetLoader(cartridge);
|
||||
FileSys::NCCHContainer cartridge_ncch(cartridge);
|
||||
if (cartridge_ncch.ReadProgramId(card_program_id) != Loader::ResultStatus::Success ||
|
||||
card_program_id != title_id) {
|
||||
return ResultNotFound;
|
||||
}
|
||||
file_path = cartridge;
|
||||
} else {
|
||||
if (Settings::values.is_new_3ds) {
|
||||
// Try the New 3DS specific variant first.
|
||||
file_path = Service::AM::GetTitleContentPath(media_type, title_id | 0x20000000,
|
||||
openfile_path.content_index);
|
||||
}
|
||||
if (!Settings::values.is_new_3ds || !FileUtil::Exists(file_path)) {
|
||||
file_path =
|
||||
Service::AM::GetTitleContentPath(media_type, title_id, openfile_path.content_index);
|
||||
}
|
||||
}
|
||||
|
||||
auto ncch_container = NCCHContainer(file_path, 0, openfile_path.content_index);
|
||||
|
||||
@ -1408,39 +1408,52 @@ void Module::ScanForTitlesImpl(Service::FS::MediaType media_type) {
|
||||
|
||||
LOG_DEBUG(Service_AM, "Starting title scan for media_type={}", static_cast<int>(media_type));
|
||||
|
||||
std::string title_path = GetMediaTitlePath(media_type);
|
||||
|
||||
FileUtil::FSTEntry entries;
|
||||
FileUtil::ScanDirectoryTree(title_path, entries, 1, &stop_scan_flag);
|
||||
for (const FileUtil::FSTEntry& tid_high : entries.children) {
|
||||
if (stop_scan_flag) {
|
||||
break;
|
||||
if (media_type == FS::MediaType::GameCard) {
|
||||
const auto& cartridge = system.GetCartridge();
|
||||
if (!cartridge.empty()) {
|
||||
u64 program_id = 0;
|
||||
FileSys::NCCHContainer cartridge_ncch(cartridge);
|
||||
Loader::ResultStatus res = cartridge_ncch.ReadProgramId(program_id);
|
||||
if (res == Loader::ResultStatus::Success) {
|
||||
am_title_list[static_cast<u32>(media_type)].push_back(program_id);
|
||||
}
|
||||
}
|
||||
for (const FileUtil::FSTEntry& tid_low : tid_high.children) {
|
||||
} else {
|
||||
std::string title_path = GetMediaTitlePath(media_type);
|
||||
|
||||
FileUtil::FSTEntry entries;
|
||||
FileUtil::ScanDirectoryTree(title_path, entries, 1, &stop_scan_flag);
|
||||
for (const FileUtil::FSTEntry& tid_high : entries.children) {
|
||||
if (stop_scan_flag) {
|
||||
break;
|
||||
}
|
||||
std::string tid_string = tid_high.virtualName + tid_low.virtualName;
|
||||
for (const FileUtil::FSTEntry& tid_low : tid_high.children) {
|
||||
if (stop_scan_flag) {
|
||||
break;
|
||||
}
|
||||
std::string tid_string = tid_high.virtualName + tid_low.virtualName;
|
||||
|
||||
if (tid_string.length() == TITLE_ID_VALID_LENGTH) {
|
||||
const u64 tid = std::stoull(tid_string, nullptr, 16);
|
||||
if (tid_string.length() == TITLE_ID_VALID_LENGTH) {
|
||||
const u64 tid = std::stoull(tid_string, nullptr, 16);
|
||||
|
||||
if (tid & TWL_TITLE_ID_FLAG) {
|
||||
// TODO(PabloMK7) Move to TWL Nand, for now only check that
|
||||
// the contents exists in CTR Nand as this is a SRL file
|
||||
// instead of NCCH.
|
||||
if (FileUtil::Exists(GetTitleContentPath(media_type, tid))) {
|
||||
am_title_list[static_cast<u32>(media_type)].push_back(tid);
|
||||
}
|
||||
} else {
|
||||
FileSys::NCCHContainer container(GetTitleContentPath(media_type, tid));
|
||||
if (container.Load() == Loader::ResultStatus::Success) {
|
||||
am_title_list[static_cast<u32>(media_type)].push_back(tid);
|
||||
if (tid & TWL_TITLE_ID_FLAG) {
|
||||
// TODO(PabloMK7) Move to TWL Nand, for now only check that
|
||||
// the contents exists in CTR Nand as this is a SRL file
|
||||
// instead of NCCH.
|
||||
if (FileUtil::Exists(GetTitleContentPath(media_type, tid))) {
|
||||
am_title_list[static_cast<u32>(media_type)].push_back(tid);
|
||||
}
|
||||
} else {
|
||||
FileSys::NCCHContainer container(GetTitleContentPath(media_type, tid));
|
||||
if (container.Load() == Loader::ResultStatus::Success) {
|
||||
am_title_list[static_cast<u32>(media_type)].push_back(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_AM, "Finished title scan for media_type={}", static_cast<int>(media_type));
|
||||
}
|
||||
|
||||
@ -1449,6 +1462,7 @@ void Module::ScanForAllTitles() {
|
||||
ScanForTicketsImpl();
|
||||
ScanForTitlesImpl(Service::FS::MediaType::NAND);
|
||||
ScanForTitlesImpl(Service::FS::MediaType::SDMC);
|
||||
ScanForTitlesImpl(Service::FS::MediaType::GameCard);
|
||||
} else {
|
||||
scan_all_future = std::async([this]() {
|
||||
std::scoped_lock lock(am_lists_mutex);
|
||||
@ -1459,6 +1473,9 @@ void Module::ScanForAllTitles() {
|
||||
if (!stop_scan_flag) {
|
||||
ScanForTitlesImpl(Service::FS::MediaType::SDMC);
|
||||
}
|
||||
if (!stop_scan_flag) {
|
||||
ScanForTitlesImpl(Service::FS::MediaType::GameCard);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1981,30 +1998,75 @@ void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
Result GetTitleInfoFromList(std::span<const u64> title_id_list, Service::FS::MediaType media_type,
|
||||
Result GetTitleInfoFromList(Core::System& system, std::span<const u64> title_id_list,
|
||||
Service::FS::MediaType media_type,
|
||||
std::vector<TitleInfo>& title_info_out) {
|
||||
title_info_out.reserve(title_id_list.size());
|
||||
for (u32 i = 0; i < title_id_list.size(); i++) {
|
||||
std::string tmd_path = GetTitleMetadataPath(media_type, title_id_list[i]);
|
||||
if (media_type == Service::FS::MediaType::GameCard) {
|
||||
auto& cartridge = system.GetCartridge();
|
||||
if (cartridge.empty()) {
|
||||
LOG_DEBUG(Service_AM, "cartridge not inserted");
|
||||
return Result(ErrorDescription::NotFound, ErrorModule::AM,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
TitleInfo title_info = {};
|
||||
title_info.tid = title_id_list[i];
|
||||
FileSys::NCCHContainer ncch_container(cartridge);
|
||||
if (ncch_container.Load() != Loader::ResultStatus::Success ||
|
||||
!ncch_container.IsNCSD()) {
|
||||
LOG_ERROR(Service_AM, "failed to load cartridge card");
|
||||
return Result(ErrorDescription::NotFound, ErrorModule::AM,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
FileSys::TitleMetadata tmd;
|
||||
if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) {
|
||||
// TODO(shinyquagsire23): This is the total size of all files this process owns,
|
||||
// including savefiles and other content. This comes close but is off.
|
||||
title_info.size = tmd.GetContentSizeByIndex(FileSys::TMDContentIndex::Main);
|
||||
title_info.version = tmd.GetTitleVersion();
|
||||
title_info.type = tmd.GetTitleType();
|
||||
// This is what Process9 does for getting the information, from disassembly.
|
||||
// It is still unclear what do those values mean, like the title info type.
|
||||
if (ncch_container.exheader_header.arm11_system_local_caps.program_id !=
|
||||
title_id_list[i]) {
|
||||
LOG_DEBUG(Service_AM,
|
||||
"cartridge has different title ID than requested title_id={:016X} != "
|
||||
"cartridge_title_id={:016X}",
|
||||
title_id_list[i],
|
||||
ncch_container.exheader_header.arm11_system_local_caps.program_id);
|
||||
return Result(ErrorDescription::NotFound, ErrorModule::AM,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
TitleInfo title_info = {};
|
||||
title_info.tid = title_id_list[i];
|
||||
title_info.version =
|
||||
(*reinterpret_cast<u16_le*>(
|
||||
&ncch_container.exheader_header.codeset_info.flags.remaster_version)
|
||||
<< 10) &
|
||||
0xFC00;
|
||||
title_info.size = 0;
|
||||
title_info.type = 0x40;
|
||||
|
||||
LOG_DEBUG(Service_AM, "found title_id={:016X} version={:04X}", title_id_list[i],
|
||||
title_info.version);
|
||||
title_info_out.push_back(title_info);
|
||||
} else {
|
||||
LOG_DEBUG(Service_AM, "not found title_id={:016X}", title_id_list[i]);
|
||||
return Result(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
ErrorLevel::Permanent);
|
||||
std::string tmd_path = GetTitleMetadataPath(media_type, title_id_list[i]);
|
||||
|
||||
TitleInfo title_info = {};
|
||||
title_info.tid = title_id_list[i];
|
||||
|
||||
FileSys::TitleMetadata tmd;
|
||||
if (tmd.Load(tmd_path) == Loader::ResultStatus::Success) {
|
||||
// TODO(shinyquagsire23): This is the total size of all files this process owns,
|
||||
// including savefiles and other content. This comes close but is off.
|
||||
title_info.size = tmd.GetContentSizeByIndex(FileSys::TMDContentIndex::Main);
|
||||
title_info.version = tmd.GetTitleVersion();
|
||||
title_info.type = tmd.GetTitleType();
|
||||
} else {
|
||||
LOG_DEBUG(Service_AM, "not found title_id={:016X}", title_id_list[i]);
|
||||
return Result(ErrorDescription::NotFound, ErrorModule::AM,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
}
|
||||
LOG_DEBUG(Service_AM, "found title_id={:016X} version={:04X}", title_id_list[i],
|
||||
title_info.version);
|
||||
title_info_out.push_back(title_info);
|
||||
}
|
||||
LOG_DEBUG(Service_AM, "found title_id={:016X} version={:04X}", title_id_list[i],
|
||||
title_info.version);
|
||||
title_info_out.push_back(title_info);
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
@ -2136,7 +2198,7 @@ void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool
|
||||
}
|
||||
|
||||
if (async_data->res.IsSuccess()) {
|
||||
async_data->res = GetTitleInfoFromList(async_data->title_id_list,
|
||||
async_data->res = GetTitleInfoFromList(am->system, async_data->title_id_list,
|
||||
async_data->media_type, async_data->out);
|
||||
}
|
||||
return 0;
|
||||
@ -2350,7 +2412,7 @@ void Module::Interface::GetDLCTitleInfos(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
if (async_data->res.IsSuccess()) {
|
||||
async_data->res = GetTitleInfoFromList(async_data->title_id_list,
|
||||
async_data->res = GetTitleInfoFromList(am->system, async_data->title_id_list,
|
||||
async_data->media_type, async_data->out);
|
||||
}
|
||||
return 0;
|
||||
@ -2497,7 +2559,7 @@ void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
if (async_data->res.IsSuccess()) {
|
||||
async_data->res = GetTitleInfoFromList(async_data->title_id_list,
|
||||
async_data->res = GetTitleInfoFromList(am->system, async_data->title_id_list,
|
||||
async_data->media_type, async_data->out);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@ -75,6 +75,20 @@ void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx)
|
||||
LOG_WARNING(Service_APT, "called size={}", size);
|
||||
}
|
||||
|
||||
void Module::NSInterface::CardUpdateInitialize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
rp.Pop<u32>(); // Shared mem size
|
||||
rp.Pop<u32>(); // Always 0
|
||||
rp.Pop<u32>(); // Shared mem handle
|
||||
|
||||
LOG_WARNING(Service_APT, "(stubbed) called");
|
||||
const Result update_not_needed(11, ErrorModule::CUP, ErrorSummary::NothingHappened,
|
||||
ErrorLevel::Status);
|
||||
|
||||
auto rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(update_not_needed);
|
||||
}
|
||||
|
||||
void Module::NSInterface::ShutdownAsync(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -74,6 +74,8 @@ public:
|
||||
*/
|
||||
void SetWirelessRebootInfo(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void CardUpdateInitialize(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* NS::ShutdownAsync service function.
|
||||
* Inputs:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -11,11 +11,21 @@ namespace Service::NS {
|
||||
|
||||
std::shared_ptr<Kernel::Process> LaunchTitle(Core::System& system, FS::MediaType media_type,
|
||||
u64 title_id) {
|
||||
std::string path = AM::GetTitleContentPath(media_type, title_id);
|
||||
auto loader = Loader::GetLoader(path);
|
||||
std::string path;
|
||||
|
||||
if (!loader) {
|
||||
LOG_WARNING(Service_NS, "Could not find .app for title 0x{:016x}", title_id);
|
||||
if (media_type == FS::MediaType::GameCard) {
|
||||
path = system.GetCartridge();
|
||||
} else {
|
||||
path = AM::GetTitleContentPath(media_type, title_id);
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(path);
|
||||
u64 program_id;
|
||||
|
||||
if (!loader || loader->ReadProgramId(program_id) != Loader::ResultStatus::Success ||
|
||||
program_id != title_id) {
|
||||
LOG_WARNING(Service_NS, "Could not load title=0x{:016x} media_type={}", title_id,
|
||||
static_cast<int>(media_type));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -42,7 +52,13 @@ std::shared_ptr<Kernel::Process> LaunchTitle(Core::System& system, FS::MediaType
|
||||
}
|
||||
|
||||
void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id) {
|
||||
auto new_path = AM::GetTitleContentPath(media_type, title_id);
|
||||
std::string new_path;
|
||||
if (media_type == FS::MediaType::GameCard) {
|
||||
new_path = system.GetCartridge();
|
||||
} else {
|
||||
new_path = AM::GetTitleContentPath(media_type, title_id);
|
||||
}
|
||||
|
||||
if (new_path.empty() || !FileUtil::Exists(new_path)) {
|
||||
// TODO: This can happen if the requested title is not installed. Need a way to find
|
||||
// non-installed titles in the game list.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -17,7 +17,7 @@ NS_S::NS_S(std::shared_ptr<Service::APT::Module> apt)
|
||||
{0x0004, nullptr, "TerminateProcess"},
|
||||
{0x0005, nullptr, "LaunchApplicationFIRM"},
|
||||
{0x0006, &NS_S::SetWirelessRebootInfo, "SetWirelessRebootInfo"},
|
||||
{0x0007, nullptr, "CardUpdateInitialize"},
|
||||
{0x0007, &NS_S::CardUpdateInitialize, "CardUpdateInitialize"},
|
||||
{0x0008, nullptr, "CardUpdateShutdown"},
|
||||
{0x000D, nullptr, "SetTWLBannerHMAC"},
|
||||
{0x000E, &NS_S::ShutdownAsync, "ShutdownAsync"},
|
||||
|
||||
@ -913,6 +913,14 @@ void FS_USER::GetFreeBytes(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void FS_USER::GetCardType(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(0); // CTR Card
|
||||
LOG_DEBUG(Service_FS, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void FS_USER::GetSdmcArchiveResource(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
@ -999,8 +1007,8 @@ void FS_USER::CardSlotIsInserted(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(false);
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
rb.Push(!system.GetCartridge().empty());
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
}
|
||||
|
||||
void FS_USER::DeleteSystemSaveData(Kernel::HLERequestContext& ctx) {
|
||||
@ -1779,7 +1787,7 @@ FS_USER::FS_USER(Core::System& system)
|
||||
{0x0810, &FS_USER::CreateLegacySystemSaveData, "CreateLegacySystemSaveData"},
|
||||
{0x0811, nullptr, "DeleteSystemSaveData"},
|
||||
{0x0812, &FS_USER::GetFreeBytes, "GetFreeBytes"},
|
||||
{0x0813, nullptr, "GetCardType"},
|
||||
{0x0813, &FS_USER::GetCardType, "GetCardType"},
|
||||
{0x0814, &FS_USER::GetSdmcArchiveResource, "GetSdmcArchiveResource"},
|
||||
{0x0815, &FS_USER::GetNandArchiveResource, "GetNandArchiveResource"},
|
||||
{0x0816, nullptr, "GetSdmcFatfsError"},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -361,6 +361,8 @@ private:
|
||||
*/
|
||||
void GetFreeBytes(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void GetCardType(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetSdmcArchiveResource service function.
|
||||
* Inputs:
|
||||
|
||||
@ -298,6 +298,10 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual std::string GetFilePath() {
|
||||
return file ? file->Filename() : "";
|
||||
}
|
||||
|
||||
protected:
|
||||
Core::System& system;
|
||||
std::unique_ptr<FileUtil::IOFile> file;
|
||||
|
||||
@ -78,6 +78,10 @@ public:
|
||||
|
||||
bool IsFileCompressed() override;
|
||||
|
||||
std::string GetFilePath() override {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Loads .code section into memory for booting
|
||||
|
||||
Loading…
Reference in New Issue
Block a user