[core/filesystem] "easier" cheats folder structure like Ryujinx (#2795)

Less annoying way to make cheats

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2795
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
lizzie 2025-10-22 00:57:42 +02:00 committed by crueter
parent 86432f9552
commit 6ff043c4fb
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
3 changed files with 65 additions and 18 deletions

View file

@ -9,3 +9,4 @@ This handbook is primarily aimed at the end-user - baking useful knowledge for e
- **[Graphics](user/Graphics.md)** - **[Graphics](user/Graphics.md)**
- **[Platforms and Architectures](user/Architectures.md)** - **[Platforms and Architectures](user/Architectures.md)**
- **[Testing](user/Testing.md)** - **[Testing](user/Testing.md)**
- **[Data, savefiles and storage](user/Storage.md)**

39
docs/user/Storage.md Normal file
View file

@ -0,0 +1,39 @@
# User Handbook - Data, savefiles and storage
## Cheats
Cheats can be placed in the two manners:
**Method 1**:
- Right click on "Open mod directory"
- Create a file called `cheat_mycheat.txt` (the file must start with `cheat_` to be recognized as one)
- Write the cheat into said text file
- Done
This method doesn't account for build IDs, so any cheats for previous builds will not be automatically filtered out.
**Method 2**:
- Right click on "Open mod directory"
- Create a folder called `My cheat`
- Inside said folder create another one: `cheats`
- Then inside said folder create a file with the build ID in hexadecimal, lowercase, for example `1234FBCDE0000000.txt`
- Write the cheat into said text file
- Done
To delete a cheat simply do the process backwards (delete the folder/file).
## Savefiles
The method to access save data is simple:
- Right click on "Open save directory"
## Maintaining a library
### ZFS
One of the best ways to keep a gallery of archives is using a ZFS pool compressed with zstd.
```sh
sudo zfs create zroot/switch`
sudo zfs set compression=zstd zroot/switch
```
A new ZFS dataset will then be available on `/zroot/switch`. From which then can be added as a normal game directory. Remember to ensure proper permissions with `chmod 755 /zroot/switch/*`.

View file

@ -316,8 +316,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
return !CollectPatches(patch_dirs, build_id).empty(); return !CollectPatches(patch_dirs, build_id).empty();
} }
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) { if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
@ -326,28 +325,36 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto& disabled = Settings::values.disabled_addons[title_id];
auto patch_dirs = load_dir->GetSubdirectories(); auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(), std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
// <mod dir> / <folder> / cheats / <build id>.txt
std::vector<Core::Memory::CheatEntry> out; std::vector<Core::Memory::CheatEntry> out;
for (const auto& subdir : patch_dirs) { for (const auto& subdir : patch_dirs) {
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) { if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) == disabled.cend()) {
continue; if (auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); cheats_dir != nullptr) {
} if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true))
std::copy(res->begin(), res->end(), std::back_inserter(out));
auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false))
if (cheats_dir != nullptr) { std::copy(res->begin(), res->end(), std::back_inserter(out));
if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) { }
std::copy(res->begin(), res->end(), std::back_inserter(out)); }
continue; }
} // Uncareless user-friendly loading of patches (must start with 'cheat_')
// <mod dir> / <cheat file>.txt
if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) { auto const patch_files = load_dir->GetFiles();
std::copy(res->begin(), res->end(), std::back_inserter(out)); for (auto const& f : patch_files) {
auto const name = f->GetName();
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
std::vector<u8> data(f->GetSize());
if (f->Read(data.data(), data.size()) == data.size()) {
const Core::Memory::TextCheatParser parser;
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
std::copy(res.begin(), res.end(), std::back_inserter(out));
} else {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
} }
} }
} }
return out; return out;
} }