diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index cc602e125c..3729a81da0 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -5,20 +5,24 @@ #include "common/common_types.h" +namespace AudioCore { + struct ADTSData { - u8 header_length; - bool MPEG2; - u8 profile; - u8 channels; - u8 channel_idx; - u8 framecount; - u8 samplerate_idx; - u32 length; - u32 samplerate; + u8 header_length = 0; + bool mpeg2 = false; + u8 profile = 0; + u8 channels = 0; + u8 channel_idx = 0; + u8 framecount = 0; + u8 samplerate_idx = 0; + u32 length = 0; + u32 samplerate = 0; }; -ADTSData ParseADTS(const char* buffer); +ADTSData ParseADTS(const u8* buffer); // last two bytes of MF AAC decoder user data // see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types u16 MFGetAACTag(const ADTSData& input); + +} // namespace AudioCore diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index 417e021761..d3dc7942e5 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -3,44 +3,59 @@ // Refer to the license.txt file included. #include #include "adts.h" +#include "common/bit_field.h" +namespace AudioCore { constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; -ADTSData ParseADTS(const char* buffer) { - u32 tmp = 0; - ADTSData out; +struct ADTSHeader { + union { + std::array raw{}; + BitFieldBE<52, 12, u64> sync_word; + BitFieldBE<51, 1, u64> mpeg2; + BitFieldBE<49, 2, u64> layer; + BitFieldBE<48, 1, u64> protection_absent; + BitFieldBE<46, 2, u64> profile; + BitFieldBE<42, 4, u64> samplerate_idx; + BitFieldBE<41, 1, u64> private_bit; + BitFieldBE<38, 3, u64> channel_idx; + BitFieldBE<37, 1, u64> originality; + BitFieldBE<36, 1, u64> home; + BitFieldBE<35, 1, u64> copyright_id; + BitFieldBE<34, 1, u64> copyright_id_start; + BitFieldBE<21, 13, u64> frame_length; + BitFieldBE<10, 11, u64> buffer_fullness; + BitFieldBE<8, 2, u64> frame_count; + }; +}; + +ADTSData ParseADTS(const u8* buffer) { + ADTSHeader header; + memcpy(header.raw.data(), buffer, sizeof(header.raw)); // sync word 0xfff - tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); - if ((tmp & 0xffff) != 0xfff0) { - out.length = 0; - return out; + if (header.sync_word != 0xfff) { + return {}; } + + ADTSData out{}; // bit 16 = no CRC - out.header_length = (buffer[1] & 0x1) ? 7 : 9; - out.MPEG2 = (buffer[1] >> 3) & 0x1; + out.header_length = header.protection_absent ? 7 : 9; + out.mpeg2 = static_cast(header.mpeg2); // bit 17 to 18 - out.profile = (buffer[2] >> 6) + 1; + out.profile = static_cast(header.profile) + 1; // bit 19 to 22 - tmp = (buffer[2] >> 2) & 0xf; - out.samplerate_idx = tmp; - out.samplerate = (tmp > 15) ? 0 : freq_table[tmp]; + out.samplerate_idx = static_cast(header.samplerate_idx); + out.samplerate = header.samplerate_idx > 15 ? 0 : freq_table[header.samplerate_idx]; // bit 24 to 26 - tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3); - out.channel_idx = tmp; - out.channels = (tmp > 7) ? 0 : channel_table[tmp]; - + out.channel_idx = static_cast(header.channel_idx); + out.channels = (header.channel_idx > 7) ? 0 : channel_table[header.channel_idx]; // bit 55 to 56 - out.framecount = (buffer[6] & 0x3) + 1; - + out.framecount = static_cast(header.frame_count + 1); // bit 31 to 43 - tmp = (buffer[3] & 0x3) << 11; - tmp |= (buffer[4] << 3) & 0x7f8; - tmp |= (buffer[5] >> 5) & 0x7; - - out.length = tmp; + out.length = static_cast(header.frame_length); return out; } @@ -61,3 +76,4 @@ u16 MFGetAACTag(const ADTSData& input) { return tag; } +} // namespace AudioCore diff --git a/src/audio_core/hle/audiotoolbox_decoder.cpp b/src/audio_core/hle/audiotoolbox_decoder.cpp index 83d4a41ae2..122b4f21ab 100644 --- a/src/audio_core/hle/audiotoolbox_decoder.cpp +++ b/src/audio_core/hle/audiotoolbox_decoder.cpp @@ -24,7 +24,7 @@ private: std::optional Decode(const BinaryMessage& request); void Clear(); - bool InitializeDecoder(ADTSData& adts_header); + bool InitializeDecoder(AudioCore::ADTSData& adts_header); static OSStatus DataFunc(AudioConverterRef in_audio_converter, u32* io_number_data_packets, AudioBufferList* io_data, @@ -33,7 +33,7 @@ private: Memory::MemorySystem& memory; - ADTSData adts_config; + AudioCore::ADTSData adts_config; AudioStreamBasicDescription output_format = {}; AudioConverterRef converter = nullptr; @@ -101,7 +101,7 @@ std::optional AudioToolboxDecoder::Impl::ProcessRequest( } } -bool AudioToolboxDecoder::Impl::InitializeDecoder(ADTSData& adts_header) { +bool AudioToolboxDecoder::Impl::InitializeDecoder(AudioCore::ADTSData& adts_header) { if (converter) { if (adts_config.channels == adts_header.channels && adts_config.samplerate == adts_header.samplerate) { @@ -183,8 +183,9 @@ std::optional AudioToolboxDecoder::Impl::Decode(const BinaryMessa return {}; } - auto data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); - auto adts_header = ParseADTS(reinterpret_cast(data)); + const auto data = + memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); + auto adts_header = AudioCore::ParseADTS(data); curr_data = data + adts_header.header_length; curr_data_len = request.decode_aac_request.size - adts_header.header_length; diff --git a/src/audio_core/hle/mediandk_decoder.cpp b/src/audio_core/hle/mediandk_decoder.cpp index 7c63b3f753..a6b39f7e57 100644 --- a/src/audio_core/hle/mediandk_decoder.cpp +++ b/src/audio_core/hle/mediandk_decoder.cpp @@ -27,7 +27,7 @@ public: ~Impl(); std::optional ProcessRequest(const BinaryMessage& request); - bool SetMediaType(const ADTSData& adts_data); + bool SetMediaType(const AudioCore::ADTSData& adts_data); private: std::optional Initalize(const BinaryMessage& request); @@ -36,8 +36,8 @@ private: Memory::MemorySystem& memory; std::unique_ptr decoder; // default: 2 channles, 48000 samplerate - ADTSData mADTSData{ - /*header_length*/ 7, /*MPEG2*/ false, /*profile*/ 2, + AudioCore::ADTSData mADTSData{ + /*header_length*/ 7, /*mpeg2*/ false, /*profile*/ 2, /*channels*/ 2, /*channel_idx*/ 2, /*framecount*/ 0, /*samplerate_idx*/ 3, /*length*/ 0, /*samplerate*/ 48000}; }; @@ -54,7 +54,7 @@ std::optional MediaNDKDecoder::Impl::Initalize(const BinaryMessag return response; } -bool MediaNDKDecoder::Impl::SetMediaType(const ADTSData& adts_data) { +bool MediaNDKDecoder::Impl::SetMediaType(const AudioCore::ADTSData& adts_data) { const char* mime = "audio/mp4a-latm"; if (decoder && mADTSData.profile == adts_data.profile && mADTSData.channel_idx == adts_data.channel_idx && @@ -141,8 +141,9 @@ std::optional MediaNDKDecoder::Impl::Decode(const BinaryMessage& return response; } - u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); - ADTSData adts_data = ParseADTS(reinterpret_cast(data)); + const u8* data = + memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); + ADTSData adts_data = AudioCore::ParseADTS(data); SetMediaType(adts_data); response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate); response.decode_aac_response.num_channels = adts_data.channels; diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index e49f86eb08..09043793b7 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -23,7 +23,8 @@ private: std::optional Decode(const BinaryMessage& request); - MFOutputState DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams); + MFOutputState DecodingLoop(AudioCore::ADTSData adts_header, + std::array, 2>& out_streams); bool transform_initialized = false; bool format_selected = false; @@ -139,7 +140,7 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryMessage& re return response; } -MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, +MFOutputState WMFDecoder::Impl::DecodingLoop(AudioCore::ADTSData adts_header, std::array, 2>& out_streams) { std::optional> output_buffer; @@ -210,14 +211,14 @@ std::optional WMFDecoder::Impl::Decode(const BinaryMessage& reque request.decode_aac_request.src_addr); return std::nullopt; } - u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); + const u8* data = + memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); std::array, 2> out_streams; unique_mfptr sample; MFInputState input_status = MFInputState::OK; MFOutputState output_status = MFOutputState::OK; - std::optional adts_meta = - DetectMediaType((char*)data, request.decode_aac_request.size); + std::optional adts_meta = DetectMediaType(data, request.decode_aac_request.size); if (!adts_meta) { LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index e71bb2f192..6cdf73f690 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -110,8 +110,9 @@ unique_mfptr CreateSample(const void* data, DWORD len, DWORD alignmen return sample; } -bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, - const UINT8* user_data, UINT32 user_data_len, GUID audio_format) { +bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, + const AudioCore::ADTSData& adts, const UINT8* user_data, + UINT32 user_data_len, GUID audio_format) { HRESULT hr = S_OK; unique_mfptr t; @@ -190,12 +191,12 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi return false; } -std::optional DetectMediaType(char* buffer, std::size_t len) { +std::optional DetectMediaType(const u8* buffer, std::size_t len) { if (len < 7) { return std::nullopt; } - ADTSData tmp; + AudioCore::ADTSData tmp; ADTSMeta result; // see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag // for the meaning of the byte array below @@ -207,7 +208,7 @@ std::optional DetectMediaType(char* buffer, std::size_t len) { UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; uint16_t tag = 0; - tmp = ParseADTS(buffer); + tmp = AudioCore::ParseADTS(buffer); if (tmp.length == 0) { return std::nullopt; } @@ -215,7 +216,7 @@ std::optional DetectMediaType(char* buffer, std::size_t len) { tag = MFGetAACTag(tmp); aac_tmp[12] |= (tag & 0xff00) >> 8; aac_tmp[13] |= (tag & 0x00ff); - std::memcpy(&(result.ADTSHeader), &tmp, sizeof(ADTSData)); + std::memcpy(&(result.ADTSHeader), &tmp, sizeof(AudioCore::ADTSData)); std::memcpy(&(result.AACTag), aac_tmp, 14); return result; } diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 77a12bef58..e515528d0b 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -99,7 +99,7 @@ void ReportError(std::string msg, HRESULT hr); // data type for transferring ADTS metadata between functions struct ADTSMeta { - ADTSData ADTSHeader; + AudioCore::ADTSData ADTSHeader; u8 AACTag[14]; }; @@ -110,10 +110,10 @@ bool InitMFDLL(); unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); unique_mfptr CreateSample(const void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); -bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, - const UINT8* user_data, UINT32 user_data_len, - GUID audio_format = MFAudioFormat_AAC); -std::optional DetectMediaType(char* buffer, std::size_t len); +bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, + const AudioCore::ADTSData& adts, const UINT8* user_data, + UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); +std::optional DetectMediaType(const u8* buffer, std::size_t len); bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format = MFAudioFormat_PCM); void MFFlush(IMFTransform* transform); diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index b9967eb688..3da6114b82 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -29,7 +29,7 @@ Config::~Config() { const std::array Config::default_buttons = { Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, - Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, + Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_V, }; const std::array, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index 77c7f601f4..da98da0d05 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -162,7 +162,7 @@ ConfigureInput::ConfigureInput(QWidget* parent) ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect, ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR, - ui->buttonHome, + ui->buttonHome, ui->buttonPower, }; analog_map_buttons = {{ diff --git a/src/citra_qt/configuration/configure_input.ui b/src/citra_qt/configuration/configure_input.ui index 2afae408e5..3e3e8e4f91 100644 --- a/src/citra_qt/configuration/configure_input.ui +++ b/src/citra_qt/configuration/configure_input.ui @@ -305,6 +305,24 @@ + + + + + Power: + + + + + + + + + + + + + @@ -340,7 +358,7 @@ - + diff --git a/src/common/settings.h b/src/common/settings.h index c7bf565fe3..6d3cbd98a6 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -100,13 +100,14 @@ enum Values { ZR, Home, + Power, NumButtons, }; constexpr int BUTTON_HID_BEGIN = A; constexpr int BUTTON_IR_BEGIN = ZL; -constexpr int BUTTON_NS_BEGIN = Home; +constexpr int BUTTON_NS_BEGIN = Power; constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN; constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN; @@ -134,6 +135,7 @@ static const std::array mapping = {{ "button_zl", "button_zr", "button_home", + "button_power", }}; } // namespace NativeButton diff --git a/src/core/core.cpp b/src/core/core.cpp index 703b49fba2..019c55f20b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -259,14 +259,13 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); return ResultStatus::ErrorGetLoader; } - std::pair, Loader::ResultStatus> system_mode = - app_loader->LoadKernelSystemMode(); - if (system_mode.second != Loader::ResultStatus::Success) { + auto memory_mode = app_loader->LoadKernelMemoryMode(); + if (memory_mode.second != Loader::ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", - static_cast(system_mode.second)); + static_cast(memory_mode.second)); - switch (system_mode.second) { + switch (memory_mode.second) { case Loader::ResultStatus::ErrorEncrypted: return ResultStatus::ErrorLoader_ErrorEncrypted; case Loader::ResultStatus::ErrorInvalidFormat: @@ -278,15 +277,15 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st } } - ASSERT(system_mode.first); - auto n3ds_mode = app_loader->LoadKernelN3dsMode(); - ASSERT(n3ds_mode.first); + ASSERT(memory_mode.first); + auto n3ds_hw_caps = app_loader->LoadNew3dsHwCapabilities(); + ASSERT(n3ds_hw_caps.first); u32 num_cores = 2; if (Settings::values.is_new_3ds) { num_cores = 4; } ResultStatus init_result{ - Init(emu_window, secondary_window, *system_mode.first, *n3ds_mode.first, num_cores)}; + Init(emu_window, secondary_window, *memory_mode.first, *n3ds_hw_caps.first, num_cores)}; if (init_result != ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", static_cast(init_result)); @@ -363,8 +362,9 @@ void System::Reschedule() { } System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, - Frontend::EmuWindow* secondary_window, u32 system_mode, - u8 n3ds_mode, u32 num_cores) { + Frontend::EmuWindow* secondary_window, + Kernel::MemoryMode memory_mode, + const Kernel::New3dsHwCapabilities& n3ds_hw_caps, u32 num_cores) { LOG_DEBUG(HW_Memory, "initialized OK"); memory = std::make_unique(); @@ -372,7 +372,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, timing = std::make_unique(num_cores, Settings::values.cpu_clock_percentage.GetValue()); kernel = std::make_unique( - *memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode); + *memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps); exclusive_monitor = MakeExclusiveMonitor(*memory, num_cores); cpu_cores.reserve(num_cores); @@ -673,10 +673,10 @@ void System::serialize(Archive& ar, const unsigned int file_version) { Shutdown(true); // Re-initialize everything like it was before - auto system_mode = this->app_loader->LoadKernelSystemMode(); - auto n3ds_mode = this->app_loader->LoadKernelN3dsMode(); + auto memory_mode = this->app_loader->LoadKernelMemoryMode(); + auto n3ds_hw_caps = this->app_loader->LoadNew3dsHwCapabilities(); [[maybe_unused]] const System::ResultStatus result = Init( - *m_emu_window, m_secondary_window, *system_mode.first, *n3ds_mode.first, num_cores); + *m_emu_window, m_secondary_window, *memory_mode.first, *n3ds_hw_caps.first, num_cores); } // flush on save, don't flush on load diff --git a/src/core/core.h b/src/core/core.h index fc9bba1e73..af72da85c0 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -342,8 +342,10 @@ private: * @return ResultStatus code, indicating if the operation succeeded. */ [[nodiscard]] ResultStatus Init(Frontend::EmuWindow& emu_window, - Frontend::EmuWindow* secondary_window, u32 system_mode, - u8 n3ds_mode, u32 num_cores); + Frontend::EmuWindow* secondary_window, + Kernel::MemoryMode memory_mode, + const Kernel::New3dsHwCapabilities& n3ds_hw_caps, + u32 num_cores); /// Reschedule the core emulation void Reschedule(); diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h index af127ea63c..a683a4ee53 100644 --- a/src/core/file_sys/ncch_container.h +++ b/src/core/file_sys/ncch_container.h @@ -165,7 +165,11 @@ struct ExHeader_StorageInfo { struct ExHeader_ARM11_SystemLocalCaps { u64_le program_id; u32_le core_version; - u8 reserved_flag; + union { + u8 n3ds_cpu_flags; + BitField<0, 1, u8> enable_l2_cache; + BitField<1, 1, u8> enable_804MHz_cpu; + }; u8 n3ds_mode; union { u8 flags0; diff --git a/src/core/file_sys/title_metadata.cpp b/src/core/file_sys/title_metadata.cpp index d6bf079f36..f9f05ec8bd 100644 --- a/src/core/file_sys/title_metadata.cpp +++ b/src/core/file_sys/title_metadata.cpp @@ -181,6 +181,12 @@ std::array TitleMetadata::GetContentCTRByIndex(std::size_t index) const return ctr; } +bool TitleMetadata::HasEncryptedContent() const { + return std::any_of(tmd_chunks.begin(), tmd_chunks.end(), [](auto& chunk) { + return (static_cast(chunk.type) & FileSys::TMDContentTypeFlag::Encrypted) != 0; + }); +} + void TitleMetadata::SetTitleID(u64 title_id) { tmd_body.title_id = title_id; } diff --git a/src/core/file_sys/title_metadata.h b/src/core/file_sys/title_metadata.h index cb52d22deb..620988568f 100644 --- a/src/core/file_sys/title_metadata.h +++ b/src/core/file_sys/title_metadata.h @@ -98,6 +98,7 @@ public: u16 GetContentTypeByIndex(std::size_t index) const; u64 GetContentSizeByIndex(std::size_t index) const; std::array GetContentCTRByIndex(std::size_t index) const; + bool HasEncryptedContent() const; void SetTitleID(u64 title_id); void SetTitleType(u32 type); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e114822493..61612d617c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -23,13 +23,15 @@ namespace Kernel { /// Initialize the kernel KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, - std::function prepare_reschedule_callback, u32 system_mode, - u32 num_cores, u8 n3ds_mode) + std::function prepare_reschedule_callback, + MemoryMode memory_mode, u32 num_cores, + const New3dsHwCapabilities& n3ds_hw_caps) : memory(memory), timing(timing), - prepare_reschedule_callback(std::move(prepare_reschedule_callback)) { + prepare_reschedule_callback(std::move(prepare_reschedule_callback)), memory_mode(memory_mode), + n3ds_hw_caps(n3ds_hw_caps) { std::generate(memory_regions.begin(), memory_regions.end(), [] { return std::make_shared(); }); - MemoryInit(system_mode, n3ds_mode); + MemoryInit(memory_mode, n3ds_hw_caps.memory_mode); resource_limits = std::make_unique(*this); for (u32 core_id = 0; core_id < num_cores; ++core_id) { @@ -176,6 +178,8 @@ void KernelSystem::serialize(Archive& ar, const unsigned int file_version) { ar& shared_page_handler; ar& stored_processes; ar& next_thread_id; + ar& memory_mode; + ar& n3ds_hw_caps; // Deliberately don't include debugger info to allow debugging through loads if (Archive::is_loading::value) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 936ce3f137..69d267de0f 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -97,11 +97,44 @@ union CoreVersion { BitField<24, 8, u32> major; }; +/// Common memory memory modes. +enum class MemoryMode : u8 { + Prod = 0, ///< 64MB app memory + Dev1 = 2, ///< 96MB app memory + Dev2 = 3, ///< 80MB app memory + Dev3 = 4, ///< 72MB app memory + Dev4 = 5, ///< 32MB app memory +}; + +/// New 3DS memory modes. +enum class New3dsMemoryMode : u8 { + Legacy = 0, ///< Use Old 3DS system mode. + NewProd = 1, ///< 124MB app memory + NewDev1 = 2, ///< 178MB app memory + NewDev2 = 3, ///< 124MB app memory +}; + +/// Structure containing N3DS hardware capability flags. +struct New3dsHwCapabilities { + bool enable_l2_cache; ///< Whether extra L2 cache should be enabled. + bool enable_804MHz_cpu; ///< Whether the CPU should run at 804MHz. + New3dsMemoryMode memory_mode; ///< The New 3DS memory mode. + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& enable_l2_cache; + ar& enable_804MHz_cpu; + ar& memory_mode; + } + friend class boost::serialization::access; +}; + class KernelSystem { public: explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, - std::function prepare_reschedule_callback, u32 system_mode, - u32 num_cores, u8 n3ds_mode); + std::function prepare_reschedule_callback, MemoryMode memory_mode, + u32 num_cores, const New3dsHwCapabilities& n3ds_hw_caps); ~KernelSystem(); using PortPair = std::pair, std::shared_ptr>; @@ -279,6 +312,14 @@ public: void ResetThreadIDs(); + MemoryMode GetMemoryMode() const { + return memory_mode; + } + + const New3dsHwCapabilities& GetNew3dsHwCapabilities() const { + return n3ds_hw_caps; + } + /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort std::unordered_map> named_ports; @@ -289,7 +330,7 @@ public: Core::Timing& timing; private: - void MemoryInit(u32 mem_type, u8 n3ds_mode); + void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode); std::function prepare_reschedule_callback; @@ -324,6 +365,9 @@ private: u32 next_thread_id; + MemoryMode memory_mode; + New3dsHwCapabilities n3ds_hw_caps; + friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int file_version); diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 8fb2a85bd6..b77a147201 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -37,29 +37,20 @@ static const u32 memory_region_sizes[8][3] = { {0x0B200000, 0x02E00000, 0x02000000}, // 7 }; -namespace MemoryMode { -enum N3DSMode : u8 { - Mode6 = 1, - Mode7 = 2, - Mode6_2 = 3, -}; -} - -void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) { - ASSERT(mem_type != 1); - +void KernelSystem::MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode) { const bool is_new_3ds = Settings::values.is_new_3ds.GetValue(); - u32 reported_mem_type = mem_type; + u32 mem_type_index = static_cast(memory_mode); + u32 reported_mem_type = static_cast(memory_mode); if (is_new_3ds) { - if (n3ds_mode == MemoryMode::Mode6 || n3ds_mode == MemoryMode::Mode6_2) { - mem_type = 6; + if (n3ds_mode == New3dsMemoryMode::NewProd || n3ds_mode == New3dsMemoryMode::NewDev2) { + mem_type_index = 6; reported_mem_type = 6; - } else if (n3ds_mode == MemoryMode::Mode7) { - mem_type = 7; + } else if (n3ds_mode == New3dsMemoryMode::NewDev1) { + mem_type_index = 7; reported_mem_type = 7; } else { // On the N3ds, all O3ds configurations (<=5) are forced to 6 instead. - mem_type = 6; + mem_type_index = 6; } } @@ -67,7 +58,7 @@ void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) { // the sizes specified in the memory_region_sizes table. VAddr base = 0; for (int i = 0; i < 3; ++i) { - memory_regions[i]->Reset(base, memory_region_sizes[mem_type][i]); + memory_regions[i]->Reset(base, memory_region_sizes[mem_type_index][i]); base += memory_regions[i]->size; } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 70e4ac7c87..855deebd6c 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -96,22 +96,38 @@ ResultVal CIAFile::Read(u64 offset, std::size_t length, u8* buffer) } ResultCode CIAFile::WriteTicket() { - container.LoadTicket(data, container.GetTicketOffset()); + auto load_result = container.LoadTicket(data, container.GetTicketOffset()); + if (load_result != Loader::ResultStatus::Success) { + LOG_ERROR(Service_AM, "Could not read ticket from CIA."); + // TODO: Correct result code. + return {ErrCodes::InvalidCIAHeader, ErrorModule::AM, ErrorSummary::InvalidArgument, + ErrorLevel::Permanent}; + } + + // TODO: Write out .tik files to nand? install_state = CIAInstallState::TicketLoaded; return RESULT_SUCCESS; } ResultCode CIAFile::WriteTitleMetadata() { - container.LoadTitleMetadata(data, container.GetTitleMetadataOffset()); + auto load_result = container.LoadTitleMetadata(data, container.GetTitleMetadataOffset()); + if (load_result != Loader::ResultStatus::Success) { + LOG_ERROR(Service_AM, "Could not read title metadata from CIA."); + // TODO: Correct result code. + return {ErrCodes::InvalidCIAHeader, ErrorModule::AM, ErrorSummary::InvalidArgument, + ErrorLevel::Permanent}; + } + FileSys::TitleMetadata tmd = container.GetTitleMetadata(); tmd.Print(); // If a TMD already exists for this app (ie 00000000.tmd), the incoming TMD // will be the same plus one, (ie 00000001.tmd), both will be kept until // the install is finalized and old contents can be discarded. - if (FileUtil::Exists(GetTitleMetadataPath(media_type, tmd.GetTitleID()))) + if (FileUtil::Exists(GetTitleMetadataPath(media_type, tmd.GetTitleID()))) { is_update = true; + } std::string tmd_path = GetTitleMetadataPath(media_type, tmd.GetTitleID(), is_update); @@ -121,28 +137,49 @@ ResultCode CIAFile::WriteTitleMetadata() { FileUtil::CreateFullPath(tmd_folder); // Save TMD so that we can start getting new .app paths - if (tmd.Save(tmd_path) != Loader::ResultStatus::Success) - return FileSys::ERROR_INSUFFICIENT_SPACE; + if (tmd.Save(tmd_path) != Loader::ResultStatus::Success) { + LOG_ERROR(Service_AM, "Failed to install title metadata file from CIA."); + // TODO: Correct result code. + return FileSys::ERROR_FILE_NOT_FOUND; + } // Create any other .app folders which may not exist yet std::string app_folder; - Common::SplitPath(GetTitleContentPath(media_type, tmd.GetTitleID(), - FileSys::TMDContentIndex::Main, is_update), - &app_folder, nullptr, nullptr); + auto main_content_path = GetTitleContentPath(media_type, tmd.GetTitleID(), + FileSys::TMDContentIndex::Main, is_update); + Common::SplitPath(main_content_path, &app_folder, nullptr, nullptr); FileUtil::CreateFullPath(app_folder); auto content_count = container.GetTitleMetadata().GetContentCount(); content_written.resize(content_count); - if (auto title_key = container.GetTicket().GetTitleKey()) { - decryption_state->content.resize(content_count); - for (std::size_t i = 0; i < content_count; ++i) { - auto ctr = tmd.GetContentCTRByIndex(i); - decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(), - ctr.data()); + content_files.clear(); + for (std::size_t i = 0; i < content_count; i++) { + auto path = GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update); + auto& file = content_files.emplace_back(path, "wb"); + if (!file.IsOpen()) { + LOG_ERROR(Service_AM, "Could not open output file '{}' for content {}.", path, i); + // TODO: Correct error code. + return FileSys::ERROR_FILE_NOT_FOUND; + } + } + + if (container.GetTitleMetadata().HasEncryptedContent()) { + if (auto title_key = container.GetTicket().GetTitleKey()) { + decryption_state->content.resize(content_count); + for (std::size_t i = 0; i < content_count; ++i) { + auto ctr = tmd.GetContentCTRByIndex(i); + decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(), + ctr.data()); + } + } else { + LOG_ERROR(Service_AM, "Could not read title key from ticket for encrypted CIA."); + // TODO: Correct error code. + return FileSys::ERROR_FILE_NOT_FOUND; } } else { - LOG_ERROR(Service_AM, "Can't get title key from ticket"); + LOG_INFO(Service_AM, + "Title has no encrypted content, skipping initializing decryption state."); } install_state = CIAInstallState::TMDLoaded; @@ -155,7 +192,7 @@ ResultVal CIAFile::WriteContentData(u64 offset, std::size_t length, // has been written since we might get a written buffer which contains multiple .app // contents or only part of a larger .app's contents. const u64 offset_max = offset + length; - for (std::size_t i = 0; i < container.GetTitleMetadata().GetContentCount(); i++) { + for (std::size_t i = 0; i < content_written.size(); i++) { if (content_written[i] < container.GetContentSize(i)) { // The size, minimum unwritten offset, and maximum unwritten offset of this content const u64 size = container.GetContentSize(i); @@ -174,22 +211,12 @@ ResultVal CIAFile::WriteContentData(u64 offset, std::size_t length, // Since the incoming TMD has already been written, we can use GetTitleContentPath // to get the content paths to write to. FileSys::TitleMetadata tmd = container.GetTitleMetadata(); - FileUtil::IOFile file(GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update), - content_written[i] ? "ab" : "wb"); - - if (!file.IsOpen()) { - return FileSys::ERROR_INSUFFICIENT_SPACE; - } + auto& file = content_files[i]; std::vector temp(buffer + (range_min - offset), buffer + (range_min - offset) + available_to_write); if ((tmd.GetContentTypeByIndex(i) & FileSys::TMDContentTypeFlag::Encrypted) != 0) { - if (decryption_state->content.size() <= i) { - // TODO: There is probably no correct error to return here. What error should be - // returned? - return FileSys::ERROR_INSUFFICIENT_SPACE; - } decryption_state->content[i].ProcessData(temp.data(), temp.data(), temp.size()); } @@ -234,8 +261,9 @@ ResultVal CIAFile::Write(u64 offset, std::size_t length, bool flush } // If we don't have a header yet, we can't pull offsets of other sections - if (install_state == CIAInstallState::InstallStarted) + if (install_state == CIAInstallState::InstallStarted) { return length; + } // If we have been given data before (or including) .app content, pull it into // our buffer, but only pull *up to* the content offset, no further. @@ -251,28 +279,30 @@ ResultVal CIAFile::Write(u64 offset, std::size_t length, bool flush std::memcpy(data.data() + copy_offset, buffer + buf_offset, buf_copy_size); } - // TODO(shinyquagsire23): Write out .tik files to nand? - // The end of our TMD is at the beginning of Content data, so ensure we have that much // buffered before trying to parse. if (written >= container.GetContentOffset() && install_state != CIAInstallState::TMDLoaded) { auto result = WriteTicket(); - if (result.IsError()) + if (result.IsError()) { return result; + } result = WriteTitleMetadata(); - if (result.IsError()) + if (result.IsError()) { return result; + } } // Content data sizes can only be retrieved from TMD data - if (install_state != CIAInstallState::TMDLoaded) + if (install_state != CIAInstallState::TMDLoaded) { return length; + } // From this point forward, data will no longer be buffered in data auto result = WriteContentData(offset, length, buffer); - if (result.Failed()) + if (result.Failed()) { return result; + } return length; } @@ -286,11 +316,13 @@ bool CIAFile::SetSize(u64 size) const { } bool CIAFile::Close() const { - bool complete = true; - for (std::size_t i = 0; i < container.GetTitleMetadata().GetContentCount(); i++) { - if (content_written[i] < container.GetContentSize(static_cast(i))) - complete = false; - } + bool complete = + install_state >= CIAInstallState::TMDLoaded && + content_written.size() == container.GetTitleMetadata().GetContentCount() && + std::all_of(content_written.begin(), content_written.end(), + [this, i = 0](auto& bytes_written) mutable { + return bytes_written >= container.GetContentSize(static_cast(i++)); + }); // Install aborted if (!complete) { @@ -314,16 +346,17 @@ bool CIAFile::Close() const { // For each content ID in the old TMD, check if there is a matching ID in the new // TMD. If a CIA contains (and wrote to) an identical ID, it should be kept while // IDs which only existed for the old TMD should be deleted. - for (u16 old_index = 0; old_index < old_tmd.GetContentCount(); old_index++) { + for (std::size_t old_index = 0; old_index < old_tmd.GetContentCount(); old_index++) { bool abort = false; - for (u16 new_index = 0; new_index < new_tmd.GetContentCount(); new_index++) { + for (std::size_t new_index = 0; new_index < new_tmd.GetContentCount(); new_index++) { if (old_tmd.GetContentIDByIndex(old_index) == new_tmd.GetContentIDByIndex(new_index)) { abort = true; } } - if (abort) + if (abort) { break; + } // If the file to delete is the current launched rom, signal the system to delete // the current rom instead of deleting it now, once all the handles to the file @@ -331,8 +364,9 @@ bool CIAFile::Close() const { std::string to_delete = GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index); if (!(Core::System::GetInstance().IsPoweredOn() && - Core::System::GetInstance().SetSelfDelete(to_delete))) + Core::System::GetInstance().SetSelfDelete(to_delete))) { FileUtil::Delete(to_delete); + } } FileUtil::Delete(old_tmd_path); @@ -357,29 +391,29 @@ InstallStatus InstallCIA(const std::string& path, Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID())); bool title_key_available = container.GetTicket().GetTitleKey().has_value(); - - for (std::size_t i = 0; i < container.GetTitleMetadata().GetContentCount(); i++) { - if ((container.GetTitleMetadata().GetContentTypeByIndex(static_cast(i)) & - FileSys::TMDContentTypeFlag::Encrypted) && - !title_key_available) { - LOG_ERROR(Service_AM, "File {} is encrypted! Aborting...", path); - return InstallStatus::ErrorEncrypted; - } + if (!title_key_available && container.GetTitleMetadata().HasEncryptedContent()) { + LOG_ERROR(Service_AM, "File {} is encrypted and no title key is available! Aborting...", + path); + return InstallStatus::ErrorEncrypted; } FileUtil::IOFile file(path, "rb"); - if (!file.IsOpen()) + if (!file.IsOpen()) { + LOG_ERROR(Service_AM, "Could not open CIA file '{}'.", path); return InstallStatus::ErrorFailedToOpenFile; + } std::array buffer; + auto file_size = file.GetSize(); std::size_t total_bytes_read = 0; - while (total_bytes_read != file.GetSize()) { + while (total_bytes_read != file_size) { std::size_t bytes_read = file.ReadBytes(buffer.data(), buffer.size()); auto result = installFile.Write(static_cast(total_bytes_read), bytes_read, true, static_cast(buffer.data())); - if (update_callback) - update_callback(total_bytes_read, file.GetSize()); + if (update_callback) { + update_callback(total_bytes_read, file_size); + } if (result.Failed()) { LOG_ERROR(Service_AM, "CIA file installation aborted with error code {:08x}", result.Code().raw); @@ -521,6 +555,11 @@ InstallStatus InstallFromNus(u64 title_id, int version) { #endif } +u64 GetTitleUpdateId(u64 title_id) { + // Real services seem to just discard and replace the whole high word. + return (title_id & 0xFFFFFFFF) | (static_cast(TID_HIGH_UPDATE) << 32); +} + Service::FS::MediaType GetTitleMediaType(u64 titleId) { u16 platform = static_cast(titleId >> 48); u16 category = static_cast((titleId >> 32) & 0xFFFF); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 674f6616b5..1bd6a399cb 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -26,6 +26,10 @@ namespace Core { class System; } +namespace FileUtil { +class IOFile; +} + namespace Service::FS { enum class MediaType : u32; } @@ -96,6 +100,7 @@ private: FileSys::CIAContainer container; std::vector data; std::vector content_written; + std::vector content_files; Service::FS::MediaType media_type; class DecryptionState; @@ -118,6 +123,13 @@ InstallStatus InstallCIA(const std::string& path, */ InstallStatus InstallFromNus(u64 title_id, int version = -1); +/** + * Get the update title ID for a title + * @param titleId the title ID + * @returns The update title ID + */ +u64 GetTitleUpdateId(u64 title_id); + /** * Get the mediatype for an installed title * @param titleId the installed title ID diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 9d00973f9f..68f3026eeb 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -19,7 +19,7 @@ SERVICE_CONSTRUCT_IMPL(Service::APT::AppletManager) namespace Service::APT { /// The interval at which the home button update callback will be called, 16.6ms -static constexpr u64 home_button_update_interval_us = 16666; +static constexpr u64 button_update_interval_us = 16666; struct AppletTitleData { // There are two possible applet ids for each applet. @@ -232,7 +232,7 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) { parameter.sender_id); if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) { - SendCaptureBufferInfo(parameter.buffer); + SetCaptureInfo(parameter.buffer); CaptureFrameBuffers(); } @@ -407,11 +407,56 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) { return RESULT_SUCCESS; } +ResultCode AppletManager::Finalize(AppletId app_id) { + auto slot = GetAppletSlotFromId(app_id); + if (slot == AppletSlot::Error) { + return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status}; + } + + auto slot_data = GetAppletSlot(slot); + slot_data->Reset(); + + auto inactive = active_slot == AppletSlot::Error; + if (!inactive) { + auto active_slot_data = GetAppletSlot(active_slot); + inactive = active_slot_data->applet_id == AppletId::None || + active_slot_data->attributes.applet_pos.Value() == AppletPos::Invalid; + } + + if (inactive) { + active_slot = GetAppletSlotFromPos(AppletPos::System); + } + + return RESULT_SUCCESS; +} + +u32 AppletManager::CountRegisteredApplet() { + return static_cast(std::count_if(applet_slots.begin(), applet_slots.end(), + [](auto& slot_data) { return slot_data.registered; })); +} + bool AppletManager::IsRegistered(AppletId app_id) { auto slot = GetAppletSlotFromId(app_id); return slot != AppletSlot::Error && GetAppletSlot(slot)->registered; } +ResultVal AppletManager::GetAttribute(AppletId app_id) { + auto slot = GetAppletSlotFromId(app_id); + if (slot == AppletSlot::Error) { + return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status); + } + + auto slot_data = GetAppletSlot(slot); + if (!slot_data->registered) { + return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status); + } + + return slot_data->attributes; +} + ResultVal AppletManager::InquireNotification(AppletId app_id) { auto slot = GetAppletSlotFromId(app_id); if (slot != AppletSlot::Error) { @@ -441,6 +486,15 @@ ResultCode AppletManager::SendNotification(Notification notification) { ErrorLevel::Status}; } +void AppletManager::SendNotificationToAll(Notification notification) { + for (auto& slot_data : applet_slots) { + if (slot_data.registered) { + slot_data.notification = notification; + slot_data.notification_event->Signal(); + } + } +} + ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { // The real APT service returns an error if there's a pending APT parameter when this function // is called. @@ -457,6 +511,8 @@ ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { last_library_launcher_slot = active_slot; last_prepared_library_applet = applet_id; + capture_buffer_info.reset(); + auto cfg = Service::CFG::GetModule(system); auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id, cfg->GetRegionValue())); @@ -594,6 +650,71 @@ ResultCode AppletManager::CancelLibraryApplet(bool app_exiting) { }); } +ResultCode AppletManager::SendDspSleep(AppletId from_applet_id, + std::shared_ptr object) { + auto lib_slot = GetAppletSlotFromPos(AppletPos::Library); + auto lib_app_id = + lib_slot != AppletSlot::Error ? GetAppletSlot(lib_slot)->applet_id : AppletId::None; + if (from_applet_id == lib_app_id) { + SendParameter({ + .sender_id = from_applet_id, + .destination_id = AppletId::Application, + .signal = SignalType::DspSleep, + .object = std::move(object), + }); + } + + auto sys_lib_slot = GetAppletSlotFromPos(AppletPos::SysLibrary); + auto sys_lib_app_id = + sys_lib_slot != AppletSlot::Error ? GetAppletSlot(sys_lib_slot)->applet_id : AppletId::None; + if (from_applet_id == sys_lib_app_id) { + auto sys_slot = GetAppletSlotFromPos(AppletPos::System); + auto sys_app_id = + sys_slot != AppletSlot::Error ? GetAppletSlot(sys_slot)->applet_id : AppletId::None; + SendParameter({ + .sender_id = from_applet_id, + .destination_id = sys_app_id, + .signal = SignalType::DspSleep, + .object = std::move(object), + }); + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::SendDspWakeUp(AppletId from_applet_id, + std::shared_ptr object) { + auto lib_slot = GetAppletSlotFromPos(AppletPos::Library); + auto lib_app_id = + lib_slot != AppletSlot::Error ? GetAppletSlot(lib_slot)->applet_id : AppletId::None; + if (from_applet_id == lib_app_id) { + SendParameter({ + .sender_id = from_applet_id, + .destination_id = AppletId::Application, + .signal = SignalType::DspSleep, + .object = std::move(object), + }); + } else { + auto sys_lib_slot = GetAppletSlotFromPos(AppletPos::SysLibrary); + auto sys_lib_app_id = sys_lib_slot != AppletSlot::Error + ? GetAppletSlot(sys_lib_slot)->applet_id + : AppletId::None; + if (from_applet_id == sys_lib_app_id) { + auto sys_slot = GetAppletSlotFromPos(AppletPos::System); + auto sys_app_id = + sys_slot != AppletSlot::Error ? GetAppletSlot(sys_slot)->applet_id : AppletId::None; + SendParameter({ + .sender_id = from_applet_id, + .destination_id = sys_app_id, + .signal = SignalType::DspSleep, + .object = std::move(object), + }); + } + } + + return RESULT_SUCCESS; +} + ResultCode AppletManager::PrepareToStartSystemApplet(AppletId applet_id) { // The real APT service returns an error if there's a pending APT parameter when this function // is called. @@ -730,6 +851,9 @@ ResultCode AppletManager::PrepareToJumpToHomeMenu() { } last_jump_to_home_slot = active_slot; + + capture_buffer_info.reset(); + if (last_jump_to_home_slot == AppletSlot::Application) { EnsureHomeMenuLoaded(); } @@ -963,10 +1087,7 @@ ResultVal AppletManager::GetAppletInfo(AppletId app_i ErrorLevel::Status); } - // TODO: Basic heuristic to guess media type, needs proper implementation. - auto media_type = ((slot_data->title_id >> 32) & 0xFFFFFFFF) == 0x00040000 - ? Service::FS::MediaType::SDMC - : Service::FS::MediaType::NAND; + auto media_type = Service::AM::GetTitleMediaType(slot_data->title_id); return AppletInfo{ .title_id = slot_data->title_id, .media_type = media_type, @@ -976,6 +1097,61 @@ ResultVal AppletManager::GetAppletInfo(AppletId app_i }; } +ResultVal AppletManager::Unknown54(u32 in_param) { + auto slot_data = GetAppletSlot(AppletSlot::Application); + if (slot_data->applet_id == AppletId::None) { + return ResultCode{ErrCodes::AppNotRunning, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Permanent}; + } + + if (in_param >= 0x80) { + // TODO: Add error description name when the parameter is known. + return ResultCode{10, ErrorModule::Applet, ErrorSummary::InvalidArgument, + ErrorLevel::Usage}; + } + + // TODO: Figure out what this logic is actually for. + auto check_target = + in_param >= 0x40 ? Service::FS::MediaType::GameCard : Service::FS::MediaType::SDMC; + auto check_update = in_param == 0x01 || in_param == 0x42; + + auto app_media_type = Service::AM::GetTitleMediaType(slot_data->title_id); + auto app_update_media_type = + Service::AM::GetTitleMediaType(Service::AM::GetTitleUpdateId(slot_data->title_id)); + if (app_media_type == check_target || (check_update && app_update_media_type == check_target)) { + return Service::FS::MediaType::SDMC; + } else { + return Service::FS::MediaType::NAND; + } +} + +TargetPlatform AppletManager::GetTargetPlatform() { + if (Settings::values.is_new_3ds.GetValue() && !new_3ds_mode_blocked) { + return TargetPlatform::New3ds; + } else { + return TargetPlatform::Old3ds; + } +} + +ApplicationRunningMode AppletManager::GetApplicationRunningMode() { + auto slot_data = GetAppletSlot(AppletSlot::Application); + if (slot_data->applet_id == AppletId::None) { + return ApplicationRunningMode::NoApplication; + } + + // APT checks whether the system is a New 3DS and the 804MHz CPU speed is enabled to determine + // the result. + auto new_3ds_mode = GetTargetPlatform() == TargetPlatform::New3ds && + system.Kernel().GetNew3dsHwCapabilities().enable_804MHz_cpu; + if (slot_data->registered) { + return new_3ds_mode ? ApplicationRunningMode::New3dsRegistered + : ApplicationRunningMode::Old3dsRegistered; + } else { + return new_3ds_mode ? ApplicationRunningMode::New3dsUnregistered + : ApplicationRunningMode::Old3dsUnregistered; + } +} + ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, ApplicationJumpFlags flags) { // A running application can not launch another application directly because the applet state @@ -988,11 +1164,8 @@ ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType // Save the title data to send it to the Home Menu when DoApplicationJump is called. auto application_slot_data = GetAppletSlot(AppletSlot::Application); app_jump_parameters.current_title_id = application_slot_data->title_id; - // TODO: Basic heuristic to guess media type, needs proper implementation. app_jump_parameters.current_media_type = - ((application_slot_data->title_id >> 32) & 0xFFFFFFFF) == 0x00040000 - ? Service::FS::MediaType::SDMC - : Service::FS::MediaType::NAND; + Service::AM::GetTitleMediaType(application_slot_data->title_id); if (flags == ApplicationJumpFlags::UseCurrentParameters) { app_jump_parameters.next_title_id = app_jump_parameters.current_title_id; app_jump_parameters.next_media_type = app_jump_parameters.current_media_type; @@ -1055,19 +1228,8 @@ ResultCode AppletManager::DoApplicationJump(const DeliverArg& arg) { return RESULT_SUCCESS; */ - auto new_path = Service::AM::GetTitleContentPath(app_jump_parameters.next_media_type, - app_jump_parameters.next_title_id); - if (new_path.empty() || !FileUtil::Exists(new_path)) { - // TODO: This can happen if the requested title is not installed. Need a way to find - // non-installed titles in the game list. - LOG_CRITICAL( - Service_APT, - "Failed to find title during application jump: {} Resetting current title instead.", - new_path); - new_path.clear(); - } - - system.RequestReset(new_path); + NS::RebootToTitle(system, app_jump_parameters.next_media_type, + app_jump_parameters.next_title_id); return RESULT_SUCCESS; } } @@ -1093,6 +1255,8 @@ ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType app_start_parameters->next_title_id = title_id; app_start_parameters->next_media_type = media_type; + capture_buffer_info.reset(); + return RESULT_SUCCESS; } @@ -1126,13 +1290,14 @@ ResultCode AppletManager::StartApplication(const std::vector& parameter, app_start_parameters.reset(); if (!paused) { - return WakeupApplication(); + return WakeupApplication(nullptr, {}); } return RESULT_SUCCESS; } -ResultCode AppletManager::WakeupApplication() { +ResultCode AppletManager::WakeupApplication(std::shared_ptr object, + const std::vector& buffer) { // Send a Wakeup signal via the apt parameter to the application once it registers itself. // The real APT service does this by spin waiting on another thread until the application is // registered. @@ -1140,6 +1305,8 @@ ResultCode AppletManager::WakeupApplication() { .sender_id = AppletId::HomeMenu, .destination_id = AppletId::Application, .signal = SignalType::Wakeup, + .object = std::move(object), + .buffer = buffer, }); return RESULT_SUCCESS; @@ -1199,48 +1366,74 @@ void AppletManager::EnsureHomeMenuLoaded() { } } -static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height, - u32 format) { - static constexpr auto screen_capture_base_vaddr = static_cast(0x1F500000); - static constexpr auto screen_width = 240; - static constexpr auto screen_width_pow2 = 256; - const auto bpp = format < 2 ? 3 : 2; +static u32 GetDisplayBufferModePixelSize(DisplayBufferMode mode) { + switch (mode) { + // NOTE: APT does in fact use pixel size 3 for R8G8B8A8 captures. + case DisplayBufferMode::R8G8B8A8: + case DisplayBufferMode::R8G8B8: + return 3; + case DisplayBufferMode::R5G6B5: + case DisplayBufferMode::R5G5B5A1: + case DisplayBufferMode::R4G4B4A4: + return 2; + case DisplayBufferMode::Unimportable: + return 0; + default: + UNREACHABLE_MSG("Unknown display buffer mode {}", mode); + return 0; + } +} - Memory::RasterizerFlushVirtualRegion(src, screen_width * height * bpp, +static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height, + DisplayBufferMode mode) { + const auto bpp = GetDisplayBufferModePixelSize(mode); + if (bpp == 0) { + return; + } + + Memory::RasterizerFlushVirtualRegion(src, GSP::FRAMEBUFFER_WIDTH * height * bpp, Memory::FlushMode::Flush); - auto dst_vaddr = screen_capture_base_vaddr + capture_offset; + // Address in VRAM that APT copies framebuffer captures to. + constexpr VAddr screen_capture_base_vaddr = Memory::VRAM_VADDR + 0x500000; + const auto dst_vaddr = screen_capture_base_vaddr + capture_offset; auto dst_ptr = system.Memory().GetPointer(dst_vaddr); + if (!dst_ptr) { + LOG_ERROR(Service_APT, + "Could not retrieve framebuffer capture destination buffer, skipping screen."); + return; + } + const auto src_ptr = system.Memory().GetPointer(src); + if (!src_ptr) { + LOG_ERROR(Service_APT, + "Could not retrieve framebuffer capture source buffer, skipping screen."); + return; + } + for (u32 y = 0; y < height; y++) { - for (u32 x = 0; x < screen_width; x++) { - auto dst_offset = - VideoCore::GetMortonOffset(x, y, bpp) + (y & ~7) * screen_width_pow2 * bpp; - auto src_offset = bpp * (screen_width * y + x); + for (u32 x = 0; x < GSP::FRAMEBUFFER_WIDTH; x++) { + const auto dst_offset = VideoCore::GetMortonOffset(x, y, bpp) + + (y & ~7) * GSP::FRAMEBUFFER_WIDTH_POW2 * bpp; + const auto src_offset = bpp * (GSP::FRAMEBUFFER_WIDTH * y + x); std::memcpy(dst_ptr + dst_offset, src_ptr + src_offset, bpp); } } - Memory::RasterizerFlushVirtualRegion(dst_vaddr, screen_width_pow2 * height * bpp, + Memory::RasterizerFlushVirtualRegion(dst_vaddr, GSP::FRAMEBUFFER_WIDTH_POW2 * height * bpp, Memory::FlushMode::Invalidate); } void AppletManager::CaptureFrameBuffers() { - auto gsp = - Core::System::GetInstance().ServiceManager().GetService("gsp::Gpu"); - auto active_thread_id = gsp->GetActiveThreadId(); - auto top_screen = gsp->GetFrameBufferInfo(active_thread_id, 0); - auto bottom_screen = gsp->GetFrameBufferInfo(active_thread_id, 1); - - auto top_fb = top_screen->framebuffer_info[top_screen->index]; - auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index]; - - CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, bottom_fb.address_left, 320, + CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, + GSP::FRAMEBUFFER_SAVE_AREA_BOTTOM, GSP::BOTTOM_FRAMEBUFFER_HEIGHT, capture_info->bottom_screen_format); - CaptureFrameBuffer(system, capture_info->top_screen_left_offset, top_fb.address_left, 400, + CaptureFrameBuffer(system, capture_info->top_screen_left_offset, + GSP::FRAMEBUFFER_SAVE_AREA_TOP_LEFT, GSP::TOP_FRAMEBUFFER_HEIGHT, capture_info->top_screen_format); if (capture_info->is_3d) { - CaptureFrameBuffer(system, capture_info->top_screen_right_offset, top_fb.address_right, 400, + CaptureFrameBuffer(system, capture_info->top_screen_right_offset, + GSP::FRAMEBUFFER_SAVE_AREA_TOP_RIGHT, GSP::TOP_FRAMEBUFFER_HEIGHT, capture_info->top_screen_format); } } @@ -1248,27 +1441,38 @@ void AppletManager::CaptureFrameBuffers() { void AppletManager::LoadInputDevices() { home_button = Input::CreateDevice( Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]); + power_button = Input::CreateDevice( + Settings::values.current_input_profile.buttons[Settings::NativeButton::Power]); } -void AppletManager::HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) { +void AppletManager::ButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) { if (is_device_reload_pending.exchange(false)) { LoadInputDevices(); } - const bool state = home_button->GetStatus(); // NOTE: We technically do support loading and jumping to home menu even if it isn't // initially registered. However since the home menu suspend is not bug-free, we don't // want normal users who didn't launch the home menu accidentally pressing the home // button binding and freezing their game, so for now, gate it to only environments // where the home menu was already loaded by the user (last condition). - if (state && !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) { - SendNotification(Notification::HomeButtonSingle); + + if (GetAppletSlot(AppletSlot::HomeMenu)->registered) { + const bool home_state = home_button->GetStatus(); + if (home_state && !last_home_button_state) { + SendNotification(Notification::HomeButtonSingle); + } + last_home_button_state = home_state; + + const bool power_state = power_button->GetStatus(); + if (power_state && !last_power_button_state) { + SendNotificationToAll(Notification::PowerButtonClick); + } + last_power_button_state = power_state; } - last_home_button_state = state; // Reschedule recurrent event - Core::System::GetInstance().CoreTiming().ScheduleEvent( - usToCycles(home_button_update_interval_us) - cycles_late, home_button_update_event); + system.CoreTiming().ScheduleEvent(usToCycles(button_update_interval_us) - cycles_late, + button_update_event); } AppletManager::AppletManager(Core::System& system) : system(system) { @@ -1286,12 +1490,11 @@ AppletManager::AppletManager(Core::System& system) : system(system) { system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter"); } HLE::Applets::Init(); - home_button_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent( - "Home Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) { - HomeButtonUpdateEvent(user_data, cycles_late); + button_update_event = system.CoreTiming().RegisterEvent( + "APT Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) { + ButtonUpdateEvent(user_data, cycles_late); }); - Core::System::GetInstance().CoreTiming().ScheduleEvent( - usToCycles(home_button_update_interval_us), home_button_update_event); + system.CoreTiming().ScheduleEvent(usToCycles(button_update_interval_us), button_update_event); } AppletManager::~AppletManager() { diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index 1f7b041deb..13bbbd2d67 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -99,6 +99,21 @@ enum class AppletId : u32 { Memolib2 = 0x409, }; +/// Application Old/New 3DS target platforms +enum class TargetPlatform : u8 { + Old3ds = 0, + New3ds = 1, +}; + +/// Application Old/New 3DS running modes +enum class ApplicationRunningMode : u8 { + NoApplication = 0, + Old3dsRegistered = 1, + New3dsRegistered = 2, + Old3dsUnregistered = 3, + New3dsUnregistered = 4, +}; + /// Holds information about the parameters used in Send/Glance/ReceiveParameter struct MessageParameter { AppletId sender_id = AppletId::None; @@ -195,6 +210,15 @@ private: friend class boost::serialization::access; }; +enum class DisplayBufferMode : u32_le { + R8G8B8A8 = 0, + R8G8B8 = 1, + R5G6B5 = 2, + R5G5B5A1 = 3, + R4G4B4A4 = 4, + Unimportable = 0xFFFFFFFF, +}; + /// Used by the application to pass information about the current framebuffer to applets. struct CaptureBufferInfo { u32_le size; @@ -202,10 +226,10 @@ struct CaptureBufferInfo { INSERT_PADDING_BYTES(0x3); // Padding for alignment u32_le top_screen_left_offset; u32_le top_screen_right_offset; - u32_le top_screen_format; + DisplayBufferMode top_screen_format; u32_le bottom_screen_left_offset; u32_le bottom_screen_right_offset; - u32_le bottom_screen_format; + DisplayBufferMode bottom_screen_format; private: template @@ -256,10 +280,14 @@ public: ResultVal Initialize(AppletId app_id, AppletAttributes attributes); ResultCode Enable(AppletAttributes attributes); + ResultCode Finalize(AppletId app_id); + u32 CountRegisteredApplet(); bool IsRegistered(AppletId app_id); + ResultVal GetAttribute(AppletId app_id); ResultVal InquireNotification(AppletId app_id); ResultCode SendNotification(Notification notification); + void SendNotificationToAll(Notification notification); ResultCode PrepareToStartLibraryApplet(AppletId applet_id); ResultCode PreloadLibraryApplet(AppletId applet_id); @@ -271,6 +299,9 @@ public: const std::vector& buffer); ResultCode CancelLibraryApplet(bool app_exiting); + ResultCode SendDspSleep(AppletId from_applet_id, std::shared_ptr object); + ResultCode SendDspWakeUp(AppletId from_applet_id, std::shared_ptr object); + ResultCode PrepareToStartSystemApplet(AppletId applet_id); ResultCode StartSystemApplet(AppletId applet_id, std::shared_ptr object, const std::vector& buffer); @@ -294,8 +325,10 @@ public: ApplicationJumpFlags flags); ResultCode DoApplicationJump(const DeliverArg& arg); - boost::optional ReceiveDeliverArg() const { - return deliver_arg; + boost::optional ReceiveDeliverArg() { + auto arg = deliver_arg; + deliver_arg = boost::none; + return arg; } void SetDeliverArg(boost::optional arg) { deliver_arg = std::move(arg); @@ -309,22 +342,34 @@ public: } return buffer; } - std::vector ReceiveCaptureBufferInfo() { - std::vector buffer = GetCaptureInfo(); - capture_info.reset(); - return buffer; - } - void SendCaptureBufferInfo(std::vector buffer) { + void SetCaptureInfo(std::vector buffer) { ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); capture_info.emplace(); std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); } + std::vector ReceiveCaptureBufferInfo() { + std::vector buffer; + if (capture_buffer_info) { + buffer.resize(sizeof(CaptureBufferInfo)); + std::memcpy(buffer.data(), &capture_buffer_info.get(), sizeof(CaptureBufferInfo)); + capture_buffer_info.reset(); + } + return buffer; + } + void SendCaptureBufferInfo(std::vector buffer) { + ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); + + capture_buffer_info.emplace(); + std::memcpy(&capture_buffer_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); + } + ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type); ResultCode StartApplication(const std::vector& parameter, const std::vector& hmac, bool paused); - ResultCode WakeupApplication(); + ResultCode WakeupApplication(std::shared_ptr object, + const std::vector& buffer); ResultCode CancelApplication(); struct AppletManInfo { @@ -349,6 +394,10 @@ public: return app_jump_parameters; } + ResultVal Unknown54(u32 in_param); + TargetPlatform GetTargetPlatform(); + ApplicationRunningMode GetApplicationRunningMode(); + private: /// APT lock retrieved via GetLockHandle. std::shared_ptr lock; @@ -366,6 +415,7 @@ private: boost::optional deliver_arg{}; boost::optional capture_info; + boost::optional capture_buffer_info; static constexpr std::size_t NumAppletSlot = 4; @@ -427,10 +477,16 @@ private: bool application_cancelled = false; AppletSlot application_close_target = AppletSlot::Error; - Core::TimingEventType* home_button_update_event; + // This flag is used to determine if an app that supports New 3DS capabilities should use them. + // It also affects the results of APT:GetTargetPlatform and APT:GetApplicationRunningMode. + bool new_3ds_mode_blocked = false; + + Core::TimingEventType* button_update_event; std::atomic is_device_reload_pending{true}; std::unique_ptr home_button; + std::unique_ptr power_button; bool last_home_button_state = false; + bool last_power_button_state = false; Core::System& system; @@ -455,7 +511,7 @@ private: void CaptureFrameBuffers(); void LoadInputDevices(); - void HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late); + void ButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late); template void serialize(Archive& ar, const unsigned int file_version) { @@ -465,6 +521,8 @@ private: ar& delayed_parameter; ar& app_start_parameters; ar& deliver_arg; + ar& capture_info; + ar& capture_buffer_info; ar& active_slot; ar& last_library_launcher_slot; ar& last_prepared_library_applet; @@ -474,6 +532,7 @@ private: ar& ordered_to_close_application; ar& application_cancelled; ar& application_close_target; + ar& new_3ds_mode_blocked; ar& lock; ar& capture_info; } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 44f6c58e87..998ba00d9e 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -16,16 +16,19 @@ #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/romfs.h" +#include "core/hle/service/am/am.h" #include "core/hle/service/apt/applet_manager.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_a.h" #include "core/hle/service/apt/apt_s.h" #include "core/hle/service/apt/apt_u.h" #include "core/hle/service/apt/bcfnt/bcfnt.h" +#include "core/hle/service/apt/ns.h" #include "core/hle/service/apt/ns_c.h" #include "core/hle/service/apt/ns_s.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/fs/archive.h" +#include "core/hle/service/fs/fs_user.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" #include "core/hw/aes/ccm.h" @@ -41,7 +44,6 @@ void Module::serialize(Archive& ar, const unsigned int file_version) { ar& shared_font_loaded; ar& shared_font_relocated; ar& cpu_percent; - ar& unknown_ns_state_field; ar& screen_capture_post_permission; ar& applet_manager; if (file_version > 0) { @@ -90,14 +92,19 @@ void Module::NSInterface::RebootSystem(Kernel::HLERequestContext& ctx) { const auto title_id = rp.Pop(); const auto media_type = static_cast(rp.Pop()); rp.Skip(1, false); // Skip padding + // TODO: Utilize requested memory type. const auto mem_type = rp.Pop(); LOG_WARNING(Service_APT, "called launch_title={}, title_id={:016X}, media_type={:02X}, mem_type={:02X}", launch_title, title_id, media_type, mem_type); - // TODO: Implement loading a specific title. - apt->system.RequestReset(); + // TODO: Handle mem type. + if (launch_title) { + NS::RebootToTitle(apt->system, media_type, title_id); + } else { + apt->system.RequestReset(); + } IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -371,6 +378,16 @@ void Module::APTInterface::Enable(Kernel::HLERequestContext& ctx) { rb.Push(apt->applet_manager->Enable(attributes)); } +void Module::APTInterface::Finalize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto applet_id = rp.PopEnum(); + + LOG_DEBUG(Service_APT, "called applet_id={:08X}", applet_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->Finalize(applet_id)); +} + void Module::APTInterface::GetAppletManInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); auto applet_pos = rp.PopEnum(); @@ -391,6 +408,16 @@ void Module::APTInterface::GetAppletManInfo(Kernel::HLERequestContext& ctx) { } } +void Module::APTInterface::CountRegisteredApplet(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(apt->applet_manager->CountRegisteredApplet()); +} + void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto app_id = rp.PopEnum(); @@ -402,6 +429,23 @@ void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id); } +void Module::APTInterface::GetAttribute(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto app_id = rp.PopEnum(); + + LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id); + + auto applet_attr = apt->applet_manager->GetAttribute(app_id); + if (applet_attr.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(applet_attr.Code()); + } else { + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(applet_attr.Unwrap().raw); + } +} + void Module::APTInterface::InquireNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto app_id = rp.PopEnum(); @@ -557,6 +601,21 @@ void Module::APTInterface::GetProgramIdOnApplicationJump(Kernel::HLERequestConte rb.Push(static_cast(parameters.next_media_type)); } +void Module::APTInterface::SendDeliverArg(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto param_size = rp.Pop(); + const auto hmac_size = rp.Pop(); + const auto param = rp.PopStaticBuffer(); + const auto hmac = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called param_size={:08X}, hmac_size={:08X}", param_size, hmac_size); + + apt->applet_manager->SetDeliverArg(DeliverArg{param, hmac}); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + void Module::APTInterface::ReceiveDeliverArg(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto param_size = rp.Pop(); @@ -611,7 +670,7 @@ void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_APT, "called"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(apt->applet_manager->WakeupApplication()); + rb.Push(apt->applet_manager->WakeupApplication(nullptr, {})); } void Module::APTInterface::CancelApplication(Kernel::HLERequestContext& ctx) { @@ -856,6 +915,28 @@ void Module::APTInterface::OrderToCloseSystemApplet(Kernel::HLERequestContext& c rb.Push(apt->applet_manager->OrderToCloseSystemApplet()); } +void Module::APTInterface::SendDspSleep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto from_app_id = rp.PopEnum(); + const auto object = rp.PopGenericObject(); + + LOG_DEBUG(Service_APT, "called, from_app_id={:08X}", from_app_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->SendDspSleep(from_app_id, object)); +} + +void Module::APTInterface::SendDspWakeUp(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto from_app_id = rp.PopEnum(); + const auto object = rp.PopGenericObject(); + + LOG_DEBUG(Service_APT, "called, from_app_id={:08X}", from_app_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->SendDspWakeUp(from_app_id, object)); +} + void Module::APTInterface::PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); @@ -972,29 +1053,124 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); } -void Module::APTInterface::SetScreenCapPostPermission(Kernel::HLERequestContext& ctx) { +void Module::APTInterface::Unknown54(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); + auto in_param = rp.Pop(); - LOG_DEBUG(Service_APT, "called, screen_capture_post_permission={}", - apt->screen_capture_post_permission); + LOG_DEBUG(Service_APT, "called, in_param={}", in_param); - apt->screen_capture_post_permission = static_cast(rp.Pop() & 0xF); + auto media_type = apt->applet_manager->Unknown54(in_param); + if (media_type.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(media_type.Code()); + } else { + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.PushEnum(media_type.Unwrap()); + } +} + +void Module::APTInterface::SetScreenCapturePostPermission(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + auto permission = rp.Pop(); + + LOG_DEBUG(Service_APT, "called, permission={}", permission); + + apt->screen_capture_post_permission = static_cast(permission & 0xF); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); // No error } -void Module::APTInterface::GetScreenCapPostPermission(Kernel::HLERequestContext& ctx) { +void Module::APTInterface::GetScreenCapturePostPermission(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - LOG_DEBUG(Service_APT, "(STUBBED) called, screen_capture_post_permission={}", - apt->screen_capture_post_permission); + LOG_DEBUG(Service_APT, "called"); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); // No error rb.Push(static_cast(apt->screen_capture_post_permission)); } +void Module::APTInterface::WakeupApplication2(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto buffer_size = rp.Pop(); + const auto object = rp.PopGenericObject(); + const auto buffer = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called buffer_size={:#010X}", buffer_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->WakeupApplication(object, buffer)); +} + +void Module::APTInterface::GetProgramId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto process_id = rp.PopPID(); + + LOG_DEBUG(Service_APT, "called process_id={}", process_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + + auto fs_user = + Core::System::GetInstance().ServiceManager().GetService("fs:USER"); + ASSERT_MSG(fs_user != nullptr, "fs:USER service is missing."); + + auto program_info_result = fs_user->GetProgramLaunchInfo(process_id); + if (program_info_result.Failed()) { + // On failure, APT still returns a success result with a program ID of 0. + rb.Push(0); + } else { + rb.Push(program_info_result.Unwrap().program_id); + } +} + +void Module::APTInterface::GetProgramInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto title_id = rp.Pop(); + const auto media_type = static_cast(rp.Pop()); + rp.Skip(1, false); // Skip padding + + LOG_WARNING(Service_APT, "called title_id={:016X}, media_type={:02X}", title_id, media_type); + + std::string path = Service::AM::GetTitleContentPath(media_type, title_id); + auto loader = Loader::GetLoader(path); + if (!loader) { + LOG_WARNING(Service_APT, "Could not find .app for title 0x{:016x}", title_id); + + // TODO: Proper error code + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_UNKNOWN); + return; + } + + auto memory_mode = loader->LoadKernelMemoryMode(); + if (memory_mode.second != Loader::ResultStatus::Success || !memory_mode.first) { + LOG_ERROR(Service_APT, "Could not load memory mode for title 0x{:016x}", title_id); + + // TODO: Proper error code + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_UNKNOWN); + return; + } + + auto core_version = loader->LoadCoreVersion(); + if (core_version.second != Loader::ResultStatus::Success || !core_version.first) { + LOG_ERROR(Service_APT, "Could not load core version for title 0x{:016x}", title_id); + + // TODO: Proper error code + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_UNKNOWN); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast(memory_mode.first.value())); + rb.Push(core_version.first.value()); +} + void Module::APTInterface::GetAppletInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto app_id = rp.PopEnum(); @@ -1154,37 +1330,83 @@ void Module::APTInterface::Unwrap(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(output); } -void Module::APTInterface::CheckNew3DSApp(Kernel::HLERequestContext& ctx) { +void Module::APTInterface::Reboot(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto title_id = rp.Pop(); + const auto media_type = static_cast(rp.Pop()); + rp.Skip(1, false); // Skip padding + const auto mem_type = rp.Pop(); + const auto firm_tid_low = rp.Pop(); + + LOG_WARNING(Service_APT, + "called title_id={:016X}, media_type={:02X}, mem_type={:02X}, firm_tid_low={:08X}", + title_id, media_type, mem_type, firm_tid_low); + + // TODO: Handle mem type and FIRM TID low. + NS::RebootToTitle(apt->system, media_type, title_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::APTInterface::HardwareResetAsync(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - LOG_WARNING(Service_APT, "(STUBBED) called"); + LOG_WARNING(Service_APT, "called"); + + apt->system.RequestReset(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::APTInterface::GetTargetPlatform(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + + LOG_DEBUG(Service_APT, "called"); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - if (apt->unknown_ns_state_field) { - rb.Push(RESULT_SUCCESS); - rb.Push(0); - } else { - PTM::CheckNew3DS(rb); - } + rb.Push(RESULT_SUCCESS); + rb.PushEnum(apt->applet_manager->GetTargetPlatform()); } void Module::APTInterface::CheckNew3DS(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); + + LOG_DEBUG(Service_APT, "called"); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - - LOG_WARNING(Service_APT, "(STUBBED) called"); - PTM::CheckNew3DS(rb); } -void Module::APTInterface::Unknown0x0103(Kernel::HLERequestContext& ctx) { +void Module::APTInterface::GetApplicationRunningMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); + + LOG_DEBUG(Service_APT, "called"); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - - LOG_WARNING(Service_APT, "(STUBBED) called"); - rb.Push(RESULT_SUCCESS); - rb.Push(Settings::values.is_new_3ds ? 2 : 1); + rb.PushEnum(apt->applet_manager->GetApplicationRunningMode()); +} + +void Module::APTInterface::IsStandardMemoryLayout(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + + LOG_DEBUG(Service_APT, "called"); + + bool is_standard; + if (Settings::values.is_new_3ds) { + // Memory layout is standard if it is not NewDev1 (178MB) + is_standard = apt->system.Kernel().GetNew3dsHwCapabilities().memory_mode != + Kernel::New3dsMemoryMode::NewDev1; + } else { + // Memory layout is standard if it is Prod (64MB) + is_standard = apt->system.Kernel().GetMemoryMode() == Kernel::MemoryMode::Prod; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(is_standard); } void Module::APTInterface::IsTitleAllowed(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 90688ff85a..3a4251691e 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -214,6 +214,15 @@ public: */ void Enable(Kernel::HLERequestContext& ctx); + /** + * APT::Finalize service function + * Inputs: + * 1 : Applet ID + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void Finalize(Kernel::HLERequestContext& ctx); + /** * APT::GetAppletManInfo service function. * Inputs: @@ -241,6 +250,15 @@ public: */ void GetAppletInfo(Kernel::HLERequestContext& ctx); + /** + * APT::CountRegisteredApplet service function + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of registered applets + */ + void CountRegisteredApplet(Kernel::HLERequestContext& ctx); + /** * APT::IsRegistered service function. This returns whether the specified AppID is * registered with NS yet. An AppID is "registered" once the process associated with the @@ -257,6 +275,17 @@ public: */ void IsRegistered(Kernel::HLERequestContext& ctx); + /** + * APT::GetAttribute service function + * Inputs: + * 1 : AppID + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Applet Attributes + */ + void GetAttribute(Kernel::HLERequestContext& ctx); + void InquireNotification(Kernel::HLERequestContext& ctx); /** @@ -596,6 +625,21 @@ public: */ void GetProgramIdOnApplicationJump(Kernel::HLERequestContext& ctx); + /** + * APT::SendDeliverArg service function + * Inputs: + * 0 : Command header [0x00340084] + * 1 : Parameter Size (capped to 0x300) + * 2 : HMAC Size (capped to 0x20) + * 3 : (Parameter Size << 14) | 2 + * 4 : Input buffer for Parameter + * 5 : (HMAC Size << 14) | 0x802 + * 6 : Input buffer for HMAC + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void SendDeliverArg(Kernel::HLERequestContext& ctx); + /** * APT::ReceiveDeliverArg service function * Inputs: @@ -687,6 +731,30 @@ public: */ void OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx); + /** + * APT::SendDspSleep service function + * Inputs: + * 1 : Source App ID + * 2 : Handle translation header (0x0) + * 3 : Handle parameter + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void SendDspSleep(Kernel::HLERequestContext& ctx); + + /** + * APT::SendDspWakeUp service function + * Inputs: + * 1 : Source App ID + * 2 : Handle translation header (0x0) + * 3 : Handle parameter + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void SendDspWakeUp(Kernel::HLERequestContext& ctx); + /** * APT::PrepareToJumpToHomeMenu service function * Inputs: @@ -818,27 +886,97 @@ public: void GetStartupArgument(Kernel::HLERequestContext& ctx); /** - * APT::SetScreenCapPostPermission service function + * APT::Unknown54 service function + * Inputs: + * 0 : Header Code[0x00540040] + * 1 : Unknown + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Media Type + */ + void Unknown54(Kernel::HLERequestContext& ctx); + + /** + * APT::SetScreenCapturePostPermission service function * Inputs: * 0 : Header Code[0x00550040] * 1 : u8 The screenshot posting permission * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ - void SetScreenCapPostPermission(Kernel::HLERequestContext& ctx); + void SetScreenCapturePostPermission(Kernel::HLERequestContext& ctx); /** - * APT::GetScreenCapPostPermission service function + * APT::GetScreenCapturePostPermission service function * Inputs: * 0 : Header Code[0x00560000] * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : u8 The screenshot posting permission */ - void GetScreenCapPostPermission(Kernel::HLERequestContext& ctx); + void GetScreenCapturePostPermission(Kernel::HLERequestContext& ctx); /** - * APT::CheckNew3DSApp service function + * APT::WakeupApplication2 service function. + * Inputs: + * 1 : Buffer parameter size, (max is 0x1000) + * 2 : Handle translation header (0x0) + * 3 : Handle parameter + * 4 : (Buffer parameter size << 14) | 2 + * 5 : Buffer parameter pointer + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code + */ + void WakeupApplication2(Kernel::HLERequestContext& ctx); + + /** + * APT::GetProgramId service function. + * Inputs: + * 1 : Process ID translation header (0x20) + * 2 : Process ID (filled in by kernel) + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : u64 Program ID + */ + void GetProgramId(Kernel::HLERequestContext& ctx); + + /** + * APT::GetProgramInfo service function. + * Inputs: + * 1-2 : Title ID + * 3 : Media Type + * 4 : Padding + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Required app memory mode + * 3 : Required app FIRM title ID low + */ + void GetProgramInfo(Kernel::HLERequestContext& ctx); + + /** + * APT::Reboot service function. + * Inputs: + * 1-2 : Title ID + * 3 : Media Type + * 4 : Padding + * 5 : Launch memory type + * 6 : FIRM title ID low 32-bits + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void Reboot(Kernel::HLERequestContext& ctx); + + /** + * APT::HardwareResetAsync service function. + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void HardwareResetAsync(Kernel::HLERequestContext& ctx); + + /** + * APT::GetTargetPlatform service function * Outputs: * 1: Result code, 0 on success, otherwise error code * 2: u8 output: 0 = Old3DS, 1 = New3DS. @@ -849,7 +987,7 @@ public: * Normally this NS state field is zero, however this state field is set to 1 * when APT:PrepareToStartApplication is used with flags bit8 is set. */ - void CheckNew3DSApp(Kernel::HLERequestContext& ctx); + void GetTargetPlatform(Kernel::HLERequestContext& ctx); /** * Wrapper for PTMSYSM:CheckNew3DS @@ -861,12 +999,20 @@ public: void CheckNew3DS(Kernel::HLERequestContext& ctx); /** - * APT::Unknown0x0103 service function. Determines whether Smash 4 allows C-Stick + * APT::GetApplicationRunningMode service function * Outputs: * 1: Result code, 0 on success otherwise error code - * 2: u8 output: 2 = New3DS+valid/initialized (in Smash 4), 1 = Old3DS or invalid + * 2: u8 output: 0 = No application, 1/3 = Old 3DS, 2/4 = New 3DS */ - void Unknown0x0103(Kernel::HLERequestContext& ctx); + void GetApplicationRunningMode(Kernel::HLERequestContext& ctx); + + /** + * APT::IsStandardMemoryLayout service function + * Outputs: + * 1: Result code, 0 on success otherwise error code + * 2: bool output: Whether the system is in its standard memory layout. + */ + void IsStandardMemoryLayout(Kernel::HLERequestContext& ctx); /** * APT::IsTitleAllowed service function @@ -906,9 +1052,6 @@ private: u32 cpu_percent = 0; ///< CPU time available to the running application - // APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode - u8 unknown_ns_state_field = 0; - std::array sys_menu_arg_buffer; ScreencapPostPermission screen_capture_post_permission = diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 260470c30c..443005858d 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -14,13 +14,13 @@ APT_A::APT_A(std::shared_ptr apt) {0x0001, &APT_A::GetLockHandle, "GetLockHandle"}, {0x0002, &APT_A::Initialize, "Initialize"}, {0x0003, &APT_A::Enable, "Enable"}, - {0x0004, nullptr, "Finalize"}, + {0x0004, &APT_A::Finalize, "Finalize"}, {0x0005, &APT_A::GetAppletManInfo, "GetAppletManInfo"}, {0x0006, &APT_A::GetAppletInfo, "GetAppletInfo"}, {0x0007, nullptr, "GetLastSignaledAppletId"}, - {0x0008, nullptr, "CountRegisteredApplet"}, + {0x0008, &APT_A::CountRegisteredApplet, "CountRegisteredApplet"}, {0x0009, &APT_A::IsRegistered, "IsRegistered"}, - {0x000A, nullptr, "GetAttribute"}, + {0x000A, &APT_A::GetAttribute, "GetAttribute"}, {0x000B, &APT_A::InquireNotification, "InquireNotification"}, {0x000C, &APT_A::SendParameter, "SendParameter"}, {0x000D, &APT_A::ReceiveParameter, "ReceiveParameter"}, @@ -39,7 +39,7 @@ APT_A::APT_A(std::shared_ptr apt) {0x001A, &APT_A::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, {0x001B, &APT_A::StartApplication, "StartApplication"}, {0x001C, &APT_A::WakeupApplication, "WakeupApplication"}, - {0x001D, nullptr, "CancelApplication"}, + {0x001D, &APT_A::CancelApplication, "CancelApplication"}, {0x001E, &APT_A::StartLibraryApplet, "StartLibraryApplet"}, {0x001F, &APT_A::StartSystemApplet, "StartSystemApplet"}, {0x0020, nullptr, "StartNewestHomeMenu"}, @@ -62,7 +62,7 @@ APT_A::APT_A(std::shared_ptr apt) {0x0031, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, {0x0032, &APT_A::DoApplicationJump, "DoApplicationJump"}, {0x0033, &APT_A::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, - {0x0034, nullptr, "SendDeliverArg"}, + {0x0034, &APT_A::SendDeliverArg, "SendDeliverArg"}, {0x0035, &APT_A::ReceiveDeliverArg, "ReceiveDeliverArg"}, {0x0036, &APT_A::LoadSysMenuArg, "LoadSysMenuArg"}, {0x0037, &APT_A::StoreSysMenuArg, "StoreSysMenuArg"}, @@ -70,8 +70,8 @@ APT_A::APT_A(std::shared_ptr apt) {0x0039, nullptr, "PrepareToStartResidentApplet"}, {0x003A, nullptr, "StartResidentApplet"}, {0x003B, &APT_A::CancelLibraryApplet, "CancelLibraryApplet"}, - {0x003C, nullptr, "SendDspSleep"}, - {0x003D, nullptr, "SendDspWakeUp"}, + {0x003C, &APT_A::SendDspSleep, "SendDspSleep"}, + {0x003D, &APT_A::SendDspWakeUp, "SendDspWakeUp"}, {0x003E, nullptr, "ReplySleepQuery"}, {0x003F, nullptr, "ReplySleepNotificationComplete"}, {0x0040, &APT_A::SendCaptureBufferInfo, "SendCaptureBufferInfo"}, @@ -82,26 +82,27 @@ APT_A::APT_A(std::shared_ptr apt) {0x0045, &APT_A::GetWirelessRebootInfo, "GetWirelessRebootInfo"}, {0x0046, &APT_A::Wrap, "Wrap"}, {0x0047, &APT_A::Unwrap, "Unwrap"}, - {0x0048, nullptr, "GetProgramInfo"}, - {0x0049, nullptr, "Reboot"}, + {0x0048, &APT_A::GetProgramInfo, "GetProgramInfo"}, + {0x0049, &APT_A::Reboot, "Reboot"}, {0x004A, &APT_A::GetCaptureInfo, "GetCaptureInfo"}, {0x004B, &APT_A::AppletUtility, "AppletUtility"}, {0x004C, nullptr, "SetFatalErrDispMode"}, {0x004D, nullptr, "GetAppletProgramInfo"}, - {0x004E, nullptr, "HardwareResetAsync"}, + {0x004E, &APT_A::HardwareResetAsync, "HardwareResetAsync"}, {0x004F, &APT_A::SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x0050, &APT_A::GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, {0x0051, &APT_A::GetStartupArgument, "GetStartupArgument"}, {0x0052, nullptr, "Wrap1"}, {0x0053, nullptr, "Unwrap1"}, - {0x0055, &APT_A::SetScreenCapPostPermission, "SetScreenCapPostPermission"}, - {0x0056, &APT_A::GetScreenCapPostPermission, "GetScreenCapPostPermission"}, - {0x0057, nullptr, "WakeupApplication2"}, - {0x0058, nullptr, "GetProgramID"}, - {0x0101, &APT_A::CheckNew3DSApp, "CheckNew3DSApp"}, + {0x0054, &APT_A::Unknown54, "Unknown54"}, + {0x0055, &APT_A::SetScreenCapturePostPermission, "SetScreenCapturePostPermission"}, + {0x0056, &APT_A::GetScreenCapturePostPermission, "GetScreenCapturePostPermission"}, + {0x0057, &APT_A::WakeupApplication2, "WakeupApplication2"}, + {0x0058, &APT_A::GetProgramId, "GetProgramId"}, + {0x0101, &APT_A::GetTargetPlatform, "GetTargetPlatform"}, {0x0102, &APT_A::CheckNew3DS, "CheckNew3DS"}, - {0x0103, &APT_A::Unknown0x0103, "Unknown0x0103"}, - {0x0104, nullptr, "IsStandardMemoryLayout"}, + {0x0103, &APT_A::GetApplicationRunningMode, "GetApplicationRunningMode"}, + {0x0104, &APT_A::IsStandardMemoryLayout, "IsStandardMemoryLayout"}, {0x0105, &APT_A::IsTitleAllowed, "IsTitleAllowed"}, // clang-format on }; diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 60718c6cd6..82cbfce85a 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -14,13 +14,13 @@ APT_S::APT_S(std::shared_ptr apt) {0x0001, &APT_S::GetLockHandle, "GetLockHandle"}, {0x0002, &APT_S::Initialize, "Initialize"}, {0x0003, &APT_S::Enable, "Enable"}, - {0x0004, nullptr, "Finalize"}, + {0x0004, &APT_S::Finalize, "Finalize"}, {0x0005, &APT_S::GetAppletManInfo, "GetAppletManInfo"}, {0x0006, &APT_S::GetAppletInfo, "GetAppletInfo"}, {0x0007, nullptr, "GetLastSignaledAppletId"}, - {0x0008, nullptr, "CountRegisteredApplet"}, + {0x0008, &APT_S::CountRegisteredApplet, "CountRegisteredApplet"}, {0x0009, &APT_S::IsRegistered, "IsRegistered"}, - {0x000A, nullptr, "GetAttribute"}, + {0x000A, &APT_S::GetAttribute, "GetAttribute"}, {0x000B, &APT_S::InquireNotification, "InquireNotification"}, {0x000C, &APT_S::SendParameter, "SendParameter"}, {0x000D, &APT_S::ReceiveParameter, "ReceiveParameter"}, @@ -39,7 +39,7 @@ APT_S::APT_S(std::shared_ptr apt) {0x001A, &APT_S::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, {0x001B, &APT_S::StartApplication, "StartApplication"}, {0x001C, &APT_S::WakeupApplication, "WakeupApplication"}, - {0x001D, nullptr, "CancelApplication"}, + {0x001D, &APT_S::CancelApplication, "CancelApplication"}, {0x001E, &APT_S::StartLibraryApplet, "StartLibraryApplet"}, {0x001F, &APT_S::StartSystemApplet, "StartSystemApplet"}, {0x0020, nullptr, "StartNewestHomeMenu"}, @@ -62,7 +62,7 @@ APT_S::APT_S(std::shared_ptr apt) {0x0031, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, {0x0032, &APT_S::DoApplicationJump, "DoApplicationJump"}, {0x0033, &APT_S::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, - {0x0034, nullptr, "SendDeliverArg"}, + {0x0034, &APT_S::SendDeliverArg, "SendDeliverArg"}, {0x0035, &APT_S::ReceiveDeliverArg, "ReceiveDeliverArg"}, {0x0036, &APT_S::LoadSysMenuArg, "LoadSysMenuArg"}, {0x0037, &APT_S::StoreSysMenuArg, "StoreSysMenuArg"}, @@ -70,8 +70,8 @@ APT_S::APT_S(std::shared_ptr apt) {0x0039, nullptr, "PrepareToStartResidentApplet"}, {0x003A, nullptr, "StartResidentApplet"}, {0x003B, &APT_S::CancelLibraryApplet, "CancelLibraryApplet"}, - {0x003C, nullptr, "SendDspSleep"}, - {0x003D, nullptr, "SendDspWakeUp"}, + {0x003C, &APT_S::SendDspSleep, "SendDspSleep"}, + {0x003D, &APT_S::SendDspWakeUp, "SendDspWakeUp"}, {0x003E, nullptr, "ReplySleepQuery"}, {0x003F, nullptr, "ReplySleepNotificationComplete"}, {0x0040, &APT_S::SendCaptureBufferInfo, "SendCaptureBufferInfo"}, @@ -82,26 +82,27 @@ APT_S::APT_S(std::shared_ptr apt) {0x0045, &APT_S::GetWirelessRebootInfo, "GetWirelessRebootInfo"}, {0x0046, &APT_S::Wrap, "Wrap"}, {0x0047, &APT_S::Unwrap, "Unwrap"}, - {0x0048, nullptr, "GetProgramInfo"}, - {0x0049, nullptr, "Reboot"}, + {0x0048, &APT_S::GetProgramInfo, "GetProgramInfo"}, + {0x0049, &APT_S::Reboot, "Reboot"}, {0x004A, &APT_S::GetCaptureInfo, "GetCaptureInfo"}, {0x004B, &APT_S::AppletUtility, "AppletUtility"}, {0x004C, nullptr, "SetFatalErrDispMode"}, {0x004D, nullptr, "GetAppletProgramInfo"}, - {0x004E, nullptr, "HardwareResetAsync"}, + {0x004E, &APT_S::HardwareResetAsync, "HardwareResetAsync"}, {0x004F, &APT_S::SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x0050, &APT_S::GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, {0x0051, &APT_S::GetStartupArgument, "GetStartupArgument"}, {0x0052, nullptr, "Wrap1"}, {0x0053, nullptr, "Unwrap1"}, - {0x0055, &APT_S::SetScreenCapPostPermission, "SetScreenCapPostPermission"}, - {0x0056, &APT_S::GetScreenCapPostPermission, "GetScreenCapPostPermission"}, - {0x0057, nullptr, "WakeupApplication2"}, - {0x0058, nullptr, "GetProgramID"}, - {0x0101, &APT_S::CheckNew3DSApp, "CheckNew3DSApp"}, + {0x0054, &APT_S::Unknown54, "Unknown54"}, + {0x0055, &APT_S::SetScreenCapturePostPermission, "SetScreenCapturePostPermission"}, + {0x0056, &APT_S::GetScreenCapturePostPermission, "GetScreenCapturePostPermission"}, + {0x0057, &APT_S::WakeupApplication2, "WakeupApplication2"}, + {0x0058, &APT_S::GetProgramId, "GetProgramId"}, + {0x0101, &APT_S::GetTargetPlatform, "GetTargetPlatform"}, {0x0102, &APT_S::CheckNew3DS, "CheckNew3DS"}, - {0x0103, &APT_S::Unknown0x0103, "Unknown0x0103"}, - {0x0104, nullptr, "IsStandardMemoryLayout"}, + {0x0103, &APT_S::GetApplicationRunningMode, "GetApplicationRunningMode"}, + {0x0104, &APT_S::IsStandardMemoryLayout, "IsStandardMemoryLayout"}, {0x0105, &APT_S::IsTitleAllowed, "IsTitleAllowed"}, // clang-format on }; diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index a78ab91b06..2d5373179f 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -14,13 +14,13 @@ APT_U::APT_U(std::shared_ptr apt) {0x0001, &APT_U::GetLockHandle, "GetLockHandle"}, {0x0002, &APT_U::Initialize, "Initialize"}, {0x0003, &APT_U::Enable, "Enable"}, - {0x0004, nullptr, "Finalize"}, + {0x0004, &APT_U::Finalize, "Finalize"}, {0x0005, &APT_U::GetAppletManInfo, "GetAppletManInfo"}, {0x0006, &APT_U::GetAppletInfo, "GetAppletInfo"}, {0x0007, nullptr, "GetLastSignaledAppletId"}, - {0x0008, nullptr, "CountRegisteredApplet"}, + {0x0008, &APT_U::CountRegisteredApplet, "CountRegisteredApplet"}, {0x0009, &APT_U::IsRegistered, "IsRegistered"}, - {0x000A, nullptr, "GetAttribute"}, + {0x000A, &APT_U::GetAttribute, "GetAttribute"}, {0x000B, &APT_U::InquireNotification, "InquireNotification"}, {0x000C, &APT_U::SendParameter, "SendParameter"}, {0x000D, &APT_U::ReceiveParameter, "ReceiveParameter"}, @@ -39,7 +39,7 @@ APT_U::APT_U(std::shared_ptr apt) {0x001A, &APT_U::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, {0x001B, &APT_U::StartApplication, "StartApplication"}, {0x001C, &APT_U::WakeupApplication, "WakeupApplication"}, - {0x001D, nullptr, "CancelApplication"}, + {0x001D, &APT_U::CancelApplication, "CancelApplication"}, {0x001E, &APT_U::StartLibraryApplet, "StartLibraryApplet"}, {0x001F, &APT_U::StartSystemApplet, "StartSystemApplet"}, {0x0020, nullptr, "StartNewestHomeMenu"}, @@ -62,7 +62,7 @@ APT_U::APT_U(std::shared_ptr apt) {0x0031, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, {0x0032, &APT_U::DoApplicationJump, "DoApplicationJump"}, {0x0033, &APT_U::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, - {0x0034, nullptr, "SendDeliverArg"}, + {0x0034, &APT_U::SendDeliverArg, "SendDeliverArg"}, {0x0035, &APT_U::ReceiveDeliverArg, "ReceiveDeliverArg"}, {0x0036, &APT_U::LoadSysMenuArg, "LoadSysMenuArg"}, {0x0037, &APT_U::StoreSysMenuArg, "StoreSysMenuArg"}, @@ -70,8 +70,8 @@ APT_U::APT_U(std::shared_ptr apt) {0x0039, nullptr, "PrepareToStartResidentApplet"}, {0x003A, nullptr, "StartResidentApplet"}, {0x003B, &APT_U::CancelLibraryApplet, "CancelLibraryApplet"}, - {0x003C, nullptr, "SendDspSleep"}, - {0x003D, nullptr, "SendDspWakeUp"}, + {0x003C, &APT_U::SendDspSleep, "SendDspSleep"}, + {0x003D, &APT_U::SendDspWakeUp, "SendDspWakeUp"}, {0x003E, nullptr, "ReplySleepQuery"}, {0x003F, nullptr, "ReplySleepNotificationComplete"}, {0x0040, &APT_U::SendCaptureBufferInfo, "SendCaptureBufferInfo"}, @@ -82,26 +82,27 @@ APT_U::APT_U(std::shared_ptr apt) {0x0045, &APT_U::GetWirelessRebootInfo, "GetWirelessRebootInfo"}, {0x0046, &APT_U::Wrap, "Wrap"}, {0x0047, &APT_U::Unwrap, "Unwrap"}, - {0x0048, nullptr, "GetProgramInfo"}, - {0x0049, nullptr, "Reboot"}, + {0x0048, &APT_U::GetProgramInfo, "GetProgramInfo"}, + {0x0049, &APT_U::Reboot, "Reboot"}, {0x004A, &APT_U::GetCaptureInfo, "GetCaptureInfo"}, {0x004B, &APT_U::AppletUtility, "AppletUtility"}, {0x004C, nullptr, "SetFatalErrDispMode"}, {0x004D, nullptr, "GetAppletProgramInfo"}, - {0x004E, nullptr, "HardwareResetAsync"}, + {0x004E, &APT_U::HardwareResetAsync, "HardwareResetAsync"}, {0x004F, &APT_U::SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x0050, &APT_U::GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, {0x0051, &APT_U::GetStartupArgument, "GetStartupArgument"}, {0x0052, nullptr, "Wrap1"}, {0x0053, nullptr, "Unwrap1"}, - {0x0055, &APT_U::SetScreenCapPostPermission, "SetScreenCapPostPermission"}, - {0x0056, &APT_U::GetScreenCapPostPermission, "GetScreenCapPostPermission"}, - {0x0057, nullptr, "WakeupApplication2"}, - {0x0058, nullptr, "GetProgramID"}, - {0x0101, &APT_U::CheckNew3DSApp, "CheckNew3DSApp"}, + {0x0054, &APT_U::Unknown54, "Unknown54"}, + {0x0055, &APT_U::SetScreenCapturePostPermission, "SetScreenCapturePostPermission"}, + {0x0056, &APT_U::GetScreenCapturePostPermission, "GetScreenCapturePostPermission"}, + {0x0057, &APT_U::WakeupApplication2, "WakeupApplication2"}, + {0x0058, &APT_U::GetProgramId, "GetProgramId"}, + {0x0101, &APT_U::GetTargetPlatform, "GetTargetPlatform"}, {0x0102, &APT_U::CheckNew3DS, "CheckNew3DS"}, - {0x0103, &APT_U::Unknown0x0103, "Unknown0x0103"}, - {0x0104, nullptr, "IsStandardMemoryLayout"}, + {0x0103, &APT_U::GetApplicationRunningMode, "GetApplicationRunningMode"}, + {0x0104, &APT_U::IsStandardMemoryLayout, "IsStandardMemoryLayout"}, {0x0105, &APT_U::IsTitleAllowed, "IsTitleAllowed"}, // clang-format on }; diff --git a/src/core/hle/service/apt/errors.h b/src/core/hle/service/apt/errors.h index ae02135130..25bffb9af4 100644 --- a/src/core/hle/service/apt/errors.h +++ b/src/core/hle/service/apt/errors.h @@ -8,5 +8,6 @@ namespace Service::APT::ErrCodes { enum { ParameterPresent = 2, InvalidAppletSlot = 4, + AppNotRunning = 11, }; } // namespace Service::APT::ErrCodes diff --git a/src/core/hle/service/apt/ns.cpp b/src/core/hle/service/apt/ns.cpp index 7c5b301386..5e859799bb 100644 --- a/src/core/hle/service/apt/ns.cpp +++ b/src/core/hle/service/apt/ns.cpp @@ -29,4 +29,17 @@ std::shared_ptr LaunchTitle(FS::MediaType media_type, u64 title return process; } +void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id) { + auto new_path = AM::GetTitleContentPath(media_type, title_id); + if (new_path.empty() || !FileUtil::Exists(new_path)) { + // TODO: This can happen if the requested title is not installed. Need a way to find + // non-installed titles in the game list. + LOG_CRITICAL(Service_APT, + "Failed to find title '{}' to jump to, resetting current title instead.", + new_path); + new_path.clear(); + } + system.RequestReset(new_path); +} + } // namespace Service::NS diff --git a/src/core/hle/service/apt/ns.h b/src/core/hle/service/apt/ns.h index 5d84106816..89fddbd332 100644 --- a/src/core/hle/service/apt/ns.h +++ b/src/core/hle/service/apt/ns.h @@ -18,4 +18,7 @@ namespace Service::NS { /// Loads and launches the title identified by title_id in the specified media type. std::shared_ptr LaunchTitle(FS::MediaType media_type, u64 title_id); +/// Reboots the system to the specified title. +void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id); + } // namespace Service::NS diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 12925a0deb..33800ad9e2 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -693,30 +693,23 @@ void FS_USER::GetProductInfo(Kernel::HLERequestContext& ctx) { void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - - u32 process_id = rp.Pop(); + const auto process_id = rp.Pop(); LOG_DEBUG(Service_FS, "process_id={}", process_id); - auto program_info = program_info_map.find(process_id); - IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); - if (program_info == program_info_map.end()) { + auto program_info_result = GetProgramLaunchInfo(process_id); + if (program_info_result.Failed()) { // Note: In this case, the rest of the parameters are not changed but the command header // remains the same. - rb.Push(ResultCode(FileSys::ErrCodes::ArchiveNotMounted, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Status)); + rb.Push(program_info_result.Code()); rb.Skip(4, false); return; } rb.Push(RESULT_SUCCESS); - rb.Push(program_info->second.program_id); - rb.Push(static_cast(program_info->second.media_type)); - - // TODO(Subv): Find out what this value means. - rb.Push(0); + rb.PushRaw(program_info_result.Unwrap()); } void FS_USER::ObsoletedCreateExtSaveData(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h index d6ecf484df..006ded62ff 100644 --- a/src/core/hle/service/fs/fs_user.h +++ b/src/core/hle/service/fs/fs_user.h @@ -7,6 +7,7 @@ #include #include #include "common/common_types.h" +#include "core/file_sys/errors.h" #include "core/hle/service/service.h" namespace Core { @@ -17,6 +18,13 @@ namespace Service::FS { class ArchiveManager; +struct ProgramInfo { + u64 program_id; + MediaType media_type; + INSERT_PADDING_BYTES(4); +}; +static_assert(sizeof(ProgramInfo) == 0x10, "ProgramInfo struct has incorrect size"); + struct ClientSlot : public Kernel::SessionRequestHandler::SessionDataBase { // We retrieves program ID for client process on FS::Initialize(WithSDKVersion) // Real 3DS matches program ID and process ID based on data registered by loader via fs:REG, so @@ -55,6 +63,17 @@ public: bool GetProductInfo(u32 process_id, ProductInfo& out_product_info); + /// Gets the registered program info of a process. + ResultVal GetProgramLaunchInfo(u32 process_id) const { + auto info = program_info_map.find(process_id); + if (info != program_info_map.end()) { + return info->second; + } else { + return ResultCode(FileSys::ErrCodes::ArchiveNotMounted, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); + } + } + private: void Initialize(Kernel::HLERequestContext& ctx); @@ -656,11 +675,6 @@ private: static ResultVal GetSpecialContentIndexFromTMD(MediaType media_type, u64 title_id, SpecialContentType type); - struct ProgramInfo { - u64 program_id; - MediaType media_type; - }; - std::unordered_map program_info_map; std::string current_gamecard_path; diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index f767fea702..abb838fde0 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -704,18 +704,73 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_GSP, "called"); } +static void CopyFrameBuffer(Core::System& system, VAddr dst, VAddr src, u32 stride, u32 lines) { + auto dst_ptr = system.Memory().GetPointer(dst); + const auto src_ptr = system.Memory().GetPointer(src); + if (!dst_ptr || !src_ptr) { + LOG_WARNING(Service_GSP, + "Could not resolve pointers for framebuffer capture, skipping screen."); + return; + } + + Memory::RasterizerFlushVirtualRegion(src, stride * lines, Memory::FlushMode::Flush); + std::memcpy(dst_ptr, src_ptr, stride * lines); + Memory::RasterizerFlushVirtualRegion(dst, stride * lines, Memory::FlushMode::Invalidate); +} + void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); + if (active_thread_id == std::numeric_limits::max()) { + LOG_WARNING(Service_GSP, "Called without an active thread."); + + // TODO: Find the right error code. + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(-1); + return; + } + LOG_INFO(Service_GSP, "called"); - // TODO: This should also DMA framebuffers into VRAM and save LCD register state. + // TODO: This should also save LCD register state. Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE, Memory::FlushMode::Flush); - auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR); + const auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR); saved_vram.emplace(std::vector(Memory::VRAM_SIZE)); std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE); + const auto top_screen = GetFrameBufferInfo(active_thread_id, 0); + if (top_screen) { + const auto top_fb = top_screen->framebuffer_info[top_screen->index]; + if (top_fb.address_left) { + CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT, top_fb.address_left, + top_fb.stride, TOP_FRAMEBUFFER_HEIGHT); + } else { + LOG_WARNING(Service_GSP, "No framebuffer bound to top left screen, skipping capture."); + } + if (top_fb.address_right) { + CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_RIGHT, top_fb.address_right, + top_fb.stride, TOP_FRAMEBUFFER_HEIGHT); + } else { + LOG_WARNING(Service_GSP, "No framebuffer bound to top right screen, skipping capture."); + } + } else { + LOG_WARNING(Service_GSP, "No top screen bound, skipping capture."); + } + + const auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1); + if (bottom_screen) { + const auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index]; + if (bottom_fb.address_left) { + CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM, bottom_fb.address_left, + bottom_fb.stride, BOTTOM_FRAMEBUFFER_HEIGHT); + } else { + LOG_WARNING(Service_GSP, "No framebuffer bound to bottom screen, skipping capture."); + } + } else { + LOG_WARNING(Service_GSP, "No bottom screen bound, skipping capture."); + } + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); } @@ -819,7 +874,7 @@ SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) { return nullptr; } -GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 2), system(system) { +GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 4), system(system) { static const FunctionInfo functions[] = { // clang-format off {0x0001, &GSP_GPU::WriteHWRegs, "WriteHWRegs"}, diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index cf976654ef..7de8191a24 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -186,6 +186,17 @@ struct CommandBuffer { }; static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size"); +constexpr u32 FRAMEBUFFER_WIDTH = 240; +constexpr u32 FRAMEBUFFER_WIDTH_POW2 = 256; +constexpr u32 TOP_FRAMEBUFFER_HEIGHT = 400; +constexpr u32 BOTTOM_FRAMEBUFFER_HEIGHT = 320; +constexpr u32 FRAMEBUFFER_HEIGHT_POW2 = 512; + +// These are the VRAM addresses that GSP copies framebuffers to in SaveVramSysArea. +constexpr VAddr FRAMEBUFFER_SAVE_AREA_TOP_LEFT = Memory::VRAM_VADDR + 0x273000; +constexpr VAddr FRAMEBUFFER_SAVE_AREA_TOP_RIGHT = Memory::VRAM_VADDR + 0x2B9800; +constexpr VAddr FRAMEBUFFER_SAVE_AREA_BOTTOM = Memory::VRAM_VADDR + 0x4C7800; + class GSP_GPU; class SessionData : public Kernel::SessionRequestHandler::SessionDataBase { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 77206c41b7..731db3808e 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -13,6 +13,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "core/file_sys/romfs_reader.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" namespace Kernel { @@ -99,23 +100,36 @@ public: virtual ResultStatus Load(std::shared_ptr& process) = 0; /** - * Loads the system mode that this application needs. - * This function defaults to 2 (96MB allocated to the application) if it can't read the + * Loads the core version (FIRM title ID low) that this application needs. + * This function defaults to 0x2 (NATIVE_FIRM) if it can't read the * information. - * @returns A pair with the optional system mode, and the status. + * @returns A pair with the optional core version, and the status. */ - virtual std::pair, ResultStatus> LoadKernelSystemMode() { - // 96MB allocated to the application. - return std::make_pair(2, ResultStatus::Success); + virtual std::pair, ResultStatus> LoadCoreVersion() { + return std::make_pair(0x2, ResultStatus::Success); } /** - * Loads the N3ds mode that this application uses. - * It defaults to 0 (O3DS default) if it can't read the information. - * @returns A pair with the optional N3ds mode, and the status. + * Loads the memory mode that this application needs. + * This function defaults to Dev1 (96MB allocated to the application) if it can't read the + * information. + * @returns A pair with the optional memory mode, and the status. */ - virtual std::pair, ResultStatus> LoadKernelN3dsMode() { - return std::make_pair(u8(0), ResultStatus::Success); + virtual std::pair, ResultStatus> LoadKernelMemoryMode() { + // 96MB allocated to the application. + return std::make_pair(Kernel::MemoryMode::Dev1, ResultStatus::Success); + } + + /** + * Loads the N3DS hardware capabilities that this application uses. + * It defaults to all disabled (O3DS) if it can't read the information. + * @returns A pair with the optional N3DS hardware capabilities, and the status. + */ + virtual std::pair, ResultStatus> + LoadNew3dsHwCapabilities() { + return std::make_pair( + Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}, + ResultStatus::Success); } /** diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index ca1fd916c1..e53d9cf6a2 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -16,6 +16,7 @@ #include "core/core.h" #include "core/file_sys/ncch_container.h" #include "core/file_sys/title_metadata.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/service/am/am.h" @@ -47,7 +48,7 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } -std::pair, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { +std::pair, ResultStatus> AppLoader_NCCH::LoadCoreVersion() { if (!is_loaded) { ResultStatus res = base_ncch.Load(); if (res != ResultStatus::Success) { @@ -55,12 +56,12 @@ std::pair, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode } } - // Set the system mode as the one from the exheader. - return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.system_mode.Value(), - ResultStatus::Success); + // Provide the core version from the exheader. + auto& ncch_caps = overlay_ncch->exheader_header.arm11_system_local_caps; + return std::make_pair(ncch_caps.core_version, ResultStatus::Success); } -std::pair, ResultStatus> AppLoader_NCCH::LoadKernelN3dsMode() { +std::pair, ResultStatus> AppLoader_NCCH::LoadKernelMemoryMode() { if (!is_loaded) { ResultStatus res = base_ncch.Load(); if (res != ResultStatus::Success) { @@ -68,9 +69,29 @@ std::pair, ResultStatus> AppLoader_NCCH::LoadKernelN3dsMode() } } - // Set the system mode as the one from the exheader. - return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.n3ds_mode, - ResultStatus::Success); + // Provide the memory mode from the exheader. + auto& ncch_caps = overlay_ncch->exheader_header.arm11_system_local_caps; + auto mode = static_cast(ncch_caps.system_mode.Value()); + return std::make_pair(mode, ResultStatus::Success); +} + +std::pair, ResultStatus> +AppLoader_NCCH::LoadNew3dsHwCapabilities() { + if (!is_loaded) { + ResultStatus res = base_ncch.Load(); + if (res != ResultStatus::Success) { + return std::make_pair(std::nullopt, res); + } + } + + // Provide the capabilities from the exheader. + auto& ncch_caps = overlay_ncch->exheader_header.arm11_system_local_caps; + auto caps = Kernel::New3dsHwCapabilities{ + ncch_caps.enable_l2_cache != 0, + ncch_caps.enable_804MHz_cpu != 0, + static_cast(ncch_caps.n3ds_mode), + }; + return std::make_pair(std::move(caps), ResultStatus::Success); } ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr& process) { diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index fa7fa7e16b..b240302de6 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -32,13 +32,16 @@ public: ResultStatus Load(std::shared_ptr& process) override; + std::pair, ResultStatus> LoadCoreVersion() override; + /** * Loads the Exheader and returns the system mode for this application. * @returns A pair with the optional system mode, and and the status. */ - std::pair, ResultStatus> LoadKernelSystemMode() override; + std::pair, ResultStatus> LoadKernelMemoryMode() override; - std::pair, ResultStatus> LoadKernelN3dsMode() override; + std::pair, ResultStatus> LoadNew3dsHwCapabilities() + override; ResultStatus IsExecutable(bool& out_executable) override; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 026d0cf23a..42adecd282 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(tests core/memory/vm_manager.cpp precompiled_headers.h audio_core/hle/hle.cpp + audio_core/hle/adts_reader.cpp audio_core/lle/lle.cpp audio_core/audio_fixures.h audio_core/decoder_tests.cpp diff --git a/src/tests/audio_core/hle/adts_reader.cpp b/src/tests/audio_core/hle/adts_reader.cpp new file mode 100644 index 0000000000..d4d3de2d8a --- /dev/null +++ b/src/tests/audio_core/hle/adts_reader.cpp @@ -0,0 +1,75 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "audio_core/hle/adts.h" + +namespace { +constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; +constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; + +AudioCore::ADTSData ParseADTS_Old(const unsigned char* buffer) { + u32 tmp = 0; + AudioCore::ADTSData out{}; + + // sync word 0xfff + tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); + if ((tmp & 0xffff) != 0xfff0) { + out.length = 0; + return out; + } + // bit 16 = no CRC + out.header_length = (buffer[1] & 0x1) ? 7 : 9; + out.mpeg2 = (buffer[1] >> 3) & 0x1; + // bit 17 to 18 + out.profile = (buffer[2] >> 6) + 1; + // bit 19 to 22 + tmp = (buffer[2] >> 2) & 0xf; + out.samplerate_idx = tmp; + out.samplerate = (tmp > 15) ? 0 : freq_table[tmp]; + // bit 24 to 26 + tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3); + out.channel_idx = tmp; + out.channels = (tmp > 7) ? 0 : channel_table[tmp]; + + // bit 55 to 56 + out.framecount = (buffer[6] & 0x3) + 1; + + // bit 31 to 43 + tmp = (buffer[3] & 0x3) << 11; + tmp |= (buffer[4] << 3) & 0x7f8; + tmp |= (buffer[5] >> 5) & 0x7; + + out.length = tmp; + + return out; +} +} // namespace + +TEST_CASE("ParseADTS fuzz", "[audio_core][hle]") { + for (u32 i = 0; i < 0x10000; i++) { + std::array adts_header; + std::string adts_header_string = "ADTS Header: "; + for (auto& it : adts_header) { + it = static_cast(rand()); + adts_header_string.append(fmt::format("{:2X} ", it)); + } + INFO(adts_header_string); + + AudioCore::ADTSData out_old_impl = + ParseADTS_Old(reinterpret_cast(adts_header.data())); + AudioCore::ADTSData out = AudioCore::ParseADTS(adts_header.data()); + + REQUIRE(out_old_impl.length == out.length); + REQUIRE(out_old_impl.channels == out.channels); + REQUIRE(out_old_impl.channel_idx == out.channel_idx); + REQUIRE(out_old_impl.framecount == out.framecount); + REQUIRE(out_old_impl.header_length == out.header_length); + REQUIRE(out_old_impl.mpeg2 == out.mpeg2); + REQUIRE(out_old_impl.profile == out.profile); + REQUIRE(out_old_impl.samplerate == out.samplerate); + REQUIRE(out_old_impl.samplerate_idx == out.samplerate_idx); + } +} diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 7d3827d035..2bbf248601 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -18,7 +18,8 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) timing = std::make_unique(1, 100); memory = std::make_unique(); kernel = std::make_unique( - *memory, *timing, [] {}, 0, 1, 0); + *memory, *timing, [] {}, Kernel::MemoryMode::Prod, 1, + Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); page_table = kernel->GetCurrentProcess()->vm_manager.page_table; diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index 2cb3557669..0ab4e2ac25 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -25,7 +25,8 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel Core::Timing timing(1, 100); Memory::MemorySystem memory; Kernel::KernelSystem kernel( - memory, timing, [] {}, 0, 1, 0); + memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, + Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); auto [server, client] = kernel.CreateSessionPair(); HLERequestContext context(kernel, std::move(server), nullptr); @@ -248,7 +249,8 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { Core::Timing timing(1, 100); Memory::MemorySystem memory; Kernel::KernelSystem kernel( - memory, timing, [] {}, 0, 1, 0); + memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, + Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); auto [server, client] = kernel.CreateSessionPair(); HLERequestContext context(kernel, std::move(server), nullptr); diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp index ea4b04f908..f0384ef294 100644 --- a/src/tests/core/memory/memory.cpp +++ b/src/tests/core/memory/memory.cpp @@ -11,7 +11,8 @@ TEST_CASE("memory.IsValidVirtualAddress", "[core][memory]") { Core::Timing timing(1, 100); Memory::MemorySystem memory; Kernel::KernelSystem kernel( - memory, timing, [] {}, 0, 1, 0); + memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, + Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); SECTION("these regions should not be mapped on an empty process") { auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); CHECK(memory.IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); diff --git a/src/tests/core/memory/vm_manager.cpp b/src/tests/core/memory/vm_manager.cpp index 9ae1e9ac25..5d1787b05f 100644 --- a/src/tests/core/memory/vm_manager.cpp +++ b/src/tests/core/memory/vm_manager.cpp @@ -17,7 +17,8 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { Core::Timing timing(1, 100); Memory::MemorySystem memory; Kernel::KernelSystem kernel( - memory, timing, [] {}, 0, 1, 0); + memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, + Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); Kernel::Process process(kernel); SECTION("mapping memory") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack.