mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-07 15:52:59 +12:00
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
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:
parent
d1f4ae2911
commit
8e20bd6220
65 changed files with 13445 additions and 26224 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
};
|
};
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -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; }
|
||||||
|
|
|
@ -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) {}
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -35,7 +35,7 @@ struct GLStateManager {
|
||||||
bool depthMask;
|
bool depthMask;
|
||||||
|
|
||||||
float clearRed, clearBlue, clearGreen, clearAlpha;
|
float clearRed, clearBlue, clearGreen, clearAlpha;
|
||||||
|
|
||||||
GLuint stencilMask;
|
GLuint stencilMask;
|
||||||
GLuint boundVAO;
|
GLuint boundVAO;
|
||||||
GLuint currentProgram;
|
GLuint currentProgram;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ struct DepthBuffer {
|
||||||
GL_DEPTH_COMPONENT,
|
GL_DEPTH_COMPONENT,
|
||||||
GL_DEPTH_STENCIL,
|
GL_DEPTH_STENCIL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<GLenum, 4> types = {
|
static constexpr std::array<GLenum, 4> types = {
|
||||||
GL_UNSIGNED_SHORT,
|
GL_UNSIGNED_SHORT,
|
||||||
GL_UNSIGNED_INT,
|
GL_UNSIGNED_INT,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -47,13 +47,13 @@ Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock* sharedMem
|
||||||
|
|
||||||
const std::u16string text = u"Pand";
|
const std::u16string text = u"Pand";
|
||||||
u32 textAddress = sharedMem->addr;
|
u32 textAddress = sharedMem->addr;
|
||||||
|
|
||||||
// Copy text to shared memory the app gave us
|
// Copy text to shared memory the app gave us
|
||||||
for (u32 i = 0; i < text.size(); i++) {
|
for (u32 i = 0; i < text.size(); i++) {
|
||||||
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) {
|
||||||
|
|
|
@ -312,7 +312,7 @@ void TeakraDSP::loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMa
|
||||||
runSlice();
|
runSlice();
|
||||||
}
|
}
|
||||||
pipeBaseAddr = teakra.RecvData(2);
|
pipeBaseAddr = teakra.RecvData(2);
|
||||||
|
|
||||||
// Schedule next DSP event
|
// Schedule next DSP event
|
||||||
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
|
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
|
|
@ -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(); }
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
@ -113,7 +113,7 @@ void Kernel::readDirectory(u32 messagePointer, Handle directory) {
|
||||||
const u32 entryCount = mem.read32(messagePointer + 4);
|
const u32 entryCount = mem.read32(messagePointer + 4);
|
||||||
const u32 outPointer = mem.read32(messagePointer + 12);
|
const u32 outPointer = mem.read32(messagePointer + 12);
|
||||||
logFileIO("Directory::Read (handle = %X, entry count = %d, out pointer = %08X)\n", directory, entryCount, outPointer);
|
logFileIO("Directory::Read (handle = %X, entry count = %d, out pointer = %08X)\n", directory, entryCount, outPointer);
|
||||||
|
|
||||||
const auto p = getObject(directory, KernelObjectType::Directory);
|
const auto p = getObject(directory, KernelObjectType::Directory);
|
||||||
if (p == nullptr) [[unlikely]] {
|
if (p == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("Called ReadDirectory on non-existent directory");
|
Helpers::panic("Called ReadDirectory on non-existent directory");
|
||||||
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -400,7 +401,7 @@ void Kernel::getSystemInfo() {
|
||||||
regs[2] = 0;
|
regs[2] = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Helpers::warn("GetSystemInfo: Unknown PandaInformation subtype %x\n", subtype);
|
Helpers::warn("GetSystemInfo: Unknown PandaInformation subtype %x\n", subtype);
|
||||||
regs[0] = Result::FailurePlaceholder;
|
regs[0] = Result::FailurePlaceholder;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -101,8 +99,8 @@ void Kernel::rescheduleThreads() {
|
||||||
// Case 1: A thread can run
|
// Case 1: A thread can run
|
||||||
if (newThreadIndex.has_value()) {
|
if (newThreadIndex.has_value()) {
|
||||||
switchThread(newThreadIndex.value());
|
switchThread(newThreadIndex.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: No other thread can run, straight to the idle thread
|
// Case 2: No other thread can run, straight to the idle thread
|
||||||
else {
|
else {
|
||||||
switchThread(idleThreadIndex);
|
switchThread(idleThreadIndex);
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ void Kernel::svcSetTimer() {
|
||||||
timer->interval = interval;
|
timer->interval = interval;
|
||||||
timer->running = true;
|
timer->running = true;
|
||||||
timer->fireTick = cpu.getTicks() + Scheduler::nsToCycles(initial);
|
timer->fireTick = cpu.getTicks() + Scheduler::nsToCycles(initial);
|
||||||
|
|
||||||
Scheduler& scheduler = cpu.getScheduler();
|
Scheduler& scheduler = cpu.getScheduler();
|
||||||
// Signal an event to poll timers as soon as possible
|
// Signal an event to poll timers as soon as possible
|
||||||
scheduler.removeEvent(Scheduler::EventType::UpdateTimers);
|
scheduler.removeEvent(Scheduler::EventType::UpdateTimers);
|
||||||
|
|
|
@ -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.
|
|
||||||
// The executable region starts at 0x00100000 and has a maximum size of 0x03F00000
|
|
||||||
u64 endAddress = (u64)vaddr + (u64)fileSize;
|
|
||||||
const bool isGood = vaddr >= VirtualAddrs::ExecutableStart && endAddress < VirtualAddrs::ExecutableEnd;
|
|
||||||
if (!isGood) {
|
|
||||||
// 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");
|
|
||||||
// return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memorySize & pageMask) {
|
// Assert that the segment will be loaded in the executable region. If it isn't then panic.
|
||||||
// Round up the size of the ELF segment to a page (4KB) boundary, as the OS can only alloc this way
|
// The executable region starts at 0x00100000 and has a maximum size of 0x03F00000
|
||||||
memorySize = (memorySize + pageSize - 1) & -pageSize;
|
u64 endAddress = (u64)vaddr + (u64)fileSize;
|
||||||
Helpers::warn("Rounding ELF segment size to %08X\n", memorySize);
|
const bool isGood = vaddr >= VirtualAddrs::ExecutableStart && endAddress < VirtualAddrs::ExecutableEnd;
|
||||||
}
|
if (!isGood) {
|
||||||
|
// 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");
|
||||||
|
// return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
// This should also assert that findPaddr doesn't fail
|
if (memorySize & pageMask) {
|
||||||
u32 fcramAddr = findPaddr(memorySize).value();
|
// Round up the size of the ELF segment to a page (4KB) boundary, as the OS can only alloc this way
|
||||||
std::memcpy(&fcram[fcramAddr], data, fileSize);
|
memorySize = (memorySize + pageSize - 1) & -pageSize;
|
||||||
|
Helpers::warn("Rounding ELF segment size to %08X\n", memorySize);
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate the segment on the OS side
|
// This should also assert that findPaddr doesn't fail
|
||||||
allocateMemory(vaddr, fcramAddr, memorySize, true, r, w, x);
|
u32 fcramAddr = findPaddr(memorySize).value();
|
||||||
}
|
std::memcpy(&fcram[fcramAddr], data, fileSize);
|
||||||
|
|
||||||
// ELF can't specify a region, make it default to USA
|
// Allocate the segment on the OS side
|
||||||
region = Regions::USA;
|
allocateMemory(vaddr, fcramAddr, memorySize, true, r, w, x);
|
||||||
return static_cast<u32>(reader.get_entry());
|
}
|
||||||
|
|
||||||
|
// ELF can't specify a region, make it default to USA
|
||||||
|
region = Regions::USA;
|
||||||
|
return static_cast<u32>(reader.get_entry());
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "services/act.hpp"
|
#include "services/act.hpp"
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
|
||||||
namespace ACTCommands {
|
namespace ACTCommands {
|
||||||
|
@ -33,7 +34,7 @@ void ACTService::initialize(u32 messagePointer) {
|
||||||
|
|
||||||
void ACTService::generateUUID(u32 messagePointer) {
|
void ACTService::generateUUID(u32 messagePointer) {
|
||||||
log("ACT::GenerateUUID (stubbed)\n");
|
log("ACT::GenerateUUID (stubbed)\n");
|
||||||
|
|
||||||
// TODO: The header is probably wrong
|
// TODO: The header is probably wrong
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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() {}
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,13 +303,13 @@ void CFGService::getCountryCodeID(u32 messagePointer) {
|
||||||
log("CFG::GetCountryCodeID (code = %04X)\n", characterCode);
|
log("CFG::GetCountryCodeID (code = %04X)\n", characterCode);
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x0A, 2, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x0A, 2, 0));
|
||||||
|
|
||||||
// If the character code is valid, return its table ID and a success code
|
// If the character code is valid, return its table ID and a success code
|
||||||
if (auto search = countryCodeToTableIDMap.find(characterCode); search != countryCodeToTableIDMap.end()) {
|
if (auto search = countryCodeToTableIDMap.find(characterCode); search != countryCodeToTableIDMap.end()) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write16(messagePointer + 8, search->second);
|
mem.write16(messagePointer + 8, search->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
Helpers::warn("CFG::GetCountryCodeID: Invalid country code %X", characterCode);
|
Helpers::warn("CFG::GetCountryCodeID: Invalid country code %X", characterCode);
|
||||||
mem.write32(messagePointer + 4, Result::CFG::NotFound);
|
mem.write32(messagePointer + 4, Result::CFG::NotFound);
|
||||||
|
@ -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;
|
||||||
|
@ -423,7 +423,7 @@ void CFGService::norInitialize(u32 messagePointer) {
|
||||||
void CFGService::norReadData(u32 messagePointer) {
|
void CFGService::norReadData(u32 messagePointer) {
|
||||||
log("CFG::NOR::ReadData\n");
|
log("CFG::NOR::ReadData\n");
|
||||||
Helpers::warn("Unimplemented CFG::NOR::ReadData");
|
Helpers::warn("Unimplemented CFG::NOR::ReadData");
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ void FRDService::handleSyncRequest(u32 messagePointer, FRDService::Type type) {
|
||||||
case FRDCommands::SetNotificationMask: setNotificationMask(messagePointer); break;
|
case FRDCommands::SetNotificationMask: setNotificationMask(messagePointer); break;
|
||||||
case FRDCommands::UpdateGameModeDescription: updateGameModeDescription(messagePointer); break;
|
case FRDCommands::UpdateGameModeDescription: updateGameModeDescription(messagePointer); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// FRD:A functions
|
// FRD:A functions
|
||||||
if (type == Type::A) {
|
if (type == Type::A) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
@ -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);
|
||||||
|
@ -226,7 +226,7 @@ void FRDService::getMyMii(u32 messagePointer) {
|
||||||
void FRDService::getMyFavoriteGame(u32 messagePointer) {
|
void FRDService::getMyFavoriteGame(u32 messagePointer) {
|
||||||
log("FRD::GetMyFavoriteGame (stubbed)\n");
|
log("FRD::GetMyFavoriteGame (stubbed)\n");
|
||||||
constexpr u64 titleID = 0;
|
constexpr u64 titleID = 0;
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0xD, 3, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0xD, 3, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write64(messagePointer + 8, titleID);
|
mem.write64(messagePointer + 8, titleID);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -238,7 +242,7 @@ public:
|
||||||
|
|
||||||
for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) {
|
for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) {
|
||||||
const u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport + NamedExportTable::NameOffset);
|
const u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport + NamedExportTable::NameOffset);
|
||||||
|
|
||||||
const std::string exportSymbolName = mem.readString(nameOffset, exportStringSize);
|
const std::string exportSymbolName = mem.readString(nameOffset, exportStringSize);
|
||||||
|
|
||||||
if (symbolName.compare(exportSymbolName) == 0) {
|
if (symbolName.compare(exportSymbolName) == 0) {
|
||||||
|
@ -713,7 +717,7 @@ public:
|
||||||
for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) {
|
for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) {
|
||||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||||
const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset);
|
const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset);
|
||||||
|
|
||||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||||
|
|
||||||
if (symbolName.compare(std::string("__aeabi_atexit")) == 0) {
|
if (symbolName.compare(std::string("__aeabi_atexit")) == 0) {
|
||||||
|
@ -725,7 +729,7 @@ public:
|
||||||
const u32 exportSymbolAddr = cro.getNamedExportSymbolAddr(std::string("nnroAeabiAtexit_"));
|
const u32 exportSymbolAddr = cro.getNamedExportSymbolAddr(std::string("nnroAeabiAtexit_"));
|
||||||
if (exportSymbolAddr != 0) {
|
if (exportSymbolAddr != 0) {
|
||||||
patchBatch(relocationOffset, exportSymbolAddr);
|
patchBatch(relocationOffset, exportSymbolAddr);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,7 +759,7 @@ public:
|
||||||
|
|
||||||
if (isResolved == 0) {
|
if (isResolved == 0) {
|
||||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||||
|
|
||||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||||
|
|
||||||
// Check every loaded CRO for the symbol (the pain)
|
// Check every loaded CRO for the symbol (the pain)
|
||||||
|
@ -864,7 +868,7 @@ public:
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool clearModules() {
|
bool clearModules() {
|
||||||
const u32 onUnresolvedAddr = getOnUnresolvedAddr();
|
const u32 onUnresolvedAddr = getOnUnresolvedAddr();
|
||||||
|
|
||||||
|
@ -879,7 +883,7 @@ public:
|
||||||
if (indexedOffset == 0) {
|
if (indexedOffset == 0) {
|
||||||
Helpers::panic("Indexed symbol offset is NULL");
|
Helpers::panic("Indexed symbol offset is NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 relocationOffset = mem.read32(indexedOffset + 8 * indexedImport + IndexedImportTable::RelocationOffset);
|
const u32 relocationOffset = mem.read32(indexedOffset + 8 * indexedImport + IndexedImportTable::RelocationOffset);
|
||||||
|
|
||||||
patchBatch(relocationOffset, onUnresolvedAddr, true);
|
patchBatch(relocationOffset, onUnresolvedAddr, true);
|
||||||
|
@ -924,7 +928,7 @@ public:
|
||||||
|
|
||||||
if (isResolved == 0) {
|
if (isResolved == 0) {
|
||||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||||
|
|
||||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||||
|
|
||||||
// Check our current CRO for the symbol
|
// Check our current CRO for the symbol
|
||||||
|
@ -988,7 +992,7 @@ public:
|
||||||
u32 currentCROPointer = loadedCRS;
|
u32 currentCROPointer = loadedCRS;
|
||||||
while (currentCROPointer != 0) {
|
while (currentCROPointer != 0) {
|
||||||
CRO cro(mem, currentCROPointer, true);
|
CRO cro(mem, currentCROPointer, true);
|
||||||
|
|
||||||
const u32 onUnresolvedAddr = cro.getOnUnresolvedAddr();
|
const u32 onUnresolvedAddr = cro.getOnUnresolvedAddr();
|
||||||
|
|
||||||
const u32 importStringSize = mem.read32(currentCROPointer + CROHeader::ImportStringSize);
|
const u32 importStringSize = mem.read32(currentCROPointer + CROHeader::ImportStringSize);
|
||||||
|
@ -1003,7 +1007,7 @@ public:
|
||||||
|
|
||||||
if (isResolved != 0) {
|
if (isResolved != 0) {
|
||||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||||
|
|
||||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||||
|
|
||||||
// Check our current CRO for the symbol
|
// Check our current CRO for the symbol
|
||||||
|
@ -1111,7 +1115,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
CRO crs(mem, loadedCRS, false);
|
CRO crs(mem, loadedCRS, false);
|
||||||
|
|
||||||
u32 headAddr = crs.getPrevCRO();
|
u32 headAddr = crs.getPrevCRO();
|
||||||
if (autoLink) {
|
if (autoLink) {
|
||||||
headAddr = crs.getNextCRO();
|
headAddr = crs.getNextCRO();
|
||||||
|
|
|
@ -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));
|
||||||
|
@ -139,7 +139,7 @@ void MICService::startSampling(u32 messagePointer) {
|
||||||
void MICService::stopSampling(u32 messagePointer) {
|
void MICService::stopSampling(u32 messagePointer) {
|
||||||
log("MIC::StopSampling\n");
|
log("MIC::StopSampling\n");
|
||||||
currentlySampling = false;
|
currentlySampling = false;
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "services/ndm.hpp"
|
#include "services/ndm.hpp"
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
|
||||||
namespace NDMCommands {
|
namespace NDMCommands {
|
||||||
|
|
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -31,10 +32,10 @@ void SSLService::initialize(u32 messagePointer) {
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
Helpers::warn("SSL service initialized twice");
|
Helpers::warn("SSL service initialized twice");
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue