Long overdue clang-format pass on most of the project (#773)
Some checks are pending
Android Build / x64 (release) (push) Waiting to run
Android Build / arm64 (release) (push) Waiting to run
HTTP Server Build / build (push) Waiting to run
Hydra Core Build / Windows (push) Waiting to run
Hydra Core Build / MacOS (push) Waiting to run
Hydra Core Build / Linux (push) Waiting to run
Hydra Core Build / Android-x64 (push) Waiting to run
Hydra Core Build / ARM-Libretro (push) Waiting to run
Linux AppImage Build / build (push) Waiting to run
Linux Build / build (push) Waiting to run
MacOS Build / MacOS-arm64 (push) Waiting to run
MacOS Build / MacOS-x86_64 (push) Waiting to run
MacOS Build / MacOS-Universal (push) Blocked by required conditions
Qt Build / Windows (push) Waiting to run
Qt Build / MacOS-arm64 (push) Waiting to run
Qt Build / MacOS-x86_64 (push) Waiting to run
Qt Build / MacOS-Universal (push) Blocked by required conditions
Qt Build / Linux (push) Waiting to run
Windows Build / build (push) Waiting to run
iOS Simulator Build / build (push) Waiting to run

This commit is contained in:
wheremyfoodat 2025-07-06 18:25:20 +03:00 committed by GitHub
parent d1f4ae2911
commit 8e20bd6220
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 13445 additions and 26224 deletions

View file

@ -15,7 +15,7 @@ namespace Audio {
bool signalledData; bool signalledData;
bool signalledSemaphore; bool signalledSemaphore;
uint audioFrameIndex = 0; // Index in our audio frame uint audioFrameIndex = 0; // Index in our audio frame
std::array<s16, 160 * 2> audioFrame; std::array<s16, 160 * 2> audioFrame;
// Get a pointer to a data memory address // Get a pointer to a data memory address

View file

@ -271,7 +271,7 @@ class ArchiveBase {
bool isSafeTextPath(const FSPath& path) { bool isSafeTextPath(const FSPath& path) {
if (path.type == PathType::UTF16) { if (path.type == PathType::UTF16) {
return isPathSafe<PathType::UTF16>(path); return isPathSafe<PathType::UTF16>(path);
} else if (path.type == PathType::ASCII){ } else if (path.type == PathType::ASCII) {
return isPathSafe<PathType::ASCII>(path); return isPathSafe<PathType::ASCII>(path);
} }

View file

@ -2,11 +2,13 @@
#include "archive_base.hpp" #include "archive_base.hpp"
class ExtSaveDataArchive : public ArchiveBase { class ExtSaveDataArchive : public ArchiveBase {
public: public:
ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), isShared(isShared), backingFolder(folder) {}
isShared(isShared), backingFolder(folder) {}
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } u64 getFreeBytes() override {
Helpers::panic("ExtSaveData::GetFreeBytes unimplemented");
return 0;
}
std::string name() override { return "ExtSaveData::" + backingFolder; } std::string name() override { return "ExtSaveData::" + backingFolder; }
HorizonResult createDirectory(const FSPath& path) override; HorizonResult createDirectory(const FSPath& path) override;
@ -29,5 +31,5 @@ public:
std::string getExtSaveDataPathFromBinary(const FSPath& path); std::string getExtSaveDataPathFromBinary(const FSPath& path);
bool isShared = false; bool isShared = false;
std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path.
}; };

View file

@ -2,10 +2,13 @@
#include "archive_base.hpp" #include "archive_base.hpp"
class NCCHArchive : public ArchiveBase { class NCCHArchive : public ArchiveBase {
public: public:
NCCHArchive(Memory& mem) : ArchiveBase(mem) {} NCCHArchive(Memory& mem) : ArchiveBase(mem) {}
u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; } u64 getFreeBytes() override {
Helpers::panic("NCCH::GetFreeBytes unimplemented");
return 0;
}
std::string name() override { return "NCCH"; } std::string name() override { return "NCCH"; }
HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult createFile(const FSPath& path, u64 size) override;

View file

@ -2,7 +2,7 @@
#include "archive_base.hpp" #include "archive_base.hpp"
class SaveDataArchive : public ArchiveBase { class SaveDataArchive : public ArchiveBase {
public: public:
SaveDataArchive(Memory& mem) : ArchiveBase(mem) {} SaveDataArchive(Memory& mem) : ArchiveBase(mem) {}
u64 getFreeBytes() override { return 32_MB; } u64 getFreeBytes() override { return 32_MB; }
@ -27,6 +27,6 @@ public:
// Returns whether the cart has save data or not // Returns whether the cart has save data or not
bool cartHasSaveData() { bool cartHasSaveData() {
auto cxi = mem.getCXI(); auto cxi = mem.getCXI();
return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data
} }
}; };

View file

@ -2,7 +2,7 @@
#include "archive_base.hpp" #include "archive_base.hpp"
class SelfNCCHArchive : public ArchiveBase { class SelfNCCHArchive : public ArchiveBase {
public: public:
SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {} SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {}
u64 getFreeBytes() override { return 0; } u64 getFreeBytes() override { return 0; }

View file

@ -3,6 +3,7 @@
class UserSaveDataArchive : public ArchiveBase { class UserSaveDataArchive : public ArchiveBase {
u32 archiveID; u32 archiveID;
public: public:
UserSaveDataArchive(Memory& mem, u32 archiveID) : ArchiveBase(mem), archiveID(archiveID) {} UserSaveDataArchive(Memory& mem, u32 archiveID) : ArchiveBase(mem), archiveID(archiveID) {}

View file

@ -1,132 +1,69 @@
// Generated with https://github.com/B3n30/citra_system_archives // Generated with https://github.com/B3n30/citra_system_archives
#pragma once #pragma once
const unsigned char BAD_WORD_LIST_DATA[] = { constexpr unsigned char BAD_WORD_LIST_DATA[] = {
0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x4c, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8c, 0x01, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x01, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3c, 0x02, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
0xff, 0xff, 0xff, 0xff, 0x8c, 0x01, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xe4, 0x01, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x3c, 0x02, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb8, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00,
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x33, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x74, 0x00,
0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00,
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xb0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00,
0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x34, 0x00, 0x2e, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x33, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x02, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x34, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x20, 0x02, 0x00, 0x00,
0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x76, 0x00, 0x65, 0x00,
0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x31, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x36, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0xb8, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x33, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x3c, 0x02, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00
0x36, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xdc, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x37, 0x00, 0x2e, 0x00,
0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x02, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x0a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x39, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x20, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x76, 0x00, 0x65, 0x00,
0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2e, 0x00,
0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00
}; };
const unsigned int BAD_WORD_LIST_DATA_len = 1508; const unsigned int BAD_WORD_LIST_DATA_len = 1508;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,7 @@ class Emulator;
namespace httplib { namespace httplib {
class Server; class Server;
struct Response; struct Response;
} } // namespace httplib
// Wrapper for httplib::Response that allows the HTTP server to wait for the response to be ready // Wrapper for httplib::Response that allows the HTTP server to wait for the response to be ready
struct DeferredResponseWrapper { struct DeferredResponseWrapper {
@ -63,7 +63,7 @@ struct HttpServer {
std::thread httpServerThread; std::thread httpServerThread;
std::queue<std::unique_ptr<HttpAction>> actionQueue; std::queue<std::unique_ptr<HttpAction>> actionQueue;
std::mutex actionQueueMutex; std::mutex actionQueueMutex;
std::unique_ptr<HttpAction> currentStepAction {}; std::unique_ptr<HttpAction> currentStepAction{};
std::map<std::string, u32> keyMap; std::map<std::string, u32> keyMap;
bool paused = false; bool paused = false;

File diff suppressed because it is too large Load diff

View file

@ -13,4 +13,4 @@ namespace Helpers {
static constexpr void static_for(Func&& f) { static constexpr void static_for(Func&& f) {
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{}); static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
} }
} } // namespace Helpers

View file

@ -3,8 +3,8 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "screen_layout.hpp"
#include "gl/context.h" #include "gl/context.h"
#include "screen_layout.hpp"
#include "window_info.h" #include "window_info.h"
// OpenGL widget for drawing the 3DS screen // OpenGL widget for drawing the 3DS screen

View file

@ -4,9 +4,9 @@
#include <filesystem> #include <filesystem>
#include "screen_layout.hpp"
#include "emulator.hpp" #include "emulator.hpp"
#include "input_mappings.hpp" #include "input_mappings.hpp"
#include "screen_layout.hpp"
class FrontendSDL { class FrontendSDL {
Emulator emu; Emulator emu;

View file

@ -241,13 +241,12 @@ struct GLStateManager {
void setBlendFunc(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha) { void setBlendFunc(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha) {
if (blendFuncSourceRGB != sourceRGB || blendFuncDestRGB != destRGB || blendFuncSourceAlpha != sourceAlpha || if (blendFuncSourceRGB != sourceRGB || blendFuncDestRGB != destRGB || blendFuncSourceAlpha != sourceAlpha ||
blendFuncDestAlpha != destAlpha) { blendFuncDestAlpha != destAlpha) {
blendFuncSourceRGB = sourceRGB; blendFuncSourceRGB = sourceRGB;
blendFuncDestRGB = destRGB; blendFuncDestRGB = destRGB;
blendFuncSourceAlpha = sourceAlpha; blendFuncSourceAlpha = sourceAlpha;
blendFuncDestAlpha = destAlpha; blendFuncDestAlpha = destAlpha;
glBlendFuncSeparate(sourceRGB, destRGB,sourceAlpha, destAlpha); glBlendFuncSeparate(sourceRGB, destRGB, sourceAlpha, destAlpha);
} }
} }

View file

@ -42,7 +42,7 @@ namespace Result {
OS = 6, OS = 6,
DBG = 7, DBG = 7,
DMNT = 8, DMNT = 8,
PDN = 9 , PDN = 9,
GSP = 10, GSP = 10,
I2C = 11, I2C = 11,
GPIO = 12, GPIO = 12,
@ -132,7 +132,7 @@ namespace Result {
}; };
class HorizonResult { class HorizonResult {
private: private:
static const uint32_t DescriptionBits = 10; static const uint32_t DescriptionBits = 10;
static const uint32_t ModuleBits = 8; static const uint32_t ModuleBits = 8;
static const uint32_t ReservedBits = 3; static const uint32_t ReservedBits = 3;
@ -156,7 +156,7 @@ namespace Result {
return (description << DescriptionOffset) | (module << ModuleOffset) | (summary << SummaryOffset) | (level << LevelOffset); return (description << DescriptionOffset) | (module << ModuleOffset) | (summary << SummaryOffset) | (level << LevelOffset);
} }
public: public:
constexpr HorizonResult() : m_value(0) {} constexpr HorizonResult() : m_value(0) {}
constexpr HorizonResult(uint32_t value) : m_value(value) {} constexpr HorizonResult(uint32_t value) : m_value(value) {}
constexpr HorizonResult(uint32_t description, HorizonResultModule module, HorizonResultSummary summary, HorizonResultLevel level) : m_value(makeValue(description, static_cast<uint32_t>(module), static_cast<uint32_t>(summary), static_cast<uint32_t>(level))) {} constexpr HorizonResult(uint32_t description, HorizonResultModule module, HorizonResultSummary summary, HorizonResultLevel level) : m_value(makeValue(description, static_cast<uint32_t>(module), static_cast<uint32_t>(summary), static_cast<uint32_t>(level))) {}
@ -193,14 +193,14 @@ namespace Result {
static_assert(std::is_trivially_destructible<HorizonResult>::value, "std::is_trivially_destructible<HorizonResult>::value"); static_assert(std::is_trivially_destructible<HorizonResult>::value, "std::is_trivially_destructible<HorizonResult>::value");
#define DEFINE_HORIZON_RESULT_MODULE(ns, value) \ #define DEFINE_HORIZON_RESULT_MODULE(ns, value) \
namespace ns::Detail {\ namespace ns::Detail { \
static constexpr HorizonResultModule ModuleId = HorizonResultModule::value; \ static constexpr HorizonResultModule ModuleId = HorizonResultModule::value; \
} }
#define DEFINE_HORIZON_RESULT(name, description, summary, level) \ #define DEFINE_HORIZON_RESULT(name, description, summary, level) \
static constexpr HorizonResult name(description, Detail::ModuleId, HorizonResultSummary::summary, HorizonResultLevel::level); static constexpr HorizonResult name(description, Detail::ModuleId, HorizonResultSummary::summary, HorizonResultLevel::level);
static constexpr HorizonResult Success(0); static constexpr HorizonResult Success(0);
static constexpr HorizonResult FailurePlaceholder(UINT32_MAX); static constexpr HorizonResult FailurePlaceholder(UINT32_MAX);
}; }; // namespace Result

View file

@ -10,29 +10,29 @@
// We use the same code for Android as well, since the values we get from Android are in the same format as SDL (m/s^2 for acceleration, rad/s for // We use the same code for Android as well, since the values we get from Android are in the same format as SDL (m/s^2 for acceleration, rad/s for
// rotation) // rotation)
namespace Sensors::SDL { namespace Sensors::SDL {
// Convert the rotation data we get from SDL sensor events to rotation data we can feed right to HID // Convert the rotation data we get from SDL sensor events to rotation data we can feed right to HID
// Returns [pitch, roll, yaw] // Returns [pitch, roll, yaw]
static glm::vec3 convertRotation(glm::vec3 rotation) { static glm::vec3 convertRotation(glm::vec3 rotation) {
// Annoyingly, Android doesn't support the <numbers> header yet so we define pi ourselves // Annoyingly, Android doesn't support the <numbers> header yet so we define pi ourselves
static constexpr double pi = 3.141592653589793; static constexpr double pi = 3.141592653589793;
// Convert the rotation from rad/s to deg/s and scale by the gyroscope coefficient in HID // Convert the rotation from rad/s to deg/s and scale by the gyroscope coefficient in HID
constexpr float scale = 180.f / pi * HIDService::gyroscopeCoeff; constexpr float scale = 180.f / pi * HIDService::gyroscopeCoeff;
// The axes are also inverted, so invert scale before the multiplication. // The axes are also inverted, so invert scale before the multiplication.
return rotation * -scale; return rotation * -scale;
} }
static glm::vec3 convertAcceleration(float* data) { static glm::vec3 convertAcceleration(float* data) {
// Set our cap to ~9 m/s^2. The 3DS sensors cap at -930 and +930, so values above this value will get clamped to 930 // Set our cap to ~9 m/s^2. The 3DS sensors cap at -930 and +930, so values above this value will get clamped to 930
// At rest (3DS laid flat on table), hardware reads around ~0 for x and z axis, and around ~480 for y axis due to gravity. // At rest (3DS laid flat on table), hardware reads around ~0 for x and z axis, and around ~480 for y axis due to gravity.
// This code tries to mimic this approximately, with offsets based on measurements from my DualShock 4. // This code tries to mimic this approximately, with offsets based on measurements from my DualShock 4.
static constexpr float accelMax = 9.f; static constexpr float accelMax = 9.f;
// We define standard gravity(g) ourself instead of using the SDL one in order for the code to work on Android too. // We define standard gravity(g) ourself instead of using the SDL one in order for the code to work on Android too.
static constexpr float standardGravity = 9.80665f; static constexpr float standardGravity = 9.80665f;
s16 x = std::clamp<s16>(s16(data[0] / accelMax * 930.f), -930, +930); s16 x = std::clamp<s16>(s16(data[0] / accelMax * 930.f), -930, +930);
s16 y = std::clamp<s16>(s16(data[1] / (standardGravity * accelMax) * 930.f - 350.f), -930, +930); s16 y = std::clamp<s16>(s16(data[1] / (standardGravity * accelMax) * 930.f - 350.f), -930, +930);
s16 z = std::clamp<s16>(s16((data[2] - 2.1f) / accelMax * 930.f), -930, +930); s16 z = std::clamp<s16>(s16((data[2] - 2.1f) / accelMax * 930.f), -930, +930);
return glm::vec3(x, y, z); return glm::vec3(x, y, z);
} }
} // namespace Sensors::SDL } // namespace Sensors::SDL

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <array> #include <array>
#include "helpers.hpp" #include "helpers.hpp"
#include "io_file.hpp" #include "io_file.hpp"
#include "nfc_types.hpp" #include "nfc_types.hpp"

View file

@ -1,13 +1,13 @@
#pragma once #pragma once
#include <optional> #include <optional>
#include "applets/applet_manager.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "kernel_types.hpp" #include "kernel_types.hpp"
#include "logger.hpp" #include "logger.hpp"
#include "memory.hpp" #include "memory.hpp"
#include "result/result.hpp" #include "result/result.hpp"
#include "applets/applet_manager.hpp"
// Yay, more circular dependencies // Yay, more circular dependencies
class Kernel; class Kernel;

View file

@ -244,18 +244,18 @@ namespace Service::NFC {
#pragma pack(1) #pragma pack(1)
struct EncryptedAmiiboFile { struct EncryptedAmiiboFile {
u8 constant_value; // Must be A5 u8 constant_value; // Must be A5
u16_be write_counter; // Number of times the amiibo has been written? u16_be write_counter; // Number of times the amiibo has been written?
u8 amiibo_version; // Amiibo file version u8 amiibo_version; // Amiibo file version
AmiiboSettings settings; // Encrypted amiibo settings AmiiboSettings settings; // Encrypted amiibo settings
HashData hmac_tag; // Hash HashData hmac_tag; // Hash
AmiiboModelInfo model_info; // Encrypted amiibo model info AmiiboModelInfo model_info; // Encrypted amiibo model info
HashData keygen_salt; // Salt HashData keygen_salt; // Salt
HashData hmac_data; // Hash HashData hmac_data; // Hash
ChecksummedMiiData owner_mii; // Encrypted Mii data ChecksummedMiiData owner_mii; // Encrypted Mii data
u64_be application_id; // Encrypted Game id u64_be application_id; // Encrypted Game id
u16_be application_write_counter; // Encrypted Counter u16_be application_write_counter; // Encrypted Counter
u32_be application_area_id; // Encrypted Game id u32_be application_area_id; // Encrypted Game id
u8 application_id_byte; u8 application_id_byte;
u8 unknown; u8 unknown;
u64 mii_extension; u64 mii_extension;
@ -274,9 +274,9 @@ namespace Service::NFC {
u16_be write_counter; // Number of times the amiibo has been written? u16_be write_counter; // Number of times the amiibo has been written?
u8 amiibo_version; // Amiibo file version u8 amiibo_version; // Amiibo file version
AmiiboSettings settings; AmiiboSettings settings;
ChecksummedMiiData owner_mii; // Mii data ChecksummedMiiData owner_mii; // Mii data
u64_be application_id; // Game id u64_be application_id; // Game id
u16_be application_write_counter; // Counter u16_be application_write_counter; // Counter
u32_be application_area_id; u32_be application_area_id;
u8 application_id_byte; u8 application_id_byte;
u8 unknown; u8 unknown;

View file

@ -18,6 +18,6 @@ namespace SystemModel {
KTR = NewNintendo3DS, KTR = NewNintendo3DS,
FTR = Nintendo2DS, FTR = Nintendo2DS,
RED = NewNintendo3DS_XL, RED = NewNintendo3DS_XL,
JAN = NewNintendo2DS_XL JAN = NewNintendo2DS_XL,
}; };
} }

View file

@ -6,118 +6,114 @@
#include <bit> #include <bit>
#include <functional> #include <functional>
#include "cpu_dynarmic.hpp" #include "cpu_dynarmic.hpp"
#include "helpers.hpp" #include "helpers.hpp"
namespace { namespace {
template <size_t N> template <size_t N>
struct StringLiteral { struct StringLiteral {
constexpr StringLiteral(const char(&str)[N]) { constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
std::copy_n(str, N, value);
}
static constexpr std::size_t strlen = N - 1; static constexpr std::size_t strlen = N - 1;
static constexpr std::size_t size = N; static constexpr std::size_t size = N;
char value[N]; char value[N];
}; };
template <StringLiteral haystack, StringLiteral needle> template <StringLiteral haystack, StringLiteral needle>
constexpr u32 GetMatchingBitsFromStringLiteral() { constexpr u32 GetMatchingBitsFromStringLiteral() {
u32 result = 0; u32 result = 0;
for (size_t i = 0; i < haystack.strlen; i++) { for (size_t i = 0; i < haystack.strlen; i++) {
for (size_t a = 0; a < needle.strlen; a++) { for (size_t a = 0; a < needle.strlen; a++) {
if (haystack.value[i] == needle.value[a]) { if (haystack.value[i] == needle.value[a]) {
result |= 1 << (haystack.strlen - 1 - i); result |= 1 << (haystack.strlen - 1 - i);
} }
} }
} }
return result; return result;
} }
template <u32 mask_> template <u32 mask_>
constexpr u32 DepositBits(u32 val) { constexpr u32 DepositBits(u32 val) {
u32 mask = mask_; u32 mask = mask_;
u32 res = 0; u32 res = 0;
for (u32 bb = 1; mask; bb += bb) { for (u32 bb = 1; mask; bb += bb) {
u32 neg_mask = 0 - mask; u32 neg_mask = 0 - mask;
if (val & bb) if (val & bb) res |= mask & neg_mask;
res |= mask & neg_mask; mask &= mask - 1;
mask &= mask - 1; }
} return res;
return res; }
}
template <StringLiteral haystack> template <StringLiteral haystack>
struct MatcherArg { struct MatcherArg {
template <StringLiteral needle> template <StringLiteral needle>
u32 Get() { u32 Get() {
return DepositBits<GetMatchingBitsFromStringLiteral<haystack, needle>()>(instruction); return DepositBits<GetMatchingBitsFromStringLiteral<haystack, needle>()>(instruction);
} }
u32 instruction; u32 instruction;
}; };
struct Matcher { struct Matcher {
u32 mask; u32 mask;
u32 expect; u32 expect;
std::function<u64(u32)> fn; std::function<u64(u32)> fn;
}; };
u64 DataProcessing_imm(auto i) { u64 DataProcessing_imm(auto i) {
if (i.template Get<"d">() == 15) { if (i.template Get<"d">() == 15) {
return 7; return 7;
} }
return 1; return 1;
} }
u64 DataProcessing_reg(auto i) { u64 DataProcessing_reg(auto i) {
if (i.template Get<"d">() == 15) { if (i.template Get<"d">() == 15) {
return 7; return 7;
} }
return 1; return 1;
} }
u64 DataProcessing_rsr(auto i) { u64 DataProcessing_rsr(auto i) {
if (i.template Get<"d">() == 15) { if (i.template Get<"d">() == 15) {
return 8; return 8;
} }
return 2; return 2;
} }
u64 LoadStoreSingle_imm(auto) { u64 LoadStoreSingle_imm(auto) { return 2; }
return 2; u64 LoadStoreSingle_reg(auto i) {
} // TODO: Load PC
u64 LoadStoreSingle_reg(auto i) { if (i.template Get<"u">() == 1 && i.template Get<"r">() == 0 && (i.template Get<"v">() == 0 || i.template Get<"v">() == 2)) {
// TODO: Load PC return 2;
if (i.template Get<"u">() == 1 && i.template Get<"r">() == 0 && }
(i.template Get<"v">() == 0 || i.template Get<"v">() == 2)) { return 4;
return 2; }
} u64 LoadStoreMultiple(auto i) {
return 4; // TODO: Load PC
} return 1 + std::popcount(i.template Get<"x">()) / 2;
u64 LoadStoreMultiple(auto i) { }
// TODO: Load PC
return 1 + std::popcount(i.template Get<"x">()) / 2;
}
u64 SupervisorCall(auto i) { u64 SupervisorCall(auto i) {
// Consume extra cycles for the GetSystemTick SVC since some games wait with it in a loop rather than // Consume extra cycles for the GetSystemTick SVC since some games wait with it in a loop rather than
// Properly sleeping until a VBlank interrupt // Properly sleeping until a VBlank interrupt
if (i.template Get<"v">() == 0x28) { if (i.template Get<"v">() == 0x28) {
return 152; return 152;
} }
return 8; return 8;
} }
#define INST(NAME, BS, CYCLES) \ #define INST(NAME, BS, CYCLES) \
Matcher{GetMatchingBitsFromStringLiteral<BS, "01">(), \ Matcher{ \
GetMatchingBitsFromStringLiteral<BS, "1">(), \ GetMatchingBitsFromStringLiteral<BS, "01">(), GetMatchingBitsFromStringLiteral<BS, "1">(), \
std::function<u64(u32)>{[](u32 instruction) -> u64 { \ std::function<u64(u32)>{[](u32 instruction) -> u64 { \
[[maybe_unused]] MatcherArg<BS> i{instruction}; \ [[maybe_unused]] MatcherArg<BS> i{instruction}; \
return (CYCLES); \ return (CYCLES); \
}}}, }} \
},
const std::array arm_matchers{ const std::array arm_matchers{
// clang-format off // clang-format off
// Branch instructions // Branch instructions
INST("BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv", 5) // v5 INST("BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv", 5) // v5
@ -389,11 +385,11 @@ namespace {
INST("RFE", "1111100--0-1----0000101000000000", 9) // v6 INST("RFE", "1111100--0-1----0000101000000000", 9) // v6
INST("SRS", "1111100--1-0110100000101000-----", 1) // v6 INST("SRS", "1111100--1-0110100000101000-----", 1) // v6
// clang-format on // clang-format on
}; };
const std::array thumb_matchers{ const std::array thumb_matchers{
// clang-format off // clang-format off
// Shift (immediate) add, subtract, move and compare instructions // Shift (immediate) add, subtract, move and compare instructions
INST("LSL (imm)", "00000vvvvvmmmddd", 1) INST("LSL (imm)", "00000vvvvvmmmddd", 1)
@ -487,23 +483,21 @@ namespace {
INST("BL (imm)", "11110Svvvvvvvvvv11j1jvvvvvvvvvvv", 4) // v4T INST("BL (imm)", "11110Svvvvvvvvvv11j1jvvvvvvvvvvv", 4) // v4T
INST("BLX (imm)", "11110Svvvvvvvvvv11j0jvvvvvvvvvvv", 5) // v5T INST("BLX (imm)", "11110Svvvvvvvvvv11j0jvvvvvvvvvvv", 5) // v5T
// clang-format on // clang-format on
}; };
} // namespace } // namespace
u64 MyEnvironment::getCyclesForInstruction(bool is_thumb, u32 instruction) { u64 MyEnvironment::getCyclesForInstruction(bool is_thumb, u32 instruction) {
if (is_thumb) { if (is_thumb) {
return 1; return 1;
} }
const auto matches_instruction = [instruction](const auto& matcher) { const auto matches_instruction = [instruction](const auto& matcher) { return (instruction & matcher.mask) == matcher.expect; };
return (instruction & matcher.mask) == matcher.expect;
};
auto iter = std::find_if(arm_matchers.begin(), arm_matchers.end(), matches_instruction); auto iter = std::find_if(arm_matchers.begin(), arm_matchers.end(), matches_instruction);
if (iter != arm_matchers.end()) { if (iter != arm_matchers.end()) {
return iter->fn(instruction); return iter->fn(instruction);
} }
return 1; return 1;
} }

View file

@ -96,7 +96,7 @@ void ActionReplay::runInstruction(const Cheat& cheat, u32 instruction) {
break; break;
} }
// clang-format off // clang-format off
#define MAKE_IF_INSTRUCTION(opcode, comparator) \ #define MAKE_IF_INSTRUCTION(opcode, comparator) \
case opcode: { \ case opcode: { \
const u32 baseAddr = Helpers::getBits<0, 28>(instruction); \ const u32 baseAddr = Helpers::getBits<0, 28>(instruction); \
@ -119,7 +119,7 @@ void ActionReplay::runInstruction(const Cheat& cheat, u32 instruction) {
// Not Equal (YYYYYYYY != [XXXXXXX + offset]) // Not Equal (YYYYYYYY != [XXXXXXX + offset])
MAKE_IF_INSTRUCTION(6, !=) MAKE_IF_INSTRUCTION(6, !=)
#undef MAKE_IF_INSTRUCTION #undef MAKE_IF_INSTRUCTION
// clang-format on // clang-format on
// BXXXXXXX 00000000 - offset = *(XXXXXXX + offset) // BXXXXXXX 00000000 - offset = *(XXXXXXX + offset)
case 0xB: { case 0xB: {
@ -185,7 +185,6 @@ void ActionReplay::executeDType(const Cheat& cheat, u32 instruction) {
*activeOffset += 1; *activeOffset += 1;
break; break;
case 0xD9000000: *activeData = read32(cheat[pc++] + *activeOffset); break; case 0xD9000000: *activeData = read32(cheat[pc++] + *activeOffset); break;
case 0xD9000001: data1 = read32(cheat[pc++] + *activeOffset); break; case 0xD9000001: data1 = read32(cheat[pc++] + *activeOffset); break;
case 0xD9000002: data2 = read32(cheat[pc++] + *activeOffset); break; case 0xD9000002: data2 = read32(cheat[pc++] + *activeOffset); break;

View file

@ -1,4 +1,5 @@
#include "applets/error_applet.hpp" #include "applets/error_applet.hpp"
#include "kernel/handles.hpp" #include "kernel/handles.hpp"
using namespace Applets; using namespace Applets;
@ -11,7 +12,7 @@ Result::HorizonResult ErrorApplet::start(const MemoryBlock* sharedMem, const std
.destID = AppletIDs::Application, .destID = AppletIDs::Application,
.signal = static_cast<u32>(APTSignal::WakeupByExit), .signal = static_cast<u32>(APTSignal::WakeupByExit),
.object = 0, .object = 0,
.data = parameters, // TODO: Figure out how the data format for this applet .data = parameters, // TODO: Figure out how the data format for this applet
}; };
nextParameter = param; nextParameter = param;

View file

@ -53,7 +53,7 @@ Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock* sharedMem
mem.write16(textAddress, u16(text[i])); mem.write16(textAddress, u16(text[i]));
textAddress += sizeof(u16); textAddress += sizeof(u16);
} }
mem.write16(textAddress, 0); // Write UTF-16 null terminator mem.write16(textAddress, 0); // Write UTF-16 null terminator
// Temporarily hardcode the pressed button to be the firs tone // Temporarily hardcode the pressed button to be the firs tone
switch (config.numButtonsM1) { switch (config.numButtonsM1) {

View file

@ -1,4 +1,5 @@
#include "cheats.hpp" #include "cheats.hpp"
#include "swap.hpp" #include "swap.hpp"
Cheats::Cheats(Memory& mem, HIDService& hid) : ar(mem, hid) { reset(); } Cheats::Cheats(Memory& mem, HIDService& hid) : ar(mem, hid) { reset(); }

View file

@ -38,8 +38,9 @@ void Kernel::arbitrateAddress() {
const s32 value = s32(regs[3]); const s32 value = s32(regs[3]);
const s64 ns = s64(u64(regs[4]) | (u64(regs[5]) << 32)); const s64 ns = s64(u64(regs[4]) | (u64(regs[5]) << 32));
logSVC("ArbitrateAddress(Handle = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address, logSVC(
arbitrationTypeToString(type), value, ns); "ArbitrateAddress(Handle = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address, arbitrationTypeToString(type), value, ns
);
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter); const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
if (arbiter == nullptr) [[unlikely]] { if (arbiter == nullptr) [[unlikely]] {
@ -61,7 +62,7 @@ void Kernel::arbitrateAddress() {
switch (static_cast<ArbitrationType>(type)) { switch (static_cast<ArbitrationType>(type)) {
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL // Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
case ArbitrationType::WaitIfLess: { case ArbitrationType::WaitIfLess: {
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
if (word < value) { if (word < value) {
sleepThreadOnArbiter(address); sleepThreadOnArbiter(address);
} }
@ -71,7 +72,7 @@ void Kernel::arbitrateAddress() {
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL // Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
// If the thread is put to sleep, the arbiter address is decremented // If the thread is put to sleep, the arbiter address is decremented
case ArbitrationType::DecrementAndWaitIfLess: { case ArbitrationType::DecrementAndWaitIfLess: {
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
if (word < value) { if (word < value) {
mem.write32(address, word - 1); mem.write32(address, word - 1);
sleepThreadOnArbiter(address); sleepThreadOnArbiter(address);
@ -79,12 +80,8 @@ void Kernel::arbitrateAddress() {
break; break;
} }
case ArbitrationType::Signal: case ArbitrationType::Signal: signalArbiter(address, value); break;
signalArbiter(address, value); default: Helpers::panic("ArbitrateAddress: Unimplemented type %s", arbitrationTypeToString(type));
break;
default:
Helpers::panic("ArbitrateAddress: Unimplemented type %s", arbitrationTypeToString(type));
} }
requireReschedule(); requireReschedule();
@ -92,8 +89,9 @@ void Kernel::arbitrateAddress() {
// Signal up to "threadCount" threads waiting on the arbiter indicated by "waitingAddress" // Signal up to "threadCount" threads waiting on the arbiter indicated by "waitingAddress"
void Kernel::signalArbiter(u32 waitingAddress, s32 threadCount) { void Kernel::signalArbiter(u32 waitingAddress, s32 threadCount) {
if (threadCount == 0) [[unlikely]] return; if (threadCount == 0) [[unlikely]]
s32 count = 0; // Number of threads we've woken up return;
s32 count = 0; // Number of threads we've woken up
// Wake threads with the highest priority threads being woken up first // Wake threads with the highest priority threads being woken up first
for (auto index : threadIndices) { for (auto index : threadIndices) {

View file

@ -10,7 +10,7 @@
namespace DirectoryOps { namespace DirectoryOps {
enum : u32 { enum : u32 {
Read = 0x08010042, Read = 0x08010042,
Close = 0x08020000 Close = 0x08020000,
}; };
} }
@ -28,7 +28,7 @@ Filename83 convertTo83(const std::string& path) {
// Convert a character to add it to the 8.3 name // Convert a character to add it to the 8.3 name
// "Characters such as + are changed to the underscore _, and letters are put in uppercase" // "Characters such as + are changed to the underscore _, and letters are put in uppercase"
// For now we put letters in uppercase until we find out what is supposed to be converted to _ and so on // For now we put letters in uppercase until we find out what is supposed to be converted to _ and so on
auto convertCharacter = [](char c) { return (char) std::toupper(c); }; auto convertCharacter = [](char c) { return (char)std::toupper(c); };
// List of forbidden character for 8.3 filenames, from Citra // List of forbidden character for 8.3 filenames, from Citra
// TODO: Use constexpr when C++20 support is solid // TODO: Use constexpr when C++20 support is solid
@ -66,7 +66,7 @@ Filename83 convertTo83(const std::string& path) {
filenameTooBig = true; filenameTooBig = true;
break; break;
} }
filename[validCharacterCount++] = convertCharacter(c); // Append character to filename filename[validCharacterCount++] = convertCharacter(c); // Append character to filename
} }
// Truncate name to 6 characters and denote that it is too big // Truncate name to 6 characters and denote that it is too big
@ -136,9 +136,9 @@ void Kernel::readDirectory(u32 messagePointer, Handle directory) {
bool isDirectory = std::filesystem::is_directory(relative); bool isDirectory = std::filesystem::is_directory(relative);
std::u16string nameU16 = relative.u16string(); std::u16string nameU16 = relative.u16string();
bool isHidden = nameU16[0] == u'.'; // If the first character is a dot then this is a hidden file/folder bool isHidden = nameU16[0] == u'.'; // If the first character is a dot then this is a hidden file/folder
const u32 entryPointer = outPointer + (count * 0x228); // 0x228 is the size of a single entry const u32 entryPointer = outPointer + (count * 0x228); // 0x228 is the size of a single entry
u32 utfPointer = entryPointer; u32 utfPointer = entryPointer;
u32 namePointer = entryPointer + 0x20C; u32 namePointer = entryPointer + 0x20C;
u32 extensionPointer = entryPointer + 0x216; u32 extensionPointer = entryPointer + 0x216;
@ -152,7 +152,7 @@ void Kernel::readDirectory(u32 messagePointer, Handle directory) {
mem.write16(utfPointer, u16(c)); mem.write16(utfPointer, u16(c));
utfPointer += sizeof(u16); utfPointer += sizeof(u16);
} }
mem.write16(utfPointer, 0); // Null terminate the UTF16 name mem.write16(utfPointer, 0); // Null terminate the UTF16 name
// Write 8.3 filename-extension // Write 8.3 filename-extension
for (auto c : shortFilename) { for (auto c : shortFilename) {

View file

@ -2,7 +2,7 @@
namespace Commands { namespace Commands {
enum : u32 { enum : u32 {
Throw = 0x00010800 Throw = 0x00010800,
}; };
} }
@ -13,7 +13,7 @@ namespace FatalErrorType {
CardRemoved = 2, CardRemoved = 2,
Exception = 3, Exception = 3,
ResultFailure = 4, ResultFailure = 4,
Logged = 5 Logged = 5,
}; };
} }
@ -21,18 +21,14 @@ namespace FatalErrorType {
void Kernel::handleErrorSyncRequest(u32 messagePointer) { void Kernel::handleErrorSyncRequest(u32 messagePointer) {
u32 cmd = mem.read32(messagePointer); u32 cmd = mem.read32(messagePointer);
switch (cmd) { switch (cmd) {
case Commands::Throw: case Commands::Throw: throwError(messagePointer); break;
throwError(messagePointer);
break;
default: default: Helpers::panic("Unimplemented err:f command %08X\n", cmd); break;
Helpers::panic("Unimplemented err:f command %08X\n", cmd);
break;
} }
} }
void Kernel::throwError(u32 messagePointer) { void Kernel::throwError(u32 messagePointer) {
const auto type = mem.read8(messagePointer + 4); // Fatal error type const auto type = mem.read8(messagePointer + 4); // Fatal error type
const u32 pc = mem.read32(messagePointer + 12); const u32 pc = mem.read32(messagePointer + 12);
const u32 pid = mem.read32(messagePointer + 16); const u32 pid = mem.read32(messagePointer + 16);
logError("Thrown fatal error @ %08X (pid = %X, type = %d)\n", pc, pid, type); logError("Thrown fatal error @ %08X (pid = %X, type = %d)\n", pc, pid, type);

View file

@ -1,8 +1,9 @@
#include "kernel.hpp"
#include "cpu.hpp"
#include <bit> #include <bit>
#include <utility> #include <utility>
#include "cpu.hpp"
#include "kernel.hpp"
const char* Kernel::resetTypeToString(u32 type) { const char* Kernel::resetTypeToString(u32 type) {
switch (type) { switch (type) {
case 0: return "One shot"; case 0: return "One shot";
@ -57,11 +58,11 @@ void Kernel::svcCreateEvent() {
const u32 outPointer = regs[0]; const u32 outPointer = regs[0];
const u32 resetType = regs[1]; const u32 resetType = regs[1];
if (resetType > 2) if (resetType > 2) {
Helpers::panic("Invalid reset type for event %d", resetType); Helpers::panic("Invalid reset type for event %d", resetType);
}
logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType)); logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType));
regs[0] = Result::Success; regs[0] = Result::Success;
regs[1] = makeEvent(static_cast<ResetType>(resetType)); regs[1] = makeEvent(static_cast<ResetType>(resetType));
} }
@ -117,7 +118,7 @@ void Kernel::waitSynchronization1() {
} }
if (!shouldWaitOnObject(object)) { if (!shouldWaitOnObject(object)) {
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
regs[0] = Result::Success; regs[0] = Result::Success;
} else { } else {
// Timeout is 0, don't bother waiting, instantly timeout // Timeout is 0, don't bother waiting, instantly timeout
@ -126,7 +127,7 @@ void Kernel::waitSynchronization1() {
return; return;
} }
regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout
auto& t = threads[currentThreadIndex]; auto& t = threads[currentThreadIndex];
t.waitList.resize(1); t.waitList.resize(1);
@ -149,13 +150,14 @@ void Kernel::waitSynchronizationN() {
s32 handleCount = regs[2]; s32 handleCount = regs[2];
bool waitAll = regs[3] != 0; bool waitAll = regs[3] != 0;
u32 ns2 = regs[4]; u32 ns2 = regs[4];
s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects
s64 ns = s64(ns1) | (s64(ns2) << 32); s64 ns = s64(ns1) | (s64(ns2) << 32);
logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns); logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns);
if (handleCount <= 0) if (handleCount <= 0) {
Helpers::panic("WaitSyncN: Invalid handle count"); Helpers::panic("WaitSyncN: Invalid handle count");
}
// Temporary hack: Until we implement service sessions properly, don't bother sleeping when WaitSyncN targets a service handle // Temporary hack: Until we implement service sessions properly, don't bother sleeping when WaitSyncN targets a service handle
// This is necessary because a lot of games use WaitSyncN with eg the CECD service // This is necessary because a lot of games use WaitSyncN with eg the CECD service
@ -169,7 +171,7 @@ void Kernel::waitSynchronizationN() {
std::vector<WaitObject> waitObjects(handleCount); std::vector<WaitObject> waitObjects(handleCount);
// We don't actually need to wait if waitAll == true unless one of the objects is not ready // We don't actually need to wait if waitAll == true unless one of the objects is not ready
bool allReady = true; // Default initialize to true, set to fault if one of the objects is not ready bool allReady = true; // Default initialize to true, set to fault if one of the objects is not ready
// Tracks whether at least one object is ready, + the index of the first ready object // Tracks whether at least one object is ready, + the index of the first ready object
// This is used when waitAll == false, because if one object is already available then we can skip the sleeping // This is used when waitAll == false, because if one object is already available then we can skip the sleeping
@ -190,13 +192,12 @@ void Kernel::waitSynchronizationN() {
// Panic if one of the objects is not a valid sync object // Panic if one of the objects is not a valid sync object
if (!isWaitable(object)) [[unlikely]] { if (!isWaitable(object)) [[unlikely]] {
Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n", Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n", object->getTypeName(), handle);
object->getTypeName(), handle);
} }
if (shouldWaitOnObject(object)) { if (shouldWaitOnObject(object)) {
allReady = false; // Derp, not all objects are ready :( allReady = false; // Derp, not all objects are ready :(
} else { /// At least one object is ready to be acquired ahead of time. If it's the first one, write it down } else { /// At least one object is ready to be acquired ahead of time. If it's the first one, write it down
if (!oneObjectReady) { if (!oneObjectReady) {
oneObjectReady = true; oneObjectReady = true;
firstReadyObjectIndex = i; firstReadyObjectIndex = i;
@ -213,12 +214,12 @@ void Kernel::waitSynchronizationN() {
// If there's ready objects, acquire the first one and return // If there's ready objects, acquire the first one and return
if (oneObjectReady) { if (oneObjectReady) {
regs[0] = Result::Success; regs[0] = Result::Success;
regs[1] = firstReadyObjectIndex; // Return index of the acquired object regs[1] = firstReadyObjectIndex; // Return index of the acquired object
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
return; return;
} }
regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout
// If the thread wakes up without timeout, this will be adjusted to the index of the handle that woke us up // If the thread wakes up without timeout, this will be adjusted to the index of the handle that woke us up
regs[1] = 0xFFFFFFFF; regs[1] = 0xFFFFFFFF;
t.waitList.resize(handleCount); t.waitList.resize(handleCount);
@ -227,8 +228,8 @@ void Kernel::waitSynchronizationN() {
t.wakeupTick = getWakeupTick(ns); t.wakeupTick = getWakeupTick(ns);
for (s32 i = 0; i < handleCount; i++) { for (s32 i = 0; i < handleCount; i++) {
t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist
waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist
} }
requireReschedule(); requireReschedule();

View file

@ -69,8 +69,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
u32 size = mem.read32(messagePointer + 12); u32 size = mem.read32(messagePointer + 12);
u32 dataPointer = mem.read32(messagePointer + 20); u32 dataPointer = mem.read32(messagePointer + 20);
logFileIO("Trying to read %X bytes from file %X, starting from offset %llX into memory address %08X\n", logFileIO("Trying to read %X bytes from file %X, starting from offset %llX into memory address %08X\n", size, fileHandle, offset, dataPointer);
size, fileHandle, offset, dataPointer);
const auto p = getObject(fileHandle, KernelObjectType::File); const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] { if (p == nullptr) [[unlikely]] {
@ -94,9 +93,8 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
if (!success) { if (!success) {
Helpers::panic("Kernel::ReadFile with file descriptor failed"); Helpers::panic("Kernel::ReadFile with file descriptor failed");
} } else {
else { for (usize i = 0; i < bytesRead; i++) {
for (size_t i = 0; i < bytesRead; i++) {
mem.write8(u32(dataPointer + i), data[i]); mem.write8(u32(dataPointer + i), data[i]);
} }
@ -124,8 +122,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
u32 writeOption = mem.read32(messagePointer + 16); u32 writeOption = mem.read32(messagePointer + 16);
u32 dataPointer = mem.read32(messagePointer + 24); u32 dataPointer = mem.read32(messagePointer + 24);
logFileIO("Trying to write %X bytes to file %X, starting from file offset %llX and memory address %08X\n", logFileIO("Trying to write %X bytes to file %X, starting from file offset %llX and memory address %08X\n", size, fileHandle, offset, dataPointer);
size, fileHandle, offset, dataPointer);
const auto p = getObject(fileHandle, KernelObjectType::File); const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] { if (p == nullptr) [[unlikely]] {
@ -137,8 +134,9 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
Helpers::panic("Tried to write closed file"); Helpers::panic("Tried to write closed file");
} }
if (!file->fd) if (!file->fd) {
Helpers::panic("[Kernel::File::WriteFile] Tried to write to file without a valid file descriptor"); Helpers::panic("[Kernel::File::WriteFile] Tried to write to file without a valid file descriptor");
}
std::unique_ptr<u8[]> data(new u8[size]); std::unique_ptr<u8[]> data(new u8[size]);
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {

View file

@ -1,7 +1,9 @@
#include <cassert>
#include "kernel.hpp" #include "kernel.hpp"
#include "kernel_types.hpp"
#include <cassert>
#include "cpu.hpp" #include "cpu.hpp"
#include "kernel_types.hpp"
Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config, LuaManager& lua) Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config, LuaManager& lua)
: cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config, lua) { : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config, lua) {
@ -17,7 +19,7 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config, Lu
t.tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize; t.tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize;
t.status = ThreadStatus::Dead; t.status = ThreadStatus::Dead;
t.waitList.clear(); t.waitList.clear();
t.waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later t.waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later
// The state below isn't necessary to initialize but we do it anyways out of caution // The state below isn't necessary to initialize but we do it anyways out of caution
t.outPointer = 0; t.outPointer = 0;
t.waitAll = false; t.waitAll = false;
@ -83,7 +85,7 @@ void Kernel::setVersion(u8 major, u8 minor) {
u16 descriptor = (u16(major) << 8) | u16(minor); u16 descriptor = (u16(major) << 8) | u16(minor);
kernelVersion = descriptor; kernelVersion = descriptor;
mem.kernelVersion = descriptor; // The memory objects needs a copy because you can read the kernel ver from config mem mem.kernelVersion = descriptor; // The memory objects needs a copy because you can read the kernel ver from config mem
} }
HorizonHandle Kernel::makeProcess(u32 id) { HorizonHandle Kernel::makeProcess(u32 id) {
@ -146,7 +148,7 @@ void Kernel::reset() {
for (auto& t : threads) { for (auto& t : threads) {
t.status = ThreadStatus::Dead; t.status = ThreadStatus::Dead;
t.waitList.clear(); t.waitList.clear();
t.threadsWaitingForTermination = 0; // No threads are waiting for this thread to terminate cause it's dead t.threadsWaitingForTermination = 0; // No threads are waiting for this thread to terminate cause it's dead
} }
for (auto& object : objects) { for (auto& object : objects) {
@ -163,7 +165,7 @@ void Kernel::reset() {
// Allocate handle #0 to a dummy object and make a main process object // Allocate handle #0 to a dummy object and make a main process object
makeObject(KernelObjectType::Dummy); makeObject(KernelObjectType::Dummy);
currentProcess = makeProcess(1); // Use ID = 1 for main process currentProcess = makeProcess(1); // Use ID = 1 for main process
// Make main thread object. We do not have to set the entrypoint and SP for it as the ROM loader does. // Make main thread object. We do not have to set the entrypoint and SP for it as the ROM loader does.
// Main thread seems to have a priority of 0x30. TODO: This creates a dummy context for thread 0, // Main thread seems to have a priority of 0x30. TODO: This creates a dummy context for thread 0,
@ -173,8 +175,8 @@ void Kernel::reset() {
setupIdleThread(); setupIdleThread();
// Create some of the OS ports // Create some of the OS ports
srvHandle = makePort("srv:"); // Service manager port srvHandle = makePort("srv:"); // Service manager port
errorPortHandle = makePort("err:f"); // Error display port errorPortHandle = makePort("err:f"); // Error display port
} }
// Get pointer to thread-local storage // Get pointer to thread-local storage
@ -273,13 +275,12 @@ void Kernel::getProcessInfo() {
regs[2] = 0; regs[2] = 0;
break; break;
case 20: // Returns 0x20000000 - <linear memory base vaddr for process> case 20: // Returns 0x20000000 - <linear memory base vaddr for process>
regs[1] = PhysicalAddrs::FCRAM - mem.getLinearHeapVaddr(); regs[1] = PhysicalAddrs::FCRAM - mem.getLinearHeapVaddr();
regs[2] = 0; regs[2] = 0;
break; break;
default: default: Helpers::panic("GetProcessInfo: unimplemented type %d", type);
Helpers::panic("GetProcessInfo: unimplemented type %d", type);
} }
regs[0] = Result::Success; regs[0] = Result::Success;

View file

@ -17,14 +17,14 @@ namespace Operation {
namespace MemoryPermissions { namespace MemoryPermissions {
enum : u32 { enum : u32 {
None = 0, // --- None = 0, // ---
Read = 1, // R-- Read = 1, // R--
Write = 2, // -W- Write = 2, // -W-
ReadWrite = 3, // RW- ReadWrite = 3, // RW-
Execute = 4, // --X Execute = 4, // --X
ReadExecute = 5, // R-X ReadExecute = 5, // R-X
WriteExecute = 6, // -WX WriteExecute = 6, // -WX
ReadWriteExecute = 7, // RWX ReadWriteExecute = 7, // RWX
DontCare = 0x10000000 DontCare = 0x10000000
}; };
@ -40,14 +40,14 @@ static constexpr bool isAligned(u32 value) {
// This has a weird ABI documented here https://www.3dbrew.org/wiki/Kernel_ABI // This has a weird ABI documented here https://www.3dbrew.org/wiki/Kernel_ABI
// TODO: Does this need to write to outaddr? // TODO: Does this need to write to outaddr?
void Kernel::controlMemory() { void Kernel::controlMemory() {
u32 operation = regs[0]; // The base address is written here u32 operation = regs[0]; // The base address is written here
u32 addr0 = regs[1]; u32 addr0 = regs[1];
u32 addr1 = regs[2]; u32 addr1 = regs[2];
u32 size = regs[3]; u32 size = regs[3];
u32 perms = regs[4]; u32 perms = regs[4];
if (perms == MemoryPermissions::DontCare) { if (perms == MemoryPermissions::DontCare) {
perms = MemoryPermissions::ReadWrite; // We make "don't care" equivalent to read-write perms = MemoryPermissions::ReadWrite; // We make "don't care" equivalent to read-write
Helpers::panic("Unimplemented allocation permission: DONTCARE"); Helpers::panic("Unimplemented allocation permission: DONTCARE");
} }
@ -57,33 +57,37 @@ void Kernel::controlMemory() {
bool x = perms & 0b100; bool x = perms & 0b100;
bool linear = operation & Operation::Linear; bool linear = operation & Operation::Linear;
if (x) if (x) {
Helpers::panic("ControlMemory: attempted to allocate executable memory"); Helpers::panic("ControlMemory: attempted to allocate executable memory");
}
if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(size)) { if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(size)) {
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size); Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
} }
logSVC("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n", logSVC(
addr0, addr1, size, operation, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : "" "ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n", addr0, addr1, size, operation, r ? 'r' : '-',
w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : ""
); );
switch (operation & 0xFF) { switch (operation & 0xFF) {
case Operation::Commit: { case Operation::Commit: {
std::optional<u32> address = mem.allocateMemory(addr0, 0, size, linear, r, w, x, true); std::optional<u32> address = mem.allocateMemory(addr0, 0, size, linear, r, w, x, true);
if (!address.has_value()) if (!address.has_value()) {
Helpers::panic("ControlMemory: Failed to allocate memory"); Helpers::panic("ControlMemory: Failed to allocate memory");
}
regs[1] = address.value(); regs[1] = address.value();
break; break;
} }
case Operation::Map: case Operation::Map: mem.mirrorMapping(addr0, addr1, size); break;
mem.mirrorMapping(addr0, addr1, size);
break;
case Operation::Protect: case Operation::Protect:
Helpers::warn("Ignoring mprotect! Hope nothing goes wrong but if the game accesses invalid memory or crashes then we prolly need to implement this\n"); Helpers::warn(
"Ignoring mprotect! Hope nothing goes wrong but if the game accesses invalid memory or crashes then we prolly need to implement "
"this\n"
);
break; break;
default: Helpers::warn("ControlMemory: unknown operation %X\n", operation); break; default: Helpers::warn("ControlMemory: unknown operation %X\n", operation); break;
@ -106,7 +110,7 @@ void Kernel::queryMemory() {
regs[2] = info.size; regs[2] = info.size;
regs[3] = info.perms; regs[3] = info.perms;
regs[4] = info.state; regs[4] = info.state;
regs[5] = 0; // page flags regs[5] = 0; // page flags
} }
// Result MapMemoryBlock(Handle memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission) // Result MapMemoryBlock(Handle memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission)
@ -126,21 +130,13 @@ void Kernel::mapMemoryBlock() {
addr = getSharedFontVaddr(); addr = getSharedFontVaddr();
} }
u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block
// Pass pointer to shared memory to the appropriate service // Pass pointer to shared memory to the appropriate service
switch (block) { switch (block) {
case KernelHandles::HIDSharedMemHandle: case KernelHandles::HIDSharedMemHandle: serviceManager.setHIDSharedMem(ptr); break;
serviceManager.setHIDSharedMem(ptr); case KernelHandles::GSPSharedMemHandle: serviceManager.setGSPSharedMem(ptr); break;
break; case KernelHandles::FontSharedMemHandle: mem.copySharedFont(ptr, addr); break;
case KernelHandles::GSPSharedMemHandle:
serviceManager.setGSPSharedMem(ptr);
break;
case KernelHandles::FontSharedMemHandle:
mem.copySharedFont(ptr, addr);
break;
case KernelHandles::CSNDSharedMemHandle: case KernelHandles::CSNDSharedMemHandle:
serviceManager.setCSNDSharedMem(ptr); serviceManager.setCSNDSharedMem(ptr);
@ -168,7 +164,7 @@ void Kernel::createMemoryBlock() {
const u32 addr = regs[1]; const u32 addr = regs[1];
const u32 size = regs[2]; const u32 size = regs[2];
u32 myPermission = regs[3]; u32 myPermission = regs[3];
u32 otherPermission = mem.read32(regs[13] + 4); // This is placed on the stack rather than r4 u32 otherPermission = mem.read32(regs[13] + 4); // This is placed on the stack rather than r4
logSVC("CreateMemoryBlock (addr = %08X, size = %08X, myPermission = %d, otherPermission = %d)\n", addr, size, myPermission, otherPermission); logSVC("CreateMemoryBlock (addr = %08X, size = %08X, myPermission = %d, otherPermission = %d)\n", addr, size, myPermission, otherPermission);
// Returns whether a permission is valid // Returns whether a permission is valid
@ -178,10 +174,9 @@ void Kernel::createMemoryBlock() {
case MemoryPermissions::Read: case MemoryPermissions::Read:
case MemoryPermissions::Write: case MemoryPermissions::Write:
case MemoryPermissions::ReadWrite: case MemoryPermissions::ReadWrite:
case MemoryPermissions::DontCare: case MemoryPermissions::DontCare: return true;
return true;
default: // Permissions with the executable flag enabled or invalid permissions are not allowed default: // Permissions with the executable flag enabled or invalid permissions are not allowed
return false; return false;
} }
}; };
@ -200,8 +195,9 @@ void Kernel::createMemoryBlock() {
// TODO: The address needs to be in a specific range otherwise it throws an invalid address error // TODO: The address needs to be in a specific range otherwise it throws an invalid address error
if (addr == 0) if (addr == 0) {
Helpers::panic("CreateMemoryBlock: Tried to use addr = 0"); Helpers::panic("CreateMemoryBlock: Tried to use addr = 0");
}
// Implement "Don't care" permission as RW // Implement "Don't care" permission as RW
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite; if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;

View file

@ -1,9 +1,10 @@
#include "kernel.hpp"
#include <cstring> #include <cstring>
#include "kernel.hpp"
HorizonHandle Kernel::makePort(const char* name) { HorizonHandle Kernel::makePort(const char* name) {
Handle ret = makeObject(KernelObjectType::Port); Handle ret = makeObject(KernelObjectType::Port);
portHandles.push_back(ret); // Push the port handle to our cache of port handles portHandles.push_back(ret); // Push the port handle to our cache of port handles
objects[ret].data = new Port(name); objects[ret].data = new Port(name);
return ret; return ret;
@ -73,7 +74,7 @@ void Kernel::connectToPort() {
// Send an IPC message to a port (typically "srv:") or a service // Send an IPC message to a port (typically "srv:") or a service
void Kernel::sendSyncRequest() { void Kernel::sendSyncRequest() {
const auto handle = regs[0]; const auto handle = regs[0];
u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80 u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80
logSVC("SendSyncRequest(session handle = %X)\n", handle); logSVC("SendSyncRequest(session handle = %X)\n", handle);
// Service calls via SendSyncRequest and file access needs to put the caller to sleep for a given amount of time // Service calls via SendSyncRequest and file access needs to put the caller to sleep for a given amount of time
@ -93,7 +94,7 @@ void Kernel::sendSyncRequest() {
// Check if our sync request is targetting a file instead of a service // Check if our sync request is targetting a file instead of a service
bool isFileOperation = getObject(handle, KernelObjectType::File) != nullptr; bool isFileOperation = getObject(handle, KernelObjectType::File) != nullptr;
if (isFileOperation) { if (isFileOperation) {
regs[0] = Result::Success; // r0 goes first here too regs[0] = Result::Success; // r0 goes first here too
handleFileOperation(messagePointer, handle); handleFileOperation(messagePointer, handle);
return; return;
} }
@ -101,7 +102,7 @@ void Kernel::sendSyncRequest() {
// Check if our sync request is targetting a directory instead of a service // Check if our sync request is targetting a directory instead of a service
bool isDirectoryOperation = getObject(handle, KernelObjectType::Directory) != nullptr; bool isDirectoryOperation = getObject(handle, KernelObjectType::Directory) != nullptr;
if (isDirectoryOperation) { if (isDirectoryOperation) {
regs[0] = Result::Success; // r0 goes first here too regs[0] = Result::Success; // r0 goes first here too
handleDirectoryOperation(messagePointer, handle); handleDirectoryOperation(messagePointer, handle);
return; return;
} }
@ -117,10 +118,10 @@ void Kernel::sendSyncRequest() {
const auto sessionData = static_cast<Session*>(session->data); const auto sessionData = static_cast<Session*>(session->data);
const Handle portHandle = sessionData->portHandle; const Handle portHandle = sessionData->portHandle;
if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port" if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port"
regs[0] = Result::Success; regs[0] = Result::Success;
serviceManager.handleSyncRequest(messagePointer); serviceManager.handleSyncRequest(messagePointer);
} else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too } else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too
regs[0] = Result::Success; regs[0] = Result::Success;
handleErrorSyncRequest(messagePointer); handleErrorSyncRequest(messagePointer);
} else { } else {

View file

@ -1,4 +1,5 @@
#include "resource_limits.hpp" #include "resource_limits.hpp"
#include "kernel.hpp" #include "kernel.hpp"
// Result GetResourceLimit(Handle* resourceLimit, Handle process) // Result GetResourceLimit(Handle* resourceLimit, Handle process)
@ -22,7 +23,7 @@ void Kernel::getResourceLimit() {
// Result GetResourceLimitLimitValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount) // Result GetResourceLimitLimitValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
void Kernel::getResourceLimitLimitValues() { void Kernel::getResourceLimitLimitValues() {
u32 values = regs[0]; // Pointer to values (The resource limits get output here) u32 values = regs[0]; // Pointer to values (The resource limits get output here)
const Handle resourceLimit = regs[1]; const Handle resourceLimit = regs[1];
u32 names = regs[2]; // Pointer to resources that we should return u32 names = regs[2]; // Pointer to resources that we should return
u32 count = regs[3]; // Number of resources u32 count = regs[3]; // Number of resources
@ -51,7 +52,7 @@ void Kernel::getResourceLimitLimitValues() {
// Result GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount) // Result GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
void Kernel::getResourceLimitCurrentValues() { void Kernel::getResourceLimitCurrentValues() {
u32 values = regs[0]; // Pointer to values (The resource limits get output here) u32 values = regs[0]; // Pointer to values (The resource limits get output here)
const Handle resourceLimit = regs[1]; const Handle resourceLimit = regs[1];
u32 names = regs[2]; // Pointer to resources that we should return u32 names = regs[2]; // Pointer to resources that we should return
u32 count = regs[3]; // Number of resources u32 count = regs[3]; // Number of resources

View file

@ -34,7 +34,7 @@ void Kernel::switchThread(int newThreadIndex) {
std::memcpy(cpu.fprs().data(), newThread.fprs.data(), cpu.fprs().size_bytes()); // Load 32 FPRs std::memcpy(cpu.fprs().data(), newThread.fprs.data(), cpu.fprs().size_bytes()); // Load 32 FPRs
cpu.setCPSR(newThread.cpsr); // Load CPSR cpu.setCPSR(newThread.cpsr); // Load CPSR
cpu.setFPSCR(newThread.fpscr); // Load FPSCR cpu.setFPSCR(newThread.fpscr); // Load FPSCR
cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register
currentThreadIndex = newThreadIndex; currentThreadIndex = newThreadIndex;
} }
@ -43,16 +43,14 @@ void Kernel::switchThread(int newThreadIndex) {
// The threads with higher priority (aka the ones with a lower priority value) should come first in the vector // The threads with higher priority (aka the ones with a lower priority value) should come first in the vector
void Kernel::sortThreads() { void Kernel::sortThreads() {
std::vector<int>& v = threadIndices; std::vector<int>& v = threadIndices;
std::sort(v.begin(), v.end(), [&](int a, int b) { std::sort(v.begin(), v.end(), [&](int a, int b) { return threads[a].priority < threads[b].priority; });
return threads[a].priority < threads[b].priority;
});
} }
bool Kernel::canThreadRun(const Thread& t) { bool Kernel::canThreadRun(const Thread& t) {
if (t.status == ThreadStatus::Ready) { if (t.status == ThreadStatus::Ready) {
return true; return true;
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 } else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAny ||
|| t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) { t.status == ThreadStatus::WaitSyncAll) {
// TODO: Set r0 to the correct error code on timeout for WaitSync{1/Any/All} // TODO: Set r0 to the correct error code on timeout for WaitSync{1/Any/All}
return cpu.getTicks() >= t.wakeupTick; return cpu.getTicks() >= t.wakeupTick;
} }
@ -111,29 +109,29 @@ void Kernel::rescheduleThreads() {
// Internal OS function to spawn a thread // Internal OS function to spawn a thread
HorizonHandle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg, ThreadStatus status) { HorizonHandle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg, ThreadStatus status) {
int index; // Index of the created thread in the threads array int index; // Index of the created thread in the threads array
if (threadCount < appResourceLimits.maxThreads) [[likely]] { // If we have not yet created over too many threads if (threadCount < appResourceLimits.maxThreads) [[likely]] { // If we have not yet created over too many threads
index = threadCount++; index = threadCount++;
} else if (aliveThreadCount < appResourceLimits.maxThreads) { // If we have created many threads but at least one is dead & reusable } else if (aliveThreadCount < appResourceLimits.maxThreads) { // If we have created many threads but at least one is dead & reusable
for (int i = 0; i < threads.size(); i++) { for (int i = 0; i < threads.size(); i++) {
if (threads[i].status == ThreadStatus::Dead) { if (threads[i].status == ThreadStatus::Dead) {
index = i; index = i;
break; break;
} }
} }
} else { // There is no thread we can use, we're screwed } else { // There is no thread we can use, we're screwed
Helpers::panic("Overflowed thread count!!"); Helpers::panic("Overflowed thread count!!");
} }
aliveThreadCount++; aliveThreadCount++;
threadIndices.push_back(index); threadIndices.push_back(index);
Thread& t = threads[index]; // Reference to thread data Thread& t = threads[index]; // Reference to thread data
Handle ret = makeObject(KernelObjectType::Thread); Handle ret = makeObject(KernelObjectType::Thread);
objects[ret].data = &t; objects[ret].data = &t;
const bool isThumb = (entrypoint & 1) != 0; // Whether the thread starts in thumb mode or not const bool isThumb = (entrypoint & 1) != 0; // Whether the thread starts in thumb mode or not
// Set up initial thread context // Set up initial thread context
t.gprs.fill(0); t.gprs.fill(0);
@ -151,7 +149,7 @@ HorizonHandle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, Pr
t.status = status; t.status = status;
t.handle = ret; t.handle = ret;
t.waitingAddress = 0; t.waitingAddress = 0;
t.threadsWaitingForTermination = 0; // Thread just spawned, no other threads waiting for it to terminate t.threadsWaitingForTermination = 0; // Thread just spawned, no other threads waiting for it to terminate
t.cpsr = CPSR::UserMode | (isThumb ? CPSR::Thumb : 0); t.cpsr = CPSR::UserMode | (isThumb ? CPSR::Thumb : 0);
t.fpscr = FPSCR::ThreadDefault; t.fpscr = FPSCR::ThreadDefault;
@ -182,15 +180,15 @@ HorizonHandle Kernel::makeMutex(bool locked) {
void Kernel::releaseMutex(Mutex* moo) { void Kernel::releaseMutex(Mutex* moo) {
// TODO: Assert lockCount > 0 before release, maybe. The SVC should be safe at least. // TODO: Assert lockCount > 0 before release, maybe. The SVC should be safe at least.
moo->lockCount--; // Decrement lock count moo->lockCount--; // Decrement lock count
// If the lock count reached 0 then the thread no longer owns the mootex and it can be given to a new one // If the lock count reached 0 then the thread no longer owns the mootex and it can be given to a new one
if (moo->lockCount == 0) { if (moo->lockCount == 0) {
moo->locked = false; moo->locked = false;
if (moo->waitlist != 0) { if (moo->waitlist != 0) {
int index = wakeupOneThread(moo->waitlist, moo->handle); // Wake up one thread and get its index int index = wakeupOneThread(moo->waitlist, moo->handle); // Wake up one thread and get its index
moo->waitlist ^= (1ull << index); // Remove thread from waitlist moo->waitlist ^= (1ull << index); // Remove thread from waitlist
// Have new thread acquire mutex // Have new thread acquire mutex
moo->locked = true; moo->locked = true;
@ -222,7 +220,7 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
switch (object->type) { switch (object->type) {
case KernelObjectType::Event: { case KernelObjectType::Event: {
Event* e = object->getData<Event>(); Event* e = object->getData<Event>();
if (e->resetType == ResetType::OneShot) { // One-shot events automatically get cleared after waking up a thread if (e->resetType == ResetType::OneShot) { // One-shot events automatically get cleared after waking up a thread
e->fired = false; e->fired = false;
} }
break; break;
@ -246,15 +244,14 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
case KernelObjectType::Semaphore: { case KernelObjectType::Semaphore: {
Semaphore* s = object->getData<Semaphore>(); Semaphore* s = object->getData<Semaphore>();
if (s->availableCount <= 0) [[unlikely]] // This should be unreachable but let's check anyways if (s->availableCount <= 0) [[unlikely]] // This should be unreachable but let's check anyways
Helpers::panic("Tried to acquire unacquirable semaphore"); Helpers::panic("Tried to acquire unacquirable semaphore");
s->availableCount -= 1; s->availableCount -= 1;
break; break;
} }
case KernelObjectType::Thread: case KernelObjectType::Thread: break;
break;
case KernelObjectType::Timer: { case KernelObjectType::Timer: {
Timer* timer = object->getData<Timer>(); Timer* timer = object->getData<Timer>();
@ -276,30 +273,30 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
// Find the waiting thread with the highest priority. // Find the waiting thread with the highest priority.
// We do this by first picking the first thread in the waitlist, then checking each other thread and comparing priority // We do this by first picking the first thread in the waitlist, then checking each other thread and comparing priority
int threadIndex = std::countr_zero(waitlist); // Index of first thread int threadIndex = std::countr_zero(waitlist); // Index of first thread
int maxPriority = threads[threadIndex].priority; // Set initial max prio to the prio of the first thread int maxPriority = threads[threadIndex].priority; // Set initial max prio to the prio of the first thread
waitlist ^= (1ull << threadIndex); // Remove thread from the waitlist waitlist ^= (1ull << threadIndex); // Remove thread from the waitlist
while (waitlist != 0) { while (waitlist != 0) {
int newThread = std::countr_zero(waitlist); // Get new thread and evaluate whether it has a higher priority int newThread = std::countr_zero(waitlist); // Get new thread and evaluate whether it has a higher priority
if (threads[newThread].priority < maxPriority) { // Low priority value means high priority if (threads[newThread].priority < maxPriority) { // Low priority value means high priority
threadIndex = newThread; threadIndex = newThread;
maxPriority = threads[newThread].priority; maxPriority = threads[newThread].priority;
} }
waitlist ^= (1ull << threadIndex); // Remove thread from waitlist waitlist ^= (1ull << threadIndex); // Remove thread from waitlist
} }
Thread& t = threads[threadIndex]; Thread& t = threads[threadIndex];
switch (t.status) { switch (t.status) {
case ThreadStatus::WaitSync1: case ThreadStatus::WaitSync1:
t.status = ThreadStatus::Ready; t.status = ThreadStatus::Ready;
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0 t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
break; break;
case ThreadStatus::WaitSyncAny: case ThreadStatus::WaitSyncAny:
t.status = ThreadStatus::Ready; t.status = ThreadStatus::Ready;
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0 t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
// Get the index of the event in the object's waitlist, write it to r1 // Get the index of the event in the object's waitlist, write it to r1
for (size_t i = 0; i < t.waitList.size(); i++) { for (size_t i = 0; i < t.waitList.size(); i++) {
@ -310,9 +307,7 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
} }
break; break;
case ThreadStatus::WaitSyncAll: case ThreadStatus::WaitSyncAll: Helpers::panic("WakeupOneThread: Thread on WaitSyncAll"); break;
Helpers::panic("WakeupOneThread: Thread on WaitSyncAll");
break;
} }
return threadIndex; return threadIndex;
@ -321,33 +316,31 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
// Wake up every single thread in the waitlist using a bit scanning algorithm // Wake up every single thread in the waitlist using a bit scanning algorithm
void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) { void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) {
while (waitlist != 0) { while (waitlist != 0) {
const uint index = std::countr_zero(waitlist); // Get one of the set bits to see which thread is waiting const uint index = std::countr_zero(waitlist); // Get one of the set bits to see which thread is waiting
waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit
// Get the thread we'll be signalling // Get the thread we'll be signalling
Thread& t = threads[index]; Thread& t = threads[index];
switch (t.status) { switch (t.status) {
case ThreadStatus::WaitSync1: case ThreadStatus::WaitSync1:
t.status = ThreadStatus::Ready; t.status = ThreadStatus::Ready;
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0 t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
break; break;
case ThreadStatus::WaitSyncAny: case ThreadStatus::WaitSyncAny:
t.status = ThreadStatus::Ready; t.status = ThreadStatus::Ready;
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0 t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
// Get the index of the event in the object's waitlist, write it to r1 // Get the index of the event in the object's waitlist, write it to r1
for (size_t i = 0; i < t.waitList.size(); i++) { for (size_t i = 0; i < t.waitList.size(); i++) {
if (t.waitList[i] == handle) { if (t.waitList[i] == handle) {
t.gprs[1] = u32(i); t.gprs[1] = u32(i);
break; break;
}
} }
} break;
break;
case ThreadStatus::WaitSyncAll: case ThreadStatus::WaitSyncAll: Helpers::panic("WakeupAllThreads: Thread on WaitSyncAll"); break;
Helpers::panic("WakeupAllThreads: Thread on WaitSyncAll");
break;
} }
} }
} }
@ -405,12 +398,11 @@ void Kernel::sleepThread(s64 ns) {
void Kernel::createThread() { void Kernel::createThread() {
u32 priority = regs[0]; u32 priority = regs[0];
u32 entrypoint = regs[1]; u32 entrypoint = regs[1];
u32 arg = regs[2]; // An argument value stored in r0 of the new thread u32 arg = regs[2]; // An argument value stored in r0 of the new thread
u32 initialSP = regs[3] & ~7; // SP is force-aligned to 8 bytes u32 initialSP = regs[3] & ~7; // SP is force-aligned to 8 bytes
s32 id = static_cast<s32>(regs[4]); s32 id = static_cast<s32>(regs[4]);
logSVC("CreateThread(entry = %08X, stacktop = %08X, arg = %X, priority = %X, processor ID = %d)\n", entrypoint, logSVC("CreateThread(entry = %08X, stacktop = %08X, arg = %X, priority = %X, processor ID = %d)\n", entrypoint, initialSP, arg, priority, id);
initialSP, arg, priority, id);
if (priority > 0x3F) [[unlikely]] { if (priority > 0x3F) [[unlikely]] {
Helpers::panic("Created thread with bad priority value %X", priority); Helpers::panic("Created thread with bad priority value %X", priority);
@ -430,7 +422,7 @@ void Kernel::createThread() {
// void SleepThread(s64 nanoseconds) // void SleepThread(s64 nanoseconds)
void Kernel::svcSleepThread() { void Kernel::svcSleepThread() {
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32)); const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
//logSVC("SleepThread(ns = %lld)\n", ns); // logSVC("SleepThread(ns = %lld)\n", ns);
regs[0] = Result::Success; regs[0] = Result::Success;
sleepThread(ns); sleepThread(ns);
@ -525,9 +517,7 @@ void Kernel::getCurrentProcessorNumber() {
// Until we properly implement per-core schedulers, return whatever processor ID passed to svcCreateThread // Until we properly implement per-core schedulers, return whatever processor ID passed to svcCreateThread
switch (id) { switch (id) {
// TODO: This is picked from exheader // TODO: This is picked from exheader
case ProcessorID::Default: case ProcessorID::Default: ret = static_cast<s32>(ProcessorID::AppCore); break;
ret = static_cast<s32>(ProcessorID::AppCore);
break;
case ProcessorID::AllCPUs: case ProcessorID::AllCPUs:
ret = static_cast<s32>(ProcessorID::AppCore); ret = static_cast<s32>(ProcessorID::AppCore);
@ -566,8 +556,9 @@ void Kernel::exitThread() {
// Remove the index of this thread from the thread indices vector // Remove the index of this thread from the thread indices vector
for (int i = 0; i < threadIndices.size(); i++) { for (int i = 0; i < threadIndices.size(); i++) {
if (threadIndices[i] == currentThreadIndex) if (threadIndices[i] == currentThreadIndex) {
threadIndices.erase(threadIndices.begin() + i); threadIndices.erase(threadIndices.begin() + i);
}
} }
Thread& t = threads[currentThreadIndex]; Thread& t = threads[currentThreadIndex];
@ -579,7 +570,7 @@ void Kernel::exitThread() {
if (t.threadsWaitingForTermination != 0) { if (t.threadsWaitingForTermination != 0) {
// TODO: Handle cloned handles? Not sure how those interact with wait object signalling // TODO: Handle cloned handles? Not sure how those interact with wait object signalling
wakeupAllThreads(t.threadsWaitingForTermination, t.handle); wakeupAllThreads(t.threadsWaitingForTermination, t.handle);
t.threadsWaitingForTermination = 0; // No other threads waiting t.threadsWaitingForTermination = 0; // No other threads waiting
} }
requireReschedule(); requireReschedule();
@ -620,11 +611,13 @@ void Kernel::svcCreateSemaphore() {
s32 maxCount = static_cast<s32>(regs[2]); s32 maxCount = static_cast<s32>(regs[2]);
logSVC("CreateSemaphore (initial count = %d, max count = %d)\n", initialCount, maxCount); logSVC("CreateSemaphore (initial count = %d, max count = %d)\n", initialCount, maxCount);
if (initialCount > maxCount) if (initialCount > maxCount) {
Helpers::panic("CreateSemaphore: Initial count higher than max count"); Helpers::panic("CreateSemaphore: Initial count higher than max count");
}
if (initialCount < 0 || maxCount < 0) if (initialCount < 0 || maxCount < 0) {
Helpers::panic("CreateSemaphore: Negative count value"); Helpers::panic("CreateSemaphore: Negative count value");
}
regs[0] = Result::Success; regs[0] = Result::Success;
regs[1] = makeSemaphore(initialCount, maxCount); regs[1] = makeSemaphore(initialCount, maxCount);
@ -642,12 +635,10 @@ void Kernel::svcReleaseSemaphore() {
return; return;
} }
if (releaseCount < 0) if (releaseCount < 0) Helpers::panic("ReleaseSemaphore: Negative count");
Helpers::panic("ReleaseSemaphore: Negative count");
Semaphore* s = object->getData<Semaphore>(); Semaphore* s = object->getData<Semaphore>();
if (s->maximumCount - s->availableCount < releaseCount) if (s->maximumCount - s->availableCount < releaseCount) Helpers::panic("ReleaseSemaphore: Release count too high");
Helpers::panic("ReleaseSemaphore: Release count too high");
// Write success and old available count to r0 and r1 respectively // Write success and old available count to r0 and r1 respectively
regs[0] = Result::Success; regs[0] = Result::Success;
@ -657,10 +648,10 @@ void Kernel::svcReleaseSemaphore() {
// Wake up threads one by one until the available count hits 0 or we run out of threads to wake up // Wake up threads one by one until the available count hits 0 or we run out of threads to wake up
while (s->availableCount > 0 && s->waitlist != 0) { while (s->availableCount > 0 && s->waitlist != 0) {
int index = wakeupOneThread(s->waitlist, handle); // Wake up highest priority thread int index = wakeupOneThread(s->waitlist, handle); // Wake up highest priority thread
s->waitlist ^= (1ull << index); // Remove thread from waitlist s->waitlist ^= (1ull << index); // Remove thread from waitlist
s->availableCount--; // Decrement available count s->availableCount--; // Decrement available count
} }
} }
@ -676,26 +667,24 @@ bool Kernel::isWaitable(const KernelObject* object) {
// Returns whether we should wait on a sync object or not // Returns whether we should wait on a sync object or not
bool Kernel::shouldWaitOnObject(KernelObject* object) { bool Kernel::shouldWaitOnObject(KernelObject* object) {
switch (object->type) { switch (object->type) {
case KernelObjectType::Event: // We should wait on an event only if it has not been signalled case KernelObjectType::Event: // We should wait on an event only if it has not been signalled
return !object->getData<Event>()->fired; return !object->getData<Event>()->fired;
case KernelObjectType::Mutex: { case KernelObjectType::Mutex: {
Mutex* moo = object->getData<Mutex>(); // mooooooooooo Mutex* moo = object->getData<Mutex>(); // mooooooooooo
return moo->locked && moo->ownerThread != currentThreadIndex; // If the current thread owns the moo then no reason to wait return moo->locked && moo->ownerThread != currentThreadIndex; // If the current thread owns the moo then no reason to wait
} }
case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait
return object->getData<Thread>()->status != ThreadStatus::Dead; return object->getData<Thread>()->status != ThreadStatus::Dead;
case KernelObjectType::Timer: // We should wait on a timer only if it has not been signalled case KernelObjectType::Timer: // We should wait on a timer only if it has not been signalled
return !object->getData<Timer>()->fired; return !object->getData<Timer>()->fired;
case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0 case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0
return object->getData<Semaphore>()->availableCount <= 0; return object->getData<Semaphore>()->availableCount <= 0;
default: default: Helpers::panic("Not sure whether to wait on object (type: %s)", object->getTypeName()); return true;
Helpers::panic("Not sure whether to wait on object (type: %s)", object->getTypeName());
return true;
} }
} }

View file

@ -1,12 +1,12 @@
#include "memory.hpp"
#include <cstring> #include <cstring>
#include "elfio/elfio.hpp" #include "elfio/elfio.hpp"
#include "memory.hpp"
using namespace ELFIO; using namespace ELFIO;
std::optional<u32> Memory::loadELF(std::ifstream& file) { std::optional<u32> Memory::loadELF(std::ifstream& file) {
loadedCXI = std::nullopt; // ELF files don't have a CXI, so set this to null loadedCXI = std::nullopt; // ELF files don't have a CXI, so set this to null
elfio reader; elfio reader;
if (!file.good() || !reader.load(file)) { if (!file.good() || !reader.load(file)) {
@ -14,56 +14,56 @@ std::optional<u32> Memory::loadELF(std::ifstream& file) {
return std::nullopt; return std::nullopt;
} }
// Allocate stack space. For ELFs we use the default stack size, which is 16KB // Allocate stack space. For ELFs we use the default stack size, which is 16KB
if (!allocateMainThreadStack(VirtualAddrs::DefaultStackSize)) { if (!allocateMainThreadStack(VirtualAddrs::DefaultStackSize)) {
// Should be unreachable // Should be unreachable
printf("Failed to allocate stack space for ELF file\n"); printf("Failed to allocate stack space for ELF file\n");
return std::nullopt; return std::nullopt;
} }
auto segNum = reader.segments.size(); auto segNum = reader.segments.size();
printf("Number of segments: %d\n", segNum); printf("Number of segments: %d\n", segNum);
printf(" # Perms Vaddr File Size Mem Size\n"); printf(" # Perms Vaddr File Size Mem Size\n");
for (int i = 0; i < segNum; ++i) { for (int i = 0; i < segNum; ++i) {
const auto seg = reader.segments[i]; const auto seg = reader.segments[i];
const auto flags = seg->get_flags(); const auto flags = seg->get_flags();
const u32 vaddr = static_cast<u32>(seg->get_virtual_address()); // Vaddr the segment is loaded in const u32 vaddr = static_cast<u32>(seg->get_virtual_address()); // Vaddr the segment is loaded in
u32 fileSize = static_cast<u32>(seg->get_file_size()); // Size of segment in file u32 fileSize = static_cast<u32>(seg->get_file_size()); // Size of segment in file
u32 memorySize = static_cast<u32>(seg->get_memory_size()); // Size of segment in memory u32 memorySize = static_cast<u32>(seg->get_memory_size()); // Size of segment in memory
u8* data = (u8*)seg->get_data(); u8* data = (u8*)seg->get_data();
// Get read/write/execute permissions for segment // Get read/write/execute permissions for segment
const bool r = (flags & 0b100) != 0; const bool r = (flags & 0b100) != 0;
const bool w = (flags & 0b010) != 0; const bool w = (flags & 0b010) != 0;
const bool x = (flags & 0b001) != 0; const bool x = (flags & 0b001) != 0;
printf("[%d] (%c%c%c)\t%08X\t%08X\t%08X\n", i, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', vaddr, fileSize, memorySize); printf("[%d] (%c%c%c)\t%08X\t%08X\t%08X\n", i, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', vaddr, fileSize, memorySize);
// Assert that the segment will be loaded in the executable region. If it isn't then panic. // Assert that the segment will be loaded in the executable region. If it isn't then panic.
// The executable region starts at 0x00100000 and has a maximum size of 0x03F00000 // The executable region starts at 0x00100000 and has a maximum size of 0x03F00000
u64 endAddress = (u64)vaddr + (u64)fileSize; u64 endAddress = (u64)vaddr + (u64)fileSize;
const bool isGood = vaddr >= VirtualAddrs::ExecutableStart && endAddress < VirtualAddrs::ExecutableEnd; const bool isGood = vaddr >= VirtualAddrs::ExecutableStart && endAddress < VirtualAddrs::ExecutableEnd;
if (!isGood) { if (!isGood) {
// We're ignoring this for now because some ELFs define a segment at the vaddr for IPC buffer mapping // We're ignoring this for now because some ELFs define a segment at the vaddr for IPC buffer mapping
// Helpers::panic("ELF is loaded at invalid place"); // Helpers::panic("ELF is loaded at invalid place");
// return std::nullopt; // return std::nullopt;
} }
if (memorySize & pageMask) { if (memorySize & pageMask) {
// Round up the size of the ELF segment to a page (4KB) boundary, as the OS can only alloc this way // Round up the size of the ELF segment to a page (4KB) boundary, as the OS can only alloc this way
memorySize = (memorySize + pageSize - 1) & -pageSize; memorySize = (memorySize + pageSize - 1) & -pageSize;
Helpers::warn("Rounding ELF segment size to %08X\n", memorySize); Helpers::warn("Rounding ELF segment size to %08X\n", memorySize);
} }
// This should also assert that findPaddr doesn't fail // This should also assert that findPaddr doesn't fail
u32 fcramAddr = findPaddr(memorySize).value(); u32 fcramAddr = findPaddr(memorySize).value();
std::memcpy(&fcram[fcramAddr], data, fileSize); std::memcpy(&fcram[fcramAddr], data, fileSize);
// Allocate the segment on the OS side // Allocate the segment on the OS side
allocateMemory(vaddr, fcramAddr, memorySize, true, r, w, x); allocateMemory(vaddr, fcramAddr, memorySize, true, r, w, x);
} }
// ELF can't specify a region, make it default to USA // ELF can't specify a region, make it default to USA
region = Regions::USA; region = Regions::USA;
return static_cast<u32>(reader.get_entry()); return static_cast<u32>(reader.get_entry());
} }

View file

@ -1,79 +1,76 @@
#include "loader/lz77.hpp"
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include "loader/lz77.hpp"
// The difference in size between the compressed and decompressed file is stored // The difference in size between the compressed and decompressed file is stored
// As a footer in the compressed file. To get the decompressed size, we extract the diff // As a footer in the compressed file. To get the decompressed size, we extract the diff
// And add it to the compressed size // And add it to the compressed size
u32 CartLZ77::decompressedSize(const u8* buffer, u32 compressedSize) { u32 CartLZ77::decompressedSize(const u8* buffer, u32 compressedSize) {
u32 sizeDiff; u32 sizeDiff;
std::memcpy(&sizeDiff, buffer + compressedSize - 4, sizeof(u32)); std::memcpy(&sizeDiff, buffer + compressedSize - 4, sizeof(u32));
return sizeDiff + compressedSize; return sizeDiff + compressedSize;
} }
bool CartLZ77::decompress(std::vector<u8>& output, const std::vector<u8>& input) { bool CartLZ77::decompress(std::vector<u8>& output, const std::vector<u8>& input) {
u32 sizeCompressed = u32(input.size() * sizeof(u8)); u32 sizeCompressed = u32(input.size() * sizeof(u8));
u32 sizeDecompressed = decompressedSize(input); u32 sizeDecompressed = decompressedSize(input);
output.resize(sizeDecompressed); output.resize(sizeDecompressed);
const u8* compressed = (u8*)input.data(); const u8* compressed = (u8*)input.data();
const u8* footer = compressed + sizeCompressed - 8; const u8* footer = compressed + sizeCompressed - 8;
u32 bufferTopAndBottom; u32 bufferTopAndBottom;
std::memcpy(&bufferTopAndBottom, footer, sizeof(u32)); std::memcpy(&bufferTopAndBottom, footer, sizeof(u32));
u32 out = sizeDecompressed; // TODO: Is this meant to be u32 or s32? u32 out = sizeDecompressed; // TODO: Is this meant to be u32 or s32?
u32 index = sizeCompressed - (Helpers::getBits<24, 8>(bufferTopAndBottom)); u32 index = sizeCompressed - (Helpers::getBits<24, 8>(bufferTopAndBottom));
u32 stopIndex = sizeCompressed - (bufferTopAndBottom & 0xffffff); u32 stopIndex = sizeCompressed - (bufferTopAndBottom & 0xffffff);
// Set all of the decompressed buffer to 0 and copy the compressed buffer to the start of it // Set all of the decompressed buffer to 0 and copy the compressed buffer to the start of it
std::fill(output.begin(), output.end(), 0); std::fill(output.begin(), output.end(), 0);
std::copy(input.begin(), input.end(), output.begin()); std::copy(input.begin(), input.end(), output.begin());
while (index > stopIndex) { while (index > stopIndex) {
u8 control = compressed[--index]; u8 control = compressed[--index];
for (uint i = 0; i < 8; i++) { for (uint i = 0; i < 8; i++) {
if (index <= stopIndex) if (index <= stopIndex) break;
break; if (index <= 0) break;
if (index <= 0) if (out <= 0) break;
break;
if (out <= 0)
break;
if (control & 0x80) { if (control & 0x80) {
// Check if compression is out of bounds // Check if compression is out of bounds
if (index < 2) if (index < 2) {
return false; return false;
index -= 2; }
index -= 2;
u32 segmentOffset = compressed[index] | (compressed[index + 1] << 8); u32 segmentOffset = compressed[index] | (compressed[index + 1] << 8);
u32 segment_size = (Helpers::getBits<12, 4>(segmentOffset)) + 3; u32 segment_size = (Helpers::getBits<12, 4>(segmentOffset)) + 3;
segmentOffset &= 0x0FFF; segmentOffset &= 0x0FFF;
segmentOffset += 2; segmentOffset += 2;
// Check if compression is out of bounds // Check if compression is out of bounds
if (out < segment_size) if (out < segment_size) return false;
return false;
for (uint j = 0; j < segment_size; j++) { for (uint j = 0; j < segment_size; j++) {
// Check if compression is out of bounds // Check if compression is out of bounds
if (out + segmentOffset >= sizeDecompressed) if (out + segmentOffset >= sizeDecompressed) return false;
return false;
u8 data = output[out + segmentOffset]; u8 data = output[out + segmentOffset];
output[--out] = data; output[--out] = data;
} }
} } else {
else { // Check if compression is out of bounds
// Check if compression is out of bounds if (out < 1) {
if (out < 1) return false;
return false; }
output[--out] = compressed[--index]; output[--out] = compressed[--index];
} }
control <<= 1; control <<= 1;
} }
} }
return true; return true;
} }

View file

@ -11,7 +11,7 @@ bool Memory::mapCXI(NCSD& ncsd, NCCH& cxi) {
printf("Data address = %08X, size = %08X\n", cxi.data.address, cxi.data.size); printf("Data address = %08X, size = %08X\n", cxi.data.address, cxi.data.size);
printf("Stack size: %08X\n", cxi.stackSize); printf("Stack size: %08X\n", cxi.stackSize);
static constexpr std::array<const char*, 7> regionNames = {"Japan", "North America", "Europe", "Australia", "China", "Korea", "Taiwan" }; static constexpr std::array<const char*, 7> regionNames = {"Japan", "North America", "Europe", "Australia", "China", "Korea", "Taiwan"};
// Set autodetected 3DS region to one of the values allowed by the CXI's SMDH // Set autodetected 3DS region to one of the values allowed by the CXI's SMDH
region = cxi.region.value(); region = cxi.region.value();

View file

@ -11,8 +11,6 @@ using namespace ScreenLayout;
void ScreenLayout::calculateCoordinates( void ScreenLayout::calculateCoordinates(
WindowCoordinates& coordinates, u32 outputWindowWidth, u32 outputWindowHeight, float topScreenPercentage, Layout layout WindowCoordinates& coordinates, u32 outputWindowWidth, u32 outputWindowHeight, float topScreenPercentage, Layout layout
) { ) {
const float destAspect = float(outputWindowWidth) / float(outputWindowHeight);
if (layout == Layout::Default || layout == Layout::DefaultFlipped) { if (layout == Layout::Default || layout == Layout::DefaultFlipped) {
// Calculate available height for each screen based on split // Calculate available height for each screen based on split
int availableTopHeight = int(outputWindowHeight * topScreenPercentage + 0.5f); int availableTopHeight = int(outputWindowHeight * topScreenPercentage + 0.5f);

View file

@ -1,4 +1,5 @@
#include "services/act.hpp" #include "services/act.hpp"
#include "ipc.hpp" #include "ipc.hpp"
namespace ACTCommands { namespace ACTCommands {

View file

@ -1,4 +1,5 @@
#include "services/am.hpp" #include "services/am.hpp"
#include "ipc.hpp" #include "ipc.hpp"
namespace AMCommands { namespace AMCommands {
@ -22,19 +23,19 @@ void AMService::handleSyncRequest(u32 messagePointer) {
} }
void AMService::listTitleInfo(u32 messagePointer) { void AMService::listTitleInfo(u32 messagePointer) {
log("AM::ListDLCOrLicenseTicketInfos\n"); // Yes this is the actual name log("AM::ListDLCOrLicenseTicketInfos\n"); // Yes this is the actual name
u32 ticketCount = mem.read32(messagePointer + 4); u32 ticketCount = mem.read32(messagePointer + 4);
u64 titleID = mem.read64(messagePointer + 8); u64 titleID = mem.read64(messagePointer + 8);
u32 pointer = mem.read32(messagePointer + 24); u32 pointer = mem.read32(messagePointer + 24);
for (u32 i = 0; i < ticketCount; i++) { for (u32 i = 0; i < ticketCount; i++) {
mem.write64(pointer, titleID); // Title ID mem.write64(pointer, titleID); // Title ID
mem.write64(pointer + 8, 0); // Ticket ID mem.write64(pointer + 8, 0); // Ticket ID
mem.write16(pointer + 16, 0); // Version mem.write16(pointer + 16, 0); // Version
mem.write16(pointer + 18, 0); // Padding mem.write16(pointer + 18, 0); // Padding
mem.write32(pointer + 20, 0); // Size mem.write32(pointer + 20, 0); // Size
pointer += 24; // = sizeof(TicketInfo) pointer += 24; // = sizeof(TicketInfo)
} }
mem.write32(messagePointer, IPC::responseHeader(0x1007, 2, 2)); mem.write32(messagePointer, IPC::responseHeader(0x1007, 2, 2));

View file

@ -6,6 +6,4 @@ void AmiiboDevice::reset() {
} }
// Load amiibo information from our raw 540 byte array // Load amiibo information from our raw 540 byte array
void AmiiboDevice::loadFromRaw() { void AmiiboDevice::loadFromRaw() {}
}

View file

@ -1,10 +1,11 @@
#include "services/apt.hpp" #include "services/apt.hpp"
#include "ipc.hpp"
#include "kernel.hpp"
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include "ipc.hpp"
#include "kernel.hpp"
namespace APTCommands { namespace APTCommands {
enum : u32 { enum : u32 {
GetLockHandle = 0x00010040, GetLockHandle = 0x00010040,
@ -84,8 +85,7 @@ void APTService::appletUtility(u32 messagePointer) {
u32 outputSize = mem.read32(messagePointer + 12); u32 outputSize = mem.read32(messagePointer + 12);
u32 inputPointer = mem.read32(messagePointer + 20); u32 inputPointer = mem.read32(messagePointer + 20);
log("APT::AppletUtility(utility = %d, input size = %x, output size = %x, inputPointer = %08X)\n", utility, inputSize, outputSize, log("APT::AppletUtility(utility = %d, input size = %x, output size = %x, inputPointer = %08X)\n", utility, inputSize, outputSize, inputPointer);
inputPointer);
std::vector<u8> out(outputSize); std::vector<u8> out(outputSize);
const u32 outputBuffer = mem.read32(messagePointer + 0x104); const u32 outputBuffer = mem.read32(messagePointer + 0x104);
@ -111,8 +111,9 @@ void APTService::getAppletInfo(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x06, 7, 0)); mem.write32(messagePointer, IPC::responseHeader(0x06, 7, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 20, 1); // 1 = registered mem.write8(messagePointer + 20, 1); // 1 = registered
mem.write8(messagePointer + 24, 1); // 1 = loaded mem.write8(messagePointer + 24, 1); // 1 = loaded
// TODO: The rest of this // TODO: The rest of this
} }
@ -122,7 +123,7 @@ void APTService::isRegistered(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x09, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x09, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 1); // Return that the app is always registered. This might break with home menu? mem.write8(messagePointer + 8, 1); // Return that the app is always registered. This might break with home menu?
} }
void APTService::preloadLibraryApplet(u32 messagePointer) { void APTService::preloadLibraryApplet(u32 messagePointer) {
@ -178,7 +179,7 @@ void APTService::checkNew3DS(u32 messagePointer) {
log("APT::CheckNew3DS\n"); log("APT::CheckNew3DS\n");
mem.write32(messagePointer, IPC::responseHeader(0x102, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x102, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS) mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
} }
// TODO: Figure out the slight way this differs from APT::CheckNew3DS // TODO: Figure out the slight way this differs from APT::CheckNew3DS
@ -186,7 +187,7 @@ void APTService::checkNew3DSApp(u32 messagePointer) {
log("APT::CheckNew3DSApp\n"); log("APT::CheckNew3DSApp\n");
mem.write32(messagePointer, IPC::responseHeader(0x101, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x101, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS) mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
} }
void APTService::enable(u32 messagePointer) { void APTService::enable(u32 messagePointer) {
@ -207,14 +208,14 @@ void APTService::initialize(u32 messagePointer) {
notificationEvent = kernel.makeEvent(ResetType::OneShot); notificationEvent = kernel.makeEvent(ResetType::OneShot);
resumeEvent = kernel.makeEvent(ResetType::OneShot); resumeEvent = kernel.makeEvent(ResetType::OneShot);
kernel.signalEvent(resumeEvent.value()); // Seems to be signalled on startup kernel.signalEvent(resumeEvent.value()); // Seems to be signalled on startup
} }
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 3)); mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 3));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0x04000000); // Translation descriptor mem.write32(messagePointer + 8, 0x04000000); // Translation descriptor
mem.write32(messagePointer + 12, notificationEvent.value()); // Notification Event Handle mem.write32(messagePointer + 12, notificationEvent.value()); // Notification Event Handle
mem.write32(messagePointer + 16, resumeEvent.value()); // Resume Event Handle mem.write32(messagePointer + 16, resumeEvent.value()); // Resume Event Handle
} }
void APTService::inquireNotification(u32 messagePointer) { void APTService::inquireNotification(u32 messagePointer) {
@ -234,11 +235,11 @@ void APTService::getLockHandle(u32 messagePointer) {
} }
mem.write32(messagePointer, IPC::responseHeader(0x1, 3, 2)); mem.write32(messagePointer, IPC::responseHeader(0x1, 3, 2));
mem.write32(messagePointer + 4, Result::Success); // Result code mem.write32(messagePointer + 4, Result::Success); // Result code
mem.write32(messagePointer + 8, 0); // AppletAttr mem.write32(messagePointer + 8, 0); // AppletAttr
mem.write32(messagePointer + 12, 0); // APT State (bit0 = Power Button State, bit1 = Order To Close State) mem.write32(messagePointer + 12, 0); // APT State (bit0 = Power Button State, bit1 = Order To Close State)
mem.write32(messagePointer + 16, 0); // Translation descriptor mem.write32(messagePointer + 16, 0); // Translation descriptor
mem.write32(messagePointer + 20, lockHandle.value()); // Lock handle mem.write32(messagePointer + 20, lockHandle.value()); // Lock handle
} }
// This apparently does nothing on the original kernel either? // This apparently does nothing on the original kernel either?
@ -254,7 +255,7 @@ void APTService::sendParameter(u32 messagePointer) {
const u32 cmd = mem.read32(messagePointer + 12); const u32 cmd = mem.read32(messagePointer + 12);
const u32 paramSize = mem.read32(messagePointer + 16); const u32 paramSize = mem.read32(messagePointer + 16);
const u32 parameterHandle = mem.read32(messagePointer + 24); // What dis? const u32 parameterHandle = mem.read32(messagePointer + 24); // What dis?
const u32 parameterPointer = mem.read32(messagePointer + 32); const u32 parameterPointer = mem.read32(messagePointer + 32);
log("APT::SendParameter (source app = %X, dest app = %X, cmd = %X, size = %X)", sourceAppID, destAppID, cmd, paramSize); log("APT::SendParameter (source app = %X, dest app = %X, cmd = %X, size = %X)", sourceAppID, destAppID, cmd, paramSize);
@ -298,7 +299,9 @@ void APTService::receiveParameter(u32 messagePointer) {
const u32 buffer = mem.read32(messagePointer + 0x100 + 4); const u32 buffer = mem.read32(messagePointer + 0x100 + 4);
log("APT::ReceiveParameter(app ID = %X, size = %04X)\n", app, size); log("APT::ReceiveParameter(app ID = %X, size = %04X)\n", app, size);
if (size > 0x1000) Helpers::panic("APT::ReceiveParameter with size > 0x1000"); if (size > 0x1000) {
Helpers::panic("APT::ReceiveParameter with size > 0x1000");
}
auto parameter = appletManager.receiveParameter(); auto parameter = appletManager.receiveParameter();
mem.write32(messagePointer, IPC::responseHeader(0xD, 4, 4)); mem.write32(messagePointer, IPC::responseHeader(0xD, 4, 4));
@ -326,7 +329,9 @@ void APTService::glanceParameter(u32 messagePointer) {
const u32 buffer = mem.read32(messagePointer + 0x100 + 4); const u32 buffer = mem.read32(messagePointer + 0x100 + 4);
log("APT::GlanceParameter(app ID = %X, size = %04X)\n", app, size); log("APT::GlanceParameter(app ID = %X, size = %04X)\n", app, size);
if (size > 0x1000) Helpers::panic("APT::GlanceParameter with size > 0x1000"); if (size > 0x1000) {
Helpers::panic("APT::GlanceParameter with size > 0x1000");
}
auto parameter = appletManager.glanceParameter(); auto parameter = appletManager.glanceParameter();
// TODO: Properly implement this. We currently stub it similar // TODO: Properly implement this. We currently stub it similar
@ -355,8 +360,8 @@ void APTService::replySleepQuery(u32 messagePointer) {
} }
void APTService::setApplicationCpuTimeLimit(u32 messagePointer) { void APTService::setApplicationCpuTimeLimit(u32 messagePointer) {
u32 fixed = mem.read32(messagePointer + 4); // MUST be 1. u32 fixed = mem.read32(messagePointer + 4); // MUST be 1.
u32 percentage = mem.read32(messagePointer + 8); // CPU time percentage between 5% and 89% u32 percentage = mem.read32(messagePointer + 8); // CPU time percentage between 5% and 89%
log("APT::SetApplicationCpuTimeLimit (percentage = %d%%)\n", percentage); log("APT::SetApplicationCpuTimeLimit (percentage = %d%%)\n", percentage);
mem.write32(messagePointer, IPC::responseHeader(0x4F, 1, 0)); mem.write32(messagePointer, IPC::responseHeader(0x4F, 1, 0));
@ -409,15 +414,16 @@ void APTService::theSmashBrosFunction(u32 messagePointer) {
} }
void APTService::getWirelessRebootInfo(u32 messagePointer) { void APTService::getWirelessRebootInfo(u32 messagePointer) {
const u32 size = mem.read32(messagePointer + 4); // Size of data to read const u32 size = mem.read32(messagePointer + 4); // Size of data to read
log("APT::GetWirelessRebootInfo (size = %X)\n", size); log("APT::GetWirelessRebootInfo (size = %X)\n", size);
if (size > 0x10) if (size > 0x10) {
Helpers::panic("APT::GetWirelessInfo with size > 0x10 bytes"); Helpers::panic("APT::GetWirelessInfo with size > 0x10 bytes");
}
mem.write32(messagePointer, IPC::responseHeader(0x45, 1, 2)); mem.write32(messagePointer, IPC::responseHeader(0x45, 1, 2));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
for (u32 i = 0; i < size; i++) { for (u32 i = 0; i < size; i++) {
mem.write8(messagePointer + 0x104 + i, 0); // Temporarily stub this until we add SetWirelessRebootInfo mem.write8(messagePointer + 0x104 + i, 0); // Temporarily stub this until we add SetWirelessRebootInfo
} }
} }

View file

@ -194,6 +194,7 @@ void BOSSService::sendProperty(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 2)); mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 2));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Read size mem.write32(messagePointer + 8, 0); // Read size
// TODO: Should this do anything else? // TODO: Should this do anything else?
} }

View file

@ -47,5 +47,5 @@ void CECDService::openAndRead(u32 messagePointer) {
// TODO: We should implement this properly the time comes // TODO: We should implement this properly the time comes
mem.write32(messagePointer, IPC::responseHeader(0x12, 2, 2)); mem.write32(messagePointer, IPC::responseHeader(0x12, 2, 2));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Bytes read mem.write32(messagePointer + 8, 0); // Bytes read
} }

View file

@ -1,13 +1,14 @@
#include "services/cfg.hpp" #include "services/cfg.hpp"
#include "services/dsp.hpp"
#include "system_models.hpp"
#include "ipc.hpp"
#include <array> #include <array>
#include <bit> #include <bit>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "ipc.hpp"
#include "services/dsp.hpp"
#include "system_models.hpp"
namespace CFGCommands { namespace CFGCommands {
enum : u32 { enum : u32 {
GetConfigInfoBlk2 = 0x00010082, GetConfigInfoBlk2 = 0x00010082,
@ -101,7 +102,7 @@ void CFGService::getSystemModel(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x05, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x05, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, SystemModel::Nintendo3DS); // TODO: Make this adjustable via GUI mem.write8(messagePointer + 8, SystemModel::Nintendo3DS); // TODO: Make this adjustable via GUI
} }
// Write a UTF16 string to 3DS memory starting at "pointer". Appends a null terminator. // Write a UTF16 string to 3DS memory starting at "pointer". Appends a null terminator.
@ -111,16 +112,15 @@ void CFGService::writeStringU16(u32 pointer, const std::u16string& string) {
pointer += 2; pointer += 2;
} }
mem.write16(pointer, static_cast<u16>(u'\0')); // Null terminator mem.write16(pointer, static_cast<u16>(u'\0')); // Null terminator
} }
void CFGService::getConfigInfoBlk2(u32 messagePointer) { void CFGService::getConfigInfoBlk2(u32 messagePointer) {
u32 size = mem.read32(messagePointer + 4); u32 size = mem.read32(messagePointer + 4);
u32 blockID = mem.read32(messagePointer + 8); u32 blockID = mem.read32(messagePointer + 8);
u32 output = mem.read32(messagePointer + 16); // Pointer to write the output data to u32 output = mem.read32(messagePointer + 16); // Pointer to write the output data to
log("CFG::GetConfigInfoBlk2 (size = %X, block ID = %X, output pointer = %08X\n", size, blockID, output); log("CFG::GetConfigInfoBlk2 (size = %X, block ID = %X, output pointer = %08X\n", size, blockID, output);
getConfigInfo(output, blockID, size, 0x2); getConfigInfo(output, blockID, size, 0x2);
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 2)); mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 2));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
@ -254,7 +254,7 @@ void CFGService::genUniqueConsoleHash(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
// We need to implement hash generation & the SHA-256 digest properly later on. We have cryptopp so the hashing isn't too hard to do // We need to implement hash generation & the SHA-256 digest properly later on. We have cryptopp so the hashing isn't too hard to do
// Let's stub it for now // Let's stub it for now
mem.write32(messagePointer + 8, 0x33646D6F ^ salt); // Lower word of hash mem.write32(messagePointer + 8, 0x33646D6F ^ salt); // Lower word of hash
mem.write32(messagePointer + 12, 0xA3534841 ^ salt); // Upper word of hash mem.write32(messagePointer + 12, 0xA3534841 ^ salt); // Upper word of hash
} }
@ -340,7 +340,7 @@ void CFGService::secureInfoGetByte101(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x407, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x407, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 0); // Secure info byte 0x101 is usually 0 according to 3DBrew mem.write8(messagePointer + 8, 0); // Secure info byte 0x101 is usually 0 according to 3DBrew
} }
void CFGService::getLocalFriendCodeSeed(u32 messagePointer) { void CFGService::getLocalFriendCodeSeed(u32 messagePointer) {
@ -379,7 +379,7 @@ void CFGService::translateCountryInfo(u32 messagePointer) {
// By default the translated code is the input // By default the translated code is the input
u32 result = country; u32 result = country;
if (direction == 0) { // Translate from version B to version A if (direction == 0) { // Translate from version B to version A
switch (country) { switch (country) {
case 0x6E040000: result = 0x6E030000; break; case 0x6E040000: result = 0x6E030000; break;
case 0x6E050000: result = 0x6E040000; break; case 0x6E050000: result = 0x6E040000; break;
@ -388,7 +388,7 @@ void CFGService::translateCountryInfo(u32 messagePointer) {
case 0x6E030000: result = 0x6E070000; break; case 0x6E030000: result = 0x6E070000; break;
default: break; default: break;
} }
} else if (direction == 1) { // Translate from version A to version B } else if (direction == 1) { // Translate from version A to version B
switch (country) { switch (country) {
case 0x6E030000: result = 0x6E040000; break; case 0x6E030000: result = 0x6E040000; break;
case 0x6E040000: result = 0x6E050000; break; case 0x6E040000: result = 0x6E050000; break;

View file

@ -36,8 +36,8 @@ void CSNDService::handleSyncRequest(u32 messagePointer) {
void CSNDService::acquireSoundChannels(u32 messagePointer) { void CSNDService::acquireSoundChannels(u32 messagePointer) {
log("CSND::AcquireSoundChannels\n"); log("CSND::AcquireSoundChannels\n");
// The CSND service talks to the DSP using the DSP FIFO to negotiate what CSND channels are allocated to the DSP, and this seems to be channels 0-7 (usually). The rest are dedicated to CSND services. // The CSND service talks to the DSP using the DSP FIFO to negotiate what CSND channels are allocated to the DSP, and this seems to be channels
// https://www.3dbrew.org/wiki/CSND_Services // 0-7 (usually). The rest are dedicated to CSND services. https://www.3dbrew.org/wiki/CSND_Services
constexpr u32 csndChannelMask = 0xFFFFFF00; constexpr u32 csndChannelMask = 0xFFFFFF00;
mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0));
@ -52,7 +52,8 @@ void CSNDService::initialize(u32 messagePointer) {
const u32 offset2 = mem.read32(messagePointer + 16); const u32 offset2 = mem.read32(messagePointer + 16);
const u32 offset3 = mem.read32(messagePointer + 20); const u32 offset3 = mem.read32(messagePointer + 20);
log("CSND::Initialize (Block size = %08X, offset0 = %X, offset1 = %X, offset2 = %X, offset3 = %X)\n", blockSize, offset0, offset1, offset2, offset3); log("CSND::Initialize (Block size = %08X, offset0 = %X, offset1 = %X, offset2 = %X, offset3 = %X)\n", blockSize, offset0, offset1, offset2,
offset3);
// Align block size to 4KB. CSND shared memory block is currently stubbed to be 0x3000 == 12KB, so panic if this is more than requested // Align block size to 4KB. CSND shared memory block is currently stubbed to be 0x3000 == 12KB, so panic if this is more than requested
blockSize = (blockSize + 0xFFF) & ~0xFFF; blockSize = (blockSize + 0xFFF) & ~0xFFF;

View file

@ -1,9 +1,10 @@
#include "services/dlp_srvr.hpp" #include "services/dlp_srvr.hpp"
#include "ipc.hpp" #include "ipc.hpp"
namespace DlpSrvrCommands { namespace DlpSrvrCommands {
enum : u32 { enum : u32 {
IsChild = 0x000E0040 IsChild = 0x000E0040,
}; };
} }
@ -22,5 +23,5 @@ void DlpSrvrService::isChild(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x0E, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x0E, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // We are responsible adults mem.write32(messagePointer + 8, 0); // We are responsible adults
} }

View file

@ -36,7 +36,7 @@ namespace DSPCommands {
namespace Result { namespace Result {
enum : u32 { enum : u32 {
HeadphonesNotInserted = 0, HeadphonesNotInserted = 0,
HeadphonesInserted = 1 HeadphonesInserted = 1,
}; };
} }
@ -85,7 +85,7 @@ void DSPService::convertProcessAddressFromDspDram(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0xC, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0xC, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, converted); // Converted address mem.write32(messagePointer + 8, converted); // Converted address
} }
void DSPService::loadComponent(u32 messagePointer) { void DSPService::loadComponent(u32 messagePointer) {
@ -109,9 +109,9 @@ void DSPService::loadComponent(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2)); mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 1); // Component loaded mem.write32(messagePointer + 8, 1); // Component loaded
mem.write32(messagePointer + 12, (size << 4) | 0xA); mem.write32(messagePointer + 12, (size << 4) | 0xA);
mem.write32(messagePointer + 16, mem.read32(messagePointer + 20)); // Component buffer mem.write32(messagePointer + 16, mem.read32(messagePointer + 20)); // Component buffer
} }
void DSPService::unloadComponent(u32 messagePointer) { void DSPService::unloadComponent(u32 messagePointer) {
@ -136,7 +136,7 @@ void DSPService::readPipeIfPossible(u32 messagePointer) {
} }
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write16(messagePointer + 8, u16(data.size())); // Number of bytes read mem.write16(messagePointer + 8, u16(data.size())); // Number of bytes read
} }
void DSPService::recvData(u32 messagePointer) { void DSPService::recvData(u32 messagePointer) {
@ -168,12 +168,10 @@ DSPService::DSPEvent& DSPService::getEventRef(u32 type, u32 pipe) {
case 1: return interrupt1; case 1: return interrupt1;
case 2: case 2:
if (pipe >= pipeCount) if (pipe >= pipeCount) Helpers::panic("Tried to access the event of an invalid pipe");
Helpers::panic("Tried to access the event of an invalid pipe");
return pipeEvents[pipe]; return pipeEvents[pipe];
default: default: Helpers::panic("Unknown type for DSP::getEventRef");
Helpers::panic("Unknown type for DSP::getEventRef");
} }
} }
@ -185,8 +183,8 @@ void DSPService::registerInterruptEvents(u32 messagePointer) {
// The event handle being 0 means we're removing an event // The event handle being 0 means we're removing an event
if (eventHandle == 0) { if (eventHandle == 0) {
DSPEvent& e = getEventRef(interrupt, channel); // Get event DSPEvent& e = getEventRef(interrupt, channel); // Get event
if (e.has_value()) { // Remove if it exists if (e.has_value()) { // Remove if it exists
totalEventCount--; totalEventCount--;
e = std::nullopt; e = std::nullopt;
} }
@ -227,7 +225,7 @@ void DSPService::getSemaphoreEventHandle(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 2)); mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 2));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
// TODO: Translation descriptor here? // TODO: Translation descriptor here?
mem.write32(messagePointer + 12, semaphoreEvent.value()); // Semaphore event handle mem.write32(messagePointer + 12, semaphoreEvent.value()); // Semaphore event handle
kernel.signalEvent(semaphoreEvent.value()); kernel.signalEvent(semaphoreEvent.value());
} }

View file

@ -89,17 +89,17 @@ void FRDService::getMyFriendKey(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x5, 5, 0)); mem.write32(messagePointer, IPC::responseHeader(0x5, 5, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Principal ID mem.write32(messagePointer + 8, 0); // Principal ID
mem.write32(messagePointer + 12, 0); // Padding (?) mem.write32(messagePointer + 12, 0); // Padding (?)
mem.write32(messagePointer + 16, 0); // Local friend code mem.write32(messagePointer + 16, 0); // Local friend code
mem.write32(messagePointer + 20, 0); mem.write32(messagePointer + 20, 0);
} }
void FRDService::getFriendKeyList(u32 messagePointer) { void FRDService::getFriendKeyList(u32 messagePointer) {
log("FRD::GetFriendKeyList\n"); log("FRD::GetFriendKeyList\n");
const u32 count = mem.read32(messagePointer + 8); // From what I understand this is a cap on the number of keys to receive? const u32 count = mem.read32(messagePointer + 8); // From what I understand this is a cap on the number of keys to receive?
constexpr u32 friendCount = 0; // And this should be the number of friends whose keys were actually received? constexpr u32 friendCount = 0; // And this should be the number of friends whose keys were actually received?
mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2)); mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
@ -147,11 +147,11 @@ void FRDService::getFriendAttributeFlags(u32 messagePointer) {
} }
void FRDService::getMyPresence(u32 messagePointer) { void FRDService::getMyPresence(u32 messagePointer) {
static constexpr u32 presenceSize = 0x12C; // A presence seems to be 12C bytes of data, not sure what it contains static constexpr u32 presenceSize = 0x12C; // A presence seems to be 12C bytes of data, not sure what it contains
log("FRD::GetMyPresence\n"); log("FRD::GetMyPresence\n");
u32 buffer = mem.read32(messagePointer + 0x104); // Buffer to write presence info to. u32 buffer = mem.read32(messagePointer + 0x104); // Buffer to write presence info to.
for (u32 i = 0; i < presenceSize; i += 4) { // Clear presence info with 0s for now for (u32 i = 0; i < presenceSize; i += 4) { // Clear presence info with 0s for now
mem.write32(buffer + i, 0); mem.write32(buffer + i, 0);
} }
@ -168,15 +168,15 @@ void FRDService::getFriendPresence(u32 messagePointer) {
} }
void FRDService::getMyProfile(u32 messagePointer) { void FRDService::getMyProfile(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x7, 3, 0)); // Not sure if the header here has the correct # of responses? mem.write32(messagePointer, IPC::responseHeader(0x7, 3, 0)); // Not sure if the header here has the correct # of responses?
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
// TODO: Should maybe make these user-configurable. Not super important though // TODO: Should maybe make these user-configurable. Not super important though
mem.write8(messagePointer + 8, static_cast<u8>(Regions::USA)); // Region mem.write8(messagePointer + 8, static_cast<u8>(Regions::USA)); // Region
mem.write8(messagePointer + 9, static_cast<u8>(CountryCodes::US)); // Country mem.write8(messagePointer + 9, static_cast<u8>(CountryCodes::US)); // Country
mem.write8(messagePointer + 10, 2); // Area (this should be Washington) mem.write8(messagePointer + 10, 2); // Area (this should be Washington)
mem.write8(messagePointer + 11, static_cast<u8>(LanguageCodes::English)); // Language mem.write8(messagePointer + 11, static_cast<u8>(LanguageCodes::English)); // Language
mem.write8(messagePointer + 12, 2); // Platform (always 2 for CTR) mem.write8(messagePointer + 12, 2); // Platform (always 2 for CTR)
// Padding // Padding
mem.write8(messagePointer + 13, 0); mem.write8(messagePointer + 13, 0);

View file

@ -1,10 +1,11 @@
#include "services/fs.hpp" #include "services/fs.hpp"
#include "kernel/kernel.hpp"
#include "io_file.hpp" #include "io_file.hpp"
#include "ipc.hpp" #include "ipc.hpp"
#include "kernel/kernel.hpp"
#include "result/result.hpp" #include "result/result.hpp"
#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does. #ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does.
#undef CreateDirectory #undef CreateDirectory
#undef CreateFile #undef CreateFile
#undef DeleteFile #undef DeleteFile
@ -47,21 +48,18 @@ namespace FSCommands {
}; };
} }
void FSService::reset() { void FSService::reset() { priority = 0; }
priority = 0;
}
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. // Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.
void FSService::initializeFilesystem() { void FSService::initializeFilesystem() {
const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory
const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND"; const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND";
const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData
const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info
const auto systemSaveDataPath = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData"; const auto systemSaveDataPath = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData";
namespace fs = std::filesystem; namespace fs = std::filesystem;
if (!fs::is_directory(nandSharedpath)) { if (!fs::is_directory(nandSharedpath)) {
fs::create_directories(nandSharedpath); fs::create_directories(nandSharedpath);
} }
@ -89,30 +87,26 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) {
case ArchiveID::SaveData: return &saveData; case ArchiveID::SaveData: return &saveData;
case ArchiveID::UserSaveData2: return &userSaveData2; case ArchiveID::UserSaveData2: return &userSaveData2;
case ArchiveID::ExtSaveData: case ArchiveID::ExtSaveData: return &extSaveData_sdmc;
return &extSaveData_sdmc;
case ArchiveID::SharedExtSaveData: case ArchiveID::SharedExtSaveData: return &sharedExtSaveData_nand;
return &sharedExtSaveData_nand;
case ArchiveID::SystemSaveData: return &systemSaveData; case ArchiveID::SystemSaveData: return &systemSaveData;
case ArchiveID::SDMC: return &sdmc; case ArchiveID::SDMC: return &sdmc;
case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly; case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly;
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
case ArchiveID::TwlPhoto: return &twlPhoto; case ArchiveID::TwlPhoto: return &twlPhoto;
case ArchiveID::TwlSound: return &twlSound; case ArchiveID::TwlSound: return &twlSound;
case ArchiveID::CardSPI: return &cardSpi; case ArchiveID::CardSPI: return &cardSpi;
default: default: Helpers::panic("Unknown archive. ID: %d\n", id); return nullptr;
Helpers::panic("Unknown archive. ID: %d\n", id);
return nullptr;
} }
} }
std::optional<HorizonHandle> FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms) { std::optional<HorizonHandle> FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms) {
FileDescriptor opened = archive->openFile(path, perms); FileDescriptor opened = archive->openFile(path, perms);
if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file
auto handle = kernel.makeObject(KernelObjectType::File); auto handle = kernel.makeObject(KernelObjectType::File);
auto& file = kernel.getObjects()[handle]; auto& file = kernel.getObjects()[handle];
@ -126,7 +120,7 @@ std::optional<HorizonHandle> FSService::openFileHandle(ArchiveBase* archive, con
Rust::Result<HorizonHandle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) { Rust::Result<HorizonHandle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) {
Rust::Result<DirectorySession, Result::HorizonResult> opened = archive->openDirectory(path); Rust::Result<DirectorySession, Result::HorizonResult> opened = archive->openDirectory(path);
if (opened.isOk()) { // If opened doesn't have a value, we failed to open the directory if (opened.isOk()) { // If opened doesn't have a value, we failed to open the directory
auto handle = kernel.makeObject(KernelObjectType::Directory); auto handle = kernel.makeObject(KernelObjectType::Directory);
auto& object = kernel.getObjects()[handle]; auto& object = kernel.getObjects()[handle];
object.data = new DirectorySession(opened.unwrap()); object.data = new DirectorySession(opened.unwrap());
@ -152,8 +146,7 @@ Rust::Result<HorizonHandle, Result::HorizonResult> FSService::openArchiveHandle(
archiveObject.data = new ArchiveSession(res.unwrap(), path); archiveObject.data = new ArchiveSession(res.unwrap(), path);
return Ok(handle); return Ok(handle);
} } else {
else {
return Err(res.unwrapErr()); return Err(res.unwrapErr());
} }
} }
@ -162,8 +155,7 @@ FSPath FSService::readPath(u32 type, u32 pointer, u32 size) {
std::vector<u8> data; std::vector<u8> data;
data.resize(size); data.resize(size);
for (u32 i = 0; i < size; i++) for (u32 i = 0; i < size; i++) data[i] = mem.read8(pointer + i);
data[i] = mem.read8(pointer + i);
return FSPath(type, data); return FSPath(type, data);
} }
@ -222,7 +214,7 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) {
} }
void FSService::closeArchive(u32 messagePointer) { void FSService::closeArchive(u32 messagePointer) {
const Handle handle = static_cast<u32>(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit const Handle handle = static_cast<u32>(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit
const auto object = kernel.getObject(handle, KernelObjectType::Archive); const auto object = kernel.getObject(handle, KernelObjectType::Archive);
log("FSService::CloseArchive(handle = %X)\n", handle); log("FSService::CloseArchive(handle = %X)\n", handle);
@ -288,7 +280,7 @@ void FSService::openFile(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::FS::FileNotFound); mem.write32(messagePointer + 4, Result::FS::FileNotFound);
} else { } else {
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor" mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor"
mem.write32(messagePointer + 12, handle.value()); mem.write32(messagePointer + 12, handle.value());
} }
} }
@ -475,8 +467,9 @@ void FSService::formatSaveData(u32 messagePointer) {
log("FS::FormatSaveData\n"); log("FS::FormatSaveData\n");
const u32 archiveID = mem.read32(messagePointer + 4); const u32 archiveID = mem.read32(messagePointer + 4);
if (archiveID != ArchiveID::SaveData) if (archiveID != ArchiveID::SaveData) {
Helpers::panic("FS::FormatSaveData: Archive is not SaveData"); Helpers::panic("FS::FormatSaveData: Archive is not SaveData");
}
// Read path and path info // Read path and path info
const u32 pathType = mem.read32(messagePointer + 8); const u32 pathType = mem.read32(messagePointer + 8);
@ -486,24 +479,24 @@ void FSService::formatSaveData(u32 messagePointer) {
// Size of a block. Seems to always be 0x200 // Size of a block. Seems to always be 0x200
const u32 blockSize = mem.read32(messagePointer + 16); const u32 blockSize = mem.read32(messagePointer + 16);
if (blockSize != 0x200 && blockSize != 0x1000) if (blockSize != 0x200 && blockSize != 0x1000) {
Helpers::panic("FS::FormatSaveData: Invalid SaveData block size"); Helpers::panic("FS::FormatSaveData: Invalid SaveData block size");
}
const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories
const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files
const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...? const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...?
const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here
const bool duplicateData = mem.read8(messagePointer + 36) != 0; const bool duplicateData = mem.read8(messagePointer + 36) != 0;
ArchiveBase::FormatInfo info { ArchiveBase::FormatInfo info{
.size = blockSize * 0x200, .size = blockSize * 0x200,
.numOfDirectories = directoryNum, .numOfDirectories = directoryNum,
.numOfFiles = fileNum, .numOfFiles = fileNum,
.duplicateData = duplicateData .duplicateData = duplicateData,
}; };
saveData.format(path, info); saveData.format(path, info);
mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0)); mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
} }
@ -517,8 +510,8 @@ void FSService::deleteExtSaveData(u32 messagePointer) {
log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID); log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID);
mem.write32(messagePointer, IPC::responseHeader(0x0852, 1, 0)); mem.write32(messagePointer, IPC::responseHeader(0x0852, 1, 0));
// TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required for Planet Robobot // TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required
// When we properly implement it, it will just be a recursive directory deletion // for Planet Robobot When we properly implement it, it will just be a recursive directory deletion
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
} }
@ -526,7 +519,8 @@ void FSService::createExtSaveData(u32 messagePointer) {
Helpers::warn("Stubbed call to FS::CreateExtSaveData!"); Helpers::warn("Stubbed call to FS::CreateExtSaveData!");
// First 4 words of parameters are the ExtSaveData info // First 4 words of parameters are the ExtSaveData info
// https://www.3dbrew.org/wiki/Filesystem_services#ExtSaveDataInfo // https://www.3dbrew.org/wiki/Filesystem_services#ExtSaveDataInfo
// This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created directory. // This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created
// directory.
const u8 mediaType = mem.read8(messagePointer + 4); const u8 mediaType = mem.read8(messagePointer + 4);
const u64 saveID = mem.read64(messagePointer + 8); const u64 saveID = mem.read64(messagePointer + 8);
const u32 numOfDirectories = mem.read32(messagePointer + 20); const u32 numOfDirectories = mem.read32(messagePointer + 20);
@ -546,18 +540,13 @@ void FSService::formatThisUserSaveData(u32 messagePointer) {
log("FS::FormatThisUserSaveData\n"); log("FS::FormatThisUserSaveData\n");
const u32 blockSize = mem.read32(messagePointer + 4); const u32 blockSize = mem.read32(messagePointer + 4);
const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories
const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files
const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...? const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...?
const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here
const bool duplicateData = mem.read8(messagePointer + 24) != 0; const bool duplicateData = mem.read8(messagePointer + 24) != 0;
ArchiveBase::FormatInfo info { ArchiveBase::FormatInfo info{.size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, .duplicateData = duplicateData};
.size = blockSize * 0x200,
.numOfDirectories = directoryNum,
.numOfFiles = fileNum,
.duplicateData = duplicateData
};
FSPath emptyPath; FSPath emptyPath;
mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0)); mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0));
@ -583,18 +572,16 @@ void FSService::controlArchive(u32 messagePointer) {
} }
switch (action) { switch (action) {
case 0: // Commit save data changes. Shouldn't need us to do anything case 0: // Commit save data changes. Shouldn't need us to do anything
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
break; break;
case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment
Helpers::warn("FS::ControlArchive: Tried to retrieve a file's last-modified timestamp"); Helpers::warn("FS::ControlArchive: Tried to retrieve a file's last-modified timestamp");
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
break; break;
default: default: Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action); break;
Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action);
break;
} }
} }
@ -678,9 +665,9 @@ void FSService::getThisSaveDataSecureValue(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x86F, 1, 0)); mem.write32(messagePointer, IPC::responseHeader(0x86F, 1, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 0); // Secure value does not exist mem.write8(messagePointer + 8, 0); // Secure value does not exist
mem.write8(messagePointer + 12, 1); // TODO: What is this? mem.write8(messagePointer + 12, 1); // TODO: What is this?
mem.write64(messagePointer + 16, 0); // Secure value mem.write64(messagePointer + 16, 0); // Secure value
} }
void FSService::setThisSaveDataSecureValue(u32 messagePointer) { void FSService::setThisSaveDataSecureValue(u32 messagePointer) {

View file

@ -1,10 +1,11 @@
#include "services/ldr_ro.hpp" #include "services/ldr_ro.hpp"
#include "ipc.hpp"
#include "kernel.hpp"
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include "ipc.hpp"
#include "kernel.hpp"
namespace LDRCommands { namespace LDRCommands {
enum : u32 { enum : u32 {
Initialize = 0x000100C2, Initialize = 0x000100C2,
@ -66,10 +67,13 @@ namespace SegmentTable {
namespace SegmentID { namespace SegmentID {
enum : u32 { enum : u32 {
TEXT, RODATA, DATA, BSS, TEXT,
RODATA,
DATA,
BSS,
}; };
} }
} } // namespace SegmentTable
namespace NamedExportTable { namespace NamedExportTable {
enum : u32 { enum : u32 {
@ -119,8 +123,8 @@ namespace RelocationPatch {
enum : u32 { enum : u32 {
SegmentOffset = 0, SegmentOffset = 0,
PatchType = 4, PatchType = 4,
IsLastEntry = 5, // For import patches IsLastEntry = 5, // For import patches
SegmentIndex = 5, // For relocation patches SegmentIndex = 5, // For relocation patches
IsResolved = 6, IsResolved = 6,
Addend = 8, Addend = 8,
}; };
@ -130,7 +134,7 @@ namespace RelocationPatch {
AbsoluteAddress = 2, AbsoluteAddress = 2,
}; };
}; };
}; }; // namespace RelocationPatch
struct CROHeaderEntry { struct CROHeaderEntry {
u32 offset, size; u32 offset, size;
@ -145,12 +149,12 @@ static const std::string CRR_MAGIC("CRR0");
class CRO { class CRO {
Memory &mem; Memory &mem;
u32 croPointer; // Origin address of CRO in RAM u32 croPointer; // Origin address of CRO in RAM
u32 oldDataSegmentOffset; u32 oldDataSegmentOffset;
bool isCRO; // False if CRS bool isCRO; // False if CRS
public: public:
CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), oldDataSegmentOffset(0), isCRO(isCRO) {} CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), oldDataSegmentOffset(0), isCRO(isCRO) {}
~CRO() = default; ~CRO() = default;

View file

@ -1,4 +1,5 @@
#include "services/mic.hpp" #include "services/mic.hpp"
#include "ipc.hpp" #include "ipc.hpp"
#include "kernel/kernel.hpp" #include "kernel/kernel.hpp"
@ -127,9 +128,8 @@ void MICService::startSampling(u32 messagePointer) {
u32 dataSize = mem.read32(messagePointer + 16); u32 dataSize = mem.read32(messagePointer + 16);
bool loop = mem.read8(messagePointer + 20); bool loop = mem.read8(messagePointer + 20);
log("MIC::StartSampling (encoding = %d, sample rate = %d, offset = %08X, size = %08X, loop: %s) (stubbed)\n", log("MIC::StartSampling (encoding = %d, sample rate = %d, offset = %08X, size = %08X, loop: %s) (stubbed)\n", encoding, sampleRate, offset,
encoding, sampleRate, offset, dataSize, loop ? "yes" : "no" dataSize, loop ? "yes" : "no");
);
currentlySampling = true; currentlySampling = true;
mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0)); mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0));

View file

@ -1,4 +1,5 @@
#include "services/ndm.hpp" #include "services/ndm.hpp"
#include "ipc.hpp" #include "ipc.hpp"
namespace NDMCommands { namespace NDMCommands {

View file

@ -1,6 +1,7 @@
#include "ipc.hpp"
#include "services/news_u.hpp" #include "services/news_u.hpp"
#include "ipc.hpp"
namespace NewsCommands { namespace NewsCommands {
enum : u32 {}; enum : u32 {};
} }

View file

@ -1,4 +1,5 @@
#include "services/nfc.hpp" #include "services/nfc.hpp"
#include "io_file.hpp" #include "io_file.hpp"
#include "ipc.hpp" #include "ipc.hpp"
#include "kernel.hpp" #include "kernel.hpp"
@ -166,7 +167,6 @@ void NFCService::communicationGetStatus(u32 messagePointer) {
mem.write8(messagePointer + 8, static_cast<u32>(adapterStatus)); mem.write8(messagePointer + 8, static_cast<u32>(adapterStatus));
} }
void NFCService::communicationGetResult(u32 messagePointer) { void NFCService::communicationGetResult(u32 messagePointer) {
log("NFC::CommunicationGetResult\n"); log("NFC::CommunicationGetResult\n");

View file

@ -1,9 +1,10 @@
#include "services/nim.hpp" #include "services/nim.hpp"
#include "ipc.hpp" #include "ipc.hpp"
namespace NIMCommands { namespace NIMCommands {
enum : u32 { enum : u32 {
Initialize = 0x00210000 Initialize = 0x00210000,
}; };
} }

View file

@ -1,7 +1,8 @@
#include "services/nwm_uds.hpp"
#include "ipc.hpp" #include "ipc.hpp"
#include "kernel.hpp" #include "kernel.hpp"
#include "result/result.hpp" #include "result/result.hpp"
#include "services/nwm_uds.hpp"
namespace NWMCommands { namespace NWMCommands {
enum : u32 { enum : u32 {

View file

@ -1,6 +1,7 @@
#include "services/ssl.hpp"
#include "ipc.hpp" #include "ipc.hpp"
#include "result/result.hpp" #include "result/result.hpp"
#include "services/ssl.hpp"
namespace SSLCommands { namespace SSLCommands {
enum : u32 { enum : u32 {
@ -34,7 +35,7 @@ void SSLService::initialize(u32 messagePointer) {
} }
initialized = true; initialized = true;
rng.seed(std::random_device()()); // Seed rng via std::random_device rng.seed(std::random_device()()); // Seed rng via std::random_device
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
} }
@ -48,7 +49,8 @@ void SSLService::generateRandomData(u32 messagePointer) {
u32 data; u32 data;
for (u32 i = 0; i < size; i++) { for (u32 i = 0; i < size; i++) {
// We don't have an available random value since we're on a multiple of 4 bytes and our Twister is 32-bit, generate a new one from the Mersenne Twister // We don't have an available random value since we're on a multiple of 4 bytes and our Twister is 32-bit, generate a new one from the
// Mersenne Twister
if ((i & 3) == 0) { if ((i & 3) == 0) {
data = rng(); data = rng();
} }

View file

@ -337,7 +337,7 @@ void Y2RService::setStandardCoeff(u32 messagePointer) {
log("Y2R::SetStandardCoeff (coefficient = %d)\n", coeff); log("Y2R::SetStandardCoeff (coefficient = %d)\n", coeff);
mem.write32(messagePointer, IPC::responseHeader(0x20, 1, 0)); mem.write32(messagePointer, IPC::responseHeader(0x20, 1, 0));
if (coeff > 3) { // Invalid coefficient, should have an error code if (coeff > 3) { // Invalid coefficient, should have an error code
Helpers::panic("Y2R: Invalid standard coefficient (coefficient = %d)\n", coeff); Helpers::panic("Y2R: Invalid standard coefficient (coefficient = %d)\n", coeff);
} }
@ -360,7 +360,7 @@ void Y2RService::getStandardCoefficientParams(u32 messagePointer) {
// Write standard coefficient parameters to output buffer // Write standard coefficient parameters to output buffer
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
const u32 pointer = messagePointer + 8 + i * sizeof(u16); // Pointer to write parameter to const u32 pointer = messagePointer + 8 + i * sizeof(u16); // Pointer to write parameter to
mem.write16(pointer, coeff[i]); mem.write16(pointer, coeff[i]);
} }
} }
@ -453,7 +453,7 @@ void Y2RService::startConversion(u32 messagePointer) {
void Y2RService::isFinishedSendingYUV(u32 messagePointer) { void Y2RService::isFinishedSendingYUV(u32 messagePointer) {
log("Y2R::IsFinishedSendingYUV"); log("Y2R::IsFinishedSendingYUV");
constexpr bool finished = true; // For now, Y2R transfers are instant constexpr bool finished = true; // For now, Y2R transfers are instant
mem.write32(messagePointer, IPC::responseHeader(0x14, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x14, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
@ -489,7 +489,7 @@ void Y2RService::isFinishedSendingV(u32 messagePointer) {
void Y2RService::isFinishedReceiving(u32 messagePointer) { void Y2RService::isFinishedReceiving(u32 messagePointer) {
log("Y2R::IsFinishedSendingReceiving"); log("Y2R::IsFinishedSendingReceiving");
constexpr bool finished = true; // For now, receiving components is also instant constexpr bool finished = true; // For now, receiving components is also instant
mem.write32(messagePointer, IPC::responseHeader(0x17, 2, 0)); mem.write32(messagePointer, IPC::responseHeader(0x17, 2, 0));
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);