metal: initial support

This commit is contained in:
Samuliak 2024-07-02 08:28:41 +02:00
parent 29d9ed7224
commit f0547d1a71
167 changed files with 28839 additions and 1271 deletions

View file

@ -33,8 +33,8 @@ namespace Audio {
SampleFormat format;
SourceType sourceType;
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer?
bool hasPlayedOnce = false; // Has the buffer been played at least once before?
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer?
bool hasPlayedOnce = false; // Has the buffer been played at least once before?
bool operator<(const Buffer& other) const {
// Lower ID = Higher priority
@ -136,7 +136,7 @@ namespace Audio {
const auto counter0 = dspRam.region0.frameCounter;
const auto counter1 = dspRam.region1.frameCounter;
// Handle wraparound cases first
// HandleType wraparound cases first
if (counter0 == 0xffff && counter1 != 0xfffe) {
return 1;
} else if (counter1 == 0xffff && counter0 != 0xfffe) {

View file

@ -8,7 +8,7 @@
namespace Common {
class CapstoneDisassembler {
csh handle; // Handle to our disassembler object
csh handle; // HandleType to our disassembler object
cs_insn* instructions = nullptr; // Pointer to instruction object
bool initialized = false;

View file

@ -7,6 +7,7 @@
#include <string>
#include <type_traits>
#include <vector>
#include "helpers.hpp"
#include "memory.hpp"
#include "result.hpp"
@ -15,13 +16,13 @@
using Result::HorizonResult;
namespace PathType {
enum : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
ASCII = 3,
UTF16 = 4,
};
enum : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
ASCII = 3,
UTF16 = 4,
};
}
namespace ArchiveID {
@ -42,83 +43,81 @@ namespace ArchiveID {
UserSaveData2 = 0x567890B4,
};
static std::string toString(u32 id) {
switch (id) {
case SelfNCCH: return "SelfNCCH";
case SaveData: return "SaveData";
case ExtSaveData: return "ExtSaveData";
case SharedExtSaveData: return "SharedExtSaveData";
case SystemSaveData: return "SystemSaveData";
case SDMC: return "SDMC";
case SDMCWriteOnly: return "SDMC (Write-only)";
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
default: return "Unknown archive";
}
}
}
static std::string toString(u32 id) {
switch (id) {
case SelfNCCH: return "SelfNCCH";
case SaveData: return "SaveData";
case ExtSaveData: return "ExtSaveData";
case SharedExtSaveData: return "SharedExtSaveData";
case SystemSaveData: return "SystemSaveData";
case SDMC: return "SDMC";
case SDMCWriteOnly: return "SDMC (Write-only)";
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
default: return "Unknown archive";
}
}
} // namespace ArchiveID
struct FSPath {
u32 type = PathType::Invalid;
u32 type = PathType::Invalid;
std::vector<u8> binary; // Path data for binary paths
std::string string; // Path data for ASCII paths
std::u16string utf16_string;
std::vector<u8> binary; // Path data for binary paths
std::string string; // Path data for ASCII paths
std::u16string utf16_string;
FSPath() {}
FSPath() {}
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
switch (type) {
case PathType::Binary:
binary = std::move(vec);
break;
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
switch (type) {
case PathType::Binary: binary = std::move(vec); break;
case PathType::ASCII:
string.resize(vec.size() - 1); // -1 because of the null terminator
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
break;
case PathType::ASCII:
string.resize(vec.size() - 1); // -1 because of the null terminator
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
break;
case PathType::UTF16: {
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
utf16_string.resize(size);
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
break;
}
; }
}
case PathType::UTF16: {
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
utf16_string.resize(size);
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
break;
};
}
}
};
struct FilePerms {
u32 raw;
u32 raw;
FilePerms(u32 val) : raw(val) {}
bool read() const { return (raw & 1) != 0; }
bool write() const { return (raw & 2) != 0; }
bool create() const { return (raw & 4) != 0; }
FilePerms(u32 val) : raw(val) {}
bool read() const { return (raw & 1) != 0; }
bool write() const { return (raw & 2) != 0; }
bool create() const { return (raw & 4) != 0; }
};
class ArchiveBase;
struct FileSession {
ArchiveBase* archive = nullptr;
FILE* fd = nullptr; // File descriptor for file sessions that require them.
FSPath path;
FSPath archivePath;
u32 priority = 0; // TODO: What does this even do
bool isOpen;
ArchiveBase* archive = nullptr;
FILE* fd = nullptr; // File descriptor for file sessions that require them.
FSPath path;
FSPath archivePath;
u32 priority = 0; // TODO: What does this even do
bool isOpen;
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true)
: archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
// For cloning a file session
FileSession(const FileSession& other) : archive(other.archive), path(other.path),
archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
// For cloning a file session
FileSession(const FileSession& other)
: archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
};
struct ArchiveSession {
ArchiveBase* archive = nullptr;
FSPath path;
bool isOpen;
ArchiveBase* archive = nullptr;
FSPath path;
bool isOpen;
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
};
struct DirectoryEntry {
@ -156,106 +155,104 @@ struct DirectorySession {
using FileDescriptor = std::optional<FILE*>;
class ArchiveBase {
public:
struct FormatInfo {
u32 size; // Archive size
u32 numOfDirectories; // Number of directories
u32 numOfFiles; // Number of files
bool duplicateData; // Whether to duplicate data or not
};
public:
struct FormatInfo {
u32 size; // Archive size
u32 numOfDirectories; // Number of directories
u32 numOfFiles; // Number of files
bool duplicateData; // Whether to duplicate data or not
};
protected:
using Handle = u32;
protected:
using HandleType = u32;
static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem;
static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem;
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree
// And access files outside of the emulator's app data folder
template <u32 format>
bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = typename String::value_type; // Char type for the path
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory
// tree And access files outside of the emulator's app data folder
template <u32 format>
bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = typename String::value_type; // Char type for the path
String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string;
dots = u"..";
} else {
pathString = path.string;
dots = "..";
}
String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string;
dots = u"..";
} else {
pathString = path.string;
dots = "..";
}
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1);
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1);
if (token == dots) {
level--;
if (level < 0) return false;
} else {
level++;
}
}
if (token == dots) {
level--;
if (level < 0) return false;
} else {
level++;
}
}
return true;
}
return true;
}
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0;
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0;
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
}
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false});
}
virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return Result::FS::AlreadyExists;
}
virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return Result::FS::AlreadyExists;
}
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(Result::FS::FileNotFoundAlt);
}
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(Result::FS::FileNotFoundAlt);
}
virtual void format(const FSPath& path, const FormatInfo& info) {
Helpers::panic("Unimplemented Format for %s archive", name().c_str());
}
virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); }
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str());
return Result::Success;
}
}
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
// Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
// Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
ArchiveBase(Memory& mem) : mem(mem) {}
ArchiveBase(Memory& mem) : mem(mem) {}
};
struct ArchiveResource {
@ -263,4 +260,4 @@ struct ArchiveResource {
u32 clusterSize; // Size of a cluster in bytes
u32 partitionCapacityInClusters;
u32 freeSpaceInClusters;
};
};

View file

@ -1,23 +1,23 @@
#pragma once
#include "helpers.hpp"
using Handle = u32;
using HandleType = u32;
namespace KernelHandles {
enum : u32 {
Max = 0xFFFF7FFF, // Max handle the kernel can automagically allocate
Max = 0xFFFF7FFF, // Max handle the kernel can automagically allocate
// Hardcoded handles
CurrentThread = 0xFFFF8000, // Used by the original kernel
CurrentProcess = 0xFFFF8001, // Used by the original kernel
AC, // Something network related
ACT, // Handles NNID accounts
AM, // Application manager
APT, // App Title something service?
BOSS, // Streetpass stuff?
CAM, // Camera service
CECD, // More Streetpass stuff?
CFG_U, // CFG service (Console & region info)
CurrentThread = 0xFFFF8000, // Used by the original kernel
CurrentProcess = 0xFFFF8001, // Used by the original kernel
AC, // Something network related
ACT, // Handles NNID accounts
AM, // Application manager
APT, // App Title something service?
BOSS, // Streetpass stuff?
CAM, // Camera service
CECD, // More Streetpass stuff?
CFG_U, // CFG service (Console & region info)
CFG_I,
CFG_S, // Used by most system apps in lieu of cfg:u
CSND, // Plays audio directly from PCM samples
@ -50,10 +50,10 @@ namespace KernelHandles {
MinServiceHandle = AC,
MaxServiceHandle = Y2R,
GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory
GSPSharedMemHandle = MaxServiceHandle + 1, // HandleType for the GSP shared memory
FontSharedMemHandle,
CSNDSharedMemHandle,
APTCaptureSharedMemHandle, // Shared memory for display capture info,
APTCaptureSharedMemHandle, // Shared memory for display capture info,
HIDSharedMemHandle,
MinSharedMemHandle = GSPSharedMemHandle,
@ -61,17 +61,13 @@ namespace KernelHandles {
};
// Returns whether "handle" belongs to one of the OS services
static constexpr bool isServiceHandle(Handle handle) {
return handle >= MinServiceHandle && handle <= MaxServiceHandle;
}
static constexpr bool isServiceHandle(HandleType handle) { return handle >= MinServiceHandle && handle <= MaxServiceHandle; }
// Returns whether "handle" belongs to one of the OS services' shared memory areas
static constexpr bool isSharedMemHandle(Handle handle) {
return handle >= MinSharedMemHandle && handle <= MaxSharedMemHandle;
}
static constexpr bool isSharedMemHandle(HandleType handle) { return handle >= MinSharedMemHandle && handle <= MaxSharedMemHandle; }
// Returns the name of a handle as a string based on the given handle
static const char* getServiceName(Handle handle) {
static const char* getServiceName(HandleType handle) {
switch (handle) {
case AC: return "AC";
case ACT: return "ACT";
@ -110,4 +106,4 @@ namespace KernelHandles {
default: return "Unknown";
}
}
}
} // namespace KernelHandles

View file

@ -34,22 +34,22 @@ class Kernel {
static_assert(appResourceLimits.maxThreads <= 63, "The waitlist system is built on the premise that <= 63 threads max can be active");
std::vector<KernelObject> objects;
std::vector<Handle> portHandles;
std::vector<Handle> mutexHandles;
std::vector<Handle> timerHandles;
std::vector<HandleType> portHandles;
std::vector<HandleType> mutexHandles;
std::vector<HandleType> timerHandles;
// Thread indices, sorted by priority
std::vector<int> threadIndices;
Handle currentProcess;
Handle mainThread;
HandleType currentProcess;
HandleType mainThread;
int currentThreadIndex;
Handle srvHandle; // Handle for the special service manager port "srv:"
Handle errorPortHandle; // Handle for the err:f port used for displaying errors
HandleType srvHandle; // HandleType for the special service manager port "srv:"
HandleType errorPortHandle; // HandleType for the err:f port used for displaying errors
u32 arbiterCount;
u32 threadCount; // How many threads in our thread pool have been used as of now (Up to 32)
u32 aliveThreadCount; // How many of these threads are actually alive?
u32 threadCount; // How many threads in our thread pool have been used as of now (Up to 32)
u32 aliveThreadCount; // How many of these threads are actually alive?
ServiceManager serviceManager;
// Top 8 bits are the major version, bottom 8 are the minor version
@ -58,29 +58,29 @@ class Kernel {
// Shows whether a reschedule will be need
bool needReschedule = false;
Handle makeArbiter();
Handle makeProcess(u32 id);
Handle makePort(const char* name);
Handle makeSession(Handle port);
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg,ThreadStatus status = ThreadStatus::Dormant);
Handle makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission);
HandleType makeArbiter();
HandleType makeProcess(u32 id);
HandleType makePort(const char* name);
HandleType makeSession(HandleType port);
HandleType makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg, ThreadStatus status = ThreadStatus::Dormant);
HandleType makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission);
public:
public:
// Needs to be public to be accessible to the APT/HID services
Handle makeEvent(ResetType resetType, Event::CallbackType callback = Event::CallbackType::None);
HandleType makeEvent(ResetType resetType, Event::CallbackType callback = Event::CallbackType::None);
// Needs to be public to be accessible to the APT/DSP services
Handle makeMutex(bool locked = false);
HandleType makeMutex(bool locked = false);
// Needs to be public to be accessible to the service manager port
Handle makeSemaphore(u32 initialCount, u32 maximumCount);
Handle makeTimer(ResetType resetType);
HandleType makeSemaphore(u32 initialCount, u32 maximumCount);
HandleType makeTimer(ResetType resetType);
void pollTimers();
// Signals an event, returns true on success or false if the event does not exist
bool signalEvent(Handle e);
bool signalEvent(HandleType e);
// Run the callback for "special" events that have callbacks
void runEventCallback(Event::CallbackType callback);
void clearEvent(Handle e) {
void clearEvent(HandleType e) {
KernelObject* object = getObject(e, KernelObjectType::Event);
if (object != nullptr) {
object->getData<Event>()->fired = false;
@ -99,19 +99,19 @@ public:
bool shouldWaitOnObject(KernelObject* object);
void releaseMutex(Mutex* moo);
void cancelTimer(Timer* timer);
void signalTimer(Handle timerHandle, Timer* timer);
void signalTimer(HandleType timerHandle, Timer* timer);
u64 getWakeupTick(s64 ns);
// Wake up the thread with the highest priority out of all threads in the waitlist
// Returns the index of the woken up thread
// Do not call this function with an empty waitlist!!!
int wakeupOneThread(u64 waitlist, Handle handle);
void wakeupAllThreads(u64 waitlist, Handle handle);
int wakeupOneThread(u64 waitlist, HandleType handle);
void wakeupAllThreads(u64 waitlist, HandleType handle);
std::optional<Handle> getPortHandle(const char* name);
std::optional<HandleType> getPortHandle(const char* name);
void deleteObjectData(KernelObject& object);
KernelObject* getProcessFromPID(Handle handle);
KernelObject* getProcessFromPID(HandleType handle);
s32 getCurrentResourceValue(const KernelObject* limit, u32 resourceName);
u32 getMaxForResource(const KernelObject* limit, u32 resourceName);
u32 getTLSPointer();
@ -178,22 +178,22 @@ public:
void waitSynchronizationN();
// File operations
void handleFileOperation(u32 messagePointer, Handle file);
void closeFile(u32 messagePointer, Handle file);
void flushFile(u32 messagePointer, Handle file);
void readFile(u32 messagePointer, Handle file);
void writeFile(u32 messagePointer, Handle file);
void getFileSize(u32 messagePointer, Handle file);
void openLinkFile(u32 messagePointer, Handle file);
void setFileSize(u32 messagePointer, Handle file);
void setFilePriority(u32 messagePointer, Handle file);
void handleFileOperation(u32 messagePointer, HandleType file);
void closeFile(u32 messagePointer, HandleType file);
void flushFile(u32 messagePointer, HandleType file);
void readFile(u32 messagePointer, HandleType file);
void writeFile(u32 messagePointer, HandleType file);
void getFileSize(u32 messagePointer, HandleType file);
void openLinkFile(u32 messagePointer, HandleType file);
void setFileSize(u32 messagePointer, HandleType file);
void setFilePriority(u32 messagePointer, HandleType file);
// Directory operations
void handleDirectoryOperation(u32 messagePointer, Handle directory);
void closeDirectory(u32 messagePointer, Handle directory);
void readDirectory(u32 messagePointer, Handle directory);
void handleDirectoryOperation(u32 messagePointer, HandleType directory);
void closeDirectory(u32 messagePointer, HandleType directory);
void readDirectory(u32 messagePointer, HandleType directory);
public:
public:
Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config);
void initializeFS() { return serviceManager.initializeFS(); }
void setVersion(u8 major, u8 minor);
@ -209,7 +209,7 @@ public:
}
}
Handle makeObject(KernelObjectType type) {
HandleType makeObject(KernelObjectType type) {
if (handleCounter > KernelHandles::Max) [[unlikely]] {
Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing");
}
@ -219,12 +219,10 @@ public:
return handleCounter++;
}
std::vector<KernelObject>& getObjects() {
return objects;
}
std::vector<KernelObject>& getObjects() { return objects; }
// Get pointer to the object with the specified handle
KernelObject* getObject(Handle handle) {
KernelObject* getObject(HandleType handle) {
// Accessing an object that has not been created
if (handle >= objects.size()) [[unlikely]] {
return nullptr;
@ -234,7 +232,7 @@ public:
}
// Get pointer to the object with the specified handle and type
KernelObject* getObject(Handle handle, KernelObjectType type) {
KernelObject* getObject(HandleType handle, KernelObjectType type) {
if (handle >= objects.size() || objects[handle].type != type) [[unlikely]] {
return nullptr;
}
@ -246,4 +244,4 @@ public:
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
void clearInstructionCache();
};
};

View file

@ -1,98 +1,102 @@
#pragma once
#include <array>
#include <cstring>
#include "fs/archive_base.hpp"
#include "handles.hpp"
#include "helpers.hpp"
#include "result/result.hpp"
enum class KernelObjectType : u8 {
AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy,
// Bundle waitable objects together in the enum to let the compiler optimize certain checks better
Event, Mutex, Port, Semaphore, Timer, Thread
AddressArbiter,
Archive,
Directory,
File,
MemoryBlock,
Process,
ResourceLimit,
Session,
Dummy,
// Bundle waitable objects together in the enum to let the compiler optimize certain checks better
Event,
Mutex,
Port,
Semaphore,
Timer,
Thread
};
enum class ResourceLimitCategory : int {
Application = 0,
SystemApplet = 1,
LibraryApplet = 2,
Misc = 3
};
enum class ResourceLimitCategory : int { Application = 0, SystemApplet = 1, LibraryApplet = 2, Misc = 3 };
// Reset types (for use with events and timers)
enum class ResetType {
OneShot = 0, // When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically.
Sticky = 1, // When the primitive is signaled, it will wake up all threads and it won't clear itself automatically.
Pulse = 2, // Only meaningful for timers: same as ONESHOT but it will periodically signal the timer instead of just once.
OneShot = 0, // When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically.
Sticky = 1, // When the primitive is signaled, it will wake up all threads and it won't clear itself automatically.
Pulse = 2, // Only meaningful for timers: same as ONESHOT but it will periodically signal the timer instead of just once.
};
enum class ArbitrationType {
Signal = 0,
WaitIfLess = 1,
DecrementAndWaitIfLess = 2,
WaitIfLessTimeout = 3,
DecrementAndWaitIfLessTimeout = 4
};
enum class ArbitrationType { Signal = 0, WaitIfLess = 1, DecrementAndWaitIfLess = 2, WaitIfLessTimeout = 3, DecrementAndWaitIfLessTimeout = 4 };
enum class ProcessorID : s32 {
AllCPUs = -1,
Default = -2,
AppCore = 0,
Syscore = 1,
New3DSExtra1 = 2,
New3DSExtra2 = 3
AllCPUs = -1,
Default = -2,
AppCore = 0,
Syscore = 1,
New3DSExtra1 = 2,
New3DSExtra2 = 3
};
struct AddressArbiter {};
struct ResourceLimits {
Handle handle;
HandleType handle;
s32 currentCommit = 0;
s32 currentCommit = 0;
};
struct Process {
// Resource limits for this process
ResourceLimits limits;
// Process ID
u32 id;
// Resource limits for this process
ResourceLimits limits;
// Process ID
u32 id;
Process(u32 id) : id(id) {}
Process(u32 id) : id(id) {}
};
struct Event {
// Some events (for now, only the DSP semaphore events) need to execute a callback when signalled
// This enum stores what kind of callback they should execute
enum class CallbackType : u32 {
None, DSPSemaphore,
};
// Some events (for now, only the DSP semaphore events) need to execute a callback when signalled
// This enum stores what kind of callback they should execute
enum class CallbackType : u32 {
None,
DSPSemaphore,
};
u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event
ResetType resetType = ResetType::OneShot;
CallbackType callback = CallbackType::None;
bool fired = false;
u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event
ResetType resetType = ResetType::OneShot;
CallbackType callback = CallbackType::None;
bool fired = false;
Event(ResetType resetType) : resetType(resetType), waitlist(0) {}
Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {}
Event(ResetType resetType) : resetType(resetType), waitlist(0) {}
Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {}
};
struct Port {
static constexpr u32 maxNameLen = 11;
static constexpr u32 maxNameLen = 11;
char name[maxNameLen + 1] = {};
bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort.
char name[maxNameLen + 1] = {};
bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort.
Port(const char* name) {
// If the name is empty (ie the first char is the null terminator) then the port is private
isPublic = name[0] != '\0';
std::strncpy(this->name, name, maxNameLen);
}
Port(const char* name) {
// If the name is empty (ie the first char is the null terminator) then the port is private
isPublic = name[0] != '\0';
std::strncpy(this->name, name, maxNameLen);
}
};
struct Session {
Handle portHandle; // The port this session is subscribed to
Session(Handle portHandle) : portHandle(portHandle) {}
HandleType portHandle; // The port this session is subscribed to
Session(HandleType portHandle) : portHandle(portHandle) {}
};
enum class ThreadStatus {
@ -115,14 +119,14 @@ struct Thread {
u32 arg;
ProcessorID processorID;
ThreadStatus status;
Handle handle; // OS handle for this thread
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
HandleType handle; // OS handle for this thread
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
// The waiting address for threads that are waiting on an AddressArbiter
u32 waitingAddress;
// For WaitSynchronization(N): A vector of objects this thread is waiting for
std::vector<Handle> waitList;
std::vector<HandleType> waitList;
// For WaitSynchronizationN: Shows whether the object should wait for all objects in the wait list or just one
bool waitAll;
// For WaitSynchronizationN: The "out" pointer
@ -141,88 +145,86 @@ struct Thread {
};
static const char* kernelObjectTypeToString(KernelObjectType t) {
switch (t) {
case KernelObjectType::AddressArbiter: return "address arbiter";
case KernelObjectType::Archive: return "archive";
case KernelObjectType::Directory: return "directory";
case KernelObjectType::Event: return "event";
case KernelObjectType::File: return "file";
case KernelObjectType::MemoryBlock: return "memory block";
case KernelObjectType::Port: return "port";
case KernelObjectType::Process: return "process";
case KernelObjectType::ResourceLimit: return "resource limit";
case KernelObjectType::Session: return "session";
case KernelObjectType::Mutex: return "mutex";
case KernelObjectType::Semaphore: return "semaphore";
case KernelObjectType::Thread: return "thread";
case KernelObjectType::Dummy: return "dummy";
default: return "unknown";
}
switch (t) {
case KernelObjectType::AddressArbiter: return "address arbiter";
case KernelObjectType::Archive: return "archive";
case KernelObjectType::Directory: return "directory";
case KernelObjectType::Event: return "event";
case KernelObjectType::File: return "file";
case KernelObjectType::MemoryBlock: return "memory block";
case KernelObjectType::Port: return "port";
case KernelObjectType::Process: return "process";
case KernelObjectType::ResourceLimit: return "resource limit";
case KernelObjectType::Session: return "session";
case KernelObjectType::Mutex: return "mutex";
case KernelObjectType::Semaphore: return "semaphore";
case KernelObjectType::Thread: return "thread";
case KernelObjectType::Dummy: return "dummy";
default: return "unknown";
}
}
struct Mutex {
u64 waitlist; // Refer to the getWaitlist function below for documentation
Handle ownerThread = 0; // Index of the thread that holds the mutex if it's locked
Handle handle; // Handle of the mutex itself
u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked
bool locked;
u64 waitlist; // Refer to the getWaitlist function below for documentation
HandleType ownerThread = 0; // Index of the thread that holds the mutex if it's locked
HandleType handle; // HandleType of the mutex itself
u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked
bool locked;
Mutex(bool lock, Handle handle) : locked(lock), waitlist(0), lockCount(lock ? 1 : 0), handle(handle) {}
Mutex(bool lock, HandleType handle) : locked(lock), waitlist(0), lockCount(lock ? 1 : 0), handle(handle) {}
};
struct Semaphore {
u64 waitlist; // Refer to the getWaitlist function below for documentation
s32 availableCount;
s32 maximumCount;
u64 waitlist; // Refer to the getWaitlist function below for documentation
s32 availableCount;
s32 maximumCount;
Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {}
Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {}
};
struct Timer {
u64 waitlist; // Refer to the getWaitlist function below for documentation
ResetType resetType = ResetType::OneShot;
u64 fireTick; // CPU tick the timer will be fired
u64 interval; // Number of ns until the timer fires for the second and future times
bool fired; // Has this timer been signalled?
bool running; // Is this timer running or stopped?
u64 fireTick; // CPU tick the timer will be fired
u64 interval; // Number of ns until the timer fires for the second and future times
bool fired; // Has this timer been signalled?
bool running; // Is this timer running or stopped?
Timer(ResetType type) : resetType(type), fireTick(0), interval(0), waitlist(0), fired(false), running(false) {}
};
struct MemoryBlock {
u32 addr = 0;
u32 size = 0;
u32 myPermission = 0;
u32 otherPermission = 0;
bool mapped = false;
u32 addr = 0;
u32 size = 0;
u32 myPermission = 0;
u32 otherPermission = 0;
bool mapped = false;
MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm) : addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm),
mapped(false) {}
MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm)
: addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm), mapped(false) {}
};
// Generic kernel object class
struct KernelObject {
Handle handle = 0; // A u32 the OS will use to identify objects
void* data = nullptr;
KernelObjectType type;
HandleType handle = 0; // A u32 the OS will use to identify objects
void* data = nullptr;
KernelObjectType type;
KernelObject(Handle handle, KernelObjectType type) : handle(handle), type(type) {}
KernelObject(HandleType handle, KernelObjectType type) : handle(handle), type(type) {}
// Our destructor does not free the data in order to avoid it being freed when our std::vector is expanded
// Thus, the kernel needs to delete it when appropriate
~KernelObject() {}
// Our destructor does not free the data in order to avoid it being freed when our std::vector is expanded
// Thus, the kernel needs to delete it when appropriate
~KernelObject() {}
template <typename T>
T* getData() {
return static_cast<T*>(data);
}
template <typename T>
T* getData() {
return static_cast<T*>(data);
}
const char* getTypeName() const {
return kernelObjectTypeToString(type);
}
const char* getTypeName() const { return kernelObjectTypeToString(type); }
// Retrieves a reference to the waitlist for a specified object
// Retrieves a reference to the waitlist for a specified object
// We return a reference because this function is only called in the kernel threading internals
// We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits.
// As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient.
@ -238,8 +240,7 @@ struct KernelObject {
case KernelObjectType::Timer: return getData<Timer>()->waitlist;
// This should be unreachable once we fully implement sync objects
default: [[unlikely]]
Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
default: [[unlikely]] Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
}
}
};
};

View file

@ -10,8 +10,8 @@
#include "crypto/aes_engine.hpp"
#include "handles.hpp"
#include "helpers.hpp"
#include "loader/ncsd.hpp"
#include "loader/3dsx.hpp"
#include "loader/ncsd.hpp"
#include "services/region_codes.hpp"
namespace PhysicalAddrs {
@ -38,7 +38,7 @@ namespace VirtualAddrs {
DefaultStackSize = 0x4000,
NormalHeapStart = 0x08000000,
LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C
LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C
LinearHeapEndOld = 0x1C000000,
LinearHeapStartNew = 0x30000000,
@ -76,37 +76,36 @@ namespace KernelMemoryTypes {
PERMISSION_W = 1 << 1,
PERMISSION_X = 1 << 2
};
// I assume this is referring to a single piece of allocated memory? If it's for pages, it makes no sense.
// If it's for multiple allocations, it also makes no sense
struct MemoryInfo {
u32 baseAddr; // Base process virtual address. Used as a paddr in lockedMemoryInfo instead
u32 baseAddr; // Base process virtual address. Used as a paddr in lockedMemoryInfo instead
u32 size; // Of what?
u32 perms; // Is this referring to a single page or?
u32 state;
u32 end() { return baseAddr + size; }
MemoryInfo(u32 baseAddr, u32 size, u32 perms, u32 state) : baseAddr(baseAddr), size(size)
, perms(perms), state(state) {}
MemoryInfo(u32 baseAddr, u32 size, u32 perms, u32 state) : baseAddr(baseAddr), size(size), perms(perms), state(state) {}
};
// Shared memory block for HID, GSP:GPU etc
struct SharedMemoryBlock {
u32 paddr; // Physical address of this block's memory
u32 size; // Size of block
u32 handle; // The handle of the shared memory block
bool mapped; // Has this block been mapped at least once?
u32 paddr; // Physical address of this block's memory
u32 size; // Size of block
u32 handle; // The handle of the shared memory block
bool mapped; // Has this block been mapped at least once?
SharedMemoryBlock(u32 paddr, u32 size, u32 handle) : paddr(paddr), size(size), handle(handle), mapped(false) {}
};
}
} // namespace KernelMemoryTypes
class Memory {
u8* fcram;
u8* dspRam; // Provided to us by Audio
u8* vram; // Provided to the memory class by the GPU class
u64& cpuTicks; // Reference to the CPU tick counter
u64& cpuTicks; // Reference to the CPU tick counter
using SharedMemoryBlock = KernelMemoryTypes::SharedMemoryBlock;
// Our dynarmic core uses page tables for reads and writes with 4096 byte pages
@ -116,19 +115,21 @@ class Memory {
std::vector<KernelMemoryTypes::MemoryInfo> memoryInfo;
std::array<SharedMemoryBlock, 5> sharedMemBlocks = {
SharedMemoryBlock(0, 0, KernelHandles::FontSharedMemHandle), // Shared memory for the system font (size is 0 because we read the size from the cmrc filesystem
SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory
SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle), // HID shared memory
SharedMemoryBlock(0, 0x3000, KernelHandles::CSNDSharedMemHandle), // CSND shared memory
SharedMemoryBlock(
0, 0, KernelHandles::FontSharedMemHandle
), // Shared memory for the system font (size is 0 because we read the size from the cmrc filesystem
SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory
SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle), // HID shared memory
SharedMemoryBlock(0, 0x3000, KernelHandles::CSNDSharedMemHandle), // CSND shared memory
SharedMemoryBlock(0, 0xE7000, KernelHandles::APTCaptureSharedMemHandle), // APT Capture Buffer memory
};
};
public:
public:
static constexpr u32 pageShift = 12;
static constexpr u32 pageSize = 1 << pageShift;
static constexpr u32 pageMask = pageSize - 1;
static constexpr u32 totalPageCount = 1 << (32 - pageShift);
static constexpr u32 FCRAM_SIZE = u32(128_MB);
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(64_MB);
static constexpr u32 FCRAM_PAGE_COUNT = FCRAM_SIZE / pageSize;
@ -138,7 +139,7 @@ public:
static constexpr u32 DSP_CODE_MEMORY_OFFSET = u32(0_KB);
static constexpr u32 DSP_DATA_MEMORY_OFFSET = u32(256_KB);
private:
private:
std::bitset<FCRAM_PAGE_COUNT> usedFCRAMPages;
std::optional<u32> findPaddr(u32 size);
u64 timeSince3DSEpoch();
@ -147,9 +148,9 @@ private:
// Report a retail unit without JTAG
static constexpr u32 envInfo = 1;
// Stored in Configuration Memory starting @ 0x1FF80060
// Stored in Configuration Memory starting @ 0x1FF80060
struct FirmwareInfo {
u8 unk; // Usually 0 according to 3DBrew
u8 unk; // Usually 0 according to 3DBrew
u8 revision;
u8 minor;
u8 major;
@ -167,8 +168,8 @@ private:
public:
u16 kernelVersion = 0;
u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore)
u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore)
u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore)
u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore)
Memory(u64& cpuTicks, const EmulatorConfig& config);
void reset();
@ -197,28 +198,20 @@ private:
u8* getFCRAM() { return fcram; }
// Total amount of OS-only FCRAM available (Can vary depending on how much FCRAM the app requests via the cart exheader)
u32 totalSysFCRAM() {
return FCRAM_SIZE - FCRAM_APPLICATION_SIZE;
}
u32 totalSysFCRAM() { return FCRAM_SIZE - FCRAM_APPLICATION_SIZE; }
// Amount of OS-only FCRAM currently available
u32 remainingSysFCRAM() {
return totalSysFCRAM() - usedSystemMemory;
}
u32 remainingSysFCRAM() { return totalSysFCRAM() - usedSystemMemory; }
// Physical FCRAM index to the start of OS FCRAM
// We allocate the first part of physical FCRAM for the application, and the rest to the OS. So the index for the OS = application ram size
u32 sysFCRAMIndex() {
return FCRAM_APPLICATION_SIZE;
}
u32 sysFCRAMIndex() { return FCRAM_APPLICATION_SIZE; }
enum class BatteryLevel {
Empty = 0, AlmostEmpty, OneBar, TwoBars, ThreeBars, FourBars
};
enum class BatteryLevel { Empty = 0, AlmostEmpty, OneBar, TwoBars, ThreeBars, FourBars };
u8 getBatteryState(bool adapterConnected, bool charging, BatteryLevel batteryLevel) {
u8 value = static_cast<u8>(batteryLevel) << 2; // Bits 2:4 are the battery level from 0 to 5
if (adapterConnected) value |= 1 << 0; // Bit 0 shows if the charger is connected
if (charging) value |= 1 << 1; // Bit 1 shows if we're charging
u8 value = static_cast<u8>(batteryLevel) << 2; // Bits 2:4 are the battery level from 0 to 5
if (adapterConnected) value |= 1 << 0; // Bit 0 shows if the charger is connected
if (charging) value |= 1 << 1; // Bit 1 shows if we're charging
return value;
}
@ -240,9 +233,7 @@ private:
}
// Returns whether "addr" is aligned to a page (4096 byte) boundary
static constexpr bool isAligned(u32 addr) {
return (addr & pageMask) == 0;
}
static constexpr bool isAligned(u32 addr) { return (addr & pageMask) == 0; }
// Allocate "size" bytes of RAM starting from FCRAM index "paddr" (We pick it ourself if paddr == 0)
// And map them to virtual address "vaddr" (We also pick it ourself if vaddr == 0).
@ -253,8 +244,9 @@ private:
// isMap: Shows whether this is a reserve operation, that allocates memory and maps it to the addr space, or if it's a map operation,
// which just maps memory from paddr to vaddr without hassle. The latter is useful for shared memory mapping, the "map" ControlMemory, op, etc
// Returns the vaddr the FCRAM was mapped to or nullopt if allocation failed
std::optional<u32> allocateMemory(u32 vaddr, u32 paddr, u32 size, bool linear, bool r = true, bool w = true, bool x = true,
bool adjustsAddrs = false, bool isMap = false);
std::optional<u32> allocateMemory(
u32 vaddr, u32 paddr, u32 size, bool linear, bool r = true, bool w = true, bool x = true, bool adjustsAddrs = false, bool isMap = false
);
KernelMemoryTypes::MemoryInfo queryMemory(u32 vaddr);
// For internal use
@ -266,7 +258,7 @@ private:
// The kernel has a second permission parameter in MapMemoryBlock but not sure what's used for
// TODO: Find out
// Returns a pointer to the FCRAM block used for the memory if allocation succeeded
u8* mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerms);
u8* mapSharedMemory(HandleType handle, u32 vaddr, u32 myPerms, u32 otherPerms);
// Mirrors the page mapping for "size" bytes starting from sourceAddress, to "size" bytes in destAddress
// All of the above must be page-aligned.

View file

@ -119,9 +119,10 @@ class MainWindow : public QMainWindow {
void sendMessage(const EmulatorMessage& message);
void dispatchMessage(const EmulatorMessage& message);
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
// Tracks whether we are using an OpenGL-backed renderer, a Vulkan-backed renderer or a Metal-backed renderer
bool usingGL = false;
bool usingVk = false;
bool usingMtl = false;
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
// This is done so when a gamepad is connected, we won't automatically override the 3DS analog stick settings with the gamepad's state

View file

@ -1,7 +1,7 @@
#pragma once
#include <array>
#include <span>
#include <optional>
#include <span>
#include "PICA/pica_vertex.hpp"
#include "PICA/regs.hpp"
@ -17,6 +17,7 @@ enum class RendererType : s8 {
OpenGL = 1,
Vulkan = 2,
Software = 3,
Metal = 4,
};
class GPU;

View file

@ -0,0 +1,32 @@
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>
#include "renderer.hpp"
class GPU;
class RendererMTL final : public Renderer {
public:
RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
~RendererMTL() override;
void reset() override;
void display() override;
void initGraphicsContext(SDL_Window* window) override;
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override;
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override;
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
void screenshot(const std::string& name) override;
void deinitGraphicsContext() override;
#ifdef PANDA3DS_FRONTEND_QT
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
#endif
private:
CA::MetalLayer* metalLayer;
MTL::Device* device;
MTL::CommandQueue* commandQueue;
};

View file

@ -8,7 +8,7 @@
#include "result/result.hpp"
class ACService {
Handle handle = KernelHandles::AC;
HandleType handle = KernelHandles::AC;
Memory& mem;
MAKE_LOG_FUNCTION(log, acLogger)
@ -25,10 +25,10 @@ class ACService {
void setClientVersion(u32 messagePointer);
bool connected = false;
std::optional<Handle> disconnectEvent = std::nullopt;
std::optional<HandleType> disconnectEvent = std::nullopt;
public:
ACService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -6,7 +6,7 @@
#include "result/result.hpp"
class ACTService {
Handle handle = KernelHandles::ACT;
HandleType handle = KernelHandles::ACT;
Memory& mem;
MAKE_LOG_FUNCTION(log, actLogger)
@ -15,8 +15,8 @@ class ACTService {
void generateUUID(u32 messagePointer);
void getAccountDataBlock(u32 messagePointer);
public:
public:
ACTService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -6,7 +6,7 @@
#include "result/result.hpp"
class AMService {
Handle handle = KernelHandles::AM;
HandleType handle = KernelHandles::AM;
Memory& mem;
MAKE_LOG_FUNCTION(log, amLogger)
@ -15,8 +15,8 @@ class AMService {
void getPatchTitleInfo(u32 messagePointer);
void listTitleInfo(u32 messagePointer);
public:
public:
AMService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -1,19 +1,17 @@
#pragma once
#include <optional>
#include "applets/applet_manager.hpp"
#include "helpers.hpp"
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
#include "applets/applet_manager.hpp"
// Yay, more circular dependencies
class Kernel;
enum class ConsoleModel : u32 {
Old3DS, New3DS
};
enum class ConsoleModel : u32 { Old3DS, New3DS };
// https://www.3dbrew.org/wiki/NS_and_APT_Services#Command
namespace APT::Transitions {
@ -41,13 +39,13 @@ namespace APT::Transitions {
}
class APTService {
Handle handle = KernelHandles::APT;
HandleType handle = KernelHandles::APT;
Memory& mem;
Kernel& kernel;
std::optional<Handle> lockHandle = std::nullopt;
std::optional<Handle> notificationEvent = std::nullopt;
std::optional<Handle> resumeEvent = std::nullopt;
std::optional<HandleType> lockHandle = std::nullopt;
std::optional<HandleType> notificationEvent = std::nullopt;
std::optional<HandleType> resumeEvent = std::nullopt;
ConsoleModel model = ConsoleModel::Old3DS;
Applets::AppletManager appletManager;
@ -99,8 +97,8 @@ class APTService {
u32 screencapPostPermission;
public:
public:
APTService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel), appletManager(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -6,7 +6,7 @@
#include "result/result.hpp"
class BOSSService {
Handle handle = KernelHandles::BOSS;
HandleType handle = KernelHandles::BOSS;
Memory& mem;
MAKE_LOG_FUNCTION(log, bossLogger)
@ -17,7 +17,7 @@ class BOSSService {
void getNewArrivalFlag(u32 messagePointer);
void getNsDataIdList(u32 messagePointer, u32 commandWord);
void getOptoutFlag(u32 messagePointer);
void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra
void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra
void getTaskIdList(u32 messagePointer);
void getTaskInfo(u32 messagePointer);
void getTaskServiceStatus(u32 messagePointer);
@ -35,8 +35,9 @@ class BOSSService {
void unregisterTask(u32 messagePointer);
s8 optoutFlag;
public:
public:
BOSSService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -12,7 +12,7 @@
class Kernel;
class CAMService {
using Event = std::optional<Handle>;
using Event = std::optional<HandleType>;
struct Port {
Event bufferErrorInterruptEvent = std::nullopt;
@ -26,7 +26,7 @@ class CAMService {
}
};
Handle handle = KernelHandles::CAM;
HandleType handle = KernelHandles::CAM;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, camLogger)
@ -56,4 +56,4 @@ class CAMService {
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -1,5 +1,6 @@
#pragma once
#include <optional>
#include "helpers.hpp"
#include "kernel_types.hpp"
#include "logger.hpp"
@ -9,19 +10,19 @@
class Kernel;
class CECDService {
Handle handle = KernelHandles::CECD;
HandleType handle = KernelHandles::CECD;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, cecdLogger)
std::optional<Handle> infoEvent;
std::optional<HandleType> infoEvent;
// Service commands
void getInfoEventHandle(u32 messagePointer);
void openAndRead(u32 messagePointer);
public:
public:
CECDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -10,13 +10,13 @@
class Kernel;
class CSNDService {
Handle handle = KernelHandles::CSND;
HandleType handle = KernelHandles::CSND;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, csndLogger)
u8* sharedMemory = nullptr;
std::optional<Handle> csndMutex = std::nullopt;
std::optional<HandleType> csndMutex = std::nullopt;
size_t sharedMemSize = 0;
bool initialized = false;
@ -30,7 +30,5 @@ class CSNDService {
void reset();
void handleSyncRequest(u32 messagePointer);
void setSharedMemory(u8* ptr) {
sharedMemory = ptr;
}
};
void setSharedMemory(u8* ptr) { sharedMemory = ptr; }
};

View file

@ -8,15 +8,15 @@
// Please forgive me for how everything in this file is named
// "dlp:SRVR" is not a nice name to work with
class DlpSrvrService {
Handle handle = KernelHandles::DLP_SRVR;
HandleType handle = KernelHandles::DLP_SRVR;
Memory& mem;
MAKE_LOG_FUNCTION(log, dlpSrvrLogger)
// Service commands
void isChild(u32 messagePointer);
public:
public:
DlpSrvrService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -14,7 +14,7 @@
class Kernel;
class DSPService {
Handle handle = KernelHandles::DSP;
HandleType handle = KernelHandles::DSP;
Memory& mem;
Kernel& kernel;
Audio::DSPCore* dsp = nullptr;
@ -24,7 +24,7 @@ class DSPService {
static constexpr size_t pipeCount = 8;
// DSP service event handles
using DSPEvent = std::optional<Handle>;
using DSPEvent = std::optional<HandleType>;
DSPEvent semaphoreEvent;
DSPEvent interrupt0;
@ -82,4 +82,4 @@ class DSPService {
void triggerInterrupt1();
ComponentDumpResult dumpComponent(const std::filesystem::path& path);
};
};

View file

@ -16,7 +16,7 @@
class Kernel;
class FSService {
Handle handle = KernelHandles::FS;
HandleType handle = KernelHandles::FS;
Memory& mem;
Kernel& kernel;
@ -38,9 +38,9 @@ class FSService {
SystemSaveDataArchive systemSaveData;
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path);
Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
Rust::Result<HandleType, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path);
Rust::Result<HandleType, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
std::optional<HandleType> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
FSPath readPath(u32 type, u32 pointer, u32 size);
const EmulatorConfig& config;
@ -81,7 +81,7 @@ class FSService {
// Used for set/get priority: Not sure what sort of priority this is referring to
u32 priority;
public:
public:
FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config)
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem),
sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1),
@ -91,4 +91,4 @@ public:
void handleSyncRequest(u32 messagePointer);
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.
void initializeFilesystem();
};
};

View file

@ -1,6 +1,7 @@
#pragma once
#include <cstring>
#include <optional>
#include "PICA/gpu.hpp"
#include "helpers.hpp"
#include "kernel_types.hpp"
@ -9,12 +10,12 @@
#include "result/result.hpp"
enum class GPUInterrupt : u8 {
PSC0 = 0, // Memory fill completed
PSC1 = 1, // ?
VBlank0 = 2, // ?
VBlank1 = 3, // ?
PPF = 4, // Display transfer finished
P3D = 5, // Command list processing finished
PSC0 = 0, // Memory fill completed
PSC1 = 1, // ?
VBlank0 = 2, // ?
VBlank1 = 3, // ?
PPF = 4, // Display transfer finished
P3D = 5, // Command list processing finished
DMA = 6
};
@ -22,17 +23,17 @@ enum class GPUInterrupt : u8 {
class Kernel;
class GPUService {
Handle handle = KernelHandles::GPU;
HandleType handle = KernelHandles::GPU;
Memory& mem;
GPU& gpu;
Kernel& kernel;
u32& currentPID; // Process ID of the current process
u8* sharedMem; // Pointer to GSP shared memory
u32& currentPID; // Process ID of the current process
u8* sharedMem; // Pointer to GSP shared memory
// At any point in time only 1 process has privileges to use rendering functions
// This is the PID of that process
u32 privilegedProcess;
std::optional<Handle> interruptEvent;
std::optional<HandleType> interruptEvent;
// Number of threads registered via RegisterInterruptRelayQueue
u32 gspThreadCount = 0;
@ -62,8 +63,8 @@ class GPUService {
// Used for saving and restoring GPU state via ImportDisplayCaptureInfo
struct CaptureInfo {
u32 leftFramebuffer; // Left framebuffer VA
u32 rightFramebuffer; // Right framebuffer VA (Top screen only)
u32 leftFramebuffer; // Left framebuffer VA
u32 rightFramebuffer; // Right framebuffer VA (Top screen only)
u32 format;
u32 stride;
};
@ -106,15 +107,14 @@ class GPUService {
FramebufferUpdate* getTopFramebufferInfo() { return getFramebufferInfo(0); }
FramebufferUpdate* getBottomFramebufferInfo() { return getFramebufferInfo(1); }
public:
GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu),
kernel(kernel), currentPID(currentPID) {}
public:
GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu), kernel(kernel), currentPID(currentPID) {}
void reset();
void handleSyncRequest(u32 messagePointer);
void requestInterrupt(GPUInterrupt type);
void setSharedMem(u8* ptr) {
sharedMem = ptr;
if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa
if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa
std::memset(ptr, 0, 0x1000);
}
}

View file

@ -6,14 +6,14 @@
#include "result/result.hpp"
class LCDService {
Handle handle = KernelHandles::LCD;
HandleType handle = KernelHandles::LCD;
Memory& mem;
MAKE_LOG_FUNCTION(log, gspLCDLogger)
// Service commands
public:
public:
LCDService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -38,7 +38,7 @@ namespace HID::Keys {
class Kernel;
class HIDService {
Handle handle = KernelHandles::HID;
HandleType handle = KernelHandles::HID;
Memory& mem;
Kernel& kernel;
u8* sharedMem = nullptr; // Pointer to HID shared memory
@ -60,7 +60,7 @@ class HIDService {
bool gyroEnabled;
bool touchScreenPressed;
std::array<std::optional<Handle>, 5> events;
std::array<std::optional<HandleType>, 5> events;
MAKE_LOG_FUNCTION(log, hidLogger)
@ -141,9 +141,7 @@ class HIDService {
touchScreenPressed = true;
}
void releaseTouchScreen() {
touchScreenPressed = false;
}
void releaseTouchScreen() { touchScreenPressed = false; }
bool isTouchScreenPressed() { return touchScreenPressed; }
};

View file

@ -5,7 +5,7 @@
#include "memory.hpp"
class HTTPService {
Handle handle = KernelHandles::HTTP;
HandleType handle = KernelHandles::HTTP;
Memory& mem;
MAKE_LOG_FUNCTION(log, httpLogger)
@ -20,4 +20,4 @@ class HTTPService {
HTTPService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -15,7 +15,7 @@ class IRUserService {
CirclePadPro = 1,
};
Handle handle = KernelHandles::IR_USER;
HandleType handle = KernelHandles::IR_USER;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, irUserLogger)
@ -29,7 +29,7 @@ class IRUserService {
void requireConnection(u32 messagePointer);
void sendIrnop(u32 messagePointer);
using IREvent = std::optional<Handle>;
using IREvent = std::optional<HandleType>;
IREvent connectionStatusEvent = std::nullopt;
IREvent receiveEvent = std::nullopt;
@ -58,4 +58,4 @@ class IRUserService {
IRUserService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -8,7 +8,7 @@
class Kernel;
class LDRService {
Handle handle = KernelHandles::LDR_RO;
HandleType handle = KernelHandles::LDR_RO;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, ldrLogger)
@ -22,8 +22,8 @@ class LDRService {
void loadCRR(u32 messagePointer);
void unloadCRO(u32 messagePointer);
public:
public:
LDRService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -7,7 +7,7 @@
namespace MCU {
class HWCService {
Handle handle = KernelHandles::MCU_HWC;
HandleType handle = KernelHandles::MCU_HWC;
Memory& mem;
MAKE_LOG_FUNCTION(log, mcuLogger)
@ -21,4 +21,4 @@ namespace MCU {
void reset();
void handleSyncRequest(u32 messagePointer);
};
} // namespace MCU
} // namespace MCU

View file

@ -9,7 +9,7 @@
class Kernel;
class MICService {
Handle handle = KernelHandles::MIC;
HandleType handle = KernelHandles::MIC;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, micLogger)
@ -29,15 +29,15 @@ class MICService {
void unmapSharedMem(u32 messagePointer);
void theCaptainToadFunction(u32 messagePointer);
u8 gain = 0; // How loud our microphone input signal is
u8 gain = 0; // How loud our microphone input signal is
bool micEnabled = false;
bool shouldClamp = false;
bool currentlySampling = false;
std::optional<Handle> eventHandle;
std::optional<HandleType> eventHandle;
public:
public:
MICService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -8,7 +8,7 @@
class NDMService {
enum class ExclusiveState : u32 { None = 0, Infrastructure = 1, LocalComms = 2, StreetPass = 3, StreetPassData = 4 };
Handle handle = KernelHandles::NDM;
HandleType handle = KernelHandles::NDM;
Memory& mem;
MAKE_LOG_FUNCTION(log, ndmLogger)
@ -25,8 +25,8 @@ class NDMService {
ExclusiveState exclusiveState = ExclusiveState::None;
public:
public:
NDMService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -5,7 +5,7 @@
#include "memory.hpp"
class NewsUService {
Handle handle = KernelHandles::NEWS_U;
HandleType handle = KernelHandles::NEWS_U;
Memory& mem;
MAKE_LOG_FUNCTION(log, newsLogger)
@ -15,4 +15,4 @@ class NewsUService {
NewsUService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -12,7 +12,7 @@
class Kernel;
class NFCService {
Handle handle = KernelHandles::NFC;
HandleType handle = KernelHandles::NFC;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, nfcLogger)
@ -34,7 +34,7 @@ class NFCService {
};
// Kernel events signaled when an NFC tag goes in and out of range respectively
std::optional<Handle> tagInRangeEvent, tagOutOfRangeEvent;
std::optional<HandleType> tagInRangeEvent, tagOutOfRangeEvent;
AmiiboDevice device;
Old3DSAdapterStatus adapterStatus;
@ -63,4 +63,4 @@ class NFCService {
void handleSyncRequest(u32 messagePointer);
bool loadAmiibo(const std::filesystem::path& path);
};
};

View file

@ -6,15 +6,15 @@
#include "result/result.hpp"
class NIMService {
Handle handle = KernelHandles::NIM;
HandleType handle = KernelHandles::NIM;
Memory& mem;
MAKE_LOG_FUNCTION(log, nimLogger)
// Service commands
void initialize(u32 messagePointer);
public:
public:
NIMService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -10,13 +10,13 @@
class Kernel;
class NwmUdsService {
Handle handle = KernelHandles::NWM_UDS;
HandleType handle = KernelHandles::NWM_UDS;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, nwmUdsLogger)
bool initialized = false;
std::optional<Handle> eventHandle = std::nullopt;
std::optional<HandleType> eventHandle = std::nullopt;
// Service commands
void initializeWithVersion(u32 messagePointer);
@ -25,4 +25,4 @@ class NwmUdsService {
NwmUdsService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -28,10 +28,10 @@
#include "services/mcu/mcu_hwc.hpp"
#include "services/mic.hpp"
#include "services/ndm.hpp"
#include "services/nwm_uds.hpp"
#include "services/news_u.hpp"
#include "services/nfc.hpp"
#include "services/nim.hpp"
#include "services/nwm_uds.hpp"
#include "services/ptm.hpp"
#include "services/soc.hpp"
#include "services/ssl.hpp"
@ -46,15 +46,15 @@ class ServiceManager {
Memory& mem;
Kernel& kernel;
std::optional<Handle> notificationSemaphore;
std::optional<HandleType> notificationSemaphore;
MAKE_LOG_FUNCTION(log, srvLogger)
ACService ac;
ACService ac;
ACTService act;
AMService am;
AMService am;
APTService apt;
BOSSService boss;
BOSSService boss;
CAMService cam;
CECDService cecd;
CFGService cfg;
@ -74,7 +74,7 @@ class ServiceManager {
NewsUService news_u;
NFCService nfc;
NwmUdsService nwm_uds;
NIMService nim;
NIMService nim;
PTMService ptm;
SOCService soc;
SSLService ssl;
@ -97,7 +97,7 @@ class ServiceManager {
void handleSyncRequest(u32 messagePointer);
// Forward a SendSyncRequest IPC message to the service with the respective handle
void sendCommandToService(u32 messagePointer, Handle handle);
void sendCommandToService(u32 messagePointer, HandleType handle);
// Wrappers for communicating with certain services
void sendGPUInterrupt(GPUInterrupt type) { gsp_gpu.requestInterrupt(type); }

View file

@ -5,7 +5,7 @@
#include "memory.hpp"
class SOCService {
Handle handle = KernelHandles::SOC;
HandleType handle = KernelHandles::SOC;
Memory& mem;
MAKE_LOG_FUNCTION(log, socLogger)
@ -14,8 +14,8 @@ class SOCService {
// Service commands
void initializeSockets(u32 messagePointer);
public:
public:
SOCService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -1,17 +1,17 @@
#pragma once
#include <random>
#include "helpers.hpp"
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include <random>
class SSLService {
Handle handle = KernelHandles::SSL;
HandleType handle = KernelHandles::SSL;
Memory& mem;
MAKE_LOG_FUNCTION(log, sslLogger)
std::mt19937 rng; // Use a Mersenne Twister for RNG since this service is supposed to have better rng than just rand()
std::mt19937 rng; // Use a Mersenne Twister for RNG since this service is supposed to have better rng than just rand()
bool initialized;
// Service commands
@ -22,4 +22,4 @@ class SSLService {
SSLService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};

View file

@ -1,6 +1,7 @@
#pragma once
#include <array>
#include <optional>
#include "helpers.hpp"
#include "kernel_types.hpp"
#include "logger.hpp"
@ -10,18 +11,15 @@
class Kernel;
class Y2RService {
Handle handle = KernelHandles::Y2R;
HandleType handle = KernelHandles::Y2R;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, y2rLogger)
std::optional<Handle> transferEndEvent;
std::optional<HandleType> transferEndEvent;
bool transferEndInterruptEnabled;
enum class BusyStatus : u32 {
NotBusy = 0,
Busy = 1
};
enum class BusyStatus : u32 { NotBusy = 0, Busy = 1 };
enum class InputFormat : u32 {
YUV422_Individual8 = 0,
@ -31,24 +29,14 @@ class Y2RService {
YUV422_Batch = 4,
};
enum class OutputFormat : u32 {
RGB32 = 0,
RGB24 = 1,
RGB15 = 2,
RGB565 = 3
};
enum class OutputFormat : u32 { RGB32 = 0, RGB24 = 1, RGB15 = 2, RGB565 = 3 };
// Clockwise rotation
enum class Rotation : u32 {
None = 0,
Rotate90 = 1,
Rotate180 = 2,
Rotate270 = 3
};
enum class Rotation : u32 { None = 0, Rotate90 = 1, Rotate180 = 2, Rotate270 = 3 };
enum class BlockAlignment : u32 {
Line = 0, // Output buffer's pixels are arranged linearly. Used when outputting to the framebuffer.
Block8x8 = 1, // Output buffer's pixels are morton swizzled. Used when outputting to a GPU texture.
Line = 0, // Output buffer's pixels are arranged linearly. Used when outputting to the framebuffer.
Block8x8 = 1, // Output buffer's pixels are morton swizzled. Used when outputting to a GPU texture.
};
// https://github.com/citra-emu/citra/blob/ac9d72a95ca9a60de8d39484a14aecf489d6d016/src/core/hle/service/cam/y2r_u.cpp#L33
@ -60,7 +48,7 @@ class Y2RService {
{{0x12A, 0x1CA, 0x88, 0x36, 0x21C, -0x1F04, 0x99C, -0x2421}}, // ITU_Rec709_Scaling
}};
CoefficientSet conversionCoefficients; // Current conversion coefficients
CoefficientSet conversionCoefficients; // Current conversion coefficients
InputFormat inputFmt;
OutputFormat outputFmt;
@ -113,8 +101,8 @@ class Y2RService {
void startConversion(u32 messagePointer);
void stopConversion(u32 messagePointer);
public:
public:
Y2RService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
};
};