1
0
mirror of https://git.suyu.dev/suyu/suyu synced 2025-12-09 06:12:07 -06:00

14 Commits

Author SHA1 Message Date
zqpvr01
2b16baf506 Remove discord (3) 2024-04-11 03:44:08 +02:00
zqpvr01
73e135bf4a Remove discord (2) 2024-04-11 03:42:30 +02:00
zqpvr01
96f822d13f Remove discord 2024-04-11 03:41:07 +02:00
TheDoctor
0de49070e4 Merge
Reviewed-on: https://git.suyu.dev/suyu/suyu/pulls/65
2024-04-10 19:56:33 +02:00
BoomMicrophone
5a92429b8c Merge branch 'dev' into dev 2024-04-01 15:09:57 +02:00
BoomMicrophone
d09ab05c1b remove comment
gotta coooooommit :D
2024-03-31 21:42:11 +02:00
BoomMicrophone
db9035cc35 remove comment
gpg timeout so i gotta coooooommit :D
2024-03-31 21:41:43 +02:00
BoomMicrophone
dcd2890af6 Merge branch 'dev' into dev 2024-03-31 17:55:38 +02:00
BoomMicrophone
9c2f3bff3f Merge branch 'dev' into dev 2024-03-28 22:43:20 +01:00
BoomMicrophone
affee8c522 fix epic fail
(I am relying on actions to do the compiling for me until i get nix)
2024-03-26 16:34:22 +01:00
BoomMicrophone
b59aaf14e9 Woah guys this is so compliant!
Signed-off-by: BoomMicrophone <boommicrophone@noreply.localhost>
2024-03-26 16:10:33 +01:00
BoomMicrophone
caa1fb094c nullglob globstar gobble up those nuts
shoutout to gpg for not working

Signed-off-by: BoomMicrophone <boommicrophone@noreply.localhost>
2024-03-26 15:57:41 +01:00
Hustler One
eba28b0df3 All relevant changes in commit 51862e862d have been 2024-03-25 21:12:28 +01:00
Hustler One
207732e71f Clarify that we indeed have builds 2024-03-25 20:48:12 +01:00
9 changed files with 281 additions and 17 deletions

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
**Note**: We do not support or condone piracy in any form. In order to use suyu, you'll need keys from your real Switch system, and games which you have legally obtained and paid for. We do not intend to make money or profit from this project.
We're in need of developers. Please join our Discord server below if you want to contribute!
We're in need of developers. Please join our Matrix below if you want to contribute!
This repo is based on Yuzu EA 4176.
<hr />
@@ -25,7 +25,7 @@ It is written in C++ with portability in mind, and we're actively working on bui
</h4>
<p align="center">
<a href="https://discord.gg/suyu">Discord</a> |
<a href="https://chat.suyu.dev">Matrix</a> |
<a href="#status">Status</a> |
<a href="#development">Development</a> |
<a href="#downloads">Downloads</a> |
@@ -48,7 +48,7 @@ We currently have builds over at the [Releases](https://git.suyu.dev/suyu/suyu/r
This project is completely free and open source, and anyone can contribute to help improve suyu.
Most of the development happens on GitLab. For development discussion, please join us on [Discord](https://discord.gg/suyu).
Most of the development happens on GitLab. For development discussion, please join us on [Matrix](https://chat.suyu.dev).
If you want to contribute, please take a look at the [Contributor's Guide](https://git.suyu.dev/suyu/suyu/wiki/Contributing) and [Developer Information](https://git.suyu.dev/suyu/suyu/wiki/Developer-Information).
You can also contact any of the developers on Discord to learn more about the current state of suyu.
@@ -77,7 +77,7 @@ We have official builds [here.](https://git.suyu.dev/suyu/suyu/releases) If any
## Support
If you have any questions, don't hesitate to ask us on [Discord](https://discord.gg/suyu). We don't bite!
If you have any questions, don't hesitate to ask us on [Matrix](https://chat.suyu.dev). We don't bite!
## License

View File

@@ -1,8 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Modified by palfaiate on <2024/03/07>
#pragma once
#include <algorithm>

View File

@@ -175,9 +175,8 @@ constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission per
return static_cast<KMemoryPermission>(
(static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserMask) |
KMemoryPermission::KernelRead |
((static_cast<KMemoryPermission>(perm) & Svc::MemoryPermission::UserWrite)
? KMemoryPermission::KernelWrite
: KMemoryPermission::None) |
((static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserWrite)
<< KMemoryPermission::KernelShift) |
(perm == Svc::MemoryPermission::None ? KMemoryPermission::NotMapped
: KMemoryPermission::None));
}

View File

@@ -1594,7 +1594,7 @@ size_t KPageTableBase::GetAliasCodeDataSize() const {
}
Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
size_t num_pages, KPageProperties& perm) {
size_t num_pages, KMemoryPermission perm) {
ASSERT(this->IsLockedByCurrentThread());
// Create a page group to hold the pages we allocate.
@@ -1615,6 +1615,7 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
}
// Map the pages.
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::None};
R_RETURN(this->Operate(page_list, address, num_pages, pg, properties, OperationType::MapGroup,
false));
}
@@ -2748,12 +2749,12 @@ Result KPageTableBase::MapPages(KProcessAddress* out_addr, size_t num_pages, siz
KScopedPageTableUpdater updater(this);
// Perform mapping operation.
KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
if (is_pa_valid) {
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties,
OperationType::Map, false));
} else {
R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, properties));
R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
}
// Update the blocks.
@@ -2792,8 +2793,7 @@ Result KPageTableBase::MapPages(KProcessAddress address, size_t num_pages, KMemo
KScopedPageTableUpdater updater(this);
// Map the pages.
KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, properties));
R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
// Update the blocks.
m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,

View File

@@ -441,7 +441,7 @@ private:
Svc::MemoryState state) const;
Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
size_t num_pages, KPageProperties& perm);
size_t num_pages, KMemoryPermission perm);
Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Modified by palfaiate on <2024/03/07>
// Reverted palfaiate's changes on <2024/03/25> -Nine-Ball
#include <QApplication>
#include <QDir>
@@ -580,6 +581,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
remove_menu->addSeparator();
QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
@@ -647,6 +651,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] {
emit RemoveFileRequested(program_id, GameListRemoveTarget::CacheStorage, path);
});
connect(dump_romfs, &QAction::triggered, [this, program_id, path]() {
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal);
});
connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() {
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC);
});
connect(verify_integrity, &QAction::triggered,
[this, path]() { emit VerifyIntegrityRequested(path); });
connect(copy_tid, &QAction::triggered,

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Modified by palfaiate on <2024/03/07>
// Reverted palfaiate's changes on <2024/03/25> -Nine-Ball
#pragma once
@@ -52,6 +53,11 @@ enum class GameListRemoveTarget {
CacheStorage,
};
enum class DumpRomFSTarget {
Normal,
SDMC,
};
enum class GameListShortcutTarget {
Desktop,
Applications,
@@ -113,6 +119,7 @@ signals:
void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
const std::string& game_path);
void RemovePlayTimeRequested(u64 program_id);
void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
void VerifyIntegrityRequested(const std::string& game_path);
void CopyTIDRequested(u64 program_id);
void CreateShortcut(u64 program_id, const std::string& game_path,

View File

@@ -1,8 +1,6 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Modified by palfaiate on <2024/03/07>
#include <cinttypes>
#include <clocale>
#include <cmath>
@@ -55,6 +53,18 @@
#include "suyu/multiplayer/state.h"
#include "suyu/util/controller_navigation.h"
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) {
return vfs->CreateDirectory(path, mode);
}
// Overloaded function, also removed by palafiate
static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir,
const std::string& path) {
return dir->CreateFile(path);
}
#include <fmt/ostream.h>
#include <glad/glad.h>
@@ -1465,6 +1475,7 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
connect(game_list, &GameList::RemovePlayTimeRequested, this,
&GMainWindow::OnGameListRemovePlayTimeData);
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
connect(game_list, &GameList::VerifyIntegrityRequested, this,
&GMainWindow::OnGameListVerifyIntegrity);
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
@@ -2369,6 +2380,68 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
}
static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog,
const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest,
bool full) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
if (dialog.wasCanceled())
return false;
std::vector<u8> buffer(CopyBufferSize);
auto last_timestamp = std::chrono::steady_clock::now();
const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file,
const FileSys::VirtualFile& dest_file) {
if (src_file == nullptr || dest_file == nullptr) {
return false;
}
if (!dest_file->Resize(src_file->GetSize())) {
return false;
}
for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) {
if (dialog.wasCanceled()) {
dest_file->Resize(0);
return false;
}
using namespace std::literals::chrono_literals;
const auto new_timestamp = std::chrono::steady_clock::now();
if ((new_timestamp - last_timestamp) > 33ms) {
last_timestamp = new_timestamp;
dialog.setValue(
static_cast<int>(std::min(read_size, total_size) * 100 / total_size));
QCoreApplication::processEvents();
}
const auto read = src_file->Read(buffer.data(), buffer.size(), i);
dest_file->Write(buffer.data(), read, i);
read_size += read;
}
return true;
};
if (full) {
for (const auto& file : src->GetFiles()) {
const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
if (!QtRawCopy(file, out))
return false;
}
}
for (const auto& dir : src->GetSubdirectories()) {
const auto out = dest->CreateSubdirectory(dir->GetName());
if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full))
return false;
}
return true;
}
QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
switch (type) {
case InstalledEntryType::Game:
@@ -2610,6 +2683,121 @@ void GMainWindow::RemoveCacheStorage(u64 program_id) {
Common::FS::RemoveDirRecursively(path);
}
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path,
DumpRomFSTarget target) {
const auto failed = [this] {
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
tr("There was an error copying the RomFS files or the user "
"cancelled the operation."));
};
const auto loader =
Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
if (loader == nullptr) {
failed();
return;
}
FileSys::VirtualFile packed_update_raw{};
loader->ReadUpdateRaw(packed_update_raw);
const auto& installed = system->GetContentProvider();
u64 title_id{};
u8 raw_type{};
if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) {
failed();
return;
}
const auto type = static_cast<FileSys::ContentRecordType>(raw_type);
const auto base_nca = installed.GetEntry(title_id, type);
if (!base_nca) {
failed();
return;
}
const FileSys::NCA update_nca{packed_update_raw, nullptr};
if (type != FileSys::ContentRecordType::Program ||
update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS ||
update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) {
packed_update_raw = {};
}
const auto base_romfs = base_nca->GetRomFS();
const auto dump_dir =
target == DumpRomFSTarget::Normal
? Common::FS::GetSuyuPath(Common::FS::SuyuPath::DumpDir)
: Common::FS::GetSuyuPath(Common::FS::SuyuPath::SDMCDir) / "atmosphere" / "contents";
const auto romfs_dir = fmt::format("{:016X}/romfs", title_id);
const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite);
if (out == nullptr) {
failed();
vfs->DeleteDirectory(path);
return;
}
bool ok = false;
const QStringList selections{tr("Full"), tr("Skeleton")};
const auto res = QInputDialog::getItem(
this, tr("Select RomFS Dump Mode"),
tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the "
"files into the new directory while <br>skeleton will only create the directory "
"structure."),
selections, 0, false, &ok);
if (!ok) {
failed();
vfs->DeleteDirectory(path);
return;
}
const auto extracted = FileSys::ExtractRomFS(romfs);
if (extracted == nullptr) {
failed();
return;
}
const auto full = res == selections.constFirst();
// The expected required space is the size of the RomFS + 1 GiB
const auto minimum_free_space = romfs->GetSize() + 0x40000000;
if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) {
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
tr("There is not enough free space at %1 to extract the RomFS. Please "
"free up space or select a different dump directory at "
"Emulation > Configure > System > Filesystem > Dump Root")
.arg(QString::fromStdString(path)));
return;
}
QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
size_t read_size = 0;
if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) {
progress.close();
QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
tr("The operation completed successfully."));
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path)));
} else {
progress.close();
failed();
vfs->DeleteDirectory(path);
}
}
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
const auto NotImplemented = [this] {
QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
@@ -4680,6 +4868,66 @@ void GMainWindow::SetFirmwareVersion() {
firmware_label->setToolTip(QString::fromStdString(display_title));
}
bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
u64* selected_title_id, u8* selected_content_record_type) {
using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
boost::container::flat_set<ContentInfo> available_title_ids;
const auto RetrieveEntries = [&](FileSys::TitleType title_type,
FileSys::ContentRecordType record_type) {
const auto entries = installed.ListEntriesFilter(title_type, record_type);
for (const auto& entry : entries) {
if (FileSys::GetBaseTitleID(entry.title_id) == program_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) {
available_title_ids.insert({entry.title_id, title_type, record_type});
}
}
};
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::HtmlDocument);
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::LegalInformation);
RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
if (available_title_ids.empty()) {
return false;
}
size_t title_index = 0;
if (available_title_ids.size() > 1) {
QStringList list;
for (auto& [title_id, title_type, record_type] : available_title_ids) {
const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id));
if (record_type == FileSys::ContentRecordType::Program) {
list.push_back(QStringLiteral("Program [%1]").arg(hex_title_id));
} else if (record_type == FileSys::ContentRecordType::HtmlDocument) {
list.push_back(QStringLiteral("HTML document [%1]").arg(hex_title_id));
} else if (record_type == FileSys::ContentRecordType::LegalInformation) {
list.push_back(QStringLiteral("Legal information [%1]").arg(hex_title_id));
} else {
list.push_back(
QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id));
}
}
bool ok;
const auto res = QInputDialog::getItem(
this, tr("Select RomFS Dump Target"),
tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
if (!ok) {
return false;
}
title_index = list.indexOf(res);
}
const auto& [title_id, title_type, record_type] = *available_title_ids.nth(title_index);
*selected_title_id = title_id;
*selected_content_record_type = static_cast<u8>(record_type);
return true;
}
bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr ||
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {

View File

@@ -51,6 +51,7 @@ class WaitTreeWidget;
enum class GameListOpenTarget;
enum class GameListRemoveTarget;
enum class GameListShortcutTarget;
enum class DumpRomFSTarget;
enum class InstalledEntryType;
class GameListPlaceholder;
@@ -347,6 +348,7 @@ private slots:
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
const std::string& game_path);
void OnGameListRemovePlayTimeData(u64 program_id);
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
void OnGameListVerifyIntegrity(const std::string& game_path);
void OnGameListCopyTID(u64 program_id);
void OnGameListNavigateToGamedbEntry(u64 program_id,