fx issues

This commit is contained in:
weihuoya
2021-06-03 23:22:50 +08:00
parent e75bad1c17
commit da67e5a847
36 changed files with 527 additions and 198 deletions

6
.gitmodules vendored
View File

@@ -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

1
externals/libyuv vendored Submodule

Submodule externals/libyuv added at 5b3351bd07

View File

@@ -1,7 +0,0 @@
add_library(lodepng
lodepng/lodepng.cpp
lodepng/lodepng.h
)
create_target_directory_groups(lodepng)
target_include_directories(lodepng INTERFACE lodepng)

Submodule externals/lodepng/lodepng deleted from 31d9704fdc

1
externals/zstd vendored

Submodule externals/zstd deleted from 10f0e6993f

View File

@@ -8,6 +8,10 @@
0004000000051400 = 极品飞车16 亡命狂飙
0004000000051500 = 极品飞车16 亡命狂飙
0004000000157900 = 皇牌空战3D 战火纷飞+
00040000000F4E00 = 新爱相随+
00040000001B4E00 = 迷托邦
00040000001B4F00 = 迷托邦
0004000000178800 = 迷托邦
0004000000884800 = 仙剑奇侠传
0004000000198500 = 金庸群侠传
0004000000190B00 = 沙漠老鼠团

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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 {};
}

View File

@@ -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

View File

@@ -38,16 +38,18 @@ static void FlipCustomTexture(u32* pixels, u32 width, u32 height) {
}
}
CustomTexCache::CustomTexCache() = default;
CustomTexCache::~CustomTexCache() = default;
bool CustomTexCache::IsTextureCached(u64 hash) const {
return custom_textures.find(hash) != custom_textures.end();
const CustomTexInfo* CustomTexCache::LoadTexture(u64 hash) {
auto iter = custom_textures.find(hash);
if (iter != custom_textures.end()) {
return &iter->second;
}
const CustomTexInfo& CustomTexCache::LookupTexture(u64 hash) const {
return custom_textures.at(hash);
auto piter = custom_texture_paths.find(hash);
if (piter == custom_texture_paths.end()) {
return nullptr;
}
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

View File

@@ -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;
};
// TODO: think of a better name for this class...
class CustomTexCache {
public:
CustomTexCache() = default;
~CustomTexCache() = default;
// init
void FindCustomTextures(u64 program_id);
void PreloadTextures();
// get texture
const CustomTexInfo* LoadTexture(u64 hash);
private:
// 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();
bool IsTextureCached(u64 hash) const;
const CustomTexInfo& LookupTexture(u64 hash) const;
void AddTexturePath(u64 hash, const std::string& path);
void FindCustomTextures(u64 program_id);
void PreloadTextures();
void LoadTexture(const CustomTexPathInfo& path_info);
bool CustomTextureExists(u64 hash) const;
const CustomTexPathInfo& LookupTexturePathInfo(u64 hash) const;
const CustomTexInfo* LoadTexture(const CustomTexPathInfo& path_info);
private:
std::unordered_map<u64, CustomTexInfo> custom_textures;
std::unordered_map<u64, CustomTexPathInfo> custom_texture_paths;
};

View File

@@ -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));
}

View File

@@ -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;

View 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 {

View File

@@ -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.
*/

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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"},

View File

@@ -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;

View 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

View 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

View File

@@ -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() {

View File

@@ -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;
};

View File

@@ -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},

View File

@@ -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

View File

@@ -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);

View File

@@ -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(

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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,