Compare commits
6 Commits
layout-fix
...
macros
Author | SHA1 | Date | |
---|---|---|---|
617e8ef969 | |||
72463c8c8c | |||
e9b149c99b | |||
475c01e053 | |||
3ae8431aef | |||
0fd8892892 |
32
dist/apple/Info.plist.in
vendored
32
dist/apple/Info.plist.in
vendored
@ -26,38 +26,6 @@
|
||||
<!-- Fixed -->
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.games</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>3ds</string>
|
||||
<string>3dsx</string>
|
||||
<string>cci</string>
|
||||
<string>cxi</string>
|
||||
<string>cia</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Nintendo 3DS File</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>elf</string>
|
||||
<string>axf</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Unix Executable and Linkable Format</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app requires camera access to emulate the 3DS's cameras.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
|
@ -7,13 +7,25 @@ package org.citra.citra_emu.features.cheats.model
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
object CheatEngine {
|
||||
external fun loadCheatFile(titleId: Long)
|
||||
external fun saveCheatFile(titleId: Long)
|
||||
class CheatEngine(titleId: Long) {
|
||||
@Keep
|
||||
private val mPointer: Long
|
||||
|
||||
init {
|
||||
mPointer = initialize(titleId)
|
||||
}
|
||||
|
||||
protected external fun finalize()
|
||||
|
||||
external fun getCheats(): Array<Cheat>
|
||||
|
||||
external fun addCheat(cheat: Cheat?)
|
||||
external fun removeCheat(index: Int)
|
||||
external fun updateCheat(index: Int, newCheat: Cheat?)
|
||||
external fun saveCheatFile()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private external fun initialize(titleId: Long): Long
|
||||
}
|
||||
}
|
||||
|
@ -47,19 +47,18 @@ class CheatsViewModel : ViewModel() {
|
||||
val detailsViewFocusChange get() = _detailsViewFocusChange.asStateFlow()
|
||||
private val _detailsViewFocusChange = MutableStateFlow(false)
|
||||
|
||||
private var titleId: Long = 0
|
||||
private var cheatEngine: CheatEngine? = null
|
||||
lateinit var cheats: Array<Cheat>
|
||||
private var cheatsNeedSaving = false
|
||||
private var selectedCheatPosition = -1
|
||||
|
||||
fun initialize(titleId_: Long) {
|
||||
titleId = titleId_;
|
||||
fun initialize(titleId: Long) {
|
||||
cheatEngine = CheatEngine(titleId)
|
||||
load()
|
||||
}
|
||||
|
||||
private fun load() {
|
||||
CheatEngine.loadCheatFile(titleId)
|
||||
cheats = CheatEngine.getCheats()
|
||||
cheats = cheatEngine!!.getCheats()
|
||||
for (i in cheats.indices) {
|
||||
cheats[i].setEnabledChangedCallback {
|
||||
cheatsNeedSaving = true
|
||||
@ -70,7 +69,7 @@ class CheatsViewModel : ViewModel() {
|
||||
|
||||
fun saveIfNeeded() {
|
||||
if (cheatsNeedSaving) {
|
||||
CheatEngine.saveCheatFile(titleId)
|
||||
cheatEngine!!.saveCheatFile()
|
||||
cheatsNeedSaving = false
|
||||
}
|
||||
}
|
||||
@ -108,7 +107,7 @@ class CheatsViewModel : ViewModel() {
|
||||
_isAdding.value = false
|
||||
_isEditing.value = false
|
||||
val position = cheats.size
|
||||
CheatEngine.addCheat(cheat)
|
||||
cheatEngine!!.addCheat(cheat)
|
||||
cheatsNeedSaving = true
|
||||
load()
|
||||
notifyCheatAdded(position)
|
||||
@ -124,7 +123,7 @@ class CheatsViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun updateSelectedCheat(newCheat: Cheat?) {
|
||||
CheatEngine.updateCheat(selectedCheatPosition, newCheat)
|
||||
cheatEngine!!.updateCheat(selectedCheatPosition, newCheat)
|
||||
cheatsNeedSaving = true
|
||||
load()
|
||||
notifyCheatUpdated(selectedCheatPosition)
|
||||
@ -142,7 +141,7 @@ class CheatsViewModel : ViewModel() {
|
||||
fun deleteSelectedCheat() {
|
||||
val position = selectedCheatPosition
|
||||
setSelectedCheat(null, -1)
|
||||
CheatEngine.removeCheat(position)
|
||||
cheatEngine!!.removeCheat(position)
|
||||
cheatsNeedSaving = true
|
||||
load()
|
||||
notifyCheatDeleted(position)
|
||||
|
@ -15,24 +15,24 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
static Cheats::CheatEngine& GetEngine() {
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
return system.CheatEngine();
|
||||
static Cheats::CheatEngine* GetPointer(JNIEnv* env, jobject obj) {
|
||||
return reinterpret_cast<Cheats::CheatEngine*>(
|
||||
env->GetLongField(obj, IDCache::GetCheatEnginePointer()));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_loadCheatFile(
|
||||
JNIEXPORT jlong JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_initialize(
|
||||
JNIEnv* env, jclass, jlong title_id) {
|
||||
GetEngine().LoadCheatFile(title_id);
|
||||
return reinterpret_cast<jlong>(new Cheats::CheatEngine(title_id, Core::System::GetInstance()));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_saveCheatFile(
|
||||
JNIEnv* env, jclass, jlong title_id) {
|
||||
GetEngine().SaveCheatFile(title_id);
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_finalize(JNIEnv* env, jobject obj) {
|
||||
delete GetPointer(env, obj);
|
||||
}
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_getCheats(JNIEnv* env, jclass) {
|
||||
auto cheats = GetEngine().GetCheats();
|
||||
Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_getCheats(JNIEnv* env, jobject obj) {
|
||||
auto cheats = GetPointer(env, obj)->GetCheats();
|
||||
|
||||
const jobjectArray array =
|
||||
env->NewObjectArray(static_cast<jsize>(cheats.size()), IDCache::GetCheatClass(), nullptr);
|
||||
@ -45,19 +45,22 @@ Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_getCheats(JNIEnv* en
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_addCheat(
|
||||
JNIEnv* env, jclass, jobject j_cheat) {
|
||||
auto cheat = *CheatFromJava(env, j_cheat);
|
||||
GetEngine().AddCheat(std::move(cheat));
|
||||
JNIEnv* env, jobject obj, jobject j_cheat) {
|
||||
GetPointer(env, obj)->AddCheat(*CheatFromJava(env, j_cheat));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_removeCheat(
|
||||
JNIEnv* env, jclass, jint index) {
|
||||
GetEngine().RemoveCheat(index);
|
||||
JNIEnv* env, jobject obj, jint index) {
|
||||
GetPointer(env, obj)->RemoveCheat(index);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_updateCheat(
|
||||
JNIEnv* env, jclass, jint index, jobject j_new_cheat) {
|
||||
auto cheat = *CheatFromJava(env, j_new_cheat);
|
||||
GetEngine().UpdateCheat(index, std::move(cheat));
|
||||
JNIEnv* env, jobject obj, jint index, jobject j_new_cheat) {
|
||||
GetPointer(env, obj)->UpdateCheat(index, *CheatFromJava(env, j_new_cheat));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_saveCheatFile(
|
||||
JNIEnv* env, jobject obj) {
|
||||
GetPointer(env, obj)->SaveCheatFile();
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,6 @@ void Config::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync_new);
|
||||
ReadSetting("Renderer", Settings::values.texture_filter);
|
||||
ReadSetting("Renderer", Settings::values.texture_sampling);
|
||||
|
||||
// Work around to map Android setting for enabling the frame limiter to the format Citra expects
|
||||
if (sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)) {
|
||||
|
@ -35,6 +35,8 @@ static jclass s_cheat_class;
|
||||
static jfieldID s_cheat_pointer;
|
||||
static jmethodID s_cheat_constructor;
|
||||
|
||||
static jfieldID s_cheat_engine_pointer;
|
||||
|
||||
static jfieldID s_game_info_pointer;
|
||||
|
||||
static jclass s_disk_cache_progress_class;
|
||||
@ -114,6 +116,10 @@ jmethodID GetCheatConstructor() {
|
||||
return s_cheat_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetCheatEnginePointer() {
|
||||
return s_cheat_engine_pointer;
|
||||
}
|
||||
|
||||
jfieldID GetGameInfoPointer() {
|
||||
return s_game_info_pointer;
|
||||
}
|
||||
@ -189,6 +195,12 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
s_cheat_constructor = env->GetMethodID(cheat_class, "<init>", "(J)V");
|
||||
env->DeleteLocalRef(cheat_class);
|
||||
|
||||
// Initialize CheatEngine
|
||||
const jclass cheat_engine_class =
|
||||
env->FindClass("org/citra/citra_emu/features/cheats/model/CheatEngine");
|
||||
s_cheat_engine_pointer = env->GetFieldID(cheat_engine_class, "mPointer", "J");
|
||||
env->DeleteLocalRef(cheat_engine_class);
|
||||
|
||||
// Initialize GameInfo
|
||||
const jclass game_info_class = env->FindClass("org/citra/citra_emu/model/GameInfo");
|
||||
s_game_info_pointer = env->GetFieldID(game_info_class, "pointer", "J");
|
||||
|
@ -35,6 +35,8 @@ jclass GetCheatClass();
|
||||
jfieldID GetCheatPointer();
|
||||
jmethodID GetCheatConstructor();
|
||||
|
||||
jfieldID GetCheatEnginePointer();
|
||||
|
||||
jfieldID GetGameInfoPointer();
|
||||
|
||||
jclass GetDiskCacheProgressClass();
|
||||
|
@ -4,11 +4,11 @@ add_library(audio_core STATIC
|
||||
codec.h
|
||||
dsp_interface.cpp
|
||||
dsp_interface.h
|
||||
hle/aac_decoder.cpp
|
||||
hle/aac_decoder.h
|
||||
hle/common.h
|
||||
hle/decoder.cpp
|
||||
hle/decoder.h
|
||||
hle/faad2_decoder.cpp
|
||||
hle/faad2_decoder.h
|
||||
hle/filter.cpp
|
||||
hle/filter.h
|
||||
hle/hle.cpp
|
||||
|
@ -1,26 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/hle/decoder.h"
|
||||
|
||||
namespace AudioCore::HLE {
|
||||
|
||||
using NeAACDecHandle = void*;
|
||||
|
||||
class AACDecoder final : public DecoderBase {
|
||||
public:
|
||||
explicit AACDecoder(Memory::MemorySystem& memory);
|
||||
~AACDecoder() override;
|
||||
BinaryMessage ProcessRequest(const BinaryMessage& request) override;
|
||||
|
||||
private:
|
||||
BinaryMessage Decode(const BinaryMessage& request);
|
||||
|
||||
Memory::MemorySystem& memory;
|
||||
NeAACDecHandle decoder = nullptr;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::HLE
|
@ -32,4 +32,34 @@ DecoderSampleRate GetSampleRateEnum(u32 sample_rate) {
|
||||
}
|
||||
}
|
||||
|
||||
DecoderBase::~DecoderBase(){};
|
||||
|
||||
NullDecoder::NullDecoder() = default;
|
||||
|
||||
NullDecoder::~NullDecoder() = default;
|
||||
|
||||
std::optional<BinaryMessage> NullDecoder::ProcessRequest(const BinaryMessage& request) {
|
||||
BinaryMessage response{};
|
||||
switch (request.header.cmd) {
|
||||
case DecoderCommand::Init:
|
||||
case DecoderCommand::Shutdown:
|
||||
case DecoderCommand::SaveState:
|
||||
case DecoderCommand::LoadState:
|
||||
response = request;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
case DecoderCommand::EncodeDecode:
|
||||
response.header.codec = request.header.codec;
|
||||
response.header.cmd = request.header.cmd;
|
||||
response.header.result = ResultStatus::Success;
|
||||
response.decode_aac_response.num_channels = 2; // Just assume stereo here
|
||||
response.decode_aac_response.size = request.decode_aac_request.size;
|
||||
response.decode_aac_response.num_samples = 1024; // Just assume 1024 here
|
||||
return response;
|
||||
default:
|
||||
LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
|
||||
static_cast<u16>(request.header.cmd));
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
} // namespace AudioCore::HLE
|
||||
|
@ -135,8 +135,21 @@ enum_le<DecoderSampleRate> GetSampleRateEnum(u32 sample_rate);
|
||||
|
||||
class DecoderBase {
|
||||
public:
|
||||
virtual ~DecoderBase() = default;
|
||||
virtual BinaryMessage ProcessRequest(const BinaryMessage& request) = 0;
|
||||
virtual ~DecoderBase();
|
||||
virtual std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) = 0;
|
||||
/// Return true if this Decoder can be loaded. Return false if the system cannot create the
|
||||
/// decoder
|
||||
virtual bool IsValid() const = 0;
|
||||
};
|
||||
|
||||
class NullDecoder final : public DecoderBase {
|
||||
public:
|
||||
NullDecoder();
|
||||
~NullDecoder() override;
|
||||
std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) override;
|
||||
bool IsValid() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace AudioCore::HLE
|
||||
|
@ -3,11 +3,30 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <neaacdec.h>
|
||||
#include "audio_core/hle/aac_decoder.h"
|
||||
#include "audio_core/hle/faad2_decoder.h"
|
||||
|
||||
namespace AudioCore::HLE {
|
||||
|
||||
AACDecoder::AACDecoder(Memory::MemorySystem& memory) : memory(memory) {
|
||||
class FAAD2Decoder::Impl {
|
||||
public:
|
||||
explicit Impl(Memory::MemorySystem& memory);
|
||||
~Impl();
|
||||
std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request);
|
||||
bool IsValid() const {
|
||||
return decoder != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
|
||||
|
||||
std::optional<BinaryMessage> Decode(const BinaryMessage& request);
|
||||
|
||||
Memory::MemorySystem& memory;
|
||||
|
||||
NeAACDecHandle decoder = nullptr;
|
||||
};
|
||||
|
||||
FAAD2Decoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) {
|
||||
decoder = NeAACDecOpen();
|
||||
if (decoder == nullptr) {
|
||||
LOG_CRITICAL(Audio_DSP, "Could not open FAAD2 decoder.");
|
||||
@ -27,7 +46,7 @@ AACDecoder::AACDecoder(Memory::MemorySystem& memory) : memory(memory) {
|
||||
LOG_INFO(Audio_DSP, "Created FAAD2 AAC decoder.");
|
||||
}
|
||||
|
||||
AACDecoder::~AACDecoder() {
|
||||
FAAD2Decoder::Impl::~Impl() {
|
||||
if (decoder) {
|
||||
NeAACDecClose(decoder);
|
||||
decoder = nullptr;
|
||||
@ -36,23 +55,16 @@ AACDecoder::~AACDecoder() {
|
||||
}
|
||||
}
|
||||
|
||||
BinaryMessage AACDecoder::ProcessRequest(const BinaryMessage& request) {
|
||||
std::optional<BinaryMessage> FAAD2Decoder::Impl::ProcessRequest(const BinaryMessage& request) {
|
||||
if (request.header.codec != DecoderCodec::DecodeAAC) {
|
||||
LOG_ERROR(Audio_DSP, "AAC decoder received unsupported codec: {}",
|
||||
LOG_ERROR(Audio_DSP, "FAAD2 AAC Decoder cannot handle such codec: {}",
|
||||
static_cast<u16>(request.header.codec));
|
||||
return {
|
||||
.header =
|
||||
{
|
||||
.result = ResultStatus::Error,
|
||||
},
|
||||
};
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (request.header.cmd) {
|
||||
case DecoderCommand::Init: {
|
||||
BinaryMessage response = request;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
return Initalize(request);
|
||||
}
|
||||
case DecoderCommand::EncodeDecode: {
|
||||
return Decode(request);
|
||||
@ -60,25 +72,26 @@ BinaryMessage AACDecoder::ProcessRequest(const BinaryMessage& request) {
|
||||
case DecoderCommand::Shutdown:
|
||||
case DecoderCommand::SaveState:
|
||||
case DecoderCommand::LoadState: {
|
||||
LOG_WARNING(Audio_DSP, "Got unimplemented AAC binary request: {}",
|
||||
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
|
||||
static_cast<u16>(request.header.cmd));
|
||||
BinaryMessage response = request;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Audio_DSP, "Got unknown AAC binary request: {}",
|
||||
LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
|
||||
static_cast<u16>(request.header.cmd));
|
||||
return {
|
||||
.header =
|
||||
{
|
||||
.result = ResultStatus::Error,
|
||||
},
|
||||
};
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
BinaryMessage AACDecoder::Decode(const BinaryMessage& request) {
|
||||
std::optional<BinaryMessage> FAAD2Decoder::Impl::Initalize(const BinaryMessage& request) {
|
||||
BinaryMessage response = request;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
}
|
||||
|
||||
std::optional<BinaryMessage> FAAD2Decoder::Impl::Decode(const BinaryMessage& request) {
|
||||
BinaryMessage response{};
|
||||
response.header.codec = request.header.codec;
|
||||
response.header.cmd = request.header.cmd;
|
||||
@ -88,10 +101,6 @@ BinaryMessage AACDecoder::Decode(const BinaryMessage& request) {
|
||||
response.decode_aac_response.num_channels = 2;
|
||||
response.decode_aac_response.num_samples = 1024;
|
||||
|
||||
if (decoder == nullptr) {
|
||||
return response;
|
||||
}
|
||||
|
||||
if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR ||
|
||||
request.decode_aac_request.src_addr + request.decode_aac_request.size >
|
||||
Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) {
|
||||
@ -162,4 +171,16 @@ BinaryMessage AACDecoder::Decode(const BinaryMessage& request) {
|
||||
return response;
|
||||
}
|
||||
|
||||
FAAD2Decoder::FAAD2Decoder(Memory::MemorySystem& memory) : impl(std::make_unique<Impl>(memory)) {}
|
||||
|
||||
FAAD2Decoder::~FAAD2Decoder() = default;
|
||||
|
||||
std::optional<BinaryMessage> FAAD2Decoder::ProcessRequest(const BinaryMessage& request) {
|
||||
return impl->ProcessRequest(request);
|
||||
}
|
||||
|
||||
bool FAAD2Decoder::IsValid() const {
|
||||
return impl->IsValid();
|
||||
}
|
||||
|
||||
} // namespace AudioCore::HLE
|
23
src/audio_core/hle/faad2_decoder.h
Normal file
23
src/audio_core/hle/faad2_decoder.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio_core/hle/decoder.h"
|
||||
|
||||
namespace AudioCore::HLE {
|
||||
|
||||
class FAAD2Decoder final : public DecoderBase {
|
||||
public:
|
||||
explicit FAAD2Decoder(Memory::MemorySystem& memory);
|
||||
~FAAD2Decoder() override;
|
||||
std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) override;
|
||||
bool IsValid() const override;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::HLE
|
@ -8,9 +8,9 @@
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/weak_ptr.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/hle/aac_decoder.h"
|
||||
#include "audio_core/hle/common.h"
|
||||
#include "audio_core/hle/decoder.h"
|
||||
#include "audio_core/hle/faad2_decoder.h"
|
||||
#include "audio_core/hle/hle.h"
|
||||
#include "audio_core/hle/mixers.h"
|
||||
#include "audio_core/hle/shared_memory.h"
|
||||
@ -24,10 +24,16 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(AudioCore::DspHle)
|
||||
|
||||
using InterruptType = Service::DSP::InterruptType;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
DspHle::DspHle()
|
||||
: DspHle(Core::System::GetInstance(), Core::System::GetInstance().Memory(),
|
||||
Core::System::GetInstance().CoreTiming()) {}
|
||||
|
||||
DspHle::DspHle(Core::System& system) : DspHle(system, system.Memory(), system.CoreTiming()) {}
|
||||
|
||||
template <class Archive>
|
||||
@ -92,7 +98,7 @@ private:
|
||||
Core::Timing& core_timing;
|
||||
Core::TimingEventType* tick_event{};
|
||||
|
||||
std::unique_ptr<HLE::DecoderBase> aac_decoder{};
|
||||
std::unique_ptr<HLE::DecoderBase> decoder{};
|
||||
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> interrupt_handler{};
|
||||
|
||||
@ -108,6 +114,13 @@ private:
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
static std::vector<std::function<std::unique_ptr<HLE::DecoderBase>(Memory::MemorySystem&)>>
|
||||
decoder_backends = {
|
||||
[](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> {
|
||||
return std::make_unique<HLE::FAAD2Decoder>(memory);
|
||||
},
|
||||
};
|
||||
|
||||
DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory, Core::Timing& timing)
|
||||
: parent(parent_), core_timing(timing) {
|
||||
dsp_memory.raw_memory.fill(0);
|
||||
@ -116,7 +129,19 @@ DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory, Core::Timing&
|
||||
source.SetMemory(memory);
|
||||
}
|
||||
|
||||
aac_decoder = std::make_unique<HLE::AACDecoder>(memory);
|
||||
for (auto& factory : decoder_backends) {
|
||||
decoder = factory(memory);
|
||||
if (decoder && decoder->IsValid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!decoder || !decoder->IsValid()) {
|
||||
LOG_WARNING(Audio_DSP,
|
||||
"Unable to load any decoders, this could cause missing audio in some games");
|
||||
decoder = std::make_unique<HLE::NullDecoder>();
|
||||
}
|
||||
|
||||
tick_event =
|
||||
core_timing.RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) {
|
||||
this->AudioTickCallback(cycles_late);
|
||||
@ -266,9 +291,12 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
|
||||
UNIMPLEMENTED();
|
||||
return;
|
||||
}
|
||||
const HLE::BinaryMessage response = aac_decoder->ProcessRequest(request);
|
||||
pipe_data[static_cast<u32>(pipe_number)].resize(sizeof(response));
|
||||
std::memcpy(pipe_data[static_cast<u32>(pipe_number)].data(), &response, sizeof(response));
|
||||
std::optional<HLE::BinaryMessage> response = decoder->ProcessRequest(request);
|
||||
if (response) {
|
||||
const HLE::BinaryMessage& value = *response;
|
||||
pipe_data[static_cast<u32>(pipe_number)].resize(sizeof(value));
|
||||
std::memcpy(pipe_data[static_cast<u32>(pipe_number)].data(), &value, sizeof(value));
|
||||
}
|
||||
|
||||
interrupt_handler(InterruptType::Pipe, DspPipe::Binary);
|
||||
break;
|
||||
|
@ -50,9 +50,13 @@ private:
|
||||
friend struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
DspHle();
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(AudioCore::DspHle)
|
||||
|
0
src/audio_core/hle/pipe.h
Normal file
0
src/audio_core/hle/pipe.h
Normal file
@ -24,8 +24,8 @@ namespace {
|
||||
constexpr std::array input_details = {
|
||||
#ifdef HAVE_CUBEB
|
||||
InputDetails{InputType::Cubeb, "Real Device (Cubeb)", true,
|
||||
[](Core::System& system, std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
if (!system.HasMicPermission()) {
|
||||
[](std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
if (!Core::System::GetInstance().HasMicPermission()) {
|
||||
LOG_WARNING(Audio,
|
||||
"Microphone permission denied, falling back to null input.");
|
||||
return std::make_unique<NullInput>();
|
||||
@ -36,8 +36,8 @@ constexpr std::array input_details = {
|
||||
#endif
|
||||
#ifdef HAVE_OPENAL
|
||||
InputDetails{InputType::OpenAL, "Real Device (OpenAL)", true,
|
||||
[](Core::System& system, std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
if (!system.HasMicPermission()) {
|
||||
[](std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
if (!Core::System::GetInstance().HasMicPermission()) {
|
||||
LOG_WARNING(Audio,
|
||||
"Microphone permission denied, falling back to null input.");
|
||||
return std::make_unique<NullInput>();
|
||||
@ -47,12 +47,12 @@ constexpr std::array input_details = {
|
||||
&ListOpenALInputDevices},
|
||||
#endif
|
||||
InputDetails{InputType::Static, "Static Noise", false,
|
||||
[](Core::System& system, std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
[](std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
return std::make_unique<StaticInput>();
|
||||
},
|
||||
[] { return std::vector<std::string>{"Static Noise"}; }},
|
||||
InputDetails{InputType::Null, "None", false,
|
||||
[](Core::System& system, std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
[](std::string_view device_id) -> std::unique_ptr<Input> {
|
||||
return std::make_unique<NullInput>();
|
||||
},
|
||||
[] { return std::vector<std::string>{"None"}; }},
|
||||
|
@ -10,10 +10,6 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class Input;
|
||||
@ -27,7 +23,7 @@ enum class InputType : u32 {
|
||||
};
|
||||
|
||||
struct InputDetails {
|
||||
using FactoryFn = std::unique_ptr<Input> (*)(Core::System& system, std::string_view device_id);
|
||||
using FactoryFn = std::unique_ptr<Input> (*)(std::string_view device_id);
|
||||
using ListDevicesFn = std::vector<std::string> (*)();
|
||||
|
||||
/// Type of this input.
|
||||
|
@ -146,7 +146,6 @@ void Config::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync_new);
|
||||
ReadSetting("Renderer", Settings::values.texture_filter);
|
||||
ReadSetting("Renderer", Settings::values.texture_sampling);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.mono_render_option);
|
||||
ReadSetting("Renderer", Settings::values.render_3d);
|
||||
|
@ -645,7 +645,6 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
ReadGlobalSetting(Settings::values.texture_filter);
|
||||
ReadGlobalSetting(Settings::values.texture_sampling);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.use_shader_jit);
|
||||
@ -1131,7 +1130,6 @@ void Config::SaveRendererValues() {
|
||||
WriteGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
WriteGlobalSetting(Settings::values.texture_filter);
|
||||
WriteGlobalSetting(Settings::values.texture_sampling);
|
||||
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(),
|
||||
|
@ -11,10 +11,8 @@
|
||||
#include "core/cheats/gateway_cheat.h"
|
||||
#include "ui_configure_cheats.h"
|
||||
|
||||
ConfigureCheats::ConfigureCheats(Cheats::CheatEngine& cheat_engine_, u64 title_id_, QWidget* parent)
|
||||
: QWidget(parent),
|
||||
ui(std::make_unique<Ui::ConfigureCheats>()), cheat_engine{cheat_engine_}, title_id{
|
||||
title_id_} {
|
||||
ConfigureCheats::ConfigureCheats(Core::System& system, u64 title_id_, QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureCheats>()), title_id{title_id_} {
|
||||
// Setup gui control settings
|
||||
ui->setupUi(this);
|
||||
ui->tableCheats->setColumnWidth(0, 30);
|
||||
@ -36,14 +34,15 @@ ConfigureCheats::ConfigureCheats(Cheats::CheatEngine& cheat_engine_, u64 title_i
|
||||
[this] { SaveCheat(ui->tableCheats->currentRow()); });
|
||||
connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat);
|
||||
|
||||
cheat_engine.LoadCheatFile(title_id);
|
||||
cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, system);
|
||||
|
||||
LoadCheats();
|
||||
}
|
||||
|
||||
ConfigureCheats::~ConfigureCheats() = default;
|
||||
|
||||
void ConfigureCheats::LoadCheats() {
|
||||
cheats = cheat_engine.GetCheats();
|
||||
cheats = cheat_engine->GetCheats();
|
||||
const int cheats_count = static_cast<int>(cheats.size());
|
||||
|
||||
ui->tableCheats->setRowCount(cheats_count);
|
||||
@ -107,12 +106,12 @@ bool ConfigureCheats::SaveCheat(int row) {
|
||||
ui->textNotes->toPlainText().toStdString());
|
||||
|
||||
if (newly_created) {
|
||||
cheat_engine.AddCheat(std::move(cheat));
|
||||
cheat_engine->AddCheat(cheat);
|
||||
newly_created = false;
|
||||
} else {
|
||||
cheat_engine.UpdateCheat(row, std::move(cheat));
|
||||
cheat_engine->UpdateCheat(row, cheat);
|
||||
}
|
||||
cheat_engine.SaveCheatFile(title_id);
|
||||
cheat_engine->SaveCheatFile();
|
||||
|
||||
int previous_row = ui->tableCheats->currentRow();
|
||||
int previous_col = ui->tableCheats->currentColumn();
|
||||
@ -162,7 +161,7 @@ void ConfigureCheats::OnCheckChanged(int state) {
|
||||
const QCheckBox* checkbox = qobject_cast<QCheckBox*>(sender());
|
||||
int row = static_cast<int>(checkbox->property("row").toInt());
|
||||
cheats[row]->SetEnabled(state);
|
||||
cheat_engine.SaveCheatFile(title_id);
|
||||
cheat_engine->SaveCheatFile();
|
||||
}
|
||||
|
||||
void ConfigureCheats::OnTextEdited() {
|
||||
@ -174,8 +173,8 @@ void ConfigureCheats::OnDeleteCheat() {
|
||||
if (newly_created) {
|
||||
newly_created = false;
|
||||
} else {
|
||||
cheat_engine.RemoveCheat(ui->tableCheats->currentRow());
|
||||
cheat_engine.SaveCheatFile(title_id);
|
||||
cheat_engine->RemoveCheat(ui->tableCheats->currentRow());
|
||||
cheat_engine->SaveCheatFile();
|
||||
}
|
||||
|
||||
LoadCheats();
|
||||
|
@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <QWidget>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@ -26,8 +25,7 @@ class ConfigureCheats : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureCheats(Cheats::CheatEngine& cheat_engine, u64 title_id_,
|
||||
QWidget* parent = nullptr);
|
||||
explicit ConfigureCheats(Core::System& system, u64 title_id, QWidget* parent = nullptr);
|
||||
~ConfigureCheats();
|
||||
bool ApplyConfiguration();
|
||||
|
||||
@ -60,9 +58,9 @@ private slots:
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureCheats> ui;
|
||||
Cheats::CheatEngine& cheat_engine;
|
||||
std::span<const std::shared_ptr<Cheats::CheatBase>> cheats;
|
||||
std::vector<std::shared_ptr<Cheats::CheatBase>> cheats;
|
||||
bool edited = false, newly_created = false;
|
||||
int last_row = -1, last_col = -1;
|
||||
u64 title_id;
|
||||
std::unique_ptr<Cheats::CheatEngine> cheat_engine;
|
||||
};
|
||||
|
@ -38,7 +38,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this);
|
||||
system_tab = std::make_unique<ConfigureSystem>(system, this);
|
||||
debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
|
||||
cheat_tab = std::make_unique<ConfigureCheats>(system.CheatEngine(), title_id, this);
|
||||
cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
|
@ -229,7 +229,6 @@ GMainWindow::GMainWindow(Core::System& system_)
|
||||
SetDefaultUIGeometry();
|
||||
RestoreUIState();
|
||||
|
||||
ConnectAppEvents();
|
||||
ConnectMenuEvents();
|
||||
ConnectWidgetEvents();
|
||||
|
||||
@ -762,21 +761,6 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GApplicationEventFilter::eventFilter(QObject* object, QEvent* event) {
|
||||
if (event->type() == QEvent::FileOpen) {
|
||||
emit FileOpen(static_cast<QFileOpenEvent*>(event));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GMainWindow::ConnectAppEvents() {
|
||||
const auto filter = new GApplicationEventFilter();
|
||||
QGuiApplication::instance()->installEventFilter(filter);
|
||||
|
||||
connect(filter, &GApplicationEventFilter::FileOpen, this, &GMainWindow::OnFileOpen);
|
||||
}
|
||||
|
||||
void GMainWindow::ConnectWidgetEvents() {
|
||||
connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
|
||||
connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory);
|
||||
@ -2768,10 +2752,6 @@ bool GMainWindow::DropAction(QDropEvent* event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GMainWindow::OnFileOpen(const QFileOpenEvent* event) {
|
||||
BootGame(event->file());
|
||||
}
|
||||
|
||||
void GMainWindow::dropEvent(QDropEvent* event) {
|
||||
DropAction(event);
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ class LoadingScreen;
|
||||
class MicroProfileDialog;
|
||||
class MultiplayerState;
|
||||
class ProfilerWidget;
|
||||
class QFileOpenEvent;
|
||||
template <typename>
|
||||
class QFutureWatcher;
|
||||
class QLabel;
|
||||
@ -97,8 +96,6 @@ public:
|
||||
bool DropAction(QDropEvent* event);
|
||||
void AcceptDropEvent(QDropEvent* event);
|
||||
|
||||
void OnFileOpen(const QFileOpenEvent* event);
|
||||
|
||||
void UninstallTitles(
|
||||
const std::vector<std::tuple<Service::FS::MediaType, u64, QString>>& titles);
|
||||
|
||||
@ -140,7 +137,6 @@ private:
|
||||
void SyncMenuUISettings();
|
||||
void RestoreUIState();
|
||||
|
||||
void ConnectAppEvents();
|
||||
void ConnectWidgetEvents();
|
||||
void ConnectMenuEvents();
|
||||
void UpdateMenuState();
|
||||
@ -386,15 +382,5 @@ protected:
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
};
|
||||
|
||||
class GApplicationEventFilter : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void FileOpen(const QFileOpenEvent* event);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(std::size_t);
|
||||
Q_DECLARE_METATYPE(Service::AM::InstallStatus);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "core/cheats/gateway_cheat.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
||||
namespace Cheats {
|
||||
|
||||
@ -17,11 +18,11 @@ namespace Cheats {
|
||||
// we use the same value
|
||||
constexpr u64 run_interval_ticks = 50'000'000;
|
||||
|
||||
CheatEngine::CheatEngine(Core::System& system_) : system{system_} {}
|
||||
|
||||
CheatEngine::~CheatEngine() {
|
||||
CheatEngine::CheatEngine(u64 title_id_, Core::System& system_)
|
||||
: system(system_), title_id{title_id_} {
|
||||
LoadCheatFile();
|
||||
if (system.IsPoweredOn()) {
|
||||
system.CoreTiming().UnscheduleEvent(event, 0);
|
||||
Connect();
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,18 +33,24 @@ void CheatEngine::Connect() {
|
||||
system.CoreTiming().ScheduleEvent(run_interval_ticks, event);
|
||||
}
|
||||
|
||||
std::span<const std::shared_ptr<CheatBase>> CheatEngine::GetCheats() const {
|
||||
std::shared_lock lock{cheats_list_mutex};
|
||||
CheatEngine::~CheatEngine() {
|
||||
if (system.IsPoweredOn()) {
|
||||
system.CoreTiming().UnscheduleEvent(event, 0);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<CheatBase>> CheatEngine::GetCheats() const {
|
||||
std::shared_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
return cheats_list;
|
||||
}
|
||||
|
||||
void CheatEngine::AddCheat(std::shared_ptr<CheatBase>&& cheat) {
|
||||
std::unique_lock lock{cheats_list_mutex};
|
||||
cheats_list.push_back(std::move(cheat));
|
||||
void CheatEngine::AddCheat(const std::shared_ptr<CheatBase>& cheat) {
|
||||
std::unique_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
cheats_list.push_back(cheat);
|
||||
}
|
||||
|
||||
void CheatEngine::RemoveCheat(std::size_t index) {
|
||||
std::unique_lock lock{cheats_list_mutex};
|
||||
std::unique_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
if (index < 0 || index >= cheats_list.size()) {
|
||||
LOG_ERROR(Core_Cheats, "Invalid index {}", index);
|
||||
return;
|
||||
@ -51,16 +58,16 @@ void CheatEngine::RemoveCheat(std::size_t index) {
|
||||
cheats_list.erase(cheats_list.begin() + index);
|
||||
}
|
||||
|
||||
void CheatEngine::UpdateCheat(std::size_t index, std::shared_ptr<CheatBase>&& new_cheat) {
|
||||
std::unique_lock lock{cheats_list_mutex};
|
||||
void CheatEngine::UpdateCheat(std::size_t index, const std::shared_ptr<CheatBase>& new_cheat) {
|
||||
std::unique_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
if (index < 0 || index >= cheats_list.size()) {
|
||||
LOG_ERROR(Core_Cheats, "Invalid index {}", index);
|
||||
return;
|
||||
}
|
||||
cheats_list[index] = std::move(new_cheat);
|
||||
cheats_list[index] = new_cheat;
|
||||
}
|
||||
|
||||
void CheatEngine::SaveCheatFile(u64 title_id) const {
|
||||
void CheatEngine::SaveCheatFile() const {
|
||||
const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir);
|
||||
const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id);
|
||||
|
||||
@ -75,14 +82,7 @@ void CheatEngine::SaveCheatFile(u64 title_id) const {
|
||||
}
|
||||
}
|
||||
|
||||
void CheatEngine::LoadCheatFile(u64 title_id) {
|
||||
{
|
||||
std::unique_lock lock{cheats_list_mutex};
|
||||
if (loaded_title_id.has_value() && loaded_title_id == title_id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CheatEngine::LoadCheatFile() {
|
||||
const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir);
|
||||
const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id);
|
||||
|
||||
@ -90,22 +90,20 @@ void CheatEngine::LoadCheatFile(u64 title_id) {
|
||||
FileUtil::CreateDir(cheat_dir);
|
||||
}
|
||||
|
||||
if (!FileUtil::Exists(filepath)) {
|
||||
if (!FileUtil::Exists(filepath))
|
||||
return;
|
||||
}
|
||||
|
||||
auto gateway_cheats = GatewayCheat::LoadFile(filepath);
|
||||
{
|
||||
std::unique_lock lock{cheats_list_mutex};
|
||||
loaded_title_id = title_id;
|
||||
cheats_list = std::move(gateway_cheats);
|
||||
std::unique_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
std::move(gateway_cheats.begin(), gateway_cheats.end(), std::back_inserter(cheats_list));
|
||||
}
|
||||
}
|
||||
|
||||
void CheatEngine::RunCallback([[maybe_unused]] std::uintptr_t user_data, s64 cycles_late) {
|
||||
{
|
||||
std::shared_lock lock{cheats_list_mutex};
|
||||
for (const auto& cheat : cheats_list) {
|
||||
std::shared_lock<std::shared_mutex> lock(cheats_list_mutex);
|
||||
for (auto& cheat : cheats_list) {
|
||||
if (cheat->IsEnabled()) {
|
||||
cheat->Execute(system);
|
||||
}
|
||||
|
@ -5,9 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@ -26,39 +24,22 @@ class CheatBase;
|
||||
|
||||
class CheatEngine {
|
||||
public:
|
||||
explicit CheatEngine(Core::System& system);
|
||||
explicit CheatEngine(u64 title_id_, Core::System& system);
|
||||
~CheatEngine();
|
||||
|
||||
/// Registers the cheat execution callback.
|
||||
void Connect();
|
||||
|
||||
/// Returns a span of the currently active cheats.
|
||||
std::span<const std::shared_ptr<CheatBase>> GetCheats() const;
|
||||
|
||||
/// Adds a cheat to the cheat engine.
|
||||
void AddCheat(std::shared_ptr<CheatBase>&& cheat);
|
||||
|
||||
/// Removes a cheat at the specified index in the cheats list.
|
||||
std::vector<std::shared_ptr<CheatBase>> GetCheats() const;
|
||||
void AddCheat(const std::shared_ptr<CheatBase>& cheat);
|
||||
void RemoveCheat(std::size_t index);
|
||||
|
||||
/// Updates a cheat at the specified index in the cheats list.
|
||||
void UpdateCheat(std::size_t index, std::shared_ptr<CheatBase>&& new_cheat);
|
||||
|
||||
/// Loads the cheat file from disk for the specified title id.
|
||||
void LoadCheatFile(u64 title_id);
|
||||
|
||||
/// Saves currently active cheats to file for the specified title id.
|
||||
void SaveCheatFile(u64 title_id) const;
|
||||
void UpdateCheat(std::size_t index, const std::shared_ptr<CheatBase>& new_cheat);
|
||||
void SaveCheatFile() const;
|
||||
|
||||
private:
|
||||
/// The cheat execution callback.
|
||||
void LoadCheatFile();
|
||||
void RunCallback(std::uintptr_t user_data, s64 cycles_late);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
Core::TimingEventType* event;
|
||||
std::optional<u64> loaded_title_id;
|
||||
std::vector<std::shared_ptr<CheatBase>> cheats_list;
|
||||
mutable std::shared_mutex cheats_list_mutex;
|
||||
Core::TimingEventType* event;
|
||||
Core::System& system;
|
||||
u64 title_id;
|
||||
};
|
||||
} // namespace Cheats
|
||||
|
@ -472,8 +472,8 @@ std::string GatewayCheat::ToString() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string& filepath) {
|
||||
std::vector<std::shared_ptr<CheatBase>> cheats;
|
||||
std::vector<std::unique_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string& filepath) {
|
||||
std::vector<std::unique_ptr<CheatBase>> cheats;
|
||||
|
||||
boost::iostreams::stream<boost::iostreams::file_descriptor_source> file;
|
||||
FileUtil::OpenFStream<std::ios_base::in>(file, filepath);
|
||||
@ -493,7 +493,7 @@ std::vector<std::shared_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string
|
||||
line = Common::StripSpaces(line); // remove spaces at front and end
|
||||
if (line.length() >= 2 && line.front() == '[') {
|
||||
if (!cheat_lines.empty()) {
|
||||
cheats.push_back(std::make_shared<GatewayCheat>(name, cheat_lines, comments));
|
||||
cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments));
|
||||
cheats.back()->SetEnabled(enabled);
|
||||
enabled = false;
|
||||
}
|
||||
@ -511,7 +511,7 @@ std::vector<std::shared_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string
|
||||
}
|
||||
}
|
||||
if (!cheat_lines.empty()) {
|
||||
cheats.push_back(std::make_shared<GatewayCheat>(name, cheat_lines, comments));
|
||||
cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments));
|
||||
cheats.back()->SetEnabled(enabled);
|
||||
}
|
||||
return cheats;
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
/// (there might be multiple lines of those hex numbers)
|
||||
/// Comment lines start with a '*'
|
||||
/// This function will pares the file for such structures
|
||||
static std::vector<std::shared_ptr<CheatBase>> LoadFile(const std::string& filepath);
|
||||
static std::vector<std::unique_ptr<CheatBase>> LoadFile(const std::string& filepath);
|
||||
|
||||
private:
|
||||
std::atomic<bool> enabled = false;
|
||||
|
@ -72,7 +72,7 @@ Core::Timing& Global() {
|
||||
return System::GetInstance().CoreTiming();
|
||||
}
|
||||
|
||||
System::System() : movie{*this}, cheat_engine{*this} {}
|
||||
System::System() : movie{*this} {}
|
||||
|
||||
System::~System() = default;
|
||||
|
||||
@ -320,10 +320,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
|
||||
static_cast<u32>(load_result));
|
||||
}
|
||||
|
||||
cheat_engine.LoadCheatFile(title_id);
|
||||
cheat_engine.Connect();
|
||||
|
||||
cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, *this);
|
||||
perf_stats = std::make_unique<PerfStats>(title_id);
|
||||
|
||||
if (Settings::values.dump_textures) {
|
||||
@ -505,11 +502,11 @@ const Memory::MemorySystem& System::Memory() const {
|
||||
}
|
||||
|
||||
Cheats::CheatEngine& System::CheatEngine() {
|
||||
return cheat_engine;
|
||||
return *cheat_engine;
|
||||
}
|
||||
|
||||
const Cheats::CheatEngine& System::CheatEngine() const {
|
||||
return cheat_engine;
|
||||
return *cheat_engine;
|
||||
}
|
||||
|
||||
void System::RegisterVideoDumper(std::shared_ptr<VideoDumper::Backend> dumper) {
|
||||
@ -563,6 +560,7 @@ void System::Shutdown(bool is_deserializing) {
|
||||
if (!is_deserializing) {
|
||||
GDBStub::Shutdown();
|
||||
perf_stats.reset();
|
||||
cheat_engine.reset();
|
||||
app_loader.reset();
|
||||
}
|
||||
custom_tex_manager.reset();
|
||||
@ -629,9 +627,7 @@ void System::ApplySettings() {
|
||||
GDBStub::ToggleServer(Settings::values.use_gdbstub.GetValue());
|
||||
|
||||
if (gpu) {
|
||||
#ifndef ANDROID
|
||||
gpu->Renderer().UpdateCurrentFramebufferLayout();
|
||||
#endif
|
||||
auto& settings = gpu->Renderer().Settings();
|
||||
settings.bg_color_update_requested = true;
|
||||
settings.shader_update_requested = true;
|
||||
@ -668,11 +664,9 @@ void System::ApplySettings() {
|
||||
Service::MIC::ReloadMic(*this);
|
||||
}
|
||||
|
||||
auto plg_ldr = Service::PLGLDR::GetService(*this);
|
||||
if (plg_ldr) {
|
||||
plg_ldr->SetEnabled(Settings::values.plugin_loader_enabled.GetValue());
|
||||
plg_ldr->SetAllowGameChangeState(Settings::values.allow_plugin_loader.GetValue());
|
||||
}
|
||||
Service::PLGLDR::PLG_LDR::SetEnabled(Settings::values.plugin_loader_enabled.GetValue());
|
||||
Service::PLGLDR::PLG_LDR::SetAllowGameChangeState(
|
||||
Settings::values.allow_plugin_loader.GetValue());
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
@ -724,7 +718,7 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
|
||||
if (Archive::is_loading::value) {
|
||||
timing->UnlockEventQueue();
|
||||
memory->SetDSP(*dsp_core);
|
||||
cheat_engine.Connect();
|
||||
cheat_engine->Connect();
|
||||
gpu->Sync();
|
||||
|
||||
// Re-register gpu callback, because gsp service changed after service_manager got
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/cheats/cheats.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/perf_stats.h"
|
||||
|
||||
@ -49,6 +48,10 @@ struct New3dsHwCapabilities;
|
||||
enum class MemoryMode : u8;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Cheats {
|
||||
class CheatEngine;
|
||||
}
|
||||
|
||||
namespace VideoDumper {
|
||||
class Backend;
|
||||
}
|
||||
@ -398,7 +401,7 @@ private:
|
||||
Core::Movie movie;
|
||||
|
||||
/// Cheats manager
|
||||
Cheats::CheatEngine cheat_engine;
|
||||
std::unique_ptr<Cheats::CheatEngine> cheat_engine;
|
||||
|
||||
/// Video dumper backend
|
||||
std::shared_ptr<VideoDumper::Backend> video_dumper;
|
||||
|
@ -65,7 +65,7 @@ static bool ReadSection(std::vector<u8>& data_out, FileUtil::IOFile& file, std::
|
||||
|
||||
Loader::ResultStatus FileSys::Plugin3GXLoader::Load(
|
||||
Service::PLGLDR::PLG_LDR::PluginLoaderContext& plg_context, Kernel::Process& process,
|
||||
Kernel::KernelSystem& kernel, Service::PLGLDR::PLG_LDR& plg_ldr) {
|
||||
Kernel::KernelSystem& kernel) {
|
||||
FileUtil::IOFile file(plg_context.plugin_path, "rb");
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_PLGLDR, "Failed to load 3GX plugin. Not found: {}",
|
||||
@ -158,12 +158,12 @@ Loader::ResultStatus FileSys::Plugin3GXLoader::Load(
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
|
||||
return Map(plg_context, process, kernel, plg_ldr);
|
||||
return Map(plg_context, process, kernel);
|
||||
}
|
||||
|
||||
Loader::ResultStatus FileSys::Plugin3GXLoader::Map(
|
||||
Service::PLGLDR::PLG_LDR::PluginLoaderContext& plg_context, Kernel::Process& process,
|
||||
Kernel::KernelSystem& kernel, Service::PLGLDR::PLG_LDR& plg_ldr) {
|
||||
Kernel::KernelSystem& kernel) {
|
||||
|
||||
// Verify exe load checksum function is available
|
||||
if (exe_load_func.empty() && plg_context.load_exe_func.empty()) {
|
||||
@ -195,7 +195,7 @@ Loader::ResultStatus FileSys::Plugin3GXLoader::Map(
|
||||
return Loader::ResultStatus::ErrorMemoryAllocationFailed;
|
||||
}
|
||||
auto backing_memory_fb = kernel.memory.GetFCRAMRef(*offset_fb);
|
||||
plg_ldr.SetPluginFBAddr(Memory::FCRAM_PADDR + *offset_fb);
|
||||
Service::PLGLDR::PLG_LDR::SetPluginFBAddr(Memory::FCRAM_PADDR + *offset_fb);
|
||||
std::fill(backing_memory_fb.GetPtr(), backing_memory_fb.GetPtr() + _3GX_fb_size, 0);
|
||||
|
||||
auto vma_heap_fb = process.vm_manager.MapBackingMemory(
|
||||
|
@ -43,8 +43,7 @@ class FileBackend;
|
||||
class Plugin3GXLoader {
|
||||
public:
|
||||
Loader::ResultStatus Load(Service::PLGLDR::PLG_LDR::PluginLoaderContext& plg_context,
|
||||
Kernel::Process& process, Kernel::KernelSystem& kernel,
|
||||
Service::PLGLDR::PLG_LDR& plg_ldr);
|
||||
Kernel::Process& process, Kernel::KernelSystem& kernel);
|
||||
|
||||
struct PluginHeader {
|
||||
u32_le magic;
|
||||
@ -69,8 +68,7 @@ public:
|
||||
|
||||
private:
|
||||
Loader::ResultStatus Map(Service::PLGLDR::PLG_LDR::PluginLoaderContext& plg_context,
|
||||
Kernel::Process& process, Kernel::KernelSystem& kernel,
|
||||
Service::PLGLDR::PLG_LDR& plg_ldr);
|
||||
Kernel::Process& process, Kernel::KernelSystem& kernel);
|
||||
|
||||
static constexpr size_t bootloader_memory_size = 0x1000;
|
||||
static void MapBootloader(Kernel::Process& process, Kernel::KernelSystem& kernel,
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace Frontend {
|
||||
void RegisterDefaultApplets(Core::System& system) {
|
||||
system.RegisterSoftwareKeyboard(std::make_shared<DefaultKeyboard>(system));
|
||||
system.RegisterSoftwareKeyboard(std::make_shared<DefaultKeyboard>());
|
||||
system.RegisterMiiSelector(std::make_shared<DefaultMiiSelector>());
|
||||
}
|
||||
} // namespace Frontend
|
||||
|
@ -144,12 +144,10 @@ const KeyboardData& SoftwareKeyboard::ReceiveData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
DefaultKeyboard::DefaultKeyboard(Core::System& system_) : system(system_) {}
|
||||
|
||||
void DefaultKeyboard::Execute(const Frontend::KeyboardConfig& config_) {
|
||||
SoftwareKeyboard::Execute(config_);
|
||||
|
||||
auto cfg = Service::CFG::GetModule(system);
|
||||
auto cfg = Service::CFG::GetModule(Core::System::GetInstance());
|
||||
std::string username = Common::UTF16ToUTF8(cfg->GetUsername());
|
||||
switch (this->config.button_config) {
|
||||
case ButtonConfig::None:
|
||||
|
@ -8,10 +8,6 @@
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
enum class AcceptedInput {
|
||||
@ -141,12 +137,8 @@ protected:
|
||||
|
||||
class DefaultKeyboard final : public SoftwareKeyboard {
|
||||
public:
|
||||
explicit DefaultKeyboard(Core::System& system_);
|
||||
void Execute(const KeyboardConfig& config) override;
|
||||
void ShowError(const std::string& error) override;
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Frontend
|
||||
|
@ -173,7 +173,8 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
TouchPressed(framebuffer_x, framebuffer_y);
|
||||
}
|
||||
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_portrait_mode) {
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height,
|
||||
bool is_portrait_mode) {
|
||||
Layout::FramebufferLayout layout;
|
||||
|
||||
// If in portrait mode, only the MobilePortrait option really makes sense
|
||||
@ -199,8 +200,7 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
||||
layout =
|
||||
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue(),
|
||||
Settings::values.large_screen_proportion.GetValue(),
|
||||
Layout::VerticalAlignment::Bottom);
|
||||
Settings::values.large_screen_proportion.GetValue());
|
||||
break;
|
||||
case Settings::LayoutOption::HybridScreen:
|
||||
layout =
|
||||
@ -208,10 +208,8 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
||||
Settings::values.upright_screen.GetValue());
|
||||
break;
|
||||
case Settings::LayoutOption::SideScreen:
|
||||
layout =
|
||||
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue(), 1.0f,
|
||||
Layout::VerticalAlignment::Bottom);
|
||||
layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue());
|
||||
break;
|
||||
#ifndef ANDROID
|
||||
case Settings::LayoutOption::SeparateWindows:
|
||||
@ -224,9 +222,8 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
||||
Settings::values.swap_screen.GetValue());
|
||||
break;
|
||||
case Settings::LayoutOption::MobileLandscape:
|
||||
layout =
|
||||
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
false, 2.25f, Layout::VerticalAlignment::Top);
|
||||
layout = Layout::MobileLandscapeFrameLayout(
|
||||
width, height, Settings::values.swap_screen.GetValue(), 2.25f, false);
|
||||
break;
|
||||
case Settings::LayoutOption::Default:
|
||||
default:
|
||||
|
@ -30,7 +30,7 @@ u32 FramebufferLayout::GetScalingRatio() const {
|
||||
|
||||
// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
|
||||
template <class T>
|
||||
static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
|
||||
static Common::Rectangle<T> maxRectangle(Common::Rectangle<T> window_area,
|
||||
float screen_aspect_ratio) {
|
||||
float scale = std::min(static_cast<float>(window_area.GetWidth()),
|
||||
window_area.GetHeight() / screen_aspect_ratio);
|
||||
@ -50,15 +50,15 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool u
|
||||
if (upright) {
|
||||
// Default layout gives equal screen sizes to the top and bottom screen
|
||||
screen_window_area = {0, 0, width / 2, height};
|
||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
top_screen = maxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
// both screens width are taken into account by dividing by 2
|
||||
emulation_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO / 2;
|
||||
} else {
|
||||
// Default layout gives equal screen sizes to the top and bottom screen
|
||||
screen_window_area = {0, 0, width, height / 2};
|
||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
// both screens height are taken into account by multiplying by 2
|
||||
emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2;
|
||||
}
|
||||
@ -71,7 +71,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool u
|
||||
// Recalculate the bottom screen to account for the height difference between right and
|
||||
// left
|
||||
screen_window_area = {0, 0, top_screen.GetWidth(), height};
|
||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
bot_screen =
|
||||
bot_screen.TranslateY((top_screen.GetHeight() - bot_screen.GetHeight()) / 2);
|
||||
if (swapped) {
|
||||
@ -96,7 +96,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool u
|
||||
// Recalculate the bottom screen to account for the width difference between top and
|
||||
// bottom
|
||||
screen_window_area = {0, 0, width, top_screen.GetHeight()};
|
||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2);
|
||||
if (swapped) {
|
||||
bot_screen = bot_screen.TranslateY(height / 2 - bot_screen.GetHeight());
|
||||
@ -124,8 +124,8 @@ FramebufferLayout MobilePortraitFrameLayout(u32 width, u32 height, bool swapped)
|
||||
FramebufferLayout res{width, height, true, true, {}, {}};
|
||||
// Default layout gives equal screen sizes to the top and bottom screen
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height / 2};
|
||||
Common::Rectangle<u32> top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||
Common::Rectangle<u32> bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
Common::Rectangle<u32> top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||
Common::Rectangle<u32> bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
|
||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
// both screens height are taken into account by multiplying by 2
|
||||
@ -151,6 +151,48 @@ FramebufferLayout MobilePortraitFrameLayout(u32 width, u32 height, bool swapped)
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout MobileLandscapeFrameLayout(u32 width, u32 height, bool swapped,
|
||||
float scale_factor, bool center_vertical) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
FramebufferLayout res{width, height, true, true, {}, {}};
|
||||
// Split the window into two parts. Give 4x width to the main screen and 1x width to the small
|
||||
// To do that, find the total emulation box and maximize that based on window size
|
||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
float emulation_aspect_ratio =
|
||||
swapped ? Core::kScreenBottomHeight * scale_factor /
|
||||
(Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth)
|
||||
: Core::kScreenTopHeight * scale_factor /
|
||||
(Core::kScreenTopWidth * scale_factor + Core::kScreenBottomWidth);
|
||||
float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
|
||||
float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO;
|
||||
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> total_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
Common::Rectangle<u32> large_screen = maxRectangle(total_rect, large_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> fourth_size_rect = total_rect.Scale(1.f / scale_factor);
|
||||
Common::Rectangle<u32> small_screen = maxRectangle(fourth_size_rect, small_screen_aspect_ratio);
|
||||
|
||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||
large_screen =
|
||||
large_screen.TranslateX((screen_window_area.GetWidth() - total_rect.GetWidth()) / 2);
|
||||
} else if (center_vertical) {
|
||||
large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2);
|
||||
}
|
||||
|
||||
// Shift the small screen to the bottom right corner
|
||||
small_screen = small_screen.TranslateX(large_screen.right);
|
||||
if (center_vertical) {
|
||||
small_screen = small_screen.TranslateY(large_screen.GetHeight() + large_screen.top -
|
||||
small_screen.GetHeight());
|
||||
}
|
||||
|
||||
res.top_screen = swapped ? small_screen : large_screen;
|
||||
res.bottom_screen = swapped ? large_screen : small_screen;
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
@ -163,13 +205,13 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
|
||||
Common::Rectangle<u32> bot_screen;
|
||||
float emulation_aspect_ratio;
|
||||
if (upright) {
|
||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
top_screen = maxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO);
|
||||
emulation_aspect_ratio =
|
||||
(swapped) ? BOT_SCREEN_UPRIGHT_ASPECT_RATIO : TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
|
||||
} else {
|
||||
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
||||
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
||||
emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
|
||||
}
|
||||
|
||||
@ -190,7 +232,7 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
|
||||
}
|
||||
|
||||
FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upright,
|
||||
float scale_factor, VerticalAlignment vertical_alignment) {
|
||||
float scale_factor) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
@ -232,10 +274,10 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> total_rect = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
Common::Rectangle<u32> large_screen = MaxRectangle(total_rect, large_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> scaled_rect = total_rect.Scale(1.f / scale_factor);
|
||||
Common::Rectangle<u32> small_screen = MaxRectangle(scaled_rect, small_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> total_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
Common::Rectangle<u32> large_screen = maxRectangle(total_rect, large_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> fourth_size_rect = total_rect.Scale(1.f / scale_factor);
|
||||
Common::Rectangle<u32> small_screen = maxRectangle(fourth_size_rect, small_screen_aspect_ratio);
|
||||
|
||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||
large_screen = large_screen.TranslateX((width - total_rect.GetWidth()) / 2);
|
||||
@ -244,46 +286,13 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
|
||||
}
|
||||
if (upright) {
|
||||
large_screen = large_screen.TranslateY(small_screen.GetHeight());
|
||||
small_screen = small_screen.TranslateY(large_screen.top - small_screen.GetHeight());
|
||||
switch (vertical_alignment) {
|
||||
case VerticalAlignment::Top:
|
||||
// Shift the small screen to the top right corner
|
||||
small_screen = small_screen.TranslateX(large_screen.left);
|
||||
break;
|
||||
case VerticalAlignment::Middle:
|
||||
// Shift the small screen to the center right
|
||||
small_screen = small_screen.TranslateX(
|
||||
((large_screen.GetWidth() - small_screen.GetWidth()) / 2) + large_screen.left);
|
||||
break;
|
||||
case VerticalAlignment::Bottom:
|
||||
// Shift the small screen to the bottom right corner
|
||||
small_screen = small_screen.TranslateX(large_screen.right - small_screen.GetWidth());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
small_screen = small_screen.TranslateX(large_screen.right - small_screen.GetWidth())
|
||||
.TranslateY(large_screen.top - small_screen.GetHeight());
|
||||
} else {
|
||||
small_screen = small_screen.TranslateX(large_screen.right);
|
||||
switch (vertical_alignment) {
|
||||
case VerticalAlignment::Top:
|
||||
// Shift the small screen to the top right corner
|
||||
small_screen = small_screen.TranslateY(large_screen.top);
|
||||
break;
|
||||
case VerticalAlignment::Middle:
|
||||
// Shift the small screen to the center right
|
||||
small_screen = small_screen.TranslateY(
|
||||
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
||||
break;
|
||||
case VerticalAlignment::Bottom:
|
||||
// Shift the small screen to the bottom right corner
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
// Shift the small screen to the bottom right corner
|
||||
small_screen =
|
||||
small_screen.TranslateX(large_screen.right)
|
||||
.TranslateY(large_screen.GetHeight() + large_screen.top - small_screen.GetHeight());
|
||||
}
|
||||
res.top_screen = swapped ? small_screen : large_screen;
|
||||
res.bottom_screen = swapped ? large_screen : small_screen;
|
||||
@ -322,11 +331,11 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> total_rect = MaxRectangle(screen_window_area, hybrid_area_aspect_ratio);
|
||||
Common::Rectangle<u32> large_main_screen = MaxRectangle(total_rect, main_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> total_rect = maxRectangle(screen_window_area, hybrid_area_aspect_ratio);
|
||||
Common::Rectangle<u32> large_main_screen = maxRectangle(total_rect, main_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> side_rect = total_rect.Scale(1.f / scale_factor);
|
||||
Common::Rectangle<u32> small_top_screen = MaxRectangle(side_rect, top_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> small_bottom_screen = MaxRectangle(side_rect, bot_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> small_top_screen = maxRectangle(side_rect, top_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> small_bottom_screen = maxRectangle(side_rect, bot_screen_aspect_ratio);
|
||||
|
||||
if (window_aspect_ratio < hybrid_area_aspect_ratio) {
|
||||
large_main_screen = large_main_screen.TranslateX((width - total_rect.GetWidth()) / 2);
|
||||
@ -364,6 +373,54 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout SideFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
FramebufferLayout res{width, height, true, true, {}, {}, !upright};
|
||||
|
||||
// Aspect ratio of both screens side by side
|
||||
float emulation_aspect_ratio =
|
||||
upright ? static_cast<float>(Core::kScreenTopWidth + Core::kScreenBottomWidth) /
|
||||
Core::kScreenTopHeight
|
||||
: static_cast<float>(Core::kScreenTopHeight) /
|
||||
(Core::kScreenTopWidth + Core::kScreenBottomWidth);
|
||||
|
||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
// Find largest Rectangle that can fit in the window size with the given aspect ratio
|
||||
Common::Rectangle<u32> screen_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
// Find sizes of top and bottom screen
|
||||
Common::Rectangle<u32> top_screen =
|
||||
upright ? maxRectangle(screen_rect, TOP_SCREEN_UPRIGHT_ASPECT_RATIO)
|
||||
: maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
|
||||
Common::Rectangle<u32> bot_screen =
|
||||
upright ? maxRectangle(screen_rect, BOT_SCREEN_UPRIGHT_ASPECT_RATIO)
|
||||
: maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
|
||||
|
||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||
// Apply borders to the left and right sides of the window.
|
||||
u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2;
|
||||
top_screen = top_screen.TranslateX(shift_horizontal);
|
||||
bot_screen = bot_screen.TranslateX(shift_horizontal);
|
||||
} else {
|
||||
// Window is narrower than the emulation content => apply borders to the top and bottom
|
||||
u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2;
|
||||
top_screen = top_screen.TranslateY(shift_vertical);
|
||||
bot_screen = bot_screen.TranslateY(shift_vertical);
|
||||
}
|
||||
if (upright) {
|
||||
// Leave the top screen at the top if we are swapped.
|
||||
res.top_screen = swapped ? top_screen : top_screen.TranslateY(bot_screen.GetHeight());
|
||||
res.bottom_screen = swapped ? bot_screen.TranslateY(top_screen.GetHeight()) : bot_screen;
|
||||
} else {
|
||||
// Move the top screen to the right if we are swapped.
|
||||
res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
|
||||
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary, bool upright) {
|
||||
// When is_secondary is true, we disable the top screen, and enable the bottom screen.
|
||||
// The same logic is found in the SingleFrameLayout using the is_swapped bool.
|
||||
@ -397,103 +454,131 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped) {
|
||||
}
|
||||
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary) {
|
||||
FramebufferLayout layout;
|
||||
if (Settings::values.custom_layout.GetValue() == true) {
|
||||
return CustomFrameLayout(std::max(Settings::values.custom_top_right.GetValue(),
|
||||
Settings::values.custom_bottom_right.GetValue()),
|
||||
std::max(Settings::values.custom_top_bottom.GetValue(),
|
||||
Settings::values.custom_bottom_bottom.GetValue()),
|
||||
Settings::values.swap_screen.GetValue());
|
||||
}
|
||||
|
||||
int width, height;
|
||||
switch (Settings::values.layout_option.GetValue()) {
|
||||
case Settings::LayoutOption::SingleScreen:
|
||||
layout = CustomFrameLayout(std::max(Settings::values.custom_top_right.GetValue(),
|
||||
Settings::values.custom_bottom_right.GetValue()),
|
||||
std::max(Settings::values.custom_top_bottom.GetValue(),
|
||||
Settings::values.custom_bottom_bottom.GetValue()),
|
||||
Settings::values.swap_screen.GetValue());
|
||||
} else {
|
||||
int width, height;
|
||||
switch (Settings::values.layout_option.GetValue()) {
|
||||
case Settings::LayoutOption::SingleScreen:
|
||||
#ifndef ANDROID
|
||||
case Settings::LayoutOption::SeparateWindows:
|
||||
case Settings::LayoutOption::SeparateWindows:
|
||||
#endif
|
||||
{
|
||||
const bool swap_screens = is_secondary || Settings::values.swap_screen.GetValue();
|
||||
if (swap_screens) {
|
||||
width = Core::kScreenBottomWidth * res_scale;
|
||||
height = Core::kScreenBottomHeight * res_scale;
|
||||
} else {
|
||||
{
|
||||
const bool swap_screens = is_secondary || Settings::values.swap_screen.GetValue();
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
if (swap_screens) {
|
||||
width = Core::kScreenBottomHeight * res_scale;
|
||||
height = Core::kScreenBottomWidth * res_scale;
|
||||
} else {
|
||||
width = Core::kScreenTopHeight * res_scale;
|
||||
height = Core::kScreenTopWidth * res_scale;
|
||||
}
|
||||
} else {
|
||||
if (swap_screens) {
|
||||
width = Core::kScreenBottomWidth * res_scale;
|
||||
height = Core::kScreenBottomHeight * res_scale;
|
||||
} else {
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
}
|
||||
}
|
||||
layout = SingleFrameLayout(width, height, swap_screens,
|
||||
Settings::values.upright_screen.GetValue());
|
||||
break;
|
||||
}
|
||||
case Settings::LayoutOption::LargeScreen:
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
if (Settings::values.swap_screen.GetValue()) {
|
||||
width = Core::kScreenBottomHeight * res_scale;
|
||||
height =
|
||||
(Core::kScreenBottomWidth +
|
||||
static_cast<int>(Core::kScreenTopWidth /
|
||||
Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
} else {
|
||||
width = Core::kScreenTopHeight * res_scale;
|
||||
height =
|
||||
(Core::kScreenTopWidth +
|
||||
static_cast<int>(Core::kScreenBottomWidth /
|
||||
Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
}
|
||||
} else {
|
||||
if (Settings::values.swap_screen.GetValue()) {
|
||||
width = (Core::kScreenBottomWidth +
|
||||
Core::kScreenTopWidth /
|
||||
static_cast<int>(
|
||||
Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
height = Core::kScreenBottomHeight * res_scale;
|
||||
} else {
|
||||
width = (Core::kScreenTopWidth +
|
||||
Core::kScreenBottomWidth /
|
||||
static_cast<int>(
|
||||
Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
}
|
||||
}
|
||||
layout = LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue(),
|
||||
Settings::values.large_screen_proportion.GetValue());
|
||||
break;
|
||||
case Settings::LayoutOption::SideScreen:
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
width = Core::kScreenTopHeight * res_scale;
|
||||
height = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
||||
} else {
|
||||
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
}
|
||||
layout = SideFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue());
|
||||
break;
|
||||
case Settings::LayoutOption::MobilePortrait:
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||
layout =
|
||||
MobilePortraitFrameLayout(width, height, Settings::values.swap_screen.GetValue());
|
||||
break;
|
||||
case Settings::LayoutOption::MobileLandscape:
|
||||
if (Settings::values.swap_screen.GetValue()) {
|
||||
width =
|
||||
(Core::kScreenBottomWidth + static_cast<int>(Core::kScreenTopWidth / 2.25f)) *
|
||||
res_scale;
|
||||
height = Core::kScreenBottomHeight * res_scale;
|
||||
} else {
|
||||
width =
|
||||
(Core::kScreenTopWidth + static_cast<int>(Core::kScreenBottomWidth / 2.25f)) *
|
||||
res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
}
|
||||
layout = MobileLandscapeFrameLayout(
|
||||
width, height, Settings::values.swap_screen.GetValue(), 2.25f, false);
|
||||
break;
|
||||
case Settings::LayoutOption::Default:
|
||||
default:
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
width = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||
height = Core::kScreenTopWidth * res_scale;
|
||||
} else {
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||
}
|
||||
layout = DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue());
|
||||
break;
|
||||
}
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
return SingleFrameLayout(width, height, swap_screens,
|
||||
Settings::values.upright_screen.GetValue());
|
||||
}
|
||||
|
||||
case Settings::LayoutOption::LargeScreen:
|
||||
if (Settings::values.swap_screen.GetValue()) {
|
||||
width = (Core::kScreenBottomWidth +
|
||||
Core::kScreenTopWidth /
|
||||
static_cast<int>(Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
height = Core::kScreenBottomHeight * res_scale;
|
||||
} else {
|
||||
width = (Core::kScreenTopWidth +
|
||||
Core::kScreenBottomWidth /
|
||||
static_cast<int>(Settings::values.large_screen_proportion.GetValue())) *
|
||||
res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
}
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue(),
|
||||
Settings::values.large_screen_proportion.GetValue(),
|
||||
VerticalAlignment::Bottom);
|
||||
|
||||
case Settings::LayoutOption::SideScreen:
|
||||
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue(), 1,
|
||||
VerticalAlignment::Middle);
|
||||
|
||||
case Settings::LayoutOption::MobilePortrait:
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||
return MobilePortraitFrameLayout(width, height, Settings::values.swap_screen.GetValue());
|
||||
|
||||
case Settings::LayoutOption::MobileLandscape: {
|
||||
constexpr float large_screen_proportion = 2.25f;
|
||||
if (Settings::values.swap_screen.GetValue()) {
|
||||
width = (Core::kScreenBottomWidth +
|
||||
static_cast<int>(Core::kScreenTopWidth / large_screen_proportion)) *
|
||||
res_scale;
|
||||
height = Core::kScreenBottomHeight * res_scale;
|
||||
} else {
|
||||
width = (Core::kScreenTopWidth +
|
||||
static_cast<int>(Core::kScreenBottomWidth / large_screen_proportion)) *
|
||||
res_scale;
|
||||
height = Core::kScreenTopHeight * res_scale;
|
||||
}
|
||||
return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), false,
|
||||
large_screen_proportion, VerticalAlignment::Top);
|
||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) {
|
||||
layout = Layout::GetCardboardSettings(layout);
|
||||
}
|
||||
|
||||
case Settings::LayoutOption::Default:
|
||||
default:
|
||||
width = Core::kScreenTopWidth * res_scale;
|
||||
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||
|
||||
if (Settings::values.upright_screen.GetValue()) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
return DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
|
||||
Settings::values.upright_screen.GetValue());
|
||||
}
|
||||
UNREACHABLE();
|
||||
return layout;
|
||||
}
|
||||
|
||||
FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout) {
|
||||
|
@ -20,31 +20,6 @@ enum class DisplayOrientation {
|
||||
PortraitFlipped, // 3DS rotated 270 degrees counter-clockwise
|
||||
};
|
||||
|
||||
/// Describes the vertical alignment of the top and bottom screens in LargeFrameLayout
|
||||
/// Top
|
||||
/// +-------------+-----+
|
||||
/// | | |
|
||||
/// | +-----+
|
||||
/// | |
|
||||
/// +-------------+
|
||||
/// Middle
|
||||
/// +-------------+
|
||||
/// | +-----+
|
||||
/// | | |
|
||||
/// | +-----+
|
||||
/// +-------------+
|
||||
/// Bottom
|
||||
/// +-------------+
|
||||
/// | |
|
||||
/// | +-----+
|
||||
/// | | |
|
||||
/// +-------------+-----+
|
||||
enum class VerticalAlignment {
|
||||
Top,
|
||||
Middle,
|
||||
Bottom,
|
||||
};
|
||||
|
||||
/// Describes the horizontal coordinates for the right eye screen when using Cardboard VR
|
||||
struct CardboardSettings {
|
||||
u32 top_screen_right_eye;
|
||||
@ -68,7 +43,7 @@ struct FramebufferLayout {
|
||||
CardboardSettings cardboard;
|
||||
|
||||
/**
|
||||
* Returns the ratio of pixel size of the top screen, compared to the native size of the 3DS
|
||||
* Returns the ration of pixel size of the top screen, compared to the native size of the 3DS
|
||||
* screen.
|
||||
*/
|
||||
u32 GetScalingRatio() const;
|
||||
@ -79,7 +54,6 @@ struct FramebufferLayout {
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be displayed above the top screen
|
||||
* @param upright if true, the screens will be rotated 90 degrees anti-clockwise
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped, bool upright);
|
||||
@ -93,12 +67,25 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped, boo
|
||||
*/
|
||||
FramebufferLayout MobilePortraitFrameLayout(u32 width, u32 height, bool is_swapped);
|
||||
|
||||
/**
|
||||
* Factory method for constructing a Frame with the a 4x size Top screen with a 1x size bottom
|
||||
* screen on the right
|
||||
* This is useful in particular because it matches well with a 1920x1080 resolution monitor
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be the large display
|
||||
* @param scale_factor Scale factor to use for bottom screen with respect to top screen
|
||||
* @param center_vertical When true, centers the top and bottom screens vertically
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout MobileLandscapeFrameLayout(u32 width, u32 height, bool is_swapped,
|
||||
float scale_factor, bool center_vertical);
|
||||
|
||||
/**
|
||||
* Factory method for constructing a FramebufferLayout with only the top or bottom screen
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be displayed (and the top won't be displayed)
|
||||
* @param upright if true, the screens will be rotated 90 degrees anti-clockwise
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool upright);
|
||||
@ -110,25 +97,32 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be the large display
|
||||
* @param upright if true, the screens will be rotated 90 degrees anti-clockwise
|
||||
* @param scale_factor The ratio between the large screen with respect to the smaller screen
|
||||
* @param vertical_alignment The vertical alignment of the smaller screen relative to the larger
|
||||
* screen
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool upright,
|
||||
float scale_factor, VerticalAlignment vertical_alignment);
|
||||
float scale_factor);
|
||||
/**
|
||||
* Factory method for constructing a frame with 2.5 times bigger top screen on the right,
|
||||
* and 1x top and bottom screen on the left
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be the large display
|
||||
* @param upright if true, the screens will be rotated 90 degrees anti-clockwise
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool upright);
|
||||
|
||||
/**
|
||||
* Factory method for constructing a Frame with the Top screen and bottom
|
||||
* screen side by side
|
||||
* This is useful for devices with small screens, like the GPDWin
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @param is_swapped if true, the bottom screen will be the left display
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout SideFrameLayout(u32 width, u32 height, bool is_swapped, bool upright);
|
||||
|
||||
/**
|
||||
* Factory method for constructing a Frame with the Top screen and bottom
|
||||
* screen on separate windows
|
||||
|
@ -5,16 +5,97 @@
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/applets/applet.h"
|
||||
#include "core/hle/applets/erreula.h"
|
||||
#include "core/hle/applets/mii_selector.h"
|
||||
#include "core/hle/applets/mint.h"
|
||||
#include "core/hle/applets/swkbd.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace HLE::Applets {
|
||||
|
||||
static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets;
|
||||
/// The CoreTiming event identifier for the Applet update callback.
|
||||
static Core::TimingEventType* applet_update_event = nullptr;
|
||||
/// The interval at which the Applet update callback will be called, 16.6ms
|
||||
static const u64 applet_update_interval_us = 16666;
|
||||
|
||||
Result Applet::Create(Service::APT::AppletId id, Service::APT::AppletId parent, bool preload,
|
||||
const std::shared_ptr<Service::APT::AppletManager>& manager) {
|
||||
switch (id) {
|
||||
case Service::APT::AppletId::SoftwareKeyboard1:
|
||||
case Service::APT::AppletId::SoftwareKeyboard2:
|
||||
applets[id] = std::make_shared<SoftwareKeyboard>(id, parent, preload, manager);
|
||||
break;
|
||||
case Service::APT::AppletId::Ed1:
|
||||
case Service::APT::AppletId::Ed2:
|
||||
applets[id] = std::make_shared<MiiSelector>(id, parent, preload, manager);
|
||||
break;
|
||||
case Service::APT::AppletId::Error:
|
||||
case Service::APT::AppletId::Error2:
|
||||
applets[id] = std::make_shared<ErrEula>(id, parent, preload, manager);
|
||||
break;
|
||||
case Service::APT::AppletId::Mint:
|
||||
case Service::APT::AppletId::Mint2:
|
||||
applets[id] = std::make_shared<Mint>(id, parent, preload, manager);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_APT, "Could not create applet {}", id);
|
||||
// TODO(Subv): Find the right error code
|
||||
return Result(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported,
|
||||
ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
Service::APT::AppletAttributes attributes;
|
||||
attributes.applet_pos.Assign(Service::APT::AppletPos::AutoLibrary);
|
||||
attributes.is_home_menu.Assign(false);
|
||||
const auto lock_handle_data = manager->GetLockHandle(attributes);
|
||||
|
||||
manager->Initialize(id, lock_handle_data->corrected_attributes);
|
||||
manager->Enable(lock_handle_data->corrected_attributes);
|
||||
if (preload) {
|
||||
manager->FinishPreloadingLibraryApplet(id);
|
||||
}
|
||||
|
||||
// Schedule the update event
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(
|
||||
usToCycles(applet_update_interval_us), applet_update_event, static_cast<u64>(id));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> Applet::Get(Service::APT::AppletId id) {
|
||||
auto itr = applets.find(id);
|
||||
if (itr != applets.end())
|
||||
return itr->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Handles updating the current Applet every time it's called.
|
||||
static void AppletUpdateEvent(u64 applet_id, s64 cycles_late) {
|
||||
const auto id = static_cast<Service::APT::AppletId>(applet_id);
|
||||
const auto applet = Applet::Get(id);
|
||||
ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id={:08X}", id);
|
||||
|
||||
if (applet->IsActive()) {
|
||||
applet->Update();
|
||||
}
|
||||
|
||||
// If the applet is still running after the last update, reschedule the event
|
||||
if (applet->IsRunning()) {
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(
|
||||
usToCycles(applet_update_interval_us) - cycles_late, applet_update_event, applet_id);
|
||||
} else {
|
||||
// Otherwise the applet has terminated, in which case we should clean it up
|
||||
applets[id] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Applet::IsRunning() const {
|
||||
return is_running;
|
||||
}
|
||||
@ -59,4 +140,13 @@ void Applet::CloseApplet(std::shared_ptr<Kernel::Object> object, const std::vect
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
void Init() {
|
||||
// Register the applet update callback
|
||||
applet_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent(
|
||||
"HLE Applet Update Event", AppletUpdateEvent);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
Core::System::GetInstance().CoreTiming().RemoveEvent(applet_update_event);
|
||||
}
|
||||
} // namespace HLE::Applets
|
||||
|
@ -14,6 +14,24 @@ class Applet {
|
||||
public:
|
||||
virtual ~Applet() = default;
|
||||
|
||||
/**
|
||||
* Creates an instance of the Applet subclass identified by the parameter.
|
||||
* and stores it in a global map.
|
||||
* @param id Id of the applet to create.
|
||||
* @param parent Id of the applet's parent.
|
||||
* @param preload Whether the applet is being preloaded.
|
||||
* @returns Result Whether the operation was successful or not.
|
||||
*/
|
||||
static Result Create(Service::APT::AppletId id, Service::APT::AppletId parent, bool preload,
|
||||
const std::shared_ptr<Service::APT::AppletManager>& manager);
|
||||
|
||||
/**
|
||||
* Retrieves the Applet instance identified by the specified id.
|
||||
* @param id Id of the Applet to retrieve.
|
||||
* @returns Requested Applet or nullptr if not found.
|
||||
*/
|
||||
static std::shared_ptr<Applet> Get(Service::APT::AppletId id);
|
||||
|
||||
/**
|
||||
* Handles a parameter from the application.
|
||||
* @param parameter Parameter data to handle.
|
||||
@ -37,9 +55,9 @@ public:
|
||||
virtual void Update() = 0;
|
||||
|
||||
protected:
|
||||
Applet(Core::System& system, Service::APT::AppletId id, Service::APT::AppletId parent,
|
||||
bool preload, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: system(system), id(id), parent(parent), preload(preload), manager(std::move(manager)) {}
|
||||
Applet(Service::APT::AppletId id, Service::APT::AppletId parent, bool preload,
|
||||
std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: id(id), parent(parent), preload(preload), manager(std::move(manager)) {}
|
||||
|
||||
/**
|
||||
* Handles a parameter from the application.
|
||||
@ -61,8 +79,6 @@ protected:
|
||||
*/
|
||||
virtual Result Finalize() = 0;
|
||||
|
||||
Core::System& system;
|
||||
|
||||
Service::APT::AppletId id; ///< Id of this Applet
|
||||
Service::APT::AppletId parent; ///< Id of this Applet's parent
|
||||
bool preload; ///< Whether the Applet is being preloaded.
|
||||
@ -81,4 +97,9 @@ private:
|
||||
std::weak_ptr<Service::APT::AppletManager> manager;
|
||||
};
|
||||
|
||||
/// Initializes the HLE applets
|
||||
void Init();
|
||||
|
||||
/// Shuts down the HLE applets
|
||||
void Shutdown();
|
||||
} // namespace HLE::Applets
|
||||
|
@ -28,7 +28,7 @@ Result ErrEula::ReceiveParameterImpl(const Service::APT::MessageParameter& param
|
||||
// TODO: allocated memory never released
|
||||
using Kernel::MemoryPermission;
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = system.Kernel().CreateSharedMemoryForApplet(
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"ErrEula Memory");
|
||||
|
||||
|
@ -11,9 +11,9 @@ namespace HLE::Applets {
|
||||
|
||||
class ErrEula final : public Applet {
|
||||
public:
|
||||
explicit ErrEula(Core::System& system, Service::APT::AppletId id, Service::APT::AppletId parent,
|
||||
bool preload, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(system, id, parent, preload, std::move(manager)) {}
|
||||
explicit ErrEula(Service::APT::AppletId id, Service::APT::AppletId parent, bool preload,
|
||||
std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(id, parent, preload, std::move(manager)) {}
|
||||
|
||||
Result ReceiveParameterImpl(const Service::APT::MessageParameter& parameter) override;
|
||||
Result Start(const Service::APT::MessageParameter& parameter) override;
|
||||
|
@ -35,7 +35,7 @@ Result MiiSelector::ReceiveParameterImpl(const Service::APT::MessageParameter& p
|
||||
|
||||
using Kernel::MemoryPermission;
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = system.Kernel().CreateSharedMemoryForApplet(
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"MiiSelector Memory");
|
||||
|
||||
@ -57,7 +57,7 @@ Result MiiSelector::Start(const Service::APT::MessageParameter& parameter) {
|
||||
std::memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
|
||||
|
||||
using namespace Frontend;
|
||||
frontend_applet = system.GetMiiSelector();
|
||||
frontend_applet = Core::System::GetInstance().GetMiiSelector();
|
||||
ASSERT(frontend_applet);
|
||||
|
||||
MiiSelectorConfig frontend_config = ToFrontendConfig(config);
|
||||
|
@ -62,9 +62,9 @@ ASSERT_REG_POSITION(guest_mii_name, 0x6C);
|
||||
|
||||
class MiiSelector final : public Applet {
|
||||
public:
|
||||
MiiSelector(Core::System& system, Service::APT::AppletId id, Service::APT::AppletId parent,
|
||||
bool preload, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(system, id, parent, preload, std::move(manager)) {}
|
||||
MiiSelector(Service::APT::AppletId id, Service::APT::AppletId parent, bool preload,
|
||||
std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(id, parent, preload, std::move(manager)) {}
|
||||
|
||||
Result ReceiveParameterImpl(const Service::APT::MessageParameter& parameter) override;
|
||||
Result Start(const Service::APT::MessageParameter& parameter) override;
|
||||
|
@ -28,7 +28,7 @@ Result Mint::ReceiveParameterImpl(const Service::APT::MessageParameter& paramete
|
||||
// TODO: allocated memory never released
|
||||
using Kernel::MemoryPermission;
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = system.Kernel().CreateSharedMemoryForApplet(
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"Mint Memory");
|
||||
|
||||
|
@ -11,9 +11,9 @@ namespace HLE::Applets {
|
||||
|
||||
class Mint final : public Applet {
|
||||
public:
|
||||
explicit Mint(Core::System& system, Service::APT::AppletId id, Service::APT::AppletId parent,
|
||||
bool preload, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(system, id, parent, preload, std::move(manager)) {}
|
||||
explicit Mint(Service::APT::AppletId id, Service::APT::AppletId parent, bool preload,
|
||||
std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(id, parent, preload, std::move(manager)) {}
|
||||
|
||||
Result ReceiveParameterImpl(const Service::APT::MessageParameter& parameter) override;
|
||||
Result Start(const Service::APT::MessageParameter& parameter) override;
|
||||
|
@ -32,7 +32,7 @@ Result SoftwareKeyboard::ReceiveParameterImpl(Service::APT::MessageParameter con
|
||||
|
||||
using Kernel::MemoryPermission;
|
||||
// Create a SharedMemory that directly points to this heap block.
|
||||
framebuffer_memory = system.Kernel().CreateSharedMemoryForApplet(
|
||||
framebuffer_memory = Core::System::GetInstance().Kernel().CreateSharedMemoryForApplet(
|
||||
0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
|
||||
"SoftwareKeyboard Memory");
|
||||
|
||||
@ -99,7 +99,7 @@ Result SoftwareKeyboard::Start(Service::APT::MessageParameter const& parameter)
|
||||
DrawScreenKeyboard();
|
||||
|
||||
using namespace Frontend;
|
||||
frontend_applet = system.GetSoftwareKeyboard();
|
||||
frontend_applet = Core::System::GetInstance().GetSoftwareKeyboard();
|
||||
ASSERT(frontend_applet);
|
||||
|
||||
frontend_applet->Execute(ToFrontendConfig(config));
|
||||
|
@ -175,9 +175,9 @@ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config
|
||||
|
||||
class SoftwareKeyboard final : public Applet {
|
||||
public:
|
||||
SoftwareKeyboard(Core::System& system, Service::APT::AppletId id, Service::APT::AppletId parent,
|
||||
bool preload, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(system, id, parent, preload, std::move(manager)) {}
|
||||
SoftwareKeyboard(Service::APT::AppletId id, Service::APT::AppletId parent, bool preload,
|
||||
std::weak_ptr<Service::APT::AppletManager> manager)
|
||||
: Applet(id, parent, preload, std::move(manager)) {}
|
||||
|
||||
Result ReceiveParameterImpl(const Service::APT::MessageParameter& parameter) override;
|
||||
Result Start(const Service::APT::MessageParameter& parameter) override;
|
||||
|
@ -18,7 +18,8 @@ SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel
|
||||
|
||||
SharedMemory::~SharedMemory() {
|
||||
for (const auto& interval : holding_memory) {
|
||||
memory_region->Free(interval.lower(), interval.upper() - interval.lower());
|
||||
kernel.GetMemoryRegion(MemoryRegion::SYSTEM)
|
||||
->Free(interval.lower(), interval.upper() - interval.lower());
|
||||
}
|
||||
|
||||
auto process = owner_process.lock();
|
||||
@ -38,18 +39,17 @@ ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory(
|
||||
std::shared_ptr<Process> owner_process, u32 size, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
|
||||
|
||||
auto memory_region = GetMemoryRegion(region);
|
||||
auto shared_memory = std::make_shared<SharedMemory>(*this);
|
||||
shared_memory->owner_process = owner_process;
|
||||
shared_memory->name = std::move(name);
|
||||
shared_memory->size = size;
|
||||
shared_memory->memory_region = memory_region;
|
||||
shared_memory->permissions = permissions;
|
||||
shared_memory->other_permissions = other_permissions;
|
||||
|
||||
if (address == 0) {
|
||||
// We need to allocate a block from the Linear Heap ourselves.
|
||||
// We'll manually allocate some memory from the linear heap in the specified region.
|
||||
auto memory_region = GetMemoryRegion(region);
|
||||
auto offset = memory_region->LinearAllocate(size);
|
||||
|
||||
ASSERT_MSG(offset, "Not enough space in region to allocate shared memory!");
|
||||
@ -93,7 +93,6 @@ std::shared_ptr<SharedMemory> KernelSystem::CreateSharedMemoryForApplet(
|
||||
shared_memory->owner_process = std::weak_ptr<Process>();
|
||||
shared_memory->name = std::move(name);
|
||||
shared_memory->size = size;
|
||||
shared_memory->memory_region = memory_region;
|
||||
shared_memory->permissions = permissions;
|
||||
shared_memory->other_permissions = other_permissions;
|
||||
for (const auto& interval : backing_blocks) {
|
||||
|
@ -94,8 +94,6 @@ private:
|
||||
std::vector<std::pair<MemoryRef, u32>> backing_blocks;
|
||||
/// Size of the memory block. Page-aligned.
|
||||
u32 size = 0;
|
||||
/// Region of memory this block exists in.
|
||||
std::shared_ptr<MemoryRegionInfo> memory_region = nullptr;
|
||||
/// Permission restrictions applied to the process which created the block.
|
||||
MemoryPermission permissions{};
|
||||
/// Permission restrictions applied to other processes mapping the block.
|
||||
@ -118,7 +116,6 @@ private:
|
||||
ar& linear_heap_phys_offset;
|
||||
ar& backing_blocks;
|
||||
ar& size;
|
||||
ar& memory_region;
|
||||
ar& permissions;
|
||||
ar& other_permissions;
|
||||
ar& owner_process;
|
||||
|
@ -20,9 +20,6 @@
|
||||
#include "core/hle/service/soc/soc_u.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Service::AC::Module)
|
||||
SERVICE_CONSTRUCT_IMPL(Service::AC::Module)
|
||||
|
||||
namespace Service::AC {
|
||||
void Module::Interface::CreateDefaultConfig(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
@ -99,7 +96,7 @@ void Module::Interface::GetWifiStatus(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
bool can_reach_internet = false;
|
||||
|
||||
std::shared_ptr<SOC::SOC_U> socu_module = SOC::GetService(ac->system);
|
||||
std::shared_ptr<SOC::SOC_U> socu_module = SOC::GetService(Core::System::GetInstance());
|
||||
if (socu_module) {
|
||||
can_reach_internet = socu_module->GetDefaultInterfaceInfo().has_value();
|
||||
}
|
||||
@ -197,13 +194,11 @@ Module::Interface::Interface(std::shared_ptr<Module> ac, const char* name, u32 m
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto& service_manager = system.ServiceManager();
|
||||
auto ac = std::make_shared<Module>(system);
|
||||
auto ac = std::make_shared<Module>();
|
||||
std::make_shared<AC_I>(ac)->InstallAsService(service_manager);
|
||||
std::make_shared<AC_U>(ac)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
Module::Module(Core::System& system_) : system(system_) {}
|
||||
|
||||
template <class Archive>
|
||||
void Module::serialize(Archive& ar, const unsigned int) {
|
||||
ar& ac_connected;
|
||||
@ -212,6 +207,7 @@ void Module::serialize(Archive& ar, const unsigned int) {
|
||||
ar& disconnect_event;
|
||||
// default_config is never written to
|
||||
}
|
||||
SERIALIZE_IMPL(Module)
|
||||
|
||||
} // namespace Service::AC
|
||||
|
||||
SERIALIZE_IMPL(Service::AC::Module)
|
||||
|
@ -19,9 +19,6 @@ class Event;
|
||||
namespace Service::AC {
|
||||
class Module final {
|
||||
public:
|
||||
explicit Module(Core::System& system_);
|
||||
~Module() = default;
|
||||
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> ac, const char* name, u32 max_session);
|
||||
@ -172,16 +169,11 @@ protected:
|
||||
std::shared_ptr<Kernel::Event> disconnect_event;
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
void serialize(Archive& ar, const unsigned int file_version);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::AC
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Service::AC::Module)
|
||||
SERVICE_CONSTRUCT(Service::AC::Module)
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <cryptopp/modes.h>
|
||||
#include <fmt/format.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/archives.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -34,9 +33,6 @@
|
||||
#include "core/loader/smdh.h"
|
||||
#include "core/nus_download.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Service::AM::Module)
|
||||
SERVICE_CONSTRUCT_IMPL(Service::AM::Module)
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr u16 PLATFORM_CTR = 0x0004;
|
||||
@ -85,9 +81,8 @@ public:
|
||||
std::vector<CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption> content;
|
||||
};
|
||||
|
||||
CIAFile::CIAFile(Core::System& system_, Service::FS::MediaType media_type)
|
||||
: system(system_), media_type(media_type),
|
||||
decryption_state(std::make_unique<DecryptionState>()) {}
|
||||
CIAFile::CIAFile(Service::FS::MediaType media_type)
|
||||
: media_type(media_type), decryption_state(std::make_unique<DecryptionState>()) {}
|
||||
|
||||
CIAFile::~CIAFile() {
|
||||
Close();
|
||||
@ -366,7 +361,8 @@ bool CIAFile::Close() const {
|
||||
// are closed.
|
||||
std::string to_delete =
|
||||
GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index);
|
||||
if (!system.IsPoweredOn() || !system.SetSelfDelete(to_delete)) {
|
||||
if (!(Core::System::GetInstance().IsPoweredOn() &&
|
||||
Core::System::GetInstance().SetSelfDelete(to_delete))) {
|
||||
FileUtil::Delete(to_delete);
|
||||
}
|
||||
}
|
||||
@ -429,7 +425,6 @@ InstallStatus InstallCIA(const std::string& path,
|
||||
FileSys::CIAContainer container;
|
||||
if (container.Load(path) == Loader::ResultStatus::Success) {
|
||||
Service::AM::CIAFile installFile(
|
||||
Core::System::GetInstance(),
|
||||
Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID()));
|
||||
|
||||
bool title_key_available = container.GetTicket().GetTitleKey().has_value();
|
||||
@ -507,7 +502,7 @@ InstallStatus InstallCIA(const std::string& path,
|
||||
InstallStatus InstallFromNus(u64 title_id, int version) {
|
||||
LOG_DEBUG(Service_AM, "Downloading {:X}", title_id);
|
||||
|
||||
CIAFile install_file{Core::System::GetInstance(), GetTitleMediaType(title_id)};
|
||||
CIAFile install_file{GetTitleMediaType(title_id)};
|
||||
|
||||
std::string path = fmt::format("/ccs/download/{:016X}/tmd", title_id);
|
||||
if (version != -1) {
|
||||
@ -1332,7 +1327,7 @@ void Module::Interface::BeginImportProgram(Kernel::HLERequestContext& ctx) {
|
||||
// Citra will store contents out to sdmc/nand
|
||||
const FileSys::Path cia_path = {};
|
||||
auto file = std::make_shared<Service::FS::File>(
|
||||
am->system.Kernel(), std::make_unique<CIAFile>(am->system, media_type), cia_path);
|
||||
am->kernel, std::make_unique<CIAFile>(media_type), cia_path);
|
||||
|
||||
am->cia_installing = true;
|
||||
|
||||
@ -1359,7 +1354,7 @@ void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext&
|
||||
// contents out to sdmc/nand
|
||||
const FileSys::Path cia_path = {};
|
||||
auto file = std::make_shared<Service::FS::File>(
|
||||
am->system.Kernel(), std::make_unique<CIAFile>(am->system, FS::MediaType::NAND), cia_path);
|
||||
am->kernel, std::make_unique<CIAFile>(FS::MediaType::NAND), cia_path);
|
||||
|
||||
am->cia_installing = true;
|
||||
|
||||
@ -1774,8 +1769,8 @@ void Module::Interface::BeginImportTicket(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
// Create our TicketFile handle for the app to write to
|
||||
auto file = std::make_shared<Service::FS::File>(
|
||||
am->system.Kernel(), std::make_unique<TicketFile>(), FileSys::Path{});
|
||||
auto file = std::make_shared<Service::FS::File>(am->kernel, std::make_unique<TicketFile>(),
|
||||
FileSys::Path{});
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(ResultSuccess); // No error
|
||||
@ -1794,19 +1789,13 @@ void Module::Interface::EndImportTicket(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void Module::serialize(Archive& ar, const unsigned int) {
|
||||
ar& cia_installing;
|
||||
ar& am_title_list;
|
||||
ar& system_updater_mutex;
|
||||
}
|
||||
SERIALIZE_IMPL(Module)
|
||||
|
||||
Module::Module(Core::System& system) : system(system) {
|
||||
Module::Module(Core::System& system) : kernel(system.Kernel()) {
|
||||
ScanForAllTitles();
|
||||
system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex");
|
||||
}
|
||||
|
||||
Module::Module(Kernel::KernelSystem& kernel) : kernel(kernel) {}
|
||||
|
||||
Module::~Module() = default;
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/construct.h"
|
||||
#include "common/swap.h"
|
||||
@ -79,7 +80,7 @@ using ProgressCallback = void(std::size_t, std::size_t);
|
||||
// A file handled returned for CIAs to be written into and subsequently installed.
|
||||
class CIAFile final : public FileSys::FileBackend {
|
||||
public:
|
||||
explicit CIAFile(Core::System& system_, Service::FS::MediaType media_type);
|
||||
explicit CIAFile(Service::FS::MediaType media_type);
|
||||
~CIAFile();
|
||||
|
||||
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
|
||||
@ -94,8 +95,6 @@ public:
|
||||
void Flush() const override;
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
// Whether it's installing an update, and what step of installation it is at
|
||||
bool is_update = false;
|
||||
CIAInstallState install_state = CIAInstallState::InstallStarted;
|
||||
@ -707,6 +706,8 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
explicit Module(Kernel::KernelSystem& kernel);
|
||||
|
||||
/**
|
||||
* Scans the for titles in a storage medium for listing.
|
||||
* @param media_type the storage medium to scan
|
||||
@ -718,13 +719,27 @@ private:
|
||||
*/
|
||||
void ScanForAllTitles();
|
||||
|
||||
Core::System& system;
|
||||
Kernel::KernelSystem& kernel;
|
||||
bool cia_installing = false;
|
||||
std::array<std::vector<u64_le>, 3> am_title_list;
|
||||
std::shared_ptr<Kernel::Mutex> system_updater_mutex;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& cia_installing;
|
||||
ar& am_title_list;
|
||||
ar& system_updater_mutex;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
static void load_construct(Archive& ar, Module* t, const unsigned int file_version) {
|
||||
::new (t) Module(Core::Global<Kernel::KernelSystem>());
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void save_construct(Archive& ar, const unsigned int file_version) const {}
|
||||
|
||||
friend class ::construct_access;
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
@ -732,5 +747,4 @@ void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Service::AM::Module)
|
||||
SERVICE_CONSTRUCT(Service::AM::Module)
|
||||
BOOST_SERIALIZATION_CONSTRUCT(Service::AM::Module);
|
||||
|
@ -8,10 +8,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/applets/applet.h"
|
||||
#include "core/hle/applets/erreula.h"
|
||||
#include "core/hle/applets/mii_selector.h"
|
||||
#include "core/hle/applets/mint.h"
|
||||
#include "core/hle/applets/swkbd.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/apt/applet_manager.h"
|
||||
#include "core/hle/service/apt/errors.h"
|
||||
@ -26,8 +22,6 @@ namespace Service::APT {
|
||||
|
||||
/// The interval at which the home button update callback will be called, 16.6ms
|
||||
static constexpr u64 button_update_interval_us = 16666;
|
||||
/// The interval at which the HLE Applet update callback will be called, 16.6ms.
|
||||
static constexpr u64 hle_applet_update_interval_us = 16666;
|
||||
|
||||
struct AppletTitleData {
|
||||
// There are two possible applet ids for each applet.
|
||||
@ -228,8 +222,8 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) {
|
||||
parameter.sender_id, parameter.destination_id, parameter.signal, parameter.buffer.size());
|
||||
|
||||
// If the applet is being HLEd, send directly to the applet.
|
||||
if (hle_applets.contains(parameter.destination_id)) {
|
||||
hle_applets[parameter.destination_id]->ReceiveParameter(parameter);
|
||||
if (auto dest_applet = HLE::Applets::Applet::Get(parameter.destination_id)) {
|
||||
dest_applet->ReceiveParameter(parameter);
|
||||
} else {
|
||||
// Otherwise, send the parameter the LLE way.
|
||||
next_parameter = parameter;
|
||||
@ -503,52 +497,6 @@ void AppletManager::SendNotificationToAll(Notification notification) {
|
||||
}
|
||||
}
|
||||
|
||||
Result AppletManager::CreateHLEApplet(AppletId id, AppletId parent, bool preload) {
|
||||
switch (id) {
|
||||
case AppletId::SoftwareKeyboard1:
|
||||
case AppletId::SoftwareKeyboard2:
|
||||
hle_applets[id] = std::make_shared<HLE::Applets::SoftwareKeyboard>(
|
||||
system, id, parent, preload, shared_from_this());
|
||||
break;
|
||||
case AppletId::Ed1:
|
||||
case AppletId::Ed2:
|
||||
hle_applets[id] = std::make_shared<HLE::Applets::MiiSelector>(system, id, parent, preload,
|
||||
shared_from_this());
|
||||
break;
|
||||
case AppletId::Error:
|
||||
case AppletId::Error2:
|
||||
hle_applets[id] = std::make_shared<HLE::Applets::ErrEula>(system, id, parent, preload,
|
||||
shared_from_this());
|
||||
break;
|
||||
case AppletId::Mint:
|
||||
case AppletId::Mint2:
|
||||
hle_applets[id] =
|
||||
std::make_shared<HLE::Applets::Mint>(system, id, parent, preload, shared_from_this());
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_APT, "Could not create applet {}", id);
|
||||
// TODO(Subv): Find the right error code
|
||||
return Result(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported,
|
||||
ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
AppletAttributes attributes;
|
||||
attributes.applet_pos.Assign(AppletPos::AutoLibrary);
|
||||
attributes.is_home_menu.Assign(false);
|
||||
const auto lock_handle_data = GetLockHandle(attributes);
|
||||
|
||||
Initialize(id, lock_handle_data->corrected_attributes);
|
||||
Enable(lock_handle_data->corrected_attributes);
|
||||
if (preload) {
|
||||
FinishPreloadingLibraryApplet(id);
|
||||
}
|
||||
|
||||
// Schedule the update event
|
||||
system.CoreTiming().ScheduleEvent(usToCycles(hle_applet_update_interval_us),
|
||||
hle_applet_update_event, static_cast<u64>(id));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) {
|
||||
// The real APT service returns an error if there's a pending APT parameter when this function
|
||||
// is called.
|
||||
@ -575,13 +523,14 @@ Result AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) {
|
||||
}
|
||||
|
||||
// If we weren't able to load the native applet title, try to fallback to an HLE implementation.
|
||||
if (hle_applets.contains(applet_id)) {
|
||||
LOG_WARNING(Service_APT, "Applet has already been started id={:03X}", applet_id);
|
||||
auto applet = HLE::Applets::Applet::Get(applet_id);
|
||||
if (applet) {
|
||||
LOG_WARNING(Service_APT, "applet has already been started id={:03X}", applet_id);
|
||||
return ResultSuccess;
|
||||
} else {
|
||||
auto parent = GetAppletSlotId(last_library_launcher_slot);
|
||||
LOG_DEBUG(Service_APT, "Creating HLE applet {:03X} with parent {:03X}", applet_id, parent);
|
||||
return CreateHLEApplet(applet_id, parent, false);
|
||||
return HLE::Applets::Applet::Create(applet_id, parent, false, shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,13 +551,14 @@ Result AppletManager::PreloadLibraryApplet(AppletId applet_id) {
|
||||
}
|
||||
|
||||
// If we weren't able to load the native applet title, try to fallback to an HLE implementation.
|
||||
if (hle_applets.contains(applet_id)) {
|
||||
LOG_WARNING(Service_APT, "Applet has already been started id={:08X}", applet_id);
|
||||
auto applet = HLE::Applets::Applet::Get(applet_id);
|
||||
if (applet) {
|
||||
LOG_WARNING(Service_APT, "applet has already been started id={:08X}", applet_id);
|
||||
return ResultSuccess;
|
||||
} else {
|
||||
auto parent = GetAppletSlotId(last_library_launcher_slot);
|
||||
LOG_DEBUG(Service_APT, "Creating HLE applet {:03X} with parent {:03X}", applet_id, parent);
|
||||
return CreateHLEApplet(applet_id, parent, true);
|
||||
return HLE::Applets::Applet::Create(applet_id, parent, true, shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1437,8 +1387,8 @@ static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr s
|
||||
return;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(src, GSP::FRAMEBUFFER_WIDTH * height * bpp,
|
||||
Memory::FlushMode::Flush);
|
||||
Memory::RasterizerFlushVirtualRegion(src, GSP::FRAMEBUFFER_WIDTH * height * bpp,
|
||||
Memory::FlushMode::Flush);
|
||||
|
||||
// Address in VRAM that APT copies framebuffer captures to.
|
||||
constexpr VAddr screen_capture_base_vaddr = Memory::VRAM_VADDR + 0x500000;
|
||||
@ -1466,8 +1416,8 @@ static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr s
|
||||
}
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(
|
||||
dst_vaddr, GSP::FRAMEBUFFER_WIDTH_POW2 * height * bpp, Memory::FlushMode::Invalidate);
|
||||
Memory::RasterizerFlushVirtualRegion(dst_vaddr, GSP::FRAMEBUFFER_WIDTH_POW2 * height * bpp,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
void AppletManager::CaptureFrameBuffers() {
|
||||
@ -1491,30 +1441,6 @@ void AppletManager::LoadInputDevices() {
|
||||
Settings::values.current_input_profile.buttons[Settings::NativeButton::Power]);
|
||||
}
|
||||
|
||||
/// Handles updating the current Applet every time it's called.
|
||||
void AppletManager::HLEAppletUpdateEvent(std::uintptr_t user_data, s64 cycles_late) {
|
||||
const auto id = static_cast<AppletId>(user_data);
|
||||
if (!hle_applets.contains(id)) {
|
||||
// Dead applet, exit event loop.
|
||||
LOG_WARNING(Service_APT, "Attempted to update dead applet id={:03X}", id);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto applet = hle_applets[id];
|
||||
if (applet->IsActive()) {
|
||||
applet->Update();
|
||||
}
|
||||
|
||||
// If the applet is still running after the last update, reschedule the event
|
||||
if (applet->IsRunning()) {
|
||||
system.CoreTiming().ScheduleEvent(usToCycles(hle_applet_update_interval_us) - cycles_late,
|
||||
hle_applet_update_event, user_data);
|
||||
} else {
|
||||
// Otherwise the applet has terminated, in which case we should clean it up
|
||||
hle_applets[id] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::ButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) {
|
||||
if (is_device_reload_pending.exchange(false)) {
|
||||
LoadInputDevices();
|
||||
@ -1559,10 +1485,7 @@ AppletManager::AppletManager(Core::System& system) : system(system) {
|
||||
slot_data.parameter_event =
|
||||
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter");
|
||||
}
|
||||
hle_applet_update_event = system.CoreTiming().RegisterEvent(
|
||||
"HLE Applet Update Event", [this](std::uintptr_t user_data, s64 cycles_late) {
|
||||
HLEAppletUpdateEvent(user_data, cycles_late);
|
||||
});
|
||||
HLE::Applets::Init();
|
||||
button_update_event = system.CoreTiming().RegisterEvent(
|
||||
"APT Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) {
|
||||
ButtonUpdateEvent(user_data, cycles_late);
|
||||
@ -1571,8 +1494,7 @@ AppletManager::AppletManager(Core::System& system) : system(system) {
|
||||
}
|
||||
|
||||
AppletManager::~AppletManager() {
|
||||
system.CoreTiming().RemoveEvent(hle_applet_update_event);
|
||||
system.CoreTiming().RemoveEvent(button_update_event);
|
||||
HLE::Applets::Shutdown();
|
||||
}
|
||||
|
||||
void AppletManager::ReloadInputDevices() {
|
||||
|
@ -23,10 +23,6 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace HLE::Applets {
|
||||
class Applet;
|
||||
}
|
||||
|
||||
namespace Service::APT {
|
||||
|
||||
/// Signals used by APT functions
|
||||
@ -295,6 +291,7 @@ public:
|
||||
|
||||
ResultVal<Notification> InquireNotification(AppletId app_id);
|
||||
Result SendNotification(Notification notification);
|
||||
void SendNotificationToAll(Notification notification);
|
||||
|
||||
Result PrepareToStartLibraryApplet(AppletId applet_id);
|
||||
Result PreloadLibraryApplet(AppletId applet_id);
|
||||
@ -484,9 +481,6 @@ private:
|
||||
// It also affects the results of APT:GetTargetPlatform and APT:GetApplicationRunningMode.
|
||||
bool new_3ds_mode_blocked = false;
|
||||
|
||||
std::unordered_map<AppletId, std::shared_ptr<HLE::Applets::Applet>> hle_applets;
|
||||
Core::TimingEventType* hle_applet_update_event;
|
||||
|
||||
Core::TimingEventType* button_update_event;
|
||||
std::atomic<bool> is_device_reload_pending{true};
|
||||
std::unique_ptr<Input::ButtonDevice> home_button;
|
||||
@ -512,15 +506,10 @@ private:
|
||||
/// otherwise it queues for sending when the application registers itself with APT::Enable.
|
||||
void SendApplicationParameterAfterRegistration(const MessageParameter& parameter);
|
||||
|
||||
void SendNotificationToAll(Notification notification);
|
||||
|
||||
void EnsureHomeMenuLoaded();
|
||||
|
||||
void CaptureFrameBuffers();
|
||||
|
||||
Result CreateHLEApplet(AppletId id, AppletId parent, bool preload);
|
||||
void HLEAppletUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
|
||||
|
||||
void LoadInputDevices();
|
||||
void ButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
|
||||
|
||||
|
@ -1133,7 +1133,8 @@ void Module::APTInterface::GetProgramId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
auto fs_user = apt->system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||
auto fs_user =
|
||||
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||
ASSERT_MSG(fs_user != nullptr, "fs:USER service is missing.");
|
||||
|
||||
auto program_info_result = fs_user->GetProgramLaunchInfo(process_id);
|
||||
|
@ -517,8 +517,8 @@ void Y2R_U::StartConversion(Kernel::HLERequestContext& ctx) {
|
||||
// dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
|
||||
u32 total_output_size =
|
||||
conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap);
|
||||
system.Memory().RasterizerFlushVirtualRegion(conversion.dst.address, total_output_size,
|
||||
Memory::FlushMode::FlushAndInvalidate);
|
||||
Memory::RasterizerFlushVirtualRegion(conversion.dst.address, total_output_size,
|
||||
Memory::FlushMode::FlushAndInvalidate);
|
||||
|
||||
HW::Y2R::PerformConversion(system.Memory(), conversion);
|
||||
|
||||
|
@ -477,10 +477,9 @@ static void CopyFrameBuffer(Core::System& system, VAddr dst, VAddr src, u32 stri
|
||||
return;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(src, stride * lines, Memory::FlushMode::Flush);
|
||||
Memory::RasterizerFlushVirtualRegion(src, stride * lines, Memory::FlushMode::Flush);
|
||||
std::memcpy(dst_ptr, src_ptr, stride * lines);
|
||||
system.Memory().RasterizerFlushVirtualRegion(dst, stride * lines,
|
||||
Memory::FlushMode::Invalidate);
|
||||
Memory::RasterizerFlushVirtualRegion(dst, stride * lines, Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
@ -498,8 +497,8 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_GSP, "called");
|
||||
|
||||
// TODO: This should also save LCD register state.
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Flush);
|
||||
Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Flush);
|
||||
const auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
saved_vram.emplace(std::vector<u8>(Memory::VRAM_SIZE));
|
||||
std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE);
|
||||
@ -549,8 +548,8 @@ void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
// TODO: This should also restore LCD register state.
|
||||
auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE);
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Invalidate);
|
||||
Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
@ -135,7 +135,7 @@ private:
|
||||
};
|
||||
|
||||
struct MIC_U::Impl {
|
||||
explicit Impl(Core::System& system) : system(system), timing(system.CoreTiming()) {
|
||||
explicit Impl(Core::System& system) : timing(system.CoreTiming()) {
|
||||
buffer_full_event =
|
||||
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event");
|
||||
buffer_write_event = timing.RegisterEvent(
|
||||
@ -376,7 +376,7 @@ struct MIC_U::Impl {
|
||||
}
|
||||
|
||||
mic = AudioCore::GetInputDetails(Settings::values.input_type.GetValue())
|
||||
.create_input(system, Settings::values.input_device.GetValue());
|
||||
.create_input(Settings::values.input_device.GetValue());
|
||||
if (was_sampling) {
|
||||
StartSampling();
|
||||
}
|
||||
@ -392,7 +392,6 @@ struct MIC_U::Impl {
|
||||
bool allow_shell_closed = false;
|
||||
bool clamp = false;
|
||||
std::unique_ptr<AudioCore::Input> mic;
|
||||
Core::System& system;
|
||||
Core::Timing& timing;
|
||||
State state{};
|
||||
Encoding encoding{};
|
||||
|
@ -36,13 +36,15 @@
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Service::PLGLDR::PLG_LDR)
|
||||
SERVICE_CONSTRUCT_IMPL(Service::PLGLDR::PLG_LDR)
|
||||
|
||||
namespace Service::PLGLDR {
|
||||
|
||||
static const Kernel::CoreVersion plgldr_version = Kernel::CoreVersion(1, 0, 0);
|
||||
const Kernel::CoreVersion PLG_LDR::plgldr_version = Kernel::CoreVersion(1, 0, 0);
|
||||
PLG_LDR::PluginLoaderContext PLG_LDR::plgldr_context;
|
||||
bool PLG_LDR::allow_game_change = true;
|
||||
PAddr PLG_LDR::plugin_fb_addr = 0;
|
||||
|
||||
PLG_LDR::PLG_LDR(Core::System& system_) : ServiceFramework{"plg:ldr", 1}, system(system_) {
|
||||
PLG_LDR::PLG_LDR() : ServiceFramework{"plg:ldr", 1} {
|
||||
static const FunctionInfo functions[] = {
|
||||
// clang-format off
|
||||
{0x0001, nullptr, "LoadPlugin"},
|
||||
@ -65,33 +67,6 @@ PLG_LDR::PLG_LDR(Core::System& system_) : ServiceFramework{"plg:ldr", 1}, system
|
||||
plgldr_context.plugin_loaded = false;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void PLG_LDR::PluginLoaderContext::serialize(Archive& ar, const unsigned int) {
|
||||
ar& is_enabled;
|
||||
ar& plugin_loaded;
|
||||
ar& is_default_path;
|
||||
ar& plugin_path;
|
||||
ar& use_user_load_parameters;
|
||||
ar& user_load_parameters;
|
||||
ar& plg_event;
|
||||
ar& plg_reply;
|
||||
ar& memory_changed_handle;
|
||||
ar& is_exe_load_function_set;
|
||||
ar& exe_load_checksum;
|
||||
ar& load_exe_func;
|
||||
ar& load_exe_args;
|
||||
}
|
||||
SERIALIZE_IMPL(PLG_LDR::PluginLoaderContext)
|
||||
|
||||
template <class Archive>
|
||||
void PLG_LDR::serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||
ar& plgldr_context;
|
||||
ar& plugin_fb_addr;
|
||||
ar& allow_game_change;
|
||||
}
|
||||
SERIALIZE_IMPL(PLG_LDR)
|
||||
|
||||
void PLG_LDR::OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kernel) {
|
||||
if (!plgldr_context.is_enabled || plgldr_context.plugin_loaded) {
|
||||
return;
|
||||
@ -116,7 +91,7 @@ void PLG_LDR::OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kerne
|
||||
std::string(plgldr_context.user_load_parameters.path + 1);
|
||||
plgldr_context.is_default_path = false;
|
||||
plgldr_context.plugin_path = plugin_file;
|
||||
plugin_loader.Load(plgldr_context, process, kernel, *this);
|
||||
plugin_loader.Load(plgldr_context, process, kernel);
|
||||
} else {
|
||||
const std::string plugin_root =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "luma/plugins/";
|
||||
@ -128,7 +103,7 @@ void PLG_LDR::OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kerne
|
||||
if (!child.isDirectory && child.physicalName.ends_with(".3gx")) {
|
||||
plgldr_context.is_default_path = false;
|
||||
plgldr_context.plugin_path = child.physicalName;
|
||||
if (plugin_loader.Load(plgldr_context, process, kernel, *this) ==
|
||||
if (plugin_loader.Load(plgldr_context, process, kernel) ==
|
||||
Loader::ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
@ -139,7 +114,7 @@ void PLG_LDR::OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kerne
|
||||
if (FileUtil::Exists(default_path)) {
|
||||
plgldr_context.is_default_path = true;
|
||||
plgldr_context.plugin_path = default_path;
|
||||
plugin_loader.Load(plgldr_context, process, kernel, *this);
|
||||
plugin_loader.Load(plgldr_context, process, kernel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,9 +134,9 @@ ResultVal<Kernel::Handle> PLG_LDR::GetMemoryChangedHandle(Kernel::KernelSystem&
|
||||
return plgldr_context.memory_changed_handle;
|
||||
}
|
||||
|
||||
std::shared_ptr<Kernel::Event> evt =
|
||||
kernel.CreateEvent(Kernel::ResetType::OneShot,
|
||||
fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14)));
|
||||
std::shared_ptr<Kernel::Event> evt = kernel.CreateEvent(
|
||||
Kernel::ResetType::OneShot,
|
||||
fmt::format("event-{:08x}", Core::System::GetInstance().GetRunningCore().GetReg(14)));
|
||||
R_TRY(kernel.GetCurrentProcess()->handle_table.Create(
|
||||
std::addressof(plgldr_context.memory_changed_handle), std::move(evt)));
|
||||
return plgldr_context.memory_changed_handle;
|
||||
@ -299,7 +274,7 @@ std::shared_ptr<PLG_LDR> GetService(Core::System& system) {
|
||||
}
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
std::make_shared<PLG_LDR>(system)->InstallAsNamedPort(system.Kernel());
|
||||
std::make_shared<PLG_LDR>()->InstallAsNamedPort(system.Kernel());
|
||||
}
|
||||
|
||||
} // namespace Service::PLGLDR
|
||||
|
@ -22,7 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
@ -70,11 +70,25 @@ public:
|
||||
u32_le load_exe_args[4] = {0};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& is_enabled;
|
||||
ar& plugin_loaded;
|
||||
ar& is_default_path;
|
||||
ar& plugin_path;
|
||||
ar& use_user_load_parameters;
|
||||
ar& user_load_parameters;
|
||||
ar& plg_event;
|
||||
ar& plg_reply;
|
||||
ar& memory_changed_handle;
|
||||
ar& is_exe_load_function_set;
|
||||
ar& exe_load_checksum;
|
||||
ar& load_exe_func;
|
||||
ar& load_exe_args;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
PLG_LDR(Core::System& system_);
|
||||
PLG_LDR();
|
||||
~PLG_LDR() {}
|
||||
|
||||
void OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kernel);
|
||||
@ -82,31 +96,31 @@ public:
|
||||
ResultVal<Kernel::Handle> GetMemoryChangedHandle(Kernel::KernelSystem& kernel);
|
||||
void OnMemoryChanged(Kernel::Process& process, Kernel::KernelSystem& kernel);
|
||||
|
||||
void SetEnabled(bool enabled) {
|
||||
static void SetEnabled(bool enabled) {
|
||||
plgldr_context.is_enabled = enabled;
|
||||
}
|
||||
bool GetEnabled() {
|
||||
static bool GetEnabled() {
|
||||
return plgldr_context.is_enabled;
|
||||
}
|
||||
void SetAllowGameChangeState(bool allow) {
|
||||
static void SetAllowGameChangeState(bool allow) {
|
||||
allow_game_change = allow;
|
||||
}
|
||||
bool GetAllowGameChangeState() {
|
||||
static bool GetAllowGameChangeState() {
|
||||
return allow_game_change;
|
||||
}
|
||||
void SetPluginFBAddr(PAddr addr) {
|
||||
static void SetPluginFBAddr(PAddr addr) {
|
||||
plugin_fb_addr = addr;
|
||||
}
|
||||
PAddr GetPluginFBAddr() {
|
||||
static PAddr GetPluginFBAddr() {
|
||||
return plugin_fb_addr;
|
||||
}
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
static const Kernel::CoreVersion plgldr_version;
|
||||
|
||||
PluginLoaderContext plgldr_context;
|
||||
PAddr plugin_fb_addr = 0;
|
||||
bool allow_game_change = true;
|
||||
static PluginLoaderContext plgldr_context;
|
||||
static PAddr plugin_fb_addr;
|
||||
static bool allow_game_change;
|
||||
|
||||
void IsEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetEnabled(Kernel::HLERequestContext& ctx);
|
||||
@ -117,7 +131,12 @@ private:
|
||||
void GetPluginPath(Kernel::HLERequestContext& ctx);
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||
ar& plgldr_context;
|
||||
ar& plugin_fb_addr;
|
||||
ar& allow_game_change;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
@ -128,4 +147,3 @@ void InstallInterfaces(Core::System& system);
|
||||
} // namespace Service::PLGLDR
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Service::PLGLDR::PLG_LDR)
|
||||
SERVICE_CONSTRUCT(Service::PLGLDR::PLG_LDR)
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "core/hle/service/ptm/ptm_u.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Service::PTM::Module)
|
||||
SERVICE_CONSTRUCT_IMPL(Service::PTM::Module)
|
||||
|
||||
namespace Service::PTM {
|
||||
|
||||
@ -137,7 +136,7 @@ void Module::Interface::CheckNew3DS(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetSystemTime(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
auto& share_page = ptm->system.Kernel().GetSharedPageHandler();
|
||||
auto& share_page = Core::System::GetInstance().Kernel().GetSharedPageHandler();
|
||||
const u64 console_time = share_page.GetSystemTimeSince2000();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
|
||||
@ -208,7 +207,7 @@ static GameCoin ReadGameCoinData() {
|
||||
return gamecoin_data;
|
||||
}
|
||||
|
||||
Module::Module(Core::System& system_) : system(system_) {
|
||||
Module::Module() {
|
||||
// Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't
|
||||
// exist
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
@ -222,14 +221,6 @@ Module::Module(Core::System& system_) : system(system_) {
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void Module::serialize(Archive& ar, const unsigned int) {
|
||||
ar& shell_open;
|
||||
ar& battery_is_charging;
|
||||
ar& pedometer_is_counting;
|
||||
}
|
||||
SERIALIZE_IMPL(Module)
|
||||
|
||||
u16 Module::GetPlayCoins() {
|
||||
return ReadGameCoinData().total_coins;
|
||||
}
|
||||
@ -247,7 +238,7 @@ Module::Interface::Interface(std::shared_ptr<Module> ptm, const char* name, u32
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto& service_manager = system.ServiceManager();
|
||||
auto ptm = std::make_shared<Module>(system);
|
||||
auto ptm = std::make_shared<Module>();
|
||||
std::make_shared<PTM_Gets>(ptm)->InstallAsService(service_manager);
|
||||
std::make_shared<PTM_Play>(ptm)->InstallAsService(service_manager);
|
||||
std::make_shared<PTM_Sets>(ptm)->InstallAsService(service_manager);
|
||||
|
@ -47,9 +47,7 @@ void CheckNew3DS(IPC::RequestBuilder& rb);
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
explicit Module(Core::System& system_);
|
||||
~Module() = default;
|
||||
|
||||
Module();
|
||||
static u16 GetPlayCoins();
|
||||
static void SetPlayCoins(u16 play_coins);
|
||||
|
||||
@ -152,14 +150,16 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
bool shell_open = true;
|
||||
bool battery_is_charging = true;
|
||||
bool pedometer_is_counting = false;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& shell_open;
|
||||
ar& battery_is_charging;
|
||||
ar& pedometer_is_counting;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
@ -168,4 +168,3 @@ void InstallInterfaces(Core::System& system);
|
||||
} // namespace Service::PTM
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Service::PTM::Module)
|
||||
SERVICE_CONSTRUCT(Service::PTM::Module)
|
||||
|
@ -249,7 +249,7 @@ void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
|
||||
// console. Normally, this is handled by NS. If notification handling is properly implemented,
|
||||
// this piece of code should be removed, and handled by subscribing from NS instead.
|
||||
if (notification_id == 0x203) {
|
||||
system.RequestShutdown();
|
||||
Core::System::GetInstance().RequestShutdown();
|
||||
} else {
|
||||
LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x{:X}, flags={}",
|
||||
notification_id, flags);
|
||||
|
@ -94,7 +94,7 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets)
|
||||
|
||||
using Kernel::CodeSet;
|
||||
|
||||
static THREEDSX_Error Load3DSXFile(Core::System& system, FileUtil::IOFile& file, u32 base_addr,
|
||||
static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr,
|
||||
std::shared_ptr<CodeSet>* out_codeset) {
|
||||
if (!file.IsOpen())
|
||||
return ERROR_FILE;
|
||||
@ -222,7 +222,7 @@ static THREEDSX_Error Load3DSXFile(Core::System& system, FileUtil::IOFile& file,
|
||||
}
|
||||
|
||||
// Create the CodeSet
|
||||
std::shared_ptr<CodeSet> code_set = system.Kernel().CreateCodeSet("", 0);
|
||||
std::shared_ptr<CodeSet> code_set = Core::System::GetInstance().Kernel().CreateCodeSet("", 0);
|
||||
|
||||
code_set->CodeSegment().offset = loadinfo.seg_ptrs[0] - program_image.data();
|
||||
code_set->CodeSegment().addr = loadinfo.seg_addrs[0];
|
||||
@ -268,24 +268,25 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& process)
|
||||
return ResultStatus::Error;
|
||||
|
||||
std::shared_ptr<CodeSet> codeset;
|
||||
if (Load3DSXFile(system, file, Memory::PROCESS_IMAGE_VADDR, &codeset) != ERROR_NONE)
|
||||
if (Load3DSXFile(file, Memory::PROCESS_IMAGE_VADDR, &codeset) != ERROR_NONE)
|
||||
return ResultStatus::Error;
|
||||
codeset->name = filename;
|
||||
|
||||
process = system.Kernel().CreateProcess(std::move(codeset));
|
||||
process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset));
|
||||
process->Set3dsxKernelCaps();
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
process->resource_limit =
|
||||
system.Kernel().ResourceLimit().GetForCategory(Kernel::ResourceLimitCategory::Application);
|
||||
process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
|
||||
Kernel::ResourceLimitCategory::Application);
|
||||
|
||||
// On real HW this is done with FS:Reg, but we can be lazy
|
||||
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||
auto fs_user =
|
||||
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||
fs_user->RegisterProgramInfo(process->GetObjectId(), process->codeset->program_id, filepath);
|
||||
|
||||
process->Run(48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
system.ArchiveManager().RegisterSelfNCCH(*this);
|
||||
Core::System::GetInstance().ArchiveManager().RegisterSelfNCCH(*this);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
|
@ -14,9 +14,9 @@ namespace Loader {
|
||||
/// Loads an 3DSX file
|
||||
class AppLoader_THREEDSX final : public AppLoader {
|
||||
public:
|
||||
AppLoader_THREEDSX(Core::System& system_, FileUtil::IOFile&& file, const std::string& filename,
|
||||
AppLoader_THREEDSX(FileUtil::IOFile&& file, const std::string& filename,
|
||||
const std::string& filepath)
|
||||
: AppLoader(system_, std::move(file)), filename(std::move(filename)), filepath(filepath) {}
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {}
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
|
@ -199,7 +199,7 @@ public:
|
||||
u32 GetFlags() const {
|
||||
return (u32)(header->e_flags);
|
||||
}
|
||||
std::shared_ptr<CodeSet> LoadInto(Core::System& system, u32 vaddr);
|
||||
std::shared_ptr<CodeSet> LoadInto(u32 vaddr);
|
||||
|
||||
int GetNumSegments() const {
|
||||
return (int)(header->e_phnum);
|
||||
@ -262,7 +262,7 @@ const char* ElfReader::GetSectionName(int section) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<CodeSet> ElfReader::LoadInto(Core::System& system, u32 vaddr) {
|
||||
std::shared_ptr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
|
||||
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
|
||||
|
||||
// Should we relocate?
|
||||
@ -290,7 +290,7 @@ std::shared_ptr<CodeSet> ElfReader::LoadInto(Core::System& system, u32 vaddr) {
|
||||
std::vector<u8> program_image(total_image_size);
|
||||
std::size_t current_image_position = 0;
|
||||
|
||||
std::shared_ptr<CodeSet> codeset = system.Kernel().CreateCodeSet("", 0);
|
||||
std::shared_ptr<CodeSet> codeset = Core::System::GetInstance().Kernel().CreateCodeSet("", 0);
|
||||
|
||||
for (unsigned int i = 0; i < header->e_phnum; ++i) {
|
||||
Elf32_Phdr* p = &segments[i];
|
||||
@ -380,15 +380,15 @@ ResultStatus AppLoader_ELF::Load(std::shared_ptr<Kernel::Process>& process) {
|
||||
return ResultStatus::Error;
|
||||
|
||||
ElfReader elf_reader(&buffer[0]);
|
||||
std::shared_ptr<CodeSet> codeset = elf_reader.LoadInto(system, Memory::PROCESS_IMAGE_VADDR);
|
||||
std::shared_ptr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
|
||||
codeset->name = filename;
|
||||
|
||||
process = system.Kernel().CreateProcess(std::move(codeset));
|
||||
process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset));
|
||||
process->Set3dsxKernelCaps();
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
process->resource_limit =
|
||||
system.Kernel().ResourceLimit().GetForCategory(Kernel::ResourceLimitCategory::Application);
|
||||
process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
|
||||
Kernel::ResourceLimitCategory::Application);
|
||||
|
||||
process->Run(48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
|
@ -14,8 +14,8 @@ namespace Loader {
|
||||
/// Loads an ELF/AXF file
|
||||
class AppLoader_ELF final : public AppLoader {
|
||||
public:
|
||||
AppLoader_ELF(Core::System& system_, FileUtil::IOFile&& file, std::string filename)
|
||||
: AppLoader(system_, std::move(file)), filename(std::move(filename)) {}
|
||||
AppLoader_ELF(FileUtil::IOFile&& file, std::string filename)
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)) {}
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <string>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/loader/3dsx.h"
|
||||
#include "core/loader/elf.h"
|
||||
@ -90,23 +89,23 @@ const char* GetFileTypeString(FileType type) {
|
||||
* @param filepath the file full path (with name)
|
||||
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
|
||||
*/
|
||||
static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileUtil::IOFile&& file,
|
||||
FileType type, const std::string& filename,
|
||||
static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type,
|
||||
const std::string& filename,
|
||||
const std::string& filepath) {
|
||||
switch (type) {
|
||||
|
||||
// 3DSX file format.
|
||||
case FileType::THREEDSX:
|
||||
return std::make_unique<AppLoader_THREEDSX>(system, std::move(file), filename, filepath);
|
||||
return std::make_unique<AppLoader_THREEDSX>(std::move(file), filename, filepath);
|
||||
|
||||
// Standard ELF file format.
|
||||
case FileType::ELF:
|
||||
return std::make_unique<AppLoader_ELF>(system, std::move(file), filename);
|
||||
return std::make_unique<AppLoader_ELF>(std::move(file), filename);
|
||||
|
||||
// NCCH/NCSD container formats.
|
||||
case FileType::CXI:
|
||||
case FileType::CCI:
|
||||
return std::make_unique<AppLoader_NCCH>(system, std::move(file), filepath);
|
||||
return std::make_unique<AppLoader_NCCH>(std::move(file), filepath);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
@ -134,8 +133,7 @@ std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
|
||||
|
||||
LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type));
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
return GetFileLoader(system, std::move(file), type, filename_filename, filename);
|
||||
return GetFileLoader(std::move(file), type, filename_filename, filename);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
@ -82,8 +82,7 @@ constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||
/// Interface for loading an application
|
||||
class AppLoader : NonCopyable {
|
||||
public:
|
||||
explicit AppLoader(Core::System& system_, FileUtil::IOFile&& file)
|
||||
: system(system_), file(std::move(file)) {}
|
||||
explicit AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {}
|
||||
virtual ~AppLoader() {}
|
||||
|
||||
/**
|
||||
@ -254,7 +253,6 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
Core::System& system;
|
||||
FileUtil::IOFile file;
|
||||
bool is_loaded = false;
|
||||
};
|
||||
|
@ -113,7 +113,8 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
(const char*)overlay_ncch->exheader_header.codeset_info.name, 8);
|
||||
|
||||
std::shared_ptr<CodeSet> codeset = system.Kernel().CreateCodeSet(process_name, program_id);
|
||||
std::shared_ptr<CodeSet> codeset =
|
||||
Core::System::GetInstance().Kernel().CreateCodeSet(process_name, program_id);
|
||||
|
||||
codeset->CodeSegment().offset = 0;
|
||||
codeset->CodeSegment().addr = overlay_ncch->exheader_header.codeset_info.text.address;
|
||||
@ -147,6 +148,7 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
codeset->entrypoint = codeset->CodeSegment().addr;
|
||||
codeset->memory = std::move(code);
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
process = system.Kernel().CreateProcess(std::move(codeset));
|
||||
|
||||
// Attach a resource limit to the process based on the resource limit category
|
||||
@ -274,6 +276,7 @@ ResultStatus AppLoader_NCCH::Load(std::shared_ptr<Kernel::Process>& process) {
|
||||
overlay_ncch = &update_ncch;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.TelemetrySession().AddField(Common::Telemetry::FieldType::Session, "ProgramId",
|
||||
program_id);
|
||||
|
||||
|
@ -15,8 +15,8 @@ namespace Loader {
|
||||
/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
|
||||
class AppLoader_NCCH final : public AppLoader {
|
||||
public:
|
||||
AppLoader_NCCH(Core::System& system_, FileUtil::IOFile&& file, const std::string& filepath)
|
||||
: AppLoader(system_, std::move(file)), base_ncch(filepath), overlay_ncch(&base_ncch),
|
||||
AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath)
|
||||
: AppLoader(std::move(file)), base_ncch(filepath), overlay_ncch(&base_ncch),
|
||||
filepath(filepath) {}
|
||||
|
||||
/**
|
||||
|
@ -264,55 +264,14 @@ public:
|
||||
return {vram_mem, addr - VRAM_VADDR};
|
||||
}
|
||||
if (addr >= PLUGIN_3GX_FB_VADDR && addr < PLUGIN_3GX_FB_VADDR_END) {
|
||||
auto plg_ldr = Service::PLGLDR::GetService(system);
|
||||
if (plg_ldr) {
|
||||
return {fcram_mem,
|
||||
addr - PLUGIN_3GX_FB_VADDR + plg_ldr->GetPluginFBAddr() - FCRAM_PADDR};
|
||||
}
|
||||
return {fcram_mem, addr - PLUGIN_3GX_FB_VADDR +
|
||||
Service::PLGLDR::PLG_LDR::GetPluginFBAddr() - FCRAM_PADDR};
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return MemoryRef{};
|
||||
}
|
||||
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) {
|
||||
const VAddr end = start + size;
|
||||
|
||||
auto CheckRegion = [&](VAddr region_start, VAddr region_end, PAddr paddr_region_start) {
|
||||
if (start >= region_end || end <= region_start) {
|
||||
// No overlap with region
|
||||
return;
|
||||
}
|
||||
|
||||
auto& renderer = system.GPU().Renderer();
|
||||
VAddr overlap_start = std::max(start, region_start);
|
||||
VAddr overlap_end = std::min(end, region_end);
|
||||
PAddr physical_start = paddr_region_start + (overlap_start - region_start);
|
||||
u32 overlap_size = overlap_end - overlap_start;
|
||||
|
||||
auto* rasterizer = renderer.Rasterizer();
|
||||
switch (mode) {
|
||||
case FlushMode::Flush:
|
||||
rasterizer->FlushRegion(physical_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::Invalidate:
|
||||
rasterizer->InvalidateRegion(physical_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::FlushAndInvalidate:
|
||||
rasterizer->FlushAndInvalidateRegion(physical_start, overlap_size);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
CheckRegion(LINEAR_HEAP_VADDR, LINEAR_HEAP_VADDR_END, FCRAM_PADDR);
|
||||
CheckRegion(NEW_LINEAR_HEAP_VADDR, NEW_LINEAR_HEAP_VADDR_END, FCRAM_PADDR);
|
||||
CheckRegion(VRAM_VADDR, VRAM_VADDR_END, VRAM_PADDR);
|
||||
auto plg_ldr = Service::PLGLDR::GetService(system);
|
||||
if (plg_ldr && plg_ldr->GetPluginFBAddr()) {
|
||||
CheckRegion(PLUGIN_3GX_FB_VADDR, PLUGIN_3GX_FB_VADDR_END, plg_ldr->GetPluginFBAddr());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
@ -386,8 +345,41 @@ std::shared_ptr<PageTable> MemorySystem::GetCurrentPageTable() const {
|
||||
return impl->current_page_table;
|
||||
}
|
||||
|
||||
void MemorySystem::RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) {
|
||||
impl->RasterizerFlushVirtualRegion(start, size, mode);
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) {
|
||||
const VAddr end = start + size;
|
||||
|
||||
auto CheckRegion = [&](VAddr region_start, VAddr region_end, PAddr paddr_region_start) {
|
||||
if (start >= region_end || end <= region_start) {
|
||||
// No overlap with region
|
||||
return;
|
||||
}
|
||||
|
||||
auto& renderer = Core::System::GetInstance().GPU().Renderer();
|
||||
VAddr overlap_start = std::max(start, region_start);
|
||||
VAddr overlap_end = std::min(end, region_end);
|
||||
PAddr physical_start = paddr_region_start + (overlap_start - region_start);
|
||||
u32 overlap_size = overlap_end - overlap_start;
|
||||
|
||||
auto* rasterizer = renderer.Rasterizer();
|
||||
switch (mode) {
|
||||
case FlushMode::Flush:
|
||||
rasterizer->FlushRegion(physical_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::Invalidate:
|
||||
rasterizer->InvalidateRegion(physical_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::FlushAndInvalidate:
|
||||
rasterizer->FlushAndInvalidateRegion(physical_start, overlap_size);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
CheckRegion(LINEAR_HEAP_VADDR, LINEAR_HEAP_VADDR_END, FCRAM_PADDR);
|
||||
CheckRegion(NEW_LINEAR_HEAP_VADDR, NEW_LINEAR_HEAP_VADDR_END, FCRAM_PADDR);
|
||||
CheckRegion(VRAM_VADDR, VRAM_VADDR_END, VRAM_PADDR);
|
||||
if (Service::PLGLDR::PLG_LDR::GetPluginFBAddr())
|
||||
CheckRegion(PLUGIN_3GX_FB_VADDR, PLUGIN_3GX_FB_VADDR_END,
|
||||
Service::PLGLDR::PLG_LDR::GetPluginFBAddr());
|
||||
}
|
||||
|
||||
void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory,
|
||||
@ -691,19 +683,16 @@ std::vector<VAddr> MemorySystem::PhysicalToVirtualAddressForRasterizer(PAddr add
|
||||
if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) {
|
||||
return {addr - VRAM_PADDR + VRAM_VADDR};
|
||||
}
|
||||
if (addr >= Service::PLGLDR::PLG_LDR::GetPluginFBAddr() &&
|
||||
addr < Service::PLGLDR::PLG_LDR::GetPluginFBAddr() + PLUGIN_3GX_FB_SIZE) {
|
||||
return {addr - Service::PLGLDR::PLG_LDR::GetPluginFBAddr() + PLUGIN_3GX_FB_VADDR};
|
||||
}
|
||||
if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) {
|
||||
return {addr - FCRAM_PADDR + LINEAR_HEAP_VADDR, addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR};
|
||||
}
|
||||
if (addr >= FCRAM_PADDR_END && addr < FCRAM_N3DS_PADDR_END) {
|
||||
return {addr - FCRAM_PADDR + NEW_LINEAR_HEAP_VADDR};
|
||||
}
|
||||
auto plg_ldr = Service::PLGLDR::GetService(impl->system);
|
||||
if (plg_ldr) {
|
||||
auto fb_addr = plg_ldr->GetPluginFBAddr();
|
||||
if (addr >= fb_addr && addr < fb_addr + PLUGIN_3GX_FB_SIZE) {
|
||||
return {addr - fb_addr + PLUGIN_3GX_FB_VADDR};
|
||||
}
|
||||
}
|
||||
// While the physical <-> virtual mapping is 1:1 for the regions supported by the cache,
|
||||
// some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond
|
||||
// the end address of VRAM, causing the Virtual->Physical translation to fail when flushing
|
||||
|
@ -235,6 +235,8 @@ enum class FlushMode {
|
||||
FlushAndInvalidate,
|
||||
};
|
||||
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
|
||||
|
||||
class MemorySystem {
|
||||
public:
|
||||
explicit MemorySystem(Core::System& system);
|
||||
@ -551,8 +553,6 @@ public:
|
||||
|
||||
void SetDSP(AudioCore::DspInterface& dsp);
|
||||
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
T Read(const VAddr vaddr);
|
||||
|
@ -22,6 +22,28 @@ namespace VideoCore {
|
||||
constexpr VAddr VADDR_LCD = 0x1ED02000;
|
||||
constexpr VAddr VADDR_GPU = 0x1EF00000;
|
||||
|
||||
static PAddr VirtualToPhysicalAddress(VAddr addr) {
|
||||
if (addr == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (addr >= Memory::VRAM_VADDR && addr <= Memory::VRAM_VADDR_END) {
|
||||
return addr - Memory::VRAM_VADDR + Memory::VRAM_PADDR;
|
||||
}
|
||||
if (addr >= Memory::LINEAR_HEAP_VADDR && addr <= Memory::LINEAR_HEAP_VADDR_END) {
|
||||
return addr - Memory::LINEAR_HEAP_VADDR + Memory::FCRAM_PADDR;
|
||||
}
|
||||
if (addr >= Memory::NEW_LINEAR_HEAP_VADDR && addr <= Memory::NEW_LINEAR_HEAP_VADDR_END) {
|
||||
return addr - Memory::NEW_LINEAR_HEAP_VADDR + Memory::FCRAM_PADDR;
|
||||
}
|
||||
if (addr >= Memory::PLUGIN_3GX_FB_VADDR && addr <= Memory::PLUGIN_3GX_FB_VADDR_END) {
|
||||
return addr - Memory::PLUGIN_3GX_FB_VADDR + Service::PLGLDR::PLG_LDR::GetPluginFBAddr();
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:08X}", addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(GPU_DisplayTransfer, "GPU", "DisplayTransfer", MP_RGB(100, 100, 255));
|
||||
MICROPROFILE_DEFINE(GPU_CmdlistProcessing, "GPU", "Cmdlist Processing", MP_RGB(100, 255, 100));
|
||||
|
||||
@ -62,31 +84,6 @@ GPU::GPU(Core::System& system, Frontend::EmuWindow& emu_window,
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
PAddr GPU::VirtualToPhysicalAddress(VAddr addr) {
|
||||
if (addr == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (addr >= Memory::VRAM_VADDR && addr <= Memory::VRAM_VADDR_END) {
|
||||
return addr - Memory::VRAM_VADDR + Memory::VRAM_PADDR;
|
||||
}
|
||||
if (addr >= Memory::LINEAR_HEAP_VADDR && addr <= Memory::LINEAR_HEAP_VADDR_END) {
|
||||
return addr - Memory::LINEAR_HEAP_VADDR + Memory::FCRAM_PADDR;
|
||||
}
|
||||
if (addr >= Memory::NEW_LINEAR_HEAP_VADDR && addr <= Memory::NEW_LINEAR_HEAP_VADDR_END) {
|
||||
return addr - Memory::NEW_LINEAR_HEAP_VADDR + Memory::FCRAM_PADDR;
|
||||
}
|
||||
if (addr >= Memory::PLUGIN_3GX_FB_VADDR && addr <= Memory::PLUGIN_3GX_FB_VADDR_END) {
|
||||
auto plg_ldr = Service::PLGLDR::GetService(impl->system);
|
||||
if (plg_ldr) {
|
||||
return addr - Memory::PLUGIN_3GX_FB_VADDR + plg_ldr->GetPluginFBAddr();
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:08X}", addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void GPU::SetInterruptHandler(Service::GSP::InterruptHandler handler) {
|
||||
impl->signal_interrupt = handler;
|
||||
impl->pica.SetInterruptHandler(handler);
|
||||
@ -110,11 +107,11 @@ void GPU::Execute(const Service::GSP::Command& command) {
|
||||
|
||||
switch (command.id) {
|
||||
case CommandId::RequestDma: {
|
||||
impl->system.Memory().RasterizerFlushVirtualRegion(
|
||||
command.dma_request.source_address, command.dma_request.size, Memory::FlushMode::Flush);
|
||||
impl->system.Memory().RasterizerFlushVirtualRegion(command.dma_request.dest_address,
|
||||
command.dma_request.size,
|
||||
Memory::FlushMode::Invalidate);
|
||||
Memory::RasterizerFlushVirtualRegion(command.dma_request.source_address,
|
||||
command.dma_request.size, Memory::FlushMode::Flush);
|
||||
Memory::RasterizerFlushVirtualRegion(command.dma_request.dest_address,
|
||||
command.dma_request.size,
|
||||
Memory::FlushMode::Invalidate);
|
||||
|
||||
// TODO(Subv): These memory accesses should not go through the application's memory mapping.
|
||||
// They should go through the GSP module's memory mapping.
|
||||
|
@ -108,8 +108,6 @@ private:
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
PAddr VirtualToPhysicalAddress(VAddr addr);
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
|
Reference in New Issue
Block a user