mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-20 04:29:13 +12:00
Merge branch 'master' into timerz
This commit is contained in:
commit
1d601e41ba
63 changed files with 871 additions and 262282 deletions
|
@ -1,5 +1,6 @@
|
|||
#include "config.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
|
@ -57,6 +58,29 @@ void EmulatorConfig::load(const std::filesystem::path& path) {
|
|||
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", true);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.contains("Battery")) {
|
||||
auto batteryResult = toml::expect<toml::value>(data.at("Battery"));
|
||||
if (batteryResult.is_ok()) {
|
||||
auto battery = batteryResult.unwrap();
|
||||
|
||||
chargerPlugged = toml::find_or<toml::boolean>(battery, "ChargerPlugged", true);
|
||||
batteryPercentage = toml::find_or<toml::integer>(battery, "BatteryPercentage", 3);
|
||||
|
||||
// Clamp battery % to [0, 100] to make sure it's a valid value
|
||||
batteryPercentage = std::clamp(batteryPercentage, 0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.contains("SD")) {
|
||||
auto sdResult = toml::expect<toml::value>(data.at("SD"));
|
||||
if (sdResult.is_ok()) {
|
||||
auto sd = sdResult.unwrap();
|
||||
|
||||
sdCardInserted = toml::find_or<toml::boolean>(sd, "UseVirtualSD", true);
|
||||
sdWriteProtected = toml::find_or<toml::boolean>(sd, "WriteProtectVirtualSD", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatorConfig::save(const std::filesystem::path& path) {
|
||||
|
@ -81,6 +105,12 @@ void EmulatorConfig::save(const std::filesystem::path& path) {
|
|||
data["GPU"]["EnableShaderJIT"] = shaderJitEnabled;
|
||||
data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType));
|
||||
|
||||
data["Battery"]["ChargerPlugged"] = chargerPlugged;
|
||||
data["Battery"]["BatteryPercentage"] = batteryPercentage;
|
||||
|
||||
data["SD"]["UseVirtualSD"] = sdCardInserted;
|
||||
data["SD"]["WriteProtectVirtualSD"] = sdWriteProtected;
|
||||
|
||||
std::ofstream file(path, std::ios::out);
|
||||
file << data;
|
||||
file.close();
|
||||
|
|
|
@ -20,9 +20,11 @@ HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
|||
// Create a file of size "size" by creating an empty one, seeking to size - 1 and just writing a 0 there
|
||||
IOFile file(p.string().c_str(), "wb");
|
||||
if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
|
||||
file.close();
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
file.close();
|
||||
return Result::FS::FileTooLarge;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,17 @@ HorizonResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
|
|||
|
||||
// If the size is 0, leave the file empty and return success
|
||||
if (size == 0) {
|
||||
file.close();
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
// If it is not empty, seek to size - 1 and write a 0 to create a file of size "size"
|
||||
else if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
|
||||
file.close();
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
file.close();
|
||||
return Result::FS::FileTooLarge;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
namespace PathType {
|
||||
enum : u32 {
|
||||
RomFS = 0,
|
||||
ExeFS = 2
|
||||
ExeFS = 2,
|
||||
UpdateRomFS = 5,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -33,8 +34,8 @@ FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& pe
|
|||
// Where to read the file from. (https://www.3dbrew.org/wiki/Filesystem_services#SelfNCCH_File_Path_Data_Format)
|
||||
// We currently only know how to read from an NCCH's RomFS, ie type = 0
|
||||
const u32 type = *(u32*)&path.binary[0]; // TODO: Get rid of UB here
|
||||
if (type != PathType::RomFS && type != PathType::ExeFS) {
|
||||
Helpers::panic("Read from NCCH's non-RomFS & non-exeFS section!");
|
||||
if (type != PathType::RomFS && type != PathType::ExeFS && type != PathType::UpdateRomFS) {
|
||||
Helpers::panic("Read from NCCH's non-RomFS & non-exeFS section! Path type: %d", type);
|
||||
}
|
||||
|
||||
return NoFile; // No file descriptor needed for RomFS
|
||||
|
@ -50,8 +51,8 @@ Rust::Result<ArchiveBase*, HorizonResult> SelfNCCHArchive::openArchive(const FSP
|
|||
}
|
||||
|
||||
std::optional<u32> SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
||||
const FSPath& path = file->path; // Path of the file
|
||||
const u32 type = *(u32*)&path.binary[0]; // Type of the path
|
||||
const FSPath& path = file->path; // Path of the file
|
||||
const u32 type = *(u32*)&path.binary[0]; // Type of the path
|
||||
|
||||
if (type == PathType::RomFS && !hasRomFS()) {
|
||||
Helpers::panic("Tried to read file from non-existent RomFS");
|
||||
|
@ -98,8 +99,23 @@ std::optional<u32> SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32
|
|||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Helpers::panic("Unimplemented file path type for SelfNCCH archive");
|
||||
// Normally, the update RomFS should overlay the cartridge RomFS when reading from this and an update is installed.
|
||||
// So to support updates, we need to perform this overlaying. For now, read from the cartridge RomFS.
|
||||
case PathType::UpdateRomFS: {
|
||||
Helpers::warn("Reading from update RomFS but updates are currently not supported! Reading from regular RomFS instead\n");
|
||||
|
||||
const u64 romFSSize = cxi->romFS.size;
|
||||
const u64 romFSOffset = cxi->romFS.offset;
|
||||
if ((offset >> 32) || (offset >= romFSSize) || (offset + size >= romFSSize)) {
|
||||
Helpers::panic("Tried to read from SelfNCCH with too big of an offset");
|
||||
}
|
||||
|
||||
fsInfo = cxi->romFS;
|
||||
offset += 0x1000;
|
||||
break;
|
||||
}
|
||||
|
||||
default: Helpers::panic("Unimplemented file path type for SelfNCCH archive");
|
||||
}
|
||||
|
||||
std::unique_ptr<u8[]> data(new u8[size]);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "cpu.hpp"
|
||||
|
||||
Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu)
|
||||
: cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this) {
|
||||
Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config)
|
||||
: cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config) {
|
||||
objects.reserve(512); // Make room for a few objects to avoid further memory allocs later
|
||||
mutexHandles.reserve(8);
|
||||
timerHandles.reserve(8);
|
||||
|
@ -36,6 +36,8 @@ void Kernel::serviceSVC(u32 svc) {
|
|||
case 0x0A: svcSleepThread(); break;
|
||||
case 0x0B: getThreadPriority(); break;
|
||||
case 0x0C: setThreadPriority(); break;
|
||||
case 0x0F: getThreadIdealProcessor(); break;
|
||||
case 0x11: getCurrentProcessorNumber(); break;
|
||||
case 0x13: svcCreateMutex(); break;
|
||||
case 0x14: svcReleaseMutex(); break;
|
||||
case 0x15: svcCreateSemaphore(); break;
|
||||
|
@ -161,7 +163,7 @@ void Kernel::reset() {
|
|||
// 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,
|
||||
// which is thankfully not used. Maybe we should prevent this
|
||||
mainThread = makeThread(0, VirtualAddrs::StackTop, 0x30, -2, 0, ThreadStatus::Running);
|
||||
mainThread = makeThread(0, VirtualAddrs::StackTop, 0x30, ProcessorID::Default, 0, ThreadStatus::Running);
|
||||
currentThreadIndex = 0;
|
||||
setupIdleThread();
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "kernel.hpp"
|
||||
#include "services/shared_font.hpp"
|
||||
|
||||
namespace Operation {
|
||||
enum : u32 {
|
||||
|
@ -137,7 +136,7 @@ void Kernel::mapMemoryBlock() {
|
|||
break;
|
||||
|
||||
case KernelHandles::FontSharedMemHandle:
|
||||
std::memcpy(ptr, _shared_font_bin, _shared_font_len);
|
||||
mem.copySharedFont(ptr);
|
||||
break;
|
||||
|
||||
default: Helpers::panic("Mapping unknown shared memory block: %X", block);
|
||||
|
|
|
@ -109,7 +109,7 @@ void Kernel::sendSyncRequest() {
|
|||
// If we're actually communicating with a port
|
||||
const auto session = getObject(handle, KernelObjectType::Session);
|
||||
if (session == nullptr) [[unlikely]] {
|
||||
Helpers::panic("SendSyncRequest: Invalid handle");
|
||||
Helpers::warn("SendSyncRequest: Invalid handle");
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
@ -127,4 +127,4 @@ void Kernel::sendSyncRequest() {
|
|||
const auto portData = objects[portHandle].getData<Port>();
|
||||
Helpers::panic("SendSyncRequest targetting port %s\n", portData->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ s32 Kernel::getCurrentResourceValue(const KernelObject* limit, u32 resourceName)
|
|||
u32 Kernel::getMaxForResource(const KernelObject* limit, u32 resourceName) {
|
||||
switch (resourceName) {
|
||||
case ResourceType::Commit: return appResourceLimits.maxCommit;
|
||||
case ResourceType::Thread: return appResourceLimits.maxThreads;
|
||||
default: Helpers::panic("Attempted to get the max of unknown kernel resource: %d\n", resourceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ void Kernel::rescheduleThreads() {
|
|||
}
|
||||
|
||||
// Internal OS function to spawn a thread
|
||||
Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg, ThreadStatus status) {
|
||||
Handle 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
|
||||
|
||||
if (threadCount < appResourceLimits.maxThreads) [[likely]] { // If we have not yet created over too many threads
|
||||
|
@ -397,8 +397,12 @@ void Kernel::createThread() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (id < -2 || id > 3) {
|
||||
Helpers::panic("Invalid processor ID in CreateThread");
|
||||
}
|
||||
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready);
|
||||
regs[1] = makeThread(entrypoint, initialSP, priority, static_cast<ProcessorID>(id), arg, ThreadStatus::Ready);
|
||||
requireReschedule();
|
||||
}
|
||||
|
||||
|
@ -449,6 +453,15 @@ void Kernel::getThreadPriority() {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::getThreadIdealProcessor() {
|
||||
const Handle handle = regs[1]; // Thread handle
|
||||
logSVC("GetThreadIdealProcessor (handle = %X)\n", handle);
|
||||
|
||||
// TODO: Not documented what this is or what it does. Citra doesn't implement it at all. Return AppCore as the ideal processor for now
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = static_cast<u32>(ProcessorID::AppCore);
|
||||
}
|
||||
|
||||
void Kernel::setThreadPriority() {
|
||||
const Handle handle = regs[0];
|
||||
const u32 priority = regs[1];
|
||||
|
@ -476,6 +489,33 @@ void Kernel::setThreadPriority() {
|
|||
requireReschedule();
|
||||
}
|
||||
|
||||
void Kernel::getCurrentProcessorNumber() {
|
||||
logSVC("GetCurrentProcessorNumber()\n");
|
||||
const ProcessorID id = threads[currentThreadIndex].processorID;
|
||||
s32 ret;
|
||||
|
||||
// Until we properly implement per-core schedulers, return whatever processor ID passed to svcCreateThread
|
||||
switch (id) {
|
||||
// TODO: This is picked from exheader
|
||||
case ProcessorID::Default:
|
||||
ret = static_cast<s32>(ProcessorID::AppCore);
|
||||
break;
|
||||
|
||||
case ProcessorID::AllCPUs:
|
||||
ret = static_cast<s32>(ProcessorID::AppCore);
|
||||
Helpers::warn("GetCurrentProcessorNumber on thread created to run on all CPUs...?\n");
|
||||
break;
|
||||
|
||||
default: ret = static_cast<s32>(id); break;
|
||||
}
|
||||
|
||||
if (ret != static_cast<s32>(ProcessorID::AppCore)) {
|
||||
Helpers::warn("GetCurrentProcessorNumber: Thread not running on appcore\n");
|
||||
}
|
||||
|
||||
regs[0] = static_cast<u32>(ret);
|
||||
}
|
||||
|
||||
void Kernel::exitThread() {
|
||||
logSVC("ExitThread\n");
|
||||
|
||||
|
@ -630,4 +670,4 @@ bool Kernel::shouldWaitOnObject(KernelObject* object) {
|
|||
Helpers::panic("Not sure whether to wait on object (type: %s)", object->getTypeName());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,6 +131,13 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!aesEngine.haveGenerator()) {
|
||||
Helpers::panic(
|
||||
"Loading an encrypted ROM but your AES keys don't seem to provide the \"generator\" constant which Panda3DS requires for decryption\n"
|
||||
"Please add it to your aes_keys.txt in a line like \"generator=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\" where the Xs are replaced with the actual generator constant value"
|
||||
);
|
||||
}
|
||||
|
||||
if (!gotCryptoKeys) {
|
||||
Helpers::panic("ROM is encrypted but it seems we couldn't get either the primary or the secondary key");
|
||||
return false;
|
||||
|
@ -279,6 +286,7 @@ bool NCCH::parseSMDH(const std::vector<u8>& smdh) {
|
|||
} else if (taiwan) {
|
||||
region = Regions::Taiwan;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<bool, Crypto::AESKey> NCCH::getPrimaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY) {
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <chrono> // For time since epoch
|
||||
#include <cmrc/cmrc.hpp>
|
||||
#include <ctime>
|
||||
|
||||
#include "config_mem.hpp"
|
||||
#include "resource_limits.hpp"
|
||||
#include "services/ptm.hpp"
|
||||
|
||||
CMRC_DECLARE(ConsoleFonts);
|
||||
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
Memory::Memory(u64& cpuTicks) : cpuTicks(cpuTicks) {
|
||||
Memory::Memory(u64& cpuTicks, const EmulatorConfig& config) : cpuTicks(cpuTicks), config(config) {
|
||||
fcram = new uint8_t[FCRAM_SIZE]();
|
||||
dspRam = new uint8_t[DSP_RAM_SIZE]();
|
||||
|
||||
|
@ -45,6 +49,12 @@ void Memory::reset() {
|
|||
|
||||
// Initialize shared memory blocks and reserve memory for them
|
||||
for (auto& e : sharedMemBlocks) {
|
||||
if (e.handle == KernelHandles::FontSharedMemHandle) {
|
||||
// Read font size from the cmrc filesystem the font is stored in
|
||||
auto fonts = cmrc::ConsoleFonts::get_filesystem();
|
||||
e.size = fonts.open("CitraSharedFontUSRelocated.bin").size();
|
||||
}
|
||||
|
||||
e.mapped = false;
|
||||
e.paddr = allocateSysMemory(e.size);
|
||||
}
|
||||
|
@ -85,7 +95,18 @@ u8 Memory::read8(u32 vaddr) {
|
|||
return *(u8*)(pointer + offset);
|
||||
} else {
|
||||
switch (vaddr) {
|
||||
case ConfigMem::BatteryState: return getBatteryState(true, true, BatteryLevel::FourBars);
|
||||
case ConfigMem::BatteryState: {
|
||||
// Set by the PTM module
|
||||
// Charger plugged: Shows whether the charger is plugged
|
||||
// Charging: Shows whether the charger is plugged and the console is actually charging, ie the battery is not full
|
||||
// BatteryLevel: A battery level calculated via PTM::GetBatteryLevel
|
||||
// These are all assembled into a bitfield and returned via config memory
|
||||
const bool chargerPlugged = config.chargerPlugged;
|
||||
const bool charging = config.chargerPlugged && (config.batteryPercentage < 100);
|
||||
const auto batteryLevel = static_cast<BatteryLevel>(PTMService::batteryPercentToLevel(config.batteryPercentage));
|
||||
|
||||
return getBatteryState(chargerPlugged, charging, batteryLevel);
|
||||
}
|
||||
case ConfigMem::EnvInfo: return envInfo;
|
||||
case ConfigMem::HardwareType: return ConfigMem::HardwareCodes::Product;
|
||||
case ConfigMem::KernelVersionMinor: return u8(kernelVersion & 0xff);
|
||||
|
@ -152,8 +173,14 @@ u32 Memory::read32(u32 vaddr) {
|
|||
|
||||
default:
|
||||
if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) {
|
||||
Helpers::warn("VRAM read!\n");
|
||||
return 0;
|
||||
static int shutUpCounter = 0;
|
||||
if (shutUpCounter < 5) { // Stop spamming about VRAM reads after the first 5
|
||||
shutUpCounter++;
|
||||
Helpers::warn("VRAM read!\n");
|
||||
}
|
||||
|
||||
// TODO: Properly handle framebuffer readbacks and the like
|
||||
return *(u32*)&vram[vaddr - VirtualAddrs::VramStart];
|
||||
}
|
||||
|
||||
Helpers::panic("Unimplemented 32-bit read, addr: %08X", vaddr);
|
||||
|
@ -481,3 +508,9 @@ Regions Memory::getConsoleRegion() {
|
|||
// For now we pick one based on the ROM header
|
||||
return region;
|
||||
}
|
||||
|
||||
void Memory::copySharedFont(u8* pointer) {
|
||||
auto fonts = cmrc::ConsoleFonts::get_filesystem();
|
||||
auto font = fonts.open("CitraSharedFontUSRelocated.bin");
|
||||
std::memcpy(pointer, font.begin(), font.size());
|
||||
}
|
|
@ -118,6 +118,7 @@ void RendererGL::initGraphicsContext(SDL_Window* window) {
|
|||
|
||||
dummyVBO.create();
|
||||
dummyVAO.create();
|
||||
gl.disableScissor();
|
||||
|
||||
// Create texture and framebuffer for the 3DS screen
|
||||
const u32 screenTextureWidth = 400; // Top screen is 400 pixels wide, bottom is 320
|
||||
|
@ -126,6 +127,24 @@ void RendererGL::initGraphicsContext(SDL_Window* window) {
|
|||
glGenTextures(1, &lightLUTTextureArray);
|
||||
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
|
||||
// Create a plain black texture for when a game reads an invalid texture. It is common for games to configure the PICA to read texture info from NULL.
|
||||
// Some games that do this are Pokemon X, Cars 2, Tomodachi Life, and more. We bind the texture to an FBO, clear it, and free the FBO
|
||||
blankTexture.create(8, 8, GL_RGBA8);
|
||||
blankTexture.bind();
|
||||
blankTexture.setMinFilter(OpenGL::Linear);
|
||||
blankTexture.setMagFilter(OpenGL::Linear);
|
||||
|
||||
OpenGL::Framebuffer dummyFBO;
|
||||
dummyFBO.createWithDrawTexture(blankTexture); // Create FBO and bind our texture to it
|
||||
dummyFBO.bind(OpenGL::DrawFramebuffer);
|
||||
|
||||
// Clear the texture and then delete FBO
|
||||
OpenGL::setViewport(8, 8);
|
||||
gl.setClearColour(0.0, 0.0, 0.0, 1.0);
|
||||
OpenGL::clearColor();
|
||||
dummyFBO.free();
|
||||
|
||||
screenTexture.create(screenTextureWidth, screenTextureHeight, GL_RGBA8);
|
||||
screenTexture.bind();
|
||||
screenTexture.setMinFilter(OpenGL::Linear);
|
||||
|
@ -143,7 +162,6 @@ void RendererGL::initGraphicsContext(SDL_Window* window) {
|
|||
GLint oldViewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, oldViewport);
|
||||
OpenGL::setViewport(screenTextureWidth, screenTextureHeight);
|
||||
gl.setClearColour(0.0, 0.0, 0.0, 1.0);
|
||||
OpenGL::clearColor();
|
||||
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
|
||||
|
||||
|
@ -321,9 +339,17 @@ void RendererGL::bindTexturesToSlots() {
|
|||
u32 format = regs[ioBase + (i == 0 ? 13 : 5)] & 0xF;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
Texture targetTex(addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
||||
OpenGL::Texture tex = getTexture(targetTex);
|
||||
tex.bind();
|
||||
|
||||
if (addr != 0) [[likely]] {
|
||||
Texture targetTex(addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
||||
OpenGL::Texture tex = getTexture(targetTex);
|
||||
tex.bind();
|
||||
} else {
|
||||
// Mapping a texture from NULL. PICA seems to read the last sampled colour, but for now we will display a black texture instead since it is far easier.
|
||||
// Games that do this don't really care what it does, they just expect the PICA to not crash, since it doesn't have a PU/MMU and can do all sorts of
|
||||
// Weird invalid memory accesses without crashing
|
||||
blankTexture.bind();
|
||||
}
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + 3);
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
|
||||
namespace ACCommands {
|
||||
enum : u32 {
|
||||
CreateDefaultConfig = 0x00010000,
|
||||
CancelConnectAsync = 0x00070002,
|
||||
CloseAsync = 0x00080004,
|
||||
GetLastErrorCode = 0x000A0000,
|
||||
RegisterDisconnectEvent = 0x00300004,
|
||||
SetClientVersion = 0x00400042,
|
||||
};
|
||||
}
|
||||
|
@ -13,12 +17,40 @@ void ACService::reset() {}
|
|||
void ACService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case ACCommands::CancelConnectAsync: cancelConnectAsync(messagePointer); break;
|
||||
case ACCommands::CloseAsync: closeAsync(messagePointer); break;
|
||||
case ACCommands::CreateDefaultConfig: createDefaultConfig(messagePointer); break;
|
||||
case ACCommands::GetLastErrorCode: getLastErrorCode(messagePointer); break;
|
||||
case ACCommands::RegisterDisconnectEvent: registerDisconnectEvent(messagePointer); break;
|
||||
case ACCommands::SetClientVersion: setClientVersion(messagePointer); break;
|
||||
default: Helpers::panic("AC service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void ACService::cancelConnectAsync(u32 messagePointer) {
|
||||
log("AC::CancelCommandAsync (stubbed)\n");
|
||||
|
||||
// TODO: Verify if this response header is correct on hardware
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x7, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void ACService::closeAsync(u32 messagePointer) {
|
||||
log("AC::CloseAsync (stubbed)\n");
|
||||
|
||||
// TODO: Verify if this response header is correct on hardware
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void ACService::createDefaultConfig(u32 messagePointer) {
|
||||
log("AC::CreateDefaultConfig (stubbed)\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// TODO: Verify response buffer on hardware
|
||||
}
|
||||
|
||||
void ACService::getLastErrorCode(u32 messagePointer) {
|
||||
log("AC::GetLastErrorCode (stubbed)\n");
|
||||
|
||||
|
@ -33,4 +65,15 @@ void ACService::setClientVersion(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x40, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void ACService::registerDisconnectEvent(u32 messagePointer) {
|
||||
log("AC::RegisterDisconnectEvent (stubbed)\n");
|
||||
const u32 pidHeader = mem.read32(messagePointer + 4);
|
||||
const u32 copyHandleHeader = mem.read32(messagePointer + 12);
|
||||
// Event signaled when disconnecting from AC
|
||||
const Handle eventHandle = mem.read32(messagePointer + 16);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x30, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
namespace AMCommands {
|
||||
enum : u32 {
|
||||
GetDLCTitleInfo = 0x10050084,
|
||||
ListTitleInfo = 0x10070102
|
||||
ListTitleInfo = 0x10070102,
|
||||
GetPatchTitleInfo = 0x100D0084,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -13,6 +14,7 @@ void AMService::reset() {}
|
|||
void AMService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case AMCommands::GetPatchTitleInfo: getPatchTitleInfo(messagePointer); break;
|
||||
case AMCommands::GetDLCTitleInfo: getDLCTitleInfo(messagePointer); break;
|
||||
case AMCommands::ListTitleInfo: listTitleInfo(messagePointer); break;
|
||||
default: Helpers::panic("AM service requested. Command: %08X\n", command);
|
||||
|
@ -42,7 +44,16 @@ void AMService::listTitleInfo(u32 messagePointer) {
|
|||
|
||||
void AMService::getDLCTitleInfo(u32 messagePointer) {
|
||||
log("AM::GetDLCTitleInfo (stubbed to fail)\n");
|
||||
Helpers::warn("Unimplemented AM::GetDLCTitleInfo. Will need to be implemented to support DLC\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1005, 1, 4));
|
||||
mem.write32(messagePointer + 4, -1);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
}
|
||||
|
||||
void AMService::getPatchTitleInfo(u32 messagePointer) {
|
||||
log("AM::GetPatchTitleInfo (stubbed to fail)\n");
|
||||
Helpers::warn("Unimplemented AM::GetDLCTitleInfo. Will need to be implemented to support updates\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x100D, 1, 4));
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
}
|
|
@ -6,14 +6,22 @@ namespace BOSSCommands {
|
|||
InitializeSession = 0x00010082,
|
||||
UnregisterStorage = 0x00030000,
|
||||
GetTaskStorageInfo = 0x00040000,
|
||||
RegisterNewArrivalEvent = 0x00080002,
|
||||
GetOptoutFlag = 0x000A0000,
|
||||
RegisterTask = 0x000B00C2,
|
||||
UnregisterTask = 0x000C0082,
|
||||
GetTaskIdList = 0x000E0000,
|
||||
GetNsDataIdList = 0x00100102,
|
||||
GetNsDataIdList1 = 0x00110102,
|
||||
SendProperty = 0x00140082,
|
||||
ReceiveProperty = 0x00160082,
|
||||
GetTaskServiceStatus = 0x001B0042,
|
||||
StartTask = 0x001C0042,
|
||||
CancelTask = 0x001E0042,
|
||||
GetTaskState = 0x00200082,
|
||||
GetTaskStatus = 0x002300C2,
|
||||
GetTaskInfo = 0x00250082,
|
||||
GetErrorCode = 0x002E0040,
|
||||
RegisterStorageEntry = 0x002F0140,
|
||||
GetStorageEntryInfo = 0x00300000,
|
||||
};
|
||||
|
@ -27,16 +35,25 @@ void BOSSService::handleSyncRequest(u32 messagePointer) {
|
|||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case BOSSCommands::CancelTask: cancelTask(messagePointer); break;
|
||||
case BOSSCommands::GetNsDataIdList: getNsDataIdList(messagePointer); break;
|
||||
case BOSSCommands::GetErrorCode: getErrorCode(messagePointer); break;
|
||||
case BOSSCommands::GetNsDataIdList:
|
||||
case BOSSCommands::GetNsDataIdList1:
|
||||
getNsDataIdList(messagePointer, command); break;
|
||||
case BOSSCommands::GetOptoutFlag: getOptoutFlag(messagePointer); break;
|
||||
case BOSSCommands::GetStorageEntryInfo: getStorageEntryInfo(messagePointer); break;
|
||||
case BOSSCommands::GetTaskIdList: getTaskIdList(messagePointer); break;
|
||||
case BOSSCommands::GetTaskInfo: getTaskInfo(messagePointer); break;
|
||||
case BOSSCommands::GetTaskServiceStatus: getTaskServiceStatus(messagePointer); break;
|
||||
case BOSSCommands::GetTaskState: getTaskState(messagePointer); break;
|
||||
case BOSSCommands::GetTaskStatus: getTaskStatus(messagePointer); break;
|
||||
case BOSSCommands::GetTaskStorageInfo: getTaskStorageInfo(messagePointer); break;
|
||||
case BOSSCommands::InitializeSession: initializeSession(messagePointer); break;
|
||||
case BOSSCommands::ReceiveProperty: receiveProperty(messagePointer); break;
|
||||
case BOSSCommands::RegisterNewArrivalEvent: registerNewArrivalEvent(messagePointer); break;
|
||||
case BOSSCommands::RegisterStorageEntry: registerStorageEntry(messagePointer); break;
|
||||
case BOSSCommands::RegisterTask: registerTask(messagePointer); break;
|
||||
case BOSSCommands::SendProperty: sendProperty(messagePointer); break;
|
||||
case BOSSCommands::StartTask: startTask(messagePointer); break;
|
||||
case BOSSCommands::UnregisterStorage: unregisterStorage(messagePointer); break;
|
||||
case BOSSCommands::UnregisterTask: unregisterTask(messagePointer); break;
|
||||
default: Helpers::panic("BOSS service requested. Command: %08X\n", command);
|
||||
|
@ -68,6 +85,28 @@ void BOSSService::getTaskState(u32 messagePointer) {
|
|||
mem.write8(messagePointer + 16, 0); // TODO: Figure out what this should be
|
||||
}
|
||||
|
||||
void BOSSService::getTaskStatus(u32 messagePointer) {
|
||||
// TODO: 3DBrew does not mention what the parameters are, or what the return values are.
|
||||
log("BOSS::GetTaskStatus (Stubbed)\n");
|
||||
|
||||
// Response values stubbed based on Citra
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x23, 2, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, 0);
|
||||
// TODO: Citra pushes a buffer here?
|
||||
}
|
||||
|
||||
void BOSSService::getTaskServiceStatus(u32 messagePointer) {
|
||||
// TODO: 3DBrew does not mention what the parameters are, or what the return values are... again
|
||||
log("BOSS::GetTaskServiceStatus (Stubbed)\n");
|
||||
|
||||
// Response values stubbed based on Citra
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1B, 2, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, 0);
|
||||
// TODO: Citra pushes a buffer here too?
|
||||
}
|
||||
|
||||
void BOSSService::getTaskStorageInfo(u32 messagePointer) {
|
||||
log("BOSS::GetTaskStorageInfo (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x4, 2, 0));
|
||||
|
@ -90,6 +129,13 @@ void BOSSService::getTaskInfo(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void BOSSService::getErrorCode(u32 messagePointer) {
|
||||
log("BOSS::GetErrorCode (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x2E, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, Result::Success); // No error code
|
||||
}
|
||||
|
||||
void BOSSService::getStorageEntryInfo(u32 messagePointer) {
|
||||
log("BOSS::GetStorageEntryInfo (undocumented)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x30, 3, 0));
|
||||
|
@ -98,33 +144,76 @@ void BOSSService::getStorageEntryInfo(u32 messagePointer) {
|
|||
mem.write16(messagePointer + 12, 0); // s16, unknown meaning
|
||||
}
|
||||
|
||||
void BOSSService::sendProperty(u32 messagePointer) {
|
||||
const u32 id = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
const u32 ptr = mem.read32(messagePointer + 16);
|
||||
|
||||
log("BOSS::SendProperty (id = %d, size = %08X, ptr = %08X) (stubbed)\n", id, size, ptr);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Read size
|
||||
// TODO: Should this do anything else?
|
||||
}
|
||||
|
||||
|
||||
void BOSSService::receiveProperty(u32 messagePointer) {
|
||||
const u32 id = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
const u32 ptr = mem.read32(messagePointer + 16);
|
||||
|
||||
log("BOSS::ReceiveProperty(stubbed) (id = %d, size = %08X, ptr = %08X)\n", id, size, ptr);
|
||||
log("BOSS::ReceiveProperty (id = %d, size = %08X, ptr = %08X) (stubbed)\n", id, size, ptr);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x16, 2, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Read size
|
||||
}
|
||||
|
||||
// This seems to accept a KEvent as a parameter and register it for something Spotpass related
|
||||
// I need to update the 3DBrew page when it's known what it does properly
|
||||
void BOSSService::registerNewArrivalEvent(u32 messagePointer) {
|
||||
const Handle eventHandle = mem.read32(messagePointer + 4); // Kernel event handle to register
|
||||
log("BOSS::RegisterNewArrivalEvent (handle = %X)\n", eventHandle);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void BOSSService::startTask(u32 messagePointer) {
|
||||
log("BOSS::StartTask (stubbed)\n");
|
||||
const u32 bufferSize = mem.read32(messagePointer + 4);
|
||||
const u32 descriptor = mem.read32(messagePointer + 8);
|
||||
const u32 bufferData = mem.read32(messagePointer + 12);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1C, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void BOSSService::cancelTask(u32 messagePointer) {
|
||||
log("BOSS::CancelTask (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1E, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void BOSSService::registerTask(u32 messagePointer) {
|
||||
log("BOSS::RegisterTask (stubbed)\n");
|
||||
const u32 bufferSize = mem.read32(messagePointer + 4);
|
||||
const u32 dataPointr = mem.read32(messagePointer + 20);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x0B, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void BOSSService::unregisterTask(u32 messagePointer) {
|
||||
log("BOSS::UnregisterTask (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x0C, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void BOSSService::getNsDataIdList(u32 messagePointer) {
|
||||
// There's multiple aliases for this command. commandWord is the first word in the IPC buffer with the command word, needed for the response header
|
||||
void BOSSService::getNsDataIdList(u32 messagePointer, u32 commandWord) {
|
||||
log("BOSS::GetNsDataIdList (stubbed)\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x10, 3, 2));
|
||||
mem.write32(messagePointer, IPC::responseHeader(commandWord >> 16, 3, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write16(messagePointer + 8, 0); // u16: Actual number of output entries.
|
||||
mem.write16(messagePointer + 12, 0); // u16: Last word-index copied to output in the internal NsDataId list.
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
#include "services/cam.hpp"
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
namespace CAMCommands {
|
||||
enum : u32 {
|
||||
GetBufferErrorInterruptEvent = 0x00060040,
|
||||
DriverInitialize = 0x00390000,
|
||||
GetMaxLines = 0x000A0080
|
||||
GetMaxLines = 0x000A0080,
|
||||
};
|
||||
}
|
||||
|
||||
void CAMService::reset() {}
|
||||
void CAMService::reset() { bufferErrorInterruptEvents.fill(std::nullopt); }
|
||||
|
||||
void CAMService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case CAMCommands::DriverInitialize: driverInitialize(messagePointer); break;
|
||||
case CAMCommands::GetBufferErrorInterruptEvent: getBufferErrorInterruptEvent(messagePointer); break;
|
||||
case CAMCommands::GetMaxLines: getMaxLines(messagePointer); break;
|
||||
default: Helpers::panic("CAM service requested. Command: %08X\n", command);
|
||||
}
|
||||
|
@ -55,4 +58,24 @@ void CAMService::getMaxLines(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, result);
|
||||
mem.write16(messagePointer + 8, lines);
|
||||
}
|
||||
}
|
||||
|
||||
void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) {
|
||||
const u32 port = mem.read32(messagePointer + 4);
|
||||
log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", port);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 2));
|
||||
|
||||
if (port >= portCount) {
|
||||
Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port");
|
||||
} else {
|
||||
auto& event = bufferErrorInterruptEvents[port];
|
||||
if (!event.has_value()) {
|
||||
event = kernel.makeEvent(ResetType::OneShot);
|
||||
}
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0);
|
||||
mem.write32(messagePointer + 12, event.value());
|
||||
}
|
||||
}
|
|
@ -1,21 +1,22 @@
|
|||
#include "services/cecd.hpp"
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
namespace CECDCommands {
|
||||
enum : u32 {
|
||||
GetInfoEventHandle = 0x000F0000
|
||||
GetInfoEventHandle = 0x000F0000,
|
||||
OpenAndRead = 0x00120104,
|
||||
};
|
||||
}
|
||||
|
||||
void CECDService::reset() {
|
||||
infoEvent = std::nullopt;
|
||||
}
|
||||
void CECDService::reset() { infoEvent = std::nullopt; }
|
||||
|
||||
void CECDService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case CECDCommands::GetInfoEventHandle: getInfoEventHandle(messagePointer); break;
|
||||
case CECDCommands::OpenAndRead: openAndRead(messagePointer); break;
|
||||
default:
|
||||
Helpers::panicDev("CECD service requested. Command: %08X\n", command);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
@ -34,4 +35,17 @@ void CECDService::getInfoEventHandle(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// TODO: Translation descriptor here?
|
||||
mem.write32(messagePointer + 12, infoEvent.value());
|
||||
}
|
||||
|
||||
void CECDService::openAndRead(u32 messagePointer) {
|
||||
const u32 bufferSize = mem.read32(messagePointer + 4);
|
||||
const u32 programID = mem.read32(messagePointer + 8);
|
||||
const u32 pathType = mem.read32(messagePointer + 12);
|
||||
const u32 bufferAddress = mem.read32(messagePointer + 32);
|
||||
log("CECD::OpenAndRead (size = %08X, address = %08X, path type = %d)\n", bufferSize, bufferAddress, pathType);
|
||||
|
||||
// TODO: We should implement this properly the time comes
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x12, 2, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Bytes read
|
||||
}
|
BIN
src/core/services/fonts/CitraSharedFontUSRelocated.bin
Normal file
BIN
src/core/services/fonts/CitraSharedFontUSRelocated.bin
Normal file
Binary file not shown.
|
@ -1,23 +1,28 @@
|
|||
#include <string>
|
||||
#include "services/frd.hpp"
|
||||
#include "services/region_codes.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "services/region_codes.hpp"
|
||||
|
||||
namespace FRDCommands {
|
||||
enum : u32 {
|
||||
HasLoggedIn = 0x00010000,
|
||||
AttachToEventNotification = 0x00200002,
|
||||
SetNotificationMask = 0x00210040,
|
||||
SetClientSdkVersion = 0x00320042,
|
||||
Logout = 0x00040000,
|
||||
GetMyFriendKey = 0x00050000,
|
||||
GetMyProfile = 0x00070000,
|
||||
GetMyPresence = 0x00080000,
|
||||
GetMyScreenName = 0x00090000,
|
||||
GetMyMii = 0x000A0000,
|
||||
GetFriendKeyList = 0x00110080
|
||||
GetFriendKeyList = 0x00110080,
|
||||
UpdateGameModeDescription = 0x001D0002,
|
||||
};
|
||||
}
|
||||
|
||||
void FRDService::reset() {}
|
||||
void FRDService::reset() { loggedIn = false; }
|
||||
|
||||
void FRDService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
|
@ -29,8 +34,11 @@ void FRDService::handleSyncRequest(u32 messagePointer) {
|
|||
case FRDCommands::GetMyPresence: getMyPresence(messagePointer); break;
|
||||
case FRDCommands::GetMyProfile: getMyProfile(messagePointer); break;
|
||||
case FRDCommands::GetMyScreenName: getMyScreenName(messagePointer); break;
|
||||
case FRDCommands::HasLoggedIn: hasLoggedIn(messagePointer); break;
|
||||
case FRDCommands::Logout: logout(messagePointer); break;
|
||||
case FRDCommands::SetClientSdkVersion: setClientSDKVersion(messagePointer); break;
|
||||
case FRDCommands::SetNotificationMask: setNotificationMask(messagePointer); break;
|
||||
case FRDCommands::UpdateGameModeDescription: updateGameModeDescription(messagePointer); break;
|
||||
default: Helpers::panic("FRD service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +48,14 @@ void FRDService::attachToEventNotification(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
// This is supposed to post stuff on your user profile so uhh can't really emulate it
|
||||
void FRDService::updateGameModeDescription(u32 messagePointer) {
|
||||
log("FRD::UpdateGameModeDescription\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1D, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FRDService::getMyFriendKey(u32 messagePointer) {
|
||||
log("FRD::GetMyFriendKey\n");
|
||||
|
||||
|
@ -134,4 +150,20 @@ void FRDService::getMyMii(u32 messagePointer) {
|
|||
// TODO: How is the mii data even returned?
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xA, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FRDService::hasLoggedIn(u32 messagePointer) {
|
||||
log("FRD::HasLoggedIn\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, loggedIn ? 1 : 0);
|
||||
}
|
||||
|
||||
void FRDService::logout(u32 messagePointer) {
|
||||
log("FRD::Logout\n");
|
||||
loggedIn = false;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x4, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
|
@ -27,6 +27,8 @@ namespace FSCommands {
|
|||
IsSdmcWritable = 0x08180000,
|
||||
GetFormatInfo = 0x084500C2,
|
||||
FormatSaveData = 0x084C0242,
|
||||
CreateExtSaveData = 0x08510242,
|
||||
DeleteExtSaveData = 0x08520100,
|
||||
InitializeWithSdkVersion = 0x08610042,
|
||||
SetPriority = 0x08620040,
|
||||
GetPriority = 0x08630000
|
||||
|
@ -144,9 +146,11 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
|||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case FSCommands::CreateDirectory: createDirectory(messagePointer); break;
|
||||
case FSCommands::CreateExtSaveData: createExtSaveData(messagePointer); break;
|
||||
case FSCommands::CreateFile: createFile(messagePointer); break;
|
||||
case FSCommands::ControlArchive: controlArchive(messagePointer); break;
|
||||
case FSCommands::CloseArchive: closeArchive(messagePointer); break;
|
||||
case FSCommands::DeleteExtSaveData: deleteExtSaveData(messagePointer); break;
|
||||
case FSCommands::DeleteFile: deleteFile(messagePointer); break;
|
||||
case FSCommands::FormatSaveData: formatSaveData(messagePointer); break;
|
||||
case FSCommands::FormatThisUserSaveData: formatThisUserSaveData(messagePointer); break;
|
||||
|
@ -455,6 +459,40 @@ void FSService::formatSaveData(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FSService::deleteExtSaveData(u32 messagePointer) {
|
||||
Helpers::warn("Stubbed call to FS::DeleteExtSaveData!");
|
||||
// First 4 words of parameters are the ExtSaveData info
|
||||
// https://www.3dbrew.org/wiki/Filesystem_services#ExtSaveDataInfo
|
||||
const u8 mediaType = mem.read8(messagePointer + 4);
|
||||
const u64 saveID = mem.read64(messagePointer + 8);
|
||||
log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID);
|
||||
|
||||
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
|
||||
// When we properly implement it, it will just be a recursive directory deletion
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FSService::createExtSaveData(u32 messagePointer) {
|
||||
Helpers::warn("Stubbed call to FS::CreateExtSaveData!");
|
||||
// First 4 words of parameters are the ExtSaveData info
|
||||
// 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.
|
||||
const u8 mediaType = mem.read8(messagePointer + 4);
|
||||
const u64 saveID = mem.read64(messagePointer + 8);
|
||||
const u32 numOfDirectories = mem.read32(messagePointer + 20);
|
||||
const u32 numOfFiles = mem.read32(messagePointer + 24);
|
||||
const u64 sizeLimit = mem.read64(messagePointer + 28);
|
||||
const u32 smdhSize = mem.read32(messagePointer + 36);
|
||||
const u32 smdhPointer = mem.read32(messagePointer + 44);
|
||||
|
||||
log("FS::CreateExtSaveData (stubbed)\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0));
|
||||
// TODO: Similar to DeleteExtSaveData, we need to refactor how our ExtSaveData stuff works before properly implementing this
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FSService::formatThisUserSaveData(u32 messagePointer) {
|
||||
log("FS::FormatThisUserSaveData\n");
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ namespace HIDCommands {
|
|||
EnableGyroscopeLow = 0x00130000,
|
||||
DisableGyroscopeLow = 0x00140000,
|
||||
GetGyroscopeLowRawToDpsCoefficient = 0x00150000,
|
||||
GetGyroscopeLowCalibrateParam = 0x00160000
|
||||
GetGyroscopeLowCalibrateParam = 0x00160000,
|
||||
GetSoundVolume = 0x00170000,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -46,6 +47,7 @@ void HIDService::handleSyncRequest(u32 messagePointer) {
|
|||
case HIDCommands::GetGyroscopeLowCalibrateParam: getGyroscopeLowCalibrateParam(messagePointer); break;
|
||||
case HIDCommands::GetGyroscopeLowRawToDpsCoefficient: getGyroscopeCoefficient(messagePointer); break;
|
||||
case HIDCommands::GetIPCHandles: getIPCHandles(messagePointer); break;
|
||||
case HIDCommands::GetSoundVolume: getSoundVolume(messagePointer); break;
|
||||
default: Helpers::panic("HID service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +109,18 @@ void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 8, Helpers::bit_cast<u32, float>(gyroscopeCoeff));
|
||||
}
|
||||
|
||||
// The volume here is in the range [0, 0x3F]
|
||||
// It is read directly from I2C Device 3 register 0x09
|
||||
// Since we currently do not have audio, set the volume a bit below max (0x30)
|
||||
void HIDService::getSoundVolume(u32 messagePointer) {
|
||||
log("HID::GetSoundVolume\n");
|
||||
constexpr u8 volume = 0x30;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x17, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, volume);
|
||||
}
|
||||
|
||||
void HIDService::getIPCHandles(u32 messagePointer) {
|
||||
log("HID::GetIPCHandles\n");
|
||||
|
||||
|
|
27
src/core/services/mcu/mcu_hwc.cpp
Normal file
27
src/core/services/mcu/mcu_hwc.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "ipc.hpp"
|
||||
#include "result/result.hpp"
|
||||
#include "services/mcu/mcu_hwc.hpp"
|
||||
|
||||
namespace MCU::HWCCommands {
|
||||
enum : u32 {
|
||||
GetBatteryLevel = 0x00050000,
|
||||
};
|
||||
}
|
||||
|
||||
void MCU::HWCService::reset() {}
|
||||
|
||||
void MCU::HWCService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case HWCCommands::GetBatteryLevel: getBatteryLevel(messagePointer); break;
|
||||
default: Helpers::panic("MCU::HWC service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void MCU::HWCService::getBatteryLevel(u32 messagePointer) {
|
||||
log("MCU::HWC::GetBatteryLevel\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, config.batteryPercentage);
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace MICCommands {
|
|||
enum : u32 {
|
||||
MapSharedMem = 0x00010042,
|
||||
StartSampling = 0x00030140,
|
||||
StopSampling = 0x00050000,
|
||||
SetGain = 0x00080040,
|
||||
GetGain = 0x00090000,
|
||||
SetPower = 0x000A0040,
|
||||
|
@ -17,6 +18,7 @@ namespace MICCommands {
|
|||
void MICService::reset() {
|
||||
micEnabled = false;
|
||||
shouldClamp = false;
|
||||
isSampling = false;
|
||||
gain = 0;
|
||||
}
|
||||
|
||||
|
@ -30,6 +32,7 @@ void MICService::handleSyncRequest(u32 messagePointer) {
|
|||
case MICCommands::SetIirFilter: setIirFilter(messagePointer); break;
|
||||
case MICCommands::SetPower: setPower(messagePointer); break;
|
||||
case MICCommands::StartSampling: startSampling(messagePointer); break;
|
||||
case MICCommands::StopSampling: stopSampling(messagePointer); break;
|
||||
case MICCommands::CaptainToadFunction: theCaptainToadFunction(messagePointer); break;
|
||||
default: Helpers::panic("MIC service requested. Command: %08X\n", command);
|
||||
}
|
||||
|
@ -88,10 +91,19 @@ void MICService::startSampling(u32 messagePointer) {
|
|||
encoding, sampleRate, offset, dataSize, loop ? "yes" : "no"
|
||||
);
|
||||
|
||||
isSampling = true;
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void MICService::stopSampling(u32 messagePointer) {
|
||||
log("MIC::StopSampling\n");
|
||||
isSampling = false;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void MICService::setIirFilter(u32 messagePointer) {
|
||||
const u32 size = mem.read32(messagePointer + 4);
|
||||
const u32 pointer = mem.read32(messagePointer + 12);
|
||||
|
|
|
@ -7,7 +7,8 @@ namespace NDMCommands {
|
|||
SuspendDaemons = 0x00060040,
|
||||
ResumeDaemons = 0x00070040,
|
||||
SuspendScheduler = 0x00080040,
|
||||
ResumeScheduler = 0x00090000
|
||||
ResumeScheduler = 0x00090000,
|
||||
ClearHalfAwakeMacFilter = 0x00170000,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -16,6 +17,7 @@ void NDMService::reset() {}
|
|||
void NDMService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case NDMCommands::ClearHalfAwakeMacFilter: clearHalfAwakeMacFilter(messagePointer); break;
|
||||
case NDMCommands::OverrideDefaultDaemons: overrideDefaultDaemons(messagePointer); break;
|
||||
case NDMCommands::ResumeDaemons: resumeDaemons(messagePointer); break;
|
||||
case NDMCommands::ResumeScheduler: resumeScheduler(messagePointer); break;
|
||||
|
@ -26,31 +28,37 @@ void NDMService::handleSyncRequest(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void NDMService::overrideDefaultDaemons(u32 messagePointer) {
|
||||
log("NDM::OverrideDefaultDaemons(stubbed)\n");
|
||||
log("NDM::OverrideDefaultDaemons (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NDMService::resumeDaemons(u32 messagePointer) {
|
||||
log("NDM::resumeDaemons(stubbed)\n");
|
||||
log("NDM::resumeDaemons (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x7, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NDMService::suspendDaemons(u32 messagePointer) {
|
||||
log("NDM::SuspendDaemons(stubbed)\n");
|
||||
log("NDM::SuspendDaemons (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NDMService::resumeScheduler(u32 messagePointer) {
|
||||
log("NDM::ResumeScheduler(stubbed)\n");
|
||||
log("NDM::ResumeScheduler (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NDMService::suspendScheduler(u32 messagePointer) {
|
||||
log("NDM::SuspendScheduler(stubbed)\n");
|
||||
log("NDM::SuspendScheduler (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NDMService::clearHalfAwakeMacFilter(u32 messagePointer) {
|
||||
log("NDM::ClearHalfAwakeMacFilter (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x17, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
15
src/core/services/news_u.cpp
Normal file
15
src/core/services/news_u.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "ipc.hpp"
|
||||
#include "services/news_u.hpp"
|
||||
|
||||
namespace NewsCommands {
|
||||
enum : u32 {};
|
||||
}
|
||||
|
||||
void NewsUService::reset() {}
|
||||
|
||||
void NewsUService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
default: Helpers::panic("news:u service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
|
@ -5,22 +5,35 @@
|
|||
namespace NFCCommands {
|
||||
enum : u32 {
|
||||
Initialize = 0x00010040,
|
||||
StartCommunication = 0x00030000,
|
||||
StopCommunication = 0x00040000,
|
||||
GetTagInRangeEvent = 0x000B0000,
|
||||
GetTagOutOfRangeEvent = 0x000C0000
|
||||
GetTagOutOfRangeEvent = 0x000C0000,
|
||||
GetTagState = 0x000D0000,
|
||||
CommunicationGetStatus = 0x000F0000,
|
||||
CommunicationGetResult = 0x00120000,
|
||||
};
|
||||
}
|
||||
|
||||
void NFCService::reset() {
|
||||
tagInRangeEvent = std::nullopt;
|
||||
tagOutOfRangeEvent = std::nullopt;
|
||||
|
||||
adapterStatus = Old3DSAdapterStatus::Idle;
|
||||
tagStatus = TagStatus::NotInitialized;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void NFCService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case NFCCommands::CommunicationGetStatus: communicationGetStatus(messagePointer); break;
|
||||
case NFCCommands::Initialize: initialize(messagePointer); break;
|
||||
case NFCCommands::GetTagInRangeEvent: getTagInRangeEvent(messagePointer); break;
|
||||
case NFCCommands::GetTagOutOfRangeEvent: getTagOutOfRangeEvent(messagePointer); break;
|
||||
case NFCCommands::GetTagState: getTagState(messagePointer); break;
|
||||
case NFCCommands::StartCommunication: startCommunication(messagePointer); break;
|
||||
case NFCCommands::StopCommunication: stopCommunication(messagePointer); break;
|
||||
default: Helpers::panic("NFC service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +42,9 @@ void NFCService::initialize(u32 messagePointer) {
|
|||
const u8 type = mem.read8(messagePointer + 4);
|
||||
log("NFC::Initialize (type = %d)\n", type);
|
||||
|
||||
adapterStatus = Old3DSAdapterStatus::InitializationComplete;
|
||||
tagStatus = TagStatus::Initialized;
|
||||
initialized = true;
|
||||
// TODO: This should error if already initialized. Also sanitize type.
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
@ -67,4 +83,42 @@ void NFCService::getTagOutOfRangeEvent(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// TODO: Translation descriptor here
|
||||
mem.write32(messagePointer + 12, tagOutOfRangeEvent.value());
|
||||
}
|
||||
|
||||
void NFCService::getTagState(u32 messagePointer) {
|
||||
log("NFC::GetTagState");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xD, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, static_cast<u8>(tagStatus));
|
||||
}
|
||||
|
||||
void NFCService::communicationGetStatus(u32 messagePointer) {
|
||||
log("NFC::CommunicationGetStatus");
|
||||
|
||||
if (!initialized) {
|
||||
Helpers::warn("NFC::CommunicationGetStatus: Old 3DS NFC Adapter not initialized\n");
|
||||
}
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xF, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, static_cast<u32>(adapterStatus));
|
||||
}
|
||||
|
||||
void NFCService::startCommunication(u32 messagePointer) {
|
||||
log("NFC::StartCommunication\n");
|
||||
// adapterStatus = Old3DSAdapterStatus::Active;
|
||||
// TODO: Actually start communication when we emulate amiibo
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NFCService::stopCommunication(u32 messagePointer) {
|
||||
log("NFC::StopCommunication\n");
|
||||
adapterStatus = Old3DSAdapterStatus::InitializationComplete;
|
||||
// TODO: Actually stop communication when we emulate amiibo
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x4, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
|
@ -27,20 +27,18 @@ void PTMService::handleSyncRequest(u32 messagePointer) {
|
|||
|
||||
void PTMService::getAdapterState(u32 messagePointer) {
|
||||
log("PTM::GetAdapterState\n");
|
||||
constexpr bool adapterConnected = true; // Pretend the 3DS is charging cause why not
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, adapterConnected ? 1 : 0);
|
||||
mem.write8(messagePointer + 8, config.chargerPlugged ? 1 : 0);
|
||||
}
|
||||
|
||||
void PTMService::getBatteryLevel(u32 messagePointer) {
|
||||
log("PTM::GetBatteryLevel");
|
||||
constexpr u8 batteryPercent = 3; // 3% battery so users can suffer
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x7, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, batteryPercentToLevel(batteryPercent));
|
||||
mem.write8(messagePointer + 8, batteryPercentToLevel(config.batteryPercentage));
|
||||
}
|
||||
|
||||
void PTMService::getStepHistory(u32 messagePointer) {
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
|
||||
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem), cecd(mem, kernel), cfg(mem),
|
||||
ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config)
|
||||
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem),
|
||||
dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel),
|
||||
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), ptm(mem), soc(mem),
|
||||
ssl(mem), y2r(mem, kernel) {}
|
||||
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mcu_hwc(mem, config), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem),
|
||||
news_u(mem), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}
|
||||
|
||||
static constexpr int MAX_NOTIFICATION_COUNT = 16;
|
||||
|
||||
|
@ -32,10 +32,13 @@ void ServiceManager::reset() {
|
|||
fs.reset();
|
||||
gsp_gpu.reset();
|
||||
gsp_lcd.reset();
|
||||
ldr.reset();
|
||||
ldr.reset();
|
||||
mcu_hwc.reset();
|
||||
mic.reset();
|
||||
nim.reset();
|
||||
ndm.reset();
|
||||
news_u.reset();
|
||||
nfc.reset();
|
||||
nim.reset();
|
||||
ptm.reset();
|
||||
soc.reset();
|
||||
ssl.reset();
|
||||
|
@ -110,8 +113,10 @@ static std::map<std::string, Handle> serviceMap = {
|
|||
{ "gsp::Gpu", KernelHandles::GPU },
|
||||
{ "gsp::Lcd", KernelHandles::LCD },
|
||||
{ "ldr:ro", KernelHandles::LDR_RO },
|
||||
{ "mcu::HWC", KernelHandles::MCU_HWC },
|
||||
{ "mic:u", KernelHandles::MIC },
|
||||
{ "ndm:u", KernelHandles::NDM },
|
||||
{ "news:u", KernelHandles::NEWS_U },
|
||||
{ "nfc:u", KernelHandles::NFC },
|
||||
{ "nim:aoc", KernelHandles::NIM },
|
||||
{ "ptm:u", KernelHandles::PTM }, // TODO: ptm:u and ptm:sysm have very different command sets
|
||||
|
@ -181,10 +186,10 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
|||
case KernelHandles::APT: [[likely]] apt.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::DSP: [[likely]] dsp.handleSyncRequest(messagePointer); break;
|
||||
|
||||
case KernelHandles::AC: ac.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::AC: ac.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::ACT: act.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::AM: am.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::AM: am.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CAM: cam.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer); break;
|
||||
|
@ -192,13 +197,15 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
|||
case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::HTTP: http.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::IR_USER: ir_user.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::FRD: frd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::FRD: frd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::LCD: gsp_lcd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::LDR_RO: ldr.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::LDR_RO: ldr.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::MCU_HWC: mcu_hwc.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::MIC: mic.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NFC: nfc.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NEWS_U: news_u.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::PTM: ptm.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::SSL: ssl.handleSyncRequest(messagePointer); break;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,6 +23,7 @@ namespace Y2RCommands {
|
|||
StartConversion = 0x00260000,
|
||||
StopConversion = 0x00270000,
|
||||
IsBusyConversion = 0x00280000,
|
||||
SetPackageParameter = 0x002901C0,
|
||||
PingProcess = 0x002A0000,
|
||||
DriverInitialize = 0x002B0000,
|
||||
DriverFinalize = 0x002C0000
|
||||
|
@ -60,6 +61,7 @@ void Y2RService::handleSyncRequest(u32 messagePointer) {
|
|||
case Y2RCommands::SetInputLineWidth: setInputLineWidth(messagePointer); break;
|
||||
case Y2RCommands::SetInputLines: setInputLines(messagePointer); break;
|
||||
case Y2RCommands::SetOutputFormat: setOutputFormat(messagePointer); break;
|
||||
case Y2RCommands::SetPackageParameter: setPackageParameter(messagePointer); break;
|
||||
case Y2RCommands::SetReceiving: setReceiving(messagePointer); break;
|
||||
case Y2RCommands::SetRotation: setRotation(messagePointer); break;
|
||||
case Y2RCommands::SetSendingY: setSendingY(messagePointer); break;
|
||||
|
@ -176,6 +178,17 @@ void Y2RService::setOutputFormat(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void Y2RService::setPackageParameter(u32 messagePointer) {
|
||||
// Package parameter is 3 words
|
||||
const u32 word1 = mem.read32(messagePointer + 4);
|
||||
const u32 word2 = mem.read32(messagePointer + 8);
|
||||
const u32 word3 = mem.read32(messagePointer + 12);
|
||||
Helpers::warn("Y2R::SetPackageParameter\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x29, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void Y2RService::setRotation(u32 messagePointer) {
|
||||
const u32 rot = mem.read32(messagePointer + 4);
|
||||
log("Y2R::SetRotation (format = %d)\n", rot);
|
||||
|
|
|
@ -12,8 +12,8 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
|
|||
#endif
|
||||
|
||||
Emulator::Emulator()
|
||||
: config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config),
|
||||
memory(cpu.getTicksRef()), cheats(memory, kernel.getServiceManager().getHID()), running(false), programRunning(false)
|
||||
: config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu, config), cpu(memory, kernel), gpu(memory, config),
|
||||
memory(cpu.getTicksRef(), config), cheats(memory, kernel.getServiceManager().getHID()), running(false), programRunning(false)
|
||||
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
||||
, httpServer(this)
|
||||
#endif
|
||||
|
@ -316,19 +316,36 @@ void Emulator::run() {
|
|||
|
||||
// Detect mouse motion events for gyroscope emulation
|
||||
case SDL_MOUSEMOTION: {
|
||||
if (romType == ROMType::None) break;
|
||||
|
||||
// Handle "dragging" across the touchscreen
|
||||
if (hid.isTouchScreenPressed()) {
|
||||
const s32 x = event.motion.x;
|
||||
const s32 y = event.motion.y;
|
||||
|
||||
// Check if touch falls in the touch screen area and register the new touch screen position
|
||||
if (y >= 240 && y <= 480 && x >= 40 && x < 40 + 320) {
|
||||
// Convert to 3DS coordinates
|
||||
u16 x_converted = static_cast<u16>(x) - 40;
|
||||
u16 y_converted = static_cast<u16>(y) - 240;
|
||||
|
||||
hid.setTouchScreenPress(x_converted, y_converted);
|
||||
}
|
||||
}
|
||||
|
||||
// We use right click to indicate we want to rotate the console. If right click is not held, then this is not a gyroscope rotation
|
||||
if (romType == ROMType::None || !holdingRightClick) break;
|
||||
if (holdingRightClick) {
|
||||
// Relative motion since last mouse motion event
|
||||
const s32 motionX = event.motion.xrel;
|
||||
const s32 motionY = event.motion.yrel;
|
||||
|
||||
// Relative motion since last mouse motion event
|
||||
const s32 motionX = event.motion.xrel;
|
||||
const s32 motionY = event.motion.yrel;
|
||||
|
||||
// The gyroscope involves lots of weird math I don't want to bother with atm
|
||||
// So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion
|
||||
const s32 roll = motionX > 0 ? 0x7f : -0x7f;
|
||||
const s32 pitch = motionY > 0 ? 0x7f : -0x7f;
|
||||
hid.setRoll(roll);
|
||||
hid.setPitch(pitch);
|
||||
// The gyroscope involves lots of weird math I don't want to bother with atm
|
||||
// So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion
|
||||
const s32 roll = motionX > 0 ? 0x7f : -0x7f;
|
||||
const s32 pitch = motionY > 0 ? 0x7f : -0x7f;
|
||||
hid.setRoll(roll);
|
||||
hid.setPitch(pitch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -232,8 +232,8 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
|||
|
||||
// Positional Light
|
||||
if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 0, 1) == 0) {
|
||||
error_unimpl = true;
|
||||
// half_vector = normalize(normalize(light_vector + v_view) + view);
|
||||
// error_unimpl = true;
|
||||
half_vector = normalize(normalize(light_vector + v_view) + view);
|
||||
}
|
||||
|
||||
// Directional light
|
||||
|
@ -328,7 +328,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
|||
if (fresnel_output2 == 1u) secondary_color.a = d[FR_LUT];
|
||||
|
||||
if (error_unimpl) {
|
||||
secondary_color = primary_color = vec4(1.0, 0., 1.0, 1.0);
|
||||
// secondary_color = primary_color = vec4(1.0, 0., 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue