mirror of
https://github.com/weihuoya/citra.git
synced 2026-01-25 04:18:23 +00:00
fx issues
This commit is contained in:
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -43,9 +43,9 @@
|
||||
[submodule "teakra"]
|
||||
path = externals/teakra
|
||||
url = https://github.com/wwylele/teakra.git
|
||||
[submodule "externals/oboe"]
|
||||
path = externals/oboe
|
||||
url = https://github.com/google/oboe.git
|
||||
[submodule "libyuv"]
|
||||
path = externals/libyuv
|
||||
url = https://github.com/lemenkov/libyuv.git
|
||||
[submodule "externals/libyuv"]
|
||||
path = externals/libyuv
|
||||
url = https://github.com/lemenkov/libyuv.git
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 8d1699ba2d...b6be02ea7f
1
externals/libyuv
vendored
Submodule
1
externals/libyuv
vendored
Submodule
Submodule externals/libyuv added at 5b3351bd07
7
externals/lodepng/CMakeLists.txt
vendored
7
externals/lodepng/CMakeLists.txt
vendored
@@ -1,7 +0,0 @@
|
||||
add_library(lodepng
|
||||
lodepng/lodepng.cpp
|
||||
lodepng/lodepng.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(lodepng)
|
||||
target_include_directories(lodepng INTERFACE lodepng)
|
||||
1
externals/lodepng/lodepng
vendored
1
externals/lodepng/lodepng
vendored
Submodule externals/lodepng/lodepng deleted from 31d9704fdc
1
externals/zstd
vendored
1
externals/zstd
vendored
Submodule externals/zstd deleted from 10f0e6993f
@@ -8,6 +8,10 @@
|
||||
0004000000051400 = 极品飞车16 亡命狂飙
|
||||
0004000000051500 = 极品飞车16 亡命狂飙
|
||||
0004000000157900 = 皇牌空战3D 战火纷飞+
|
||||
00040000000F4E00 = 新爱相随+
|
||||
00040000001B4E00 = 迷托邦
|
||||
00040000001B4F00 = 迷托邦
|
||||
0004000000178800 = 迷托邦
|
||||
0004000000884800 = 仙剑奇侠传
|
||||
0004000000198500 = 金庸群侠传
|
||||
0004000000190B00 = 沙漠老鼠团
|
||||
|
||||
@@ -7,7 +7,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.0'
|
||||
classpath 'com.android.tools.build:gradle:4.2.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
@@ -119,6 +119,7 @@ void BootGame(const std::string& path) {
|
||||
result = system.RunLoop();
|
||||
if (result == Core::System::ResultStatus::ShutdownRequested) {
|
||||
// End emulation execution
|
||||
s_render_window->UpdateSurface(nullptr);
|
||||
break;
|
||||
} else if (result != Core::System::ResultStatus::Success) {
|
||||
s_stop_running = true;
|
||||
@@ -210,6 +211,32 @@ static std::vector<Loader::SMDH::GameRegion> GetGameRegions(Loader::AppLoader* l
|
||||
return {};
|
||||
}
|
||||
|
||||
static void UpdateDisplayRotation() {
|
||||
// display rotation
|
||||
NativeLibrary::current_display_rotation = NativeLibrary::GetDisplayRotation();
|
||||
|
||||
// custom layout
|
||||
if (NativeLibrary::IsPortrait()) {
|
||||
Settings::values.custom_top_left = Config::Get(Config::PORTRAIT_TOP_LEFT);
|
||||
Settings::values.custom_top_top = Config::Get(Config::PORTRAIT_TOP_TOP);
|
||||
Settings::values.custom_top_right = Config::Get(Config::PORTRAIT_TOP_RIGHT);
|
||||
Settings::values.custom_top_bottom = Config::Get(Config::PORTRAIT_TOP_BOTTOM);
|
||||
Settings::values.custom_bottom_left = Config::Get(Config::PORTRAIT_BOTTOM_LEFT);
|
||||
Settings::values.custom_bottom_top = Config::Get(Config::PORTRAIT_BOTTOM_TOP);
|
||||
Settings::values.custom_bottom_right = Config::Get(Config::PORTRAIT_BOTTOM_RIGHT);
|
||||
Settings::values.custom_bottom_bottom = Config::Get(Config::PORTRAIT_BOTTOM_BOTTOM);
|
||||
} else {
|
||||
Settings::values.custom_top_left = Config::Get(Config::LANDSCAPE_TOP_LEFT);
|
||||
Settings::values.custom_top_top = Config::Get(Config::LANDSCAPE_TOP_TOP);
|
||||
Settings::values.custom_top_right = Config::Get(Config::LANDSCAPE_TOP_RIGHT);
|
||||
Settings::values.custom_top_bottom = Config::Get(Config::LANDSCAPE_TOP_BOTTOM);
|
||||
Settings::values.custom_bottom_left = Config::Get(Config::LANDSCAPE_BOTTOM_LEFT);
|
||||
Settings::values.custom_bottom_top = Config::Get(Config::LANDSCAPE_BOTTOM_TOP);
|
||||
Settings::values.custom_bottom_right = Config::Get(Config::LANDSCAPE_BOTTOM_RIGHT);
|
||||
Settings::values.custom_bottom_bottom = Config::Get(Config::LANDSCAPE_BOTTOM_BOTTOM);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -837,6 +864,8 @@ JNIEXPORT jstring JNICALL Java_org_citra_emu_utils_TranslateHelper_GoogleTransla
|
||||
aa = (aa & 2147483647) + 2147483648;
|
||||
}
|
||||
aa %= 1000000;
|
||||
|
||||
env->DeleteLocalRef(stringClass);
|
||||
return JniHelper::Wrap(fmt::format("{}.{}", aa, aa ^ ttk0));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ struct CubebSink::Impl {
|
||||
};
|
||||
|
||||
CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_unique<Impl>()) {
|
||||
if (cubeb_init(&impl->ctx, "Citra Output", nullptr) != CUBEB_OK) {
|
||||
if (cubeb_init(&impl->ctx, "Citra Output", "aaudio") != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return;
|
||||
}
|
||||
@@ -117,8 +117,8 @@ void CubebSink::SetCallback(std::function<void(s16*, std::size_t)> cb) {
|
||||
|
||||
long CubebSink::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames) {
|
||||
Impl* impl = static_cast<Impl*>(user_data);
|
||||
s16* buffer = reinterpret_cast<s16*>(output_buffer);
|
||||
auto* impl = static_cast<Impl*>(user_data);
|
||||
auto* buffer = static_cast<s16*>(output_buffer);
|
||||
|
||||
if (!impl || !impl->cb) {
|
||||
LOG_DEBUG(Audio_Sink, "Emitting zeros");
|
||||
@@ -166,7 +166,7 @@ std::vector<std::string> ListCubebSinkDevices() {
|
||||
std::vector<std::string> device_list;
|
||||
cubeb* ctx;
|
||||
|
||||
if (cubeb_init(&ctx, "CitraEnumerator", nullptr) != CUBEB_OK) {
|
||||
if (cubeb_init(&ctx, "CitraEnumerator", "aaudio") != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -311,6 +311,8 @@ add_library(core STATIC
|
||||
hle/service/ldr_ro/cro_helper.h
|
||||
hle/service/ldr_ro/ldr_ro.cpp
|
||||
hle/service/ldr_ro/ldr_ro.h
|
||||
hle/service/luma/hbldr.cpp
|
||||
hle/service/luma/hbldr.h
|
||||
hle/service/mic_u.cpp
|
||||
hle/service/mic_u.h
|
||||
hle/service/mcu/mcu.cpp
|
||||
|
||||
@@ -38,16 +38,18 @@ static void FlipCustomTexture(u32* pixels, u32 width, u32 height) {
|
||||
}
|
||||
}
|
||||
|
||||
CustomTexCache::CustomTexCache() = default;
|
||||
const CustomTexInfo* CustomTexCache::LoadTexture(u64 hash) {
|
||||
auto iter = custom_textures.find(hash);
|
||||
if (iter != custom_textures.end()) {
|
||||
return &iter->second;
|
||||
}
|
||||
|
||||
CustomTexCache::~CustomTexCache() = default;
|
||||
auto piter = custom_texture_paths.find(hash);
|
||||
if (piter == custom_texture_paths.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CustomTexCache::IsTextureCached(u64 hash) const {
|
||||
return custom_textures.find(hash) != custom_textures.end();
|
||||
}
|
||||
|
||||
const CustomTexInfo& CustomTexCache::LookupTexture(u64 hash) const {
|
||||
return custom_textures.at(hash);
|
||||
return LoadTexture(piter->second);
|
||||
}
|
||||
|
||||
void CustomTexCache::AddTexturePath(u64 hash, const std::string& path) {
|
||||
@@ -95,9 +97,10 @@ void CustomTexCache::PreloadTextures() {
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTexCache::LoadTexture(const CustomTexPathInfo& path_info) {
|
||||
const CustomTexInfo* CustomTexCache::LoadTexture(const CustomTexPathInfo& path_info) {
|
||||
const auto& image_interface = Core::System::GetInstance().GetImageInterface();
|
||||
auto& tex_info = custom_textures[path_info.hash];
|
||||
auto* result = &tex_info;
|
||||
if (image_interface->DecodePNG(tex_info.tex, tex_info.width, tex_info.height, path_info.path)) {
|
||||
// Make sure the texture size is a power of 2
|
||||
if (!(tex_info.width & (tex_info.width - 1)) &&
|
||||
@@ -108,18 +111,12 @@ void CustomTexCache::LoadTexture(const CustomTexPathInfo& path_info) {
|
||||
} else {
|
||||
LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path);
|
||||
custom_textures.erase(path_info.hash);
|
||||
result = nullptr;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Render_OpenGL, "Failed to load custom texture {}", path_info.path);
|
||||
}
|
||||
}
|
||||
|
||||
bool CustomTexCache::CustomTextureExists(u64 hash) const {
|
||||
return custom_texture_paths.count(hash);
|
||||
}
|
||||
|
||||
const CustomTexPathInfo& CustomTexCache::LookupTexturePathInfo(u64 hash) const {
|
||||
return custom_texture_paths.at(hash);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Frontend {
|
||||
class ImageInterface;
|
||||
} // namespace Frontend
|
||||
|
||||
namespace Core {
|
||||
struct CustomTexInfo {
|
||||
u64 hash;
|
||||
@@ -22,29 +18,29 @@ struct CustomTexInfo {
|
||||
std::vector<u8> tex;
|
||||
};
|
||||
|
||||
// This is to avoid parsing the filename multiple times
|
||||
struct CustomTexPathInfo {
|
||||
std::string path;
|
||||
u64 hash;
|
||||
};
|
||||
|
||||
// TODO: think of a better name for this class...
|
||||
class CustomTexCache {
|
||||
public:
|
||||
explicit CustomTexCache();
|
||||
~CustomTexCache();
|
||||
CustomTexCache() = default;
|
||||
~CustomTexCache() = default;
|
||||
|
||||
bool IsTextureCached(u64 hash) const;
|
||||
const CustomTexInfo& LookupTexture(u64 hash) const;
|
||||
|
||||
void AddTexturePath(u64 hash, const std::string& path);
|
||||
// init
|
||||
void FindCustomTextures(u64 program_id);
|
||||
void PreloadTextures();
|
||||
void LoadTexture(const CustomTexPathInfo& path_info);
|
||||
bool CustomTextureExists(u64 hash) const;
|
||||
const CustomTexPathInfo& LookupTexturePathInfo(u64 hash) const;
|
||||
|
||||
// get texture
|
||||
const CustomTexInfo* LoadTexture(u64 hash);
|
||||
|
||||
private:
|
||||
// This is to avoid parsing the filename multiple times
|
||||
struct CustomTexPathInfo {
|
||||
std::string path;
|
||||
u64 hash;
|
||||
};
|
||||
|
||||
void AddTexturePath(u64 hash, const std::string& path);
|
||||
const CustomTexInfo* LoadTexture(const CustomTexPathInfo& path_info);
|
||||
|
||||
std::unordered_map<u64, CustomTexInfo> custom_textures;
|
||||
std::unordered_map<u64, CustomTexPathInfo> custom_texture_paths;
|
||||
};
|
||||
|
||||
@@ -114,14 +114,16 @@ static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decom
|
||||
return true;
|
||||
}
|
||||
|
||||
NCCHContainer::NCCHContainer(const std::string& filepath, u32 ncch_offset)
|
||||
: ncch_offset(ncch_offset), filepath(filepath) {
|
||||
NCCHContainer::NCCHContainer(const std::string& filepath, u32 ncch_offset, u32 partition)
|
||||
: ncch_offset(ncch_offset), partition(partition), filepath(filepath) {
|
||||
file = FileUtil::IOFile(filepath, "rb");
|
||||
}
|
||||
|
||||
Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 ncch_offset) {
|
||||
Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 ncch_offset,
|
||||
u32 partition) {
|
||||
this->filepath = filepath;
|
||||
this->ncch_offset = ncch_offset;
|
||||
this->partition = partition;
|
||||
file = FileUtil::IOFile(filepath, "rb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
@@ -150,8 +152,13 @@ Loader::ResultStatus NCCHContainer::LoadHeader() {
|
||||
|
||||
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
||||
if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
|
||||
LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!");
|
||||
ncch_offset += 0x4000;
|
||||
NCSD_Header ncsd_header;
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncsd_header, sizeof(NCSD_Header));
|
||||
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
||||
ASSERT(partition < 8);
|
||||
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
||||
LOG_ERROR(Service_FS, "{}", ncch_offset);
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
||||
}
|
||||
@@ -178,8 +185,12 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
|
||||
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
||||
if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
|
||||
LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!");
|
||||
ncch_offset += 0x4000;
|
||||
NCSD_Header ncsd_header;
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncsd_header, sizeof(NCSD_Header));
|
||||
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
||||
ASSERT(partition < 8);
|
||||
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,31 @@
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/romfs_reader.h"
|
||||
|
||||
enum NCSDContentIndex { Main = 0, Manual = 1, DLP = 2, New3DSUpdate = 6, Update = 7 };
|
||||
|
||||
struct NCSD_Partitions {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct NCSD_Header {
|
||||
u8 signature[0x100];
|
||||
u32_le magic;
|
||||
u32_le media_size;
|
||||
u8 media_id[8];
|
||||
u8 partition_fs_type[8];
|
||||
u8 partition_crypt_type[8];
|
||||
NCSD_Partitions partitions[8];
|
||||
u8 extended_header_hash[0x20];
|
||||
u32_le additional_header_size;
|
||||
u32_le sector_zero_offset;
|
||||
u8 partition_flags[8];
|
||||
u8 partition_id_table[0x40];
|
||||
u8 reserved[0x30];
|
||||
};
|
||||
|
||||
static_assert(sizeof(NCSD_Header) == 0x200, "NCCH header structure size is wrong");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym)
|
||||
|
||||
@@ -167,12 +192,16 @@ struct ExHeader_ARM11_SystemLocalCaps {
|
||||
};
|
||||
|
||||
struct ExHeader_ARM11_KernelCaps {
|
||||
u32_le descriptors[28];
|
||||
static constexpr std::size_t NUM_DESCRIPTORS = 28;
|
||||
|
||||
u32_le descriptors[NUM_DESCRIPTORS];
|
||||
u8 reserved[0x10];
|
||||
};
|
||||
|
||||
struct ExHeader_ARM9_AccessControl {
|
||||
u8 descriptors[15];
|
||||
static constexpr std::size_t NUM_DESCRIPTORS = 15;
|
||||
|
||||
u8 descriptors[NUM_DESCRIPTORS];
|
||||
u8 descversion;
|
||||
};
|
||||
|
||||
@@ -205,10 +234,11 @@ namespace FileSys {
|
||||
*/
|
||||
class NCCHContainer {
|
||||
public:
|
||||
NCCHContainer(const std::string& filepath, u32 ncch_offset = 0);
|
||||
NCCHContainer(const std::string& filepath, u32 ncch_offset = 0, u32 partition = 0);
|
||||
NCCHContainer() {}
|
||||
|
||||
Loader::ResultStatus OpenFile(const std::string& filepath, u32 ncch_offset = 0);
|
||||
Loader::ResultStatus OpenFile(const std::string& filepath, u32 ncch_offset = 0,
|
||||
u32 partition = 0);
|
||||
|
||||
/**
|
||||
* Ensure NCCH header is loaded and ready for reading sections
|
||||
@@ -335,6 +365,7 @@ private:
|
||||
|
||||
u32 ncch_offset = 0; // Offset to NCCH header, can be 0 for NCCHs or non-zero for CIAs/NCSDs
|
||||
u32 exefs_offset = 0;
|
||||
u32 partition = 0;
|
||||
|
||||
std::string filepath;
|
||||
FileUtil::IOFile file;
|
||||
|
||||
@@ -114,6 +114,22 @@ void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
void Process::Set3dsxKernelCaps() {
|
||||
svc_access_mask.set();
|
||||
|
||||
address_mappings = {
|
||||
{0x1FF50000, 0x8000, true}, // part of DSP RAM
|
||||
{0x1FF70000, 0x8000, true}, // part of DSP RAM
|
||||
{0x1F000000, 0x600000, false}, // entire VRAM
|
||||
};
|
||||
|
||||
// Similar to Rosalina, we set kernel version to a recent one.
|
||||
// This is 11.2.0, to be consistent with core/hle/kernel/config_mem.cpp
|
||||
// TODO: refactor kernel version out so it is configurable and consistent
|
||||
// among all relevant places.
|
||||
kernel_version = 0x234;
|
||||
}
|
||||
|
||||
void Process::Run(s32 main_thread_priority, u32 stack_size) {
|
||||
memory_region = kernel.GetMemoryRegion(flags.memory_region);
|
||||
|
||||
@@ -142,7 +158,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
|
||||
status = ProcessStatus::Running;
|
||||
|
||||
vm_manager.LogLayout(Log::Level::Debug);
|
||||
Kernel::SetupMainThread(kernel, codeset->entrypoint, main_thread_priority, SharedFrom(this));
|
||||
Kernel::SetupMainThread(kernel, codeset->entrypoint, main_thread_priority, *this);
|
||||
}
|
||||
|
||||
VAddr Process::GetLinearHeapAreaAddress() const {
|
||||
|
||||
@@ -155,6 +155,11 @@ public:
|
||||
*/
|
||||
void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
|
||||
|
||||
/**
|
||||
* Set up the default kernel capability for 3DSX.
|
||||
*/
|
||||
void Set3dsxKernelCaps();
|
||||
|
||||
/**
|
||||
* Applies address space changes and launches the process main thread.
|
||||
*/
|
||||
|
||||
@@ -61,6 +61,7 @@ enum ConfigBlockID {
|
||||
StateNameBlockID = 0x000B0002,
|
||||
EULAVersionBlockID = 0x000D0000,
|
||||
ConsoleModelBlockID = 0x000F0004,
|
||||
DebugModeBlockID = 0x00130000,
|
||||
};
|
||||
|
||||
struct UsernameBlock {
|
||||
@@ -451,9 +452,7 @@ ResultCode Module::FormatConfig() {
|
||||
if (!res.IsSuccess())
|
||||
return res;
|
||||
|
||||
u32 random_number;
|
||||
u64 console_id;
|
||||
GenerateConsoleUniqueId(random_number, console_id);
|
||||
const auto [random_number, console_id] = GenerateConsoleUniqueId();
|
||||
|
||||
u64_le console_id_le = console_id;
|
||||
res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(console_id_le), 0xE, &console_id_le);
|
||||
@@ -529,6 +528,11 @@ ResultCode Module::FormatConfig() {
|
||||
if (!res.IsSuccess())
|
||||
return res;
|
||||
|
||||
// 0x00130000 - DebugMode (0x100 for debug mode)
|
||||
res = CreateConfigInfoBlk(DebugModeBlockID, 0x4, 0xE, zero_buffer);
|
||||
if (!res.IsSuccess())
|
||||
return res;
|
||||
|
||||
// 0x00170000 - Unknown
|
||||
res = CreateConfigInfoBlk(0x00170000, 0x4, 0xE, zero_buffer);
|
||||
if (!res.IsSuccess())
|
||||
@@ -708,13 +712,18 @@ u8 Module::GetCountryCode() {
|
||||
return block.country_code;
|
||||
}
|
||||
|
||||
void Module::GenerateConsoleUniqueId(u32& random_number, u64& console_id) {
|
||||
std::pair<u32, u64> Module::GenerateConsoleUniqueId() const {
|
||||
CryptoPP::AutoSeededRandomPool rng;
|
||||
random_number = rng.GenerateWord32(0, 0xFFFF);
|
||||
const u32 random_number = rng.GenerateWord32(0, 0xFFFF);
|
||||
|
||||
u64_le local_friend_code_seed;
|
||||
rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
|
||||
sizeof(local_friend_code_seed));
|
||||
console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
|
||||
|
||||
const u64 console_id =
|
||||
(local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
|
||||
|
||||
return std::make_pair(random_number, console_id);
|
||||
}
|
||||
|
||||
ResultCode Module::SetConsoleUniqueId(u32 random_number, u64 console_id) {
|
||||
@@ -759,9 +768,7 @@ void Module::SetEULAVersion(const EULAVersion& version) {
|
||||
|
||||
std::shared_ptr<Module> GetModule(Core::System& system) {
|
||||
auto cfg = system.ServiceManager().GetService<Service::CFG::Module::Interface>("cfg:u");
|
||||
if (!cfg)
|
||||
return nullptr;
|
||||
return cfg->GetModule();
|
||||
return !cfg ? nullptr : cfg->GetModule();
|
||||
}
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
|
||||
@@ -383,12 +383,13 @@ public:
|
||||
|
||||
/**
|
||||
* Generates a new random console unique id.
|
||||
* @param random_number a random generated 16bit number stored at 0x90002, used for generating
|
||||
* the
|
||||
* console_id
|
||||
* @param console_id the randomly created console id
|
||||
*
|
||||
* @returns A pair containing a random number and a random console ID.
|
||||
*
|
||||
* @note The random number is a random generated 16bit number stored at 0x90002, used for
|
||||
* generating the console ID.
|
||||
*/
|
||||
void GenerateConsoleUniqueId(u32& random_number, u64& console_id);
|
||||
std::pair<u32, u64> GenerateConsoleUniqueId() const;
|
||||
|
||||
/**
|
||||
* Sets the random_number and the console unique id in the config savegame.
|
||||
|
||||
@@ -31,6 +31,16 @@
|
||||
|
||||
namespace Service::FS {
|
||||
|
||||
MediaType GetMediaTypeFromPath(std::string_view path) {
|
||||
if (path.rfind(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 0) == 0) {
|
||||
return MediaType::NAND;
|
||||
}
|
||||
if (path.rfind(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 0) == 0) {
|
||||
return MediaType::SDMC;
|
||||
}
|
||||
return MediaType::GameCard;
|
||||
}
|
||||
|
||||
ArchiveBackend* ArchiveManager::GetArchive(ArchiveHandle handle) {
|
||||
auto itr = handle_map.find(handle);
|
||||
return (itr == handle_map.end()) ? nullptr : itr->second.get();
|
||||
|
||||
@@ -46,6 +46,14 @@ enum class ArchiveIdCode : u32 {
|
||||
/// Media types for the archives
|
||||
enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 };
|
||||
|
||||
MediaType GetMediaTypeFromPath(std::string_view path);
|
||||
|
||||
enum class SpecialContentType : u8 {
|
||||
Update = 1,
|
||||
Manual = 2,
|
||||
DLPChild = 3,
|
||||
};
|
||||
|
||||
typedef u64 ArchiveHandle;
|
||||
|
||||
struct ArchiveResource {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/ncch_container.h"
|
||||
#include "core/file_sys/seed_db.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -574,6 +575,14 @@ void FS_USER::CardSlotIsInserted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) FS_USER CardSlotIsInserted called");
|
||||
}
|
||||
|
||||
void FS_USER::GetCardType(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x813, 0, 0);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(false); // CTR card = 0, TWL card = 1
|
||||
LOG_DEBUG(Service_FS, "(STUBBED) FS_USER GetCardType called");
|
||||
}
|
||||
|
||||
void FS_USER::DeleteSystemSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x857, 2, 0);
|
||||
u32 savedata_high = rp.Pop<u32>();
|
||||
@@ -720,13 +729,11 @@ void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service_FS, "FS_USER GetProgramLaunchInfo process_id={}", process_id);
|
||||
|
||||
// TODO(Subv): The real FS service manages its own process list and only checks the processes
|
||||
// that were registered with the 'fs:REG' service.
|
||||
auto process = system.Kernel().GetProcessById(process_id);
|
||||
auto program_info = program_info_map.find(process_id);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
|
||||
|
||||
if (process == nullptr) {
|
||||
if (program_info == program_info_map.end()) {
|
||||
// Note: In this case, the rest of the parameters are not changed but the command header
|
||||
// remains the same.
|
||||
rb.Push(ResultCode(FileSys::ErrCodes::ArchiveNotMounted, ErrorModule::FS,
|
||||
@@ -735,13 +742,9 @@ void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 program_id = process->codeset->program_id;
|
||||
|
||||
auto media_type = Service::AM::GetTitleMediaType(program_id);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(program_id);
|
||||
rb.Push(static_cast<u8>(media_type));
|
||||
rb.Push(program_info->second.program_id);
|
||||
rb.Push(static_cast<u8>(program_info->second.media_type));
|
||||
|
||||
// TODO(Subv): Find out what this value means.
|
||||
rb.Push<u32>(0);
|
||||
@@ -790,6 +793,32 @@ void FS_USER::ObsoletedDeleteExtSaveData(Kernel::HLERequestContext& ctx) {
|
||||
static_cast<u32>(media_type));
|
||||
}
|
||||
|
||||
void FS_USER::GetSpecialContentIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x83A, 4, 0);
|
||||
const MediaType media_type = static_cast<MediaType>(rp.Pop<u8>());
|
||||
const u64 title_id = rp.Pop<u64>();
|
||||
const auto type = rp.PopEnum<SpecialContentType>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, media_type={:08X} type={:08X}, title_id={:016X}",
|
||||
static_cast<u32>(media_type), static_cast<u32>(type), title_id);
|
||||
|
||||
ResultVal<u16> index;
|
||||
if (media_type == MediaType::GameCard) {
|
||||
index = GetSpecialContentIndexFromGameCard(title_id, type);
|
||||
} else {
|
||||
index = GetSpecialContentIndexFromTMD(media_type, title_id, type);
|
||||
}
|
||||
|
||||
if (index.Succeeded()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(index.Unwrap());
|
||||
} else {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(index.Code());
|
||||
}
|
||||
}
|
||||
|
||||
void FS_USER::GetNumSeeds(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestBuilder rb{ctx, 0x87D, 2, 0};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -846,6 +875,66 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u64>(0); // the secure value
|
||||
}
|
||||
|
||||
void FS_USER::Register(u32 process_id, u64 program_id, const std::string& filepath) {
|
||||
const MediaType media_type = GetMediaTypeFromPath(filepath);
|
||||
program_info_map.insert_or_assign(process_id, ProgramInfo{program_id, media_type});
|
||||
if (media_type == MediaType::GameCard) {
|
||||
current_gamecard_path = filepath;
|
||||
}
|
||||
}
|
||||
|
||||
std::string FS_USER::GetCurrentGamecardPath() const {
|
||||
return current_gamecard_path;
|
||||
}
|
||||
|
||||
ResultVal<u16> FS_USER::GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type) {
|
||||
// TODO(B3N30) check if on real 3DS NCSD is checked if partition exists
|
||||
|
||||
if (type > SpecialContentType::DLPChild) {
|
||||
// Maybe type 4 is New 3DS update/partition 6 but this needs more research
|
||||
// TODO(B3N30): Find correct result code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SpecialContentType::Update:
|
||||
return MakeResult(static_cast<u16>(NCSDContentIndex::Update));
|
||||
case SpecialContentType::Manual:
|
||||
return MakeResult(static_cast<u16>(NCSDContentIndex::Manual));
|
||||
case SpecialContentType::DLPChild:
|
||||
return MakeResult(static_cast<u16>(NCSDContentIndex::DLP));
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<u16> FS_USER::GetSpecialContentIndexFromTMD(MediaType media_type, u64 title_id,
|
||||
SpecialContentType type) {
|
||||
if (type > SpecialContentType::DLPChild) {
|
||||
// TODO(B3N30): Find correct result code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
std::string tmd_path = AM::GetTitleMetadataPath(media_type, title_id);
|
||||
|
||||
FileSys::TitleMetadata tmd;
|
||||
if (tmd.Load(tmd_path) != Loader::ResultStatus::Success || type == SpecialContentType::Update) {
|
||||
// TODO(B3N30): Find correct result code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
// TODO(B3N30): Does real 3DS check if content exists in TMD?
|
||||
|
||||
switch (type) {
|
||||
case SpecialContentType::Manual:
|
||||
return MakeResult(static_cast<u16>(FileSys::TMDContentIndex::Manual));
|
||||
case SpecialContentType::DLPChild:
|
||||
return MakeResult(static_cast<u16>(FileSys::TMDContentIndex::DLP));
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
FS_USER::FS_USER(Core::System& system)
|
||||
: ServiceFramework("fs:USER", 30), system(system), archives(system.ArchiveManager()) {
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -869,7 +958,7 @@ FS_USER::FS_USER(Core::System& system)
|
||||
{0x08100200, &FS_USER::CreateLegacySystemSaveData, "CreateLegacySystemSaveData"},
|
||||
{0x08110040, nullptr, "DeleteSystemSaveData"},
|
||||
{0x08120080, &FS_USER::GetFreeBytes, "GetFreeBytes"},
|
||||
{0x08130000, nullptr, "GetCardType"},
|
||||
{0x08130000, &FS_USER::GetCardType, "GetCardType"},
|
||||
{0x08140000, &FS_USER::GetSdmcArchiveResource, "GetSdmcArchiveResource"},
|
||||
{0x08150000, &FS_USER::GetNandArchiveResource, "GetNandArchiveResource"},
|
||||
{0x08160000, nullptr, "GetSdmcFatfsError"},
|
||||
@@ -908,7 +997,7 @@ FS_USER::FS_USER(Core::System& system)
|
||||
{0x08370040, nullptr, "SetCardSpiBaudRate"},
|
||||
{0x08380040, nullptr, "SetCardSpiBusMode"},
|
||||
{0x08390000, nullptr, "SendInitializeInfoTo9"},
|
||||
{0x083A0100, nullptr, "GetSpecialContentIndex"},
|
||||
{0x083A0100, &FS_USER::GetSpecialContentIndex, "GetSpecialContentIndex"},
|
||||
{0x083B00C2, nullptr, "GetLegacyRomHeader"},
|
||||
{0x083C00C2, nullptr, "GetLegacyBannerData"},
|
||||
{0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
|
||||
@@ -957,6 +1046,10 @@ FS_USER::FS_USER(Core::System& system)
|
||||
{0x08680000, nullptr, "GetMediaType"},
|
||||
{0x08690000, nullptr, "GetNandEraseCount"},
|
||||
{0x086A0082, nullptr, "ReadNandReport"},
|
||||
{0x086B00C2, nullptr, "SetOtherSaveDataSecureValue"},
|
||||
{0x086C00C2, nullptr, "GetOtherSaveDataSecureValue"},
|
||||
{0x086E00C0, nullptr, "SetThisSaveDataSecureValue"},
|
||||
{0x086F0040, nullptr, "GetThisSaveDataSecureValue"},
|
||||
{0x087A0180, &FS_USER::AddSeed, "AddSeed"},
|
||||
{0x087D0000, &FS_USER::GetNumSeeds, "GetNumSeeds"},
|
||||
{0x088600C0, nullptr, "CheckUpdatedDat"},
|
||||
|
||||
@@ -28,6 +28,20 @@ class FS_USER final : public ServiceFramework<FS_USER, ClientSlot> {
|
||||
public:
|
||||
explicit FS_USER(Core::System& system);
|
||||
|
||||
// On real HW this is part of FS:Reg. But since that module is only used by loader and pm, which
|
||||
// we HLEed, we can just directly use it here
|
||||
void Register(u32 process_id, u64 program_id, const std::string& filepath);
|
||||
|
||||
std::string GetCurrentGamecardPath() const;
|
||||
|
||||
u64 GetProgramID(u32 pid) const {
|
||||
auto iter = program_info_map.find(pid);
|
||||
if (iter != program_info_map.end()) {
|
||||
return iter->second.program_id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -517,6 +531,20 @@ private:
|
||||
*/
|
||||
void ObsoletedDeleteExtSaveData(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetSpecialContentIndex service function.
|
||||
* Inputs:
|
||||
* 0 : 0x083A0100
|
||||
* 1 : Media type
|
||||
* 2-3 : Program ID
|
||||
* 4 : Special content type
|
||||
* Outputs:
|
||||
* 0 : 0x083A0080
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Special content index
|
||||
*/
|
||||
void GetSpecialContentIndex(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetNumSeeds service function.
|
||||
* Inputs:
|
||||
@@ -569,6 +597,20 @@ private:
|
||||
*/
|
||||
void GetSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void GetCardType(Kernel::HLERequestContext& ctx);
|
||||
|
||||
static ResultVal<u16> GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type);
|
||||
static ResultVal<u16> GetSpecialContentIndexFromTMD(MediaType media_type, u64 title_id,
|
||||
SpecialContentType type);
|
||||
|
||||
struct ProgramInfo {
|
||||
u64 program_id;
|
||||
MediaType media_type;
|
||||
};
|
||||
|
||||
std::unordered_map<u32, ProgramInfo> program_info_map;
|
||||
std::string current_gamecard_path;
|
||||
|
||||
u32 priority = -1; ///< For SetPriority and GetPriority service functions
|
||||
|
||||
Core::System& system;
|
||||
|
||||
26
src/core/hle/service/luma/hbldr.cpp
Normal file
26
src/core/hle/service/luma/hbldr.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/luma/hbldr.h"
|
||||
|
||||
namespace Service::HBLDR {
|
||||
|
||||
HBLDR::HBLDR() : ServiceFramework("hb:ldr") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00010180, nullptr, "LoadProcess"},
|
||||
{0x00020002, nullptr, "SetTarget"},
|
||||
{0x00030002, nullptr, "SetArgv"},
|
||||
{0x00040002, nullptr, "PatchExHeaderInfo"},
|
||||
{0x00050000, nullptr, "DebugNextApplicationByForce"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
std::make_shared<HBLDR>()->InstallAsNamedPort(system.Kernel());
|
||||
}
|
||||
|
||||
} // namespace Service::NDM
|
||||
23
src/core/hle/service/luma/hbldr.h
Normal file
23
src/core/hle/service/luma/hbldr.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::HBLDR {
|
||||
|
||||
class HBLDR final : public ServiceFramework<HBLDR> {
|
||||
public:
|
||||
HBLDR();
|
||||
};
|
||||
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::NDM
|
||||
@@ -22,14 +22,15 @@ struct TagInfo {
|
||||
static_assert(sizeof(TagInfo) == 0x2C, "TagInfo is an invalid size");
|
||||
|
||||
struct AmiiboConfig {
|
||||
u16_le lastwritedate_year;
|
||||
u8 lastwritedate_month;
|
||||
u8 lastwritedate_day;
|
||||
u16_le write_counter;
|
||||
std::array<u8, 3> characterID;
|
||||
u16_le last_write_year;
|
||||
u8 last_write_month;
|
||||
u8 last_write_day;
|
||||
u16_le write_count;
|
||||
u16_le char_id;
|
||||
u8 char_variant;
|
||||
u8 series;
|
||||
u16_le amiiboID;
|
||||
u8 type;
|
||||
u16_be model_number;
|
||||
u8 figure_type;
|
||||
u8 pagex4_byte3;
|
||||
u16_le appdata_size;
|
||||
INSERT_PADDING_BYTES(0x30);
|
||||
@@ -144,17 +145,21 @@ void Module::Interface::GetAmiiboConfig(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x18, 0, 0);
|
||||
|
||||
AmiiboConfig amiibo_config{};
|
||||
amiibo_config.lastwritedate_year = 2017;
|
||||
amiibo_config.lastwritedate_month = 10;
|
||||
amiibo_config.lastwritedate_day = 10;
|
||||
amiibo_config.write_counter = 0x0;
|
||||
std::memcpy(amiibo_config.characterID.data(), &nfc->amiibo_data.char_id,
|
||||
sizeof(nfc->amiibo_data.char_id));
|
||||
if (!nfc->amiibo_decrypted) {
|
||||
/* Dummy data */
|
||||
amiibo_config.last_write_year = 2017;
|
||||
amiibo_config.last_write_month = 10;
|
||||
amiibo_config.last_write_day = 10;
|
||||
amiibo_config.write_count = 1;
|
||||
|
||||
amiibo_config.char_id = nfc->amiibo_data.char_id;
|
||||
amiibo_config.char_variant = nfc->amiibo_data.char_variant;
|
||||
amiibo_config.series = nfc->amiibo_data.series;
|
||||
amiibo_config.amiiboID = nfc->amiibo_data.model_number;
|
||||
amiibo_config.type = nfc->amiibo_data.figure_type;
|
||||
amiibo_config.model_number = nfc->amiibo_data.model_number;
|
||||
amiibo_config.figure_type = nfc->amiibo_data.figure_type;
|
||||
amiibo_config.pagex4_byte3 = 0x0;
|
||||
amiibo_config.appdata_size = 0xD8;
|
||||
amiibo_config.appdata_size = 0;
|
||||
}
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(17, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -295,16 +300,16 @@ void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
IdentificationBlockReply identification_block_reply{};
|
||||
identification_block_reply.char_id = nfc->amiibo_data.char_id;
|
||||
identification_block_reply.char_variant = nfc->amiibo_data.char_variant;
|
||||
identification_block_reply.series = nfc->amiibo_data.series;
|
||||
identification_block_reply.model_number = nfc->amiibo_data.model_number;
|
||||
identification_block_reply.figure_type = nfc->amiibo_data.figure_type;
|
||||
IdentificationBlockReply reply{};
|
||||
reply.char_id = nfc->amiibo_data.char_id;
|
||||
reply.char_variant = nfc->amiibo_data.char_variant;
|
||||
reply.series = nfc->amiibo_data.series;
|
||||
reply.model_number = nfc->amiibo_data.model_number;
|
||||
reply.figure_type = nfc->amiibo_data.figure_type;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<IdentificationBlockReply>(identification_block_reply);
|
||||
rb.PushRaw<IdentificationBlockReply>(reply);
|
||||
LOG_INFO(Service_NFC, "nfc GetIdentificationBlock called");
|
||||
}
|
||||
|
||||
@@ -312,11 +317,25 @@ std::shared_ptr<Module> Module::Interface::GetModule() const {
|
||||
return nfc;
|
||||
}
|
||||
|
||||
void Module::Interface::LoadAmiibo(const AmiiboData& amiibo_data) {
|
||||
bool Module::Interface::LoadAmiibo(const std::string& fullpath) {
|
||||
std::lock_guard lock(HLE::g_hle_lock);
|
||||
nfc->amiibo_data = amiibo_data;
|
||||
FileUtil::IOFile amiibo_file(fullpath, "rb");
|
||||
bool success = false;
|
||||
|
||||
if (!amiibo_file.IsOpen()) {
|
||||
LOG_ERROR(Service_NFC, "Could not open amiibo file \"{}\"", fullpath);
|
||||
} else if (amiibo_file.ReadBytes(&nfc->amiibo_data, sizeof(AmiiboData)) != sizeof(AmiiboData)) {
|
||||
LOG_ERROR(Service_NFC, "Could not read amiibo data from file \"{}\"", fullpath);
|
||||
} else {
|
||||
nfc->amiibo_decrypted = false;
|
||||
nfc->amiibo_filename = fullpath;
|
||||
nfc->amiibo_in_range = true;
|
||||
nfc->SyncTagState();
|
||||
success = true;
|
||||
}
|
||||
amiibo_file.Close();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Module::Interface::RemoveAmiibo() {
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
|
||||
std::shared_ptr<Module> GetModule() const;
|
||||
|
||||
void LoadAmiibo(const AmiiboData& amiibo_data);
|
||||
bool LoadAmiibo(const std::string& fullpath);
|
||||
|
||||
void RemoveAmiibo();
|
||||
|
||||
@@ -240,6 +240,8 @@ private:
|
||||
CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized;
|
||||
|
||||
AmiiboData amiibo_data{};
|
||||
bool amiibo_decrypted = false;
|
||||
std::string amiibo_filename;
|
||||
bool amiibo_in_range = false;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "core/hle/service/http_c.h"
|
||||
#include "core/hle/service/ir/ir.h"
|
||||
#include "core/hle/service/ldr_ro/ldr_ro.h"
|
||||
#include "core/hle/service/luma/hbldr.h"
|
||||
#include "core/hle/service/mcu/mcu.h"
|
||||
#include "core/hle/service/mic_u.h"
|
||||
#include "core/hle/service/mvd/mvd.h"
|
||||
@@ -56,7 +57,7 @@
|
||||
|
||||
namespace Service {
|
||||
|
||||
const std::array<ServiceModuleInfo, 40> service_module_map{
|
||||
const std::array<ServiceModuleInfo, 41> service_module_map{
|
||||
{{"FS", 0x00040130'00001102, FS::InstallInterfaces},
|
||||
{"PM", 0x00040130'00001202, PM::InstallInterfaces},
|
||||
{"LDR", 0x00040130'00003702, LDR::InstallInterfaces},
|
||||
@@ -96,6 +97,7 @@ const std::array<ServiceModuleInfo, 40> service_module_map{
|
||||
{"SSL", 0x00040130'00002F02, SSL::InstallInterfaces},
|
||||
{"PS", 0x00040130'00003102, PS::InstallInterfaces},
|
||||
{"MCU", 0x00040130'00001F02, MCU::InstallInterfaces},
|
||||
{"HBLDR", 0x00040130'00006902, HBLDR::InstallInterfaces},
|
||||
// no HLE implementation
|
||||
{"CDC", 0x00040130'00001802, nullptr},
|
||||
{"GPIO", 0x00040130'00001B02, nullptr},
|
||||
|
||||
@@ -190,6 +190,6 @@ struct ServiceModuleInfo {
|
||||
std::function<void(Core::System&)> init_function;
|
||||
};
|
||||
|
||||
extern const std::array<ServiceModuleInfo, 40> service_module_map;
|
||||
extern const std::array<ServiceModuleInfo, 41> service_module_map;
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/fs/fs_user.h"
|
||||
#include "core/loader/3dsx.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -267,13 +268,17 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& process)
|
||||
codeset->name = filename;
|
||||
|
||||
process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset));
|
||||
process->svc_access_mask.set();
|
||||
process->address_mappings = default_address_mappings;
|
||||
process->Set3dsxKernelCaps();
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
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 =
|
||||
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||
fs_user->Register(process->GetObjectId(), process->codeset->program_id, filepath);
|
||||
|
||||
process->Run(48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
Core::System::GetInstance().ArchiveManager().RegisterSelfNCCH(*this);
|
||||
|
||||
@@ -396,8 +396,7 @@ ResultStatus AppLoader_ELF::Load(std::shared_ptr<Kernel::Process>& process) {
|
||||
codeset->name = filename;
|
||||
|
||||
process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset));
|
||||
process->svc_access_mask.set();
|
||||
process->address_mappings = default_address_mappings;
|
||||
process->Set3dsxKernelCaps();
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/fs/fs_user.h"
|
||||
#include "core/loader/ncch.h"
|
||||
#include "core/loader/smdh.h"
|
||||
#include "core/memory.h"
|
||||
@@ -138,14 +139,21 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
overlay_ncch->exheader_header.arm11_system_local_caps.ideal_processor;
|
||||
|
||||
// Copy data while converting endianness
|
||||
std::array<u32, ARRAY_SIZE(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors)>
|
||||
kernel_caps;
|
||||
using KernelCaps = std::array<u32, ExHeader_ARM11_KernelCaps::NUM_DESCRIPTORS>;
|
||||
KernelCaps kernel_caps;
|
||||
std::copy_n(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(),
|
||||
begin(kernel_caps));
|
||||
process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
|
||||
|
||||
s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority;
|
||||
u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size;
|
||||
|
||||
// On real HW this is done with FS:Reg, but we can be lazy
|
||||
auto fs_user =
|
||||
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>(
|
||||
"fs:USER");
|
||||
fs_user->Register(process->process_id, process->codeset->program_id, filepath);
|
||||
|
||||
process->Run(priority, stack_size);
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
@@ -195,7 +203,6 @@ ResultStatus AppLoader_NCCH::Load(std::shared_ptr<Kernel::Process>& process) {
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.TelemetrySession().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
|
||||
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
Network::GameInfo game_info;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#ifdef ARCHITECTURE_ARM64
|
||||
#include <android/log.h>
|
||||
#include <arm_neon.h>
|
||||
|
||||
#endif
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
@@ -55,15 +54,6 @@ RasterizerOpenGL::RasterizerOpenGL()
|
||||
AllowShadow = (GLAD_GL_ARB_shader_image_load_store && GLAD_GL_ARB_shader_image_size &&
|
||||
GLAD_GL_ARB_framebuffer_no_attachments) ||
|
||||
Settings::values.allow_shadow;
|
||||
if (!AllowShadow) {
|
||||
LOG_WARNING(Render_OpenGL,
|
||||
"Shadow might not be able to render because of unsupported OpenGL extensions.");
|
||||
}
|
||||
|
||||
if (!GLAD_GL_ARB_texture_barrier) {
|
||||
LOG_WARNING(Render_OpenGL,
|
||||
"ARB_texture_barrier not supported. Some games might produce artifacts.");
|
||||
}
|
||||
|
||||
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
||||
state.clip_distance[0] = true;
|
||||
@@ -190,7 +180,7 @@ RasterizerOpenGL::RasterizerOpenGL()
|
||||
SyncEntireState();
|
||||
}
|
||||
|
||||
RasterizerOpenGL::~RasterizerOpenGL() {}
|
||||
RasterizerOpenGL::~RasterizerOpenGL() = default;
|
||||
|
||||
void RasterizerOpenGL::SyncEntireState() {
|
||||
// Sync fixed function OpenGL state
|
||||
@@ -528,6 +518,15 @@ void RasterizerOpenGL::BindFramebufferColor(OpenGLState& state, const Surface& s
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
surface->texture.handle, 0);
|
||||
framebuffer_info.color_attachment = surface->texture.handle;
|
||||
framebuffer_info.color_width = surface->width;
|
||||
framebuffer_info.color_height = surface->height;
|
||||
}
|
||||
if (framebuffer_info.depth_attachment &&
|
||||
(framebuffer_info.color_width > framebuffer_info.depth_width ||
|
||||
framebuffer_info.color_height > framebuffer_info.depth_height)) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
framebuffer_info.depth_attachment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,8 +537,14 @@ void RasterizerOpenGL::BindFramebufferDepthStencil(OpenGLState& state, const Sur
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
surface->texture.handle, 0);
|
||||
framebuffer_info.depth_attachment = surface->texture.handle;
|
||||
framebuffer_info.width = surface->width;
|
||||
framebuffer_info.height = surface->height;
|
||||
framebuffer_info.depth_width = surface->width;
|
||||
framebuffer_info.depth_height = surface->height;
|
||||
}
|
||||
if (framebuffer_info.color_attachment &&
|
||||
(framebuffer_info.depth_width > framebuffer_info.color_width ||
|
||||
framebuffer_info.depth_height > framebuffer_info.color_height)) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
framebuffer_info.color_attachment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,8 +555,14 @@ void RasterizerOpenGL::BindFramebufferDepth(OpenGLState& state, const Surface& s
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
surface->texture.handle, 0);
|
||||
framebuffer_info.depth_attachment = surface->texture.handle;
|
||||
framebuffer_info.width = surface->width;
|
||||
framebuffer_info.height = surface->height;
|
||||
framebuffer_info.depth_width = surface->width;
|
||||
framebuffer_info.depth_height = surface->height;
|
||||
}
|
||||
if (framebuffer_info.color_attachment &&
|
||||
(framebuffer_info.depth_width > framebuffer_info.color_width ||
|
||||
framebuffer_info.depth_height > framebuffer_info.color_height)) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
framebuffer_info.color_attachment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,9 +600,13 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
u32 res_scale = 1;
|
||||
GLuint color_attachment = 0;
|
||||
u32 color_width = 0;
|
||||
u32 color_height = 0;
|
||||
if (color_surface) {
|
||||
res_scale = color_surface->res_scale;
|
||||
color_attachment = color_surface->texture.handle;
|
||||
color_width = color_surface->width;
|
||||
color_height = color_surface->height;
|
||||
} else if (depth_surface) {
|
||||
res_scale = depth_surface->res_scale;
|
||||
}
|
||||
@@ -734,8 +749,10 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
}
|
||||
framebuffer_info.color_attachment = 0;
|
||||
framebuffer_info.depth_attachment = 0;
|
||||
framebuffer_info.width = 0;
|
||||
framebuffer_info.height = 0;
|
||||
framebuffer_info.color_width = 0;
|
||||
framebuffer_info.color_height = 0;
|
||||
framebuffer_info.depth_width = 0;
|
||||
framebuffer_info.depth_height = 0;
|
||||
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
|
||||
color_surface->GetScaledWidth());
|
||||
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
|
||||
@@ -749,6 +766,8 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
color_attachment, 0);
|
||||
framebuffer_info.color_attachment = color_attachment;
|
||||
framebuffer_info.color_width = color_width;
|
||||
framebuffer_info.color_height = color_height;
|
||||
}
|
||||
if (depth_surface != nullptr) {
|
||||
if (has_stencil) {
|
||||
@@ -757,8 +776,8 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, depth_surface->texture.handle, 0);
|
||||
framebuffer_info.depth_attachment = depth_surface->texture.handle;
|
||||
framebuffer_info.width = depth_surface->width;
|
||||
framebuffer_info.height = depth_surface->height;
|
||||
framebuffer_info.depth_width = depth_surface->width;
|
||||
framebuffer_info.depth_height = depth_surface->height;
|
||||
}
|
||||
} else if (framebuffer_info.depth_attachment != depth_surface->texture.handle) {
|
||||
// attach depth
|
||||
@@ -768,18 +787,18 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
framebuffer_info.depth_attachment = depth_surface->texture.handle;
|
||||
framebuffer_info.width = depth_surface->width;
|
||||
framebuffer_info.height = depth_surface->height;
|
||||
framebuffer_info.depth_width = depth_surface->width;
|
||||
framebuffer_info.depth_height = depth_surface->height;
|
||||
}
|
||||
} else if (framebuffer_info.depth_attachment != 0) {
|
||||
if (framebuffer_info.width < surfaces_rect.right ||
|
||||
framebuffer_info.height < surfaces_rect.top) {
|
||||
if (framebuffer_info.depth_width < surfaces_rect.right ||
|
||||
framebuffer_info.depth_height < surfaces_rect.top) {
|
||||
// clear both depth and stencil attachment
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
framebuffer_info.depth_attachment = 0;
|
||||
framebuffer_info.width = 0;
|
||||
framebuffer_info.height = 0;
|
||||
framebuffer_info.depth_width = 0;
|
||||
framebuffer_info.depth_height = 0;
|
||||
} else {
|
||||
state.depth.test_enabled = false;
|
||||
state.depth.write_mask = GL_FALSE;
|
||||
|
||||
@@ -310,8 +310,10 @@ private:
|
||||
struct {
|
||||
GLuint color_attachment;
|
||||
GLuint depth_attachment;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 color_width;
|
||||
u32 color_height;
|
||||
u32 depth_width;
|
||||
u32 depth_height;
|
||||
} framebuffer_info{};
|
||||
|
||||
std::array<SamplerInfo, 3> texture_samplers;
|
||||
|
||||
@@ -333,8 +333,7 @@ static void BlitTextures(GLuint src_tex, const Common::Rectangle<u32>& src_rect,
|
||||
static void BlitTextures(const Surface& src, const Common::Rectangle<u32>& src_rect,
|
||||
const Surface& dst, const Common::Rectangle<u32>& dst_rect) {
|
||||
if (src->pixel_format == dst->pixel_format &&
|
||||
src->pixel_format < SurfaceParams::PixelFormat::IA8 &&
|
||||
src_rect.bottom < src_rect.top) {
|
||||
src->pixel_format < SurfaceParams::PixelFormat::IA8 && src_rect.bottom < src_rect.top) {
|
||||
// same color format, same size, don't flip vertically
|
||||
u32 src_width = src_rect.GetWidth();
|
||||
u32 src_height = src_rect.GetHeight();
|
||||
@@ -343,9 +342,9 @@ static void BlitTextures(const Surface& src, const Common::Rectangle<u32>& src_r
|
||||
u32 dst_height = dst_rect.GetHeight();
|
||||
|
||||
if (src_width == dst_width && src_height == dst_height) {
|
||||
glCopyImageSubData(src->texture.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0,
|
||||
dst->texture.handle, GL_TEXTURE_2D, 0, dst_rect.left, dst_rect.bottom, 0,
|
||||
src_width, src_height, 1);
|
||||
glCopyImageSubData(src->texture.handle, GL_TEXTURE_2D, 0, src_rect.left,
|
||||
src_rect.bottom, 0, dst->texture.handle, GL_TEXTURE_2D, 0,
|
||||
dst_rect.left, dst_rect.bottom, 0, src_width, src_height, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -483,8 +482,8 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
|
||||
return;
|
||||
}
|
||||
if (src_surface->CanSubRect(subrect_params)) {
|
||||
BlitTextures(src_surface, src_surface->GetScaledSubRect(subrect_params),
|
||||
dst_surface, dst_surface->GetScaledSubRect(subrect_params));
|
||||
BlitTextures(src_surface, src_surface->GetScaledSubRect(subrect_params), dst_surface,
|
||||
dst_surface->GetScaledSubRect(subrect_params));
|
||||
return;
|
||||
}
|
||||
UNREACHABLE();
|
||||
@@ -612,28 +611,14 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
|
||||
|
||||
const Core::CustomTexInfo* CachedSurface::LoadCustomTexture(u64 tex_hash,
|
||||
Common::Rectangle<u32>& custom_rect) {
|
||||
const Core::CustomTexInfo* tex_info = nullptr;
|
||||
auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache();
|
||||
|
||||
if (custom_tex_cache.IsTextureCached(tex_hash)) {
|
||||
tex_info = &custom_tex_cache.LookupTexture(tex_hash);
|
||||
} else {
|
||||
if (custom_tex_cache.CustomTextureExists(tex_hash)) {
|
||||
const auto& path_info = custom_tex_cache.LookupTexturePathInfo(tex_hash);
|
||||
custom_tex_cache.LoadTexture(path_info);
|
||||
if (custom_tex_cache.IsTextureCached(tex_hash)) {
|
||||
tex_info = &custom_tex_cache.LookupTexture(tex_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto* tex_info = custom_tex_cache.LoadTexture(tex_hash);
|
||||
if (tex_info) {
|
||||
custom_rect.left = (custom_rect.left * tex_info->width) / width;
|
||||
custom_rect.top = (custom_rect.top * tex_info->height) / height;
|
||||
custom_rect.right = (custom_rect.right * tex_info->width) / width;
|
||||
custom_rect.bottom = (custom_rect.bottom * tex_info->height) / height;
|
||||
}
|
||||
|
||||
return tex_info;
|
||||
}
|
||||
|
||||
@@ -907,20 +892,19 @@ RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
|
||||
g_draw_framebuffer.Release();
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_BlitSurface, "OpenGL", "BlitSurface", MP_RGB(128, 192, 64));
|
||||
bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface,
|
||||
const Common::Rectangle<u32>& src_rect,
|
||||
const Surface& dst_surface,
|
||||
const Common::Rectangle<u32>& dst_rect) {
|
||||
MICROPROFILE_SCOPE(OpenGL_BlitSurface);
|
||||
|
||||
if (!SurfaceParams::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format))
|
||||
return false;
|
||||
|
||||
if (SurfaceParams::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) {
|
||||
dst_surface->InvalidateAllWatcher();
|
||||
|
||||
BlitTextures(src_surface, src_rect, dst_surface, dst_rect);
|
||||
return true;
|
||||
} else {
|
||||
LOG_INFO(Render_OpenGL, "BlitSurfaces failed from: {} to: {}", src_surface->pixel_format,
|
||||
dst_surface->pixel_format);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||
@@ -1665,7 +1649,6 @@ void RasterizerCacheOpenGL::SetScaleFactor(u16 scale) {
|
||||
while (!surface_cache.empty())
|
||||
UnregisterSurface(*surface_cache.begin()->second.begin());
|
||||
texture_cube_cache.clear();
|
||||
surface_texture_cache.clear();
|
||||
resolution_scale_factor = scale;
|
||||
}
|
||||
|
||||
@@ -1785,14 +1768,13 @@ void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int del
|
||||
const PAddr interval_end_addr = boost::icl::last_next(interval) << Memory::PAGE_BITS;
|
||||
const u32 interval_size = interval_end_addr - interval_start_addr;
|
||||
|
||||
if (delta > 0 && count == delta)
|
||||
if (count == delta) {
|
||||
VideoCore::Memory()->RasterizerMarkRegionCached(interval_start_addr, interval_size,
|
||||
true);
|
||||
else if (delta < 0 && count == -delta)
|
||||
} else if (count == -delta) {
|
||||
VideoCore::Memory()->RasterizerMarkRegionCached(interval_start_addr, interval_size,
|
||||
false);
|
||||
else
|
||||
ASSERT(count >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (delta < 0)
|
||||
|
||||
@@ -487,7 +487,7 @@ bool RendererOpenGL::TryPresent() {
|
||||
|
||||
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
|
||||
// readback since we won't be doing any blending
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// Recreate the presentation FBO if the color attachment was changed
|
||||
if (frame->color_reloaded) {
|
||||
@@ -876,14 +876,13 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
|
||||
// prefer `glBufferData` than `glBufferSubData` on mobile device
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STREAM_DRAW);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
if (bg_texture.handle) {
|
||||
// background image
|
||||
GLuint handle = OpenGLState::BindShaderProgram(bg_shader.handle);
|
||||
OpenGLState::BindTexture2D(0, bg_texture.handle);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
OpenGLState::BindShaderProgram(handle);
|
||||
} else {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
// Draws texture to the emulator window,
|
||||
|
||||
Reference in New Issue
Block a user