Compare commits

..

66 Commits

Author SHA1 Message Date
Subv
4cc1e180ec GPU: Implemented the R16 and R16F texture formats. 2018-07-24 13:39:16 -05:00
bunnei
316c994f55 Merge pull request #798 from lioncash/const
arm_dynarmic: Make MakeJit() a const member function
2018-07-24 04:48:06 -07:00
bunnei
23e85b6b9f Merge pull request #797 from lioncash/explicit
core: Make converting constructors explicit where applicable
2018-07-24 04:47:26 -07:00
bunnei
1cbf7ac6ea Merge pull request #795 from lioncash/decl
apm/interface: Remove redundant declaration of InstallInterfaces()
2018-07-24 04:46:41 -07:00
bunnei
0f830d08f1 Merge pull request #799 from Subv/tex_r32f
GPU: Implement texture format R32F.
2018-07-24 04:46:07 -07:00
bunnei
d092ea0870 Merge pull request #794 from lioncash/ref
mutex: Pass SharedPtr to GetHighestPriorityMutexWaitingThread() by reference
2018-07-24 04:45:34 -07:00
bunnei
b70f757913 Merge pull request #796 from bunnei/gl-uint
maxwell_to_gl: Implement VertexAttribute::Type::UnsignedInt.
2018-07-24 04:44:56 -07:00
bunnei
54af9c206a Merge pull request #789 from bunnei/tex-wrap-border
maxwell_to_gl: Implement Texture::WrapMode::Border.
2018-07-24 04:44:37 -07:00
bunnei
2f029577c7 Merge pull request #793 from lioncash/priv
ipc_helpers: Make member variables of ResponseBuilder private
2018-07-23 21:23:27 -07:00
bunnei
bc5b65a1b0 Merge pull request #785 from lioncash/fs
partition_filesystem: Use std::move where applicable
2018-07-23 20:36:59 -07:00
bunnei
69c45ce71c gl_rasterizer: Implement texture border color. 2018-07-23 23:34:42 -04:00
bunnei
6b3e54621f maxwell_to_gl: Implement Texture::WrapMode::Border. 2018-07-23 23:34:41 -04:00
Subv
ccc42702b5 GPU: Implement texture format R32F. 2018-07-23 22:21:31 -05:00
Lioncash
7d124ec82b arm_dynarmic: Make MakeJit() a const member function
This functions doesn't modify instance state, so it can be a made a
const member function.
2018-07-23 23:19:37 -04:00
Lioncash
1d755abce4 core: Make converting constructors explicit where applicable
Avoids unwanted implicit conversions. Thankfully, given the large amount
of cleanup in past PRs, only this tiny amount is left over to cover.
2018-07-23 23:13:22 -04:00
bunnei
1ff3bea6c7 Merge pull request #791 from bunnei/rg32f-rgba32f-bgra8
gl_rasterizer_cache: Implement formats BGRA8_UNORM/RGBA32_FLOAT/RG32_FLOAT
2018-07-23 20:13:19 -07:00
bunnei
2ff86f5765 maxwell_to_gl: Implement VertexAttribute::Type::UnsignedInt. 2018-07-23 23:12:14 -04:00
bunnei
92304181d5 Merge pull request #792 from lioncash/retval
gl_shader_decompiler: Correct return value of WriteTexsInstruction()
2018-07-23 20:06:48 -07:00
Lioncash
8b83adfed6 apm/interface: Remove redundant declaration of InstallInterfaces()
This is already declared in apm/apm.h
2018-07-23 23:01:04 -04:00
Lioncash
42b5158c96 mutex: Pass SharedPtr to GetHighestPriorityMutexWaitingThread() by reference
The pointed to thread's members are simply observed in this case, so we
don't need to copy it here.
2018-07-23 22:54:35 -04:00
bunnei
47ac369180 Merge pull request #790 from bunnei/shader-print-instr
gl_shader_decompiler: Print instruction value in shader comments.
2018-07-23 19:48:47 -07:00
bunnei
c2b4ff5d48 Merge pull request #788 from bunnei/shader-check-zero
gl_shader_decompiler: Check if SetRegister result is ZeroIndex.
2018-07-23 19:44:05 -07:00
Zach Hilman
59cb258409 VFS Regression and Accuracy Fixes (#776)
* Regression and Mode Fixes

* Review Fixes

* string_view correction

* Add operator& for FileSys::Mode

* Return std::string from SanitizePath

* Farming Simulator Fix

* Use != With mode operator&
2018-07-23 19:40:35 -07:00
Lioncash
22fd3f0026 hle_ipc: Make constructors explicit where applicable 2018-07-23 22:40:24 -04:00
Lioncash
33e2033af5 gl_shader_decompiler: Correct return value of WriteTexsInstruction()
This should be returning void, not a std::string
2018-07-23 22:31:58 -04:00
Lioncash
2a1daf8f83 ipc_helpers: Make member variables of ResponseBuilder private
These aren't used externally at all, so they can be made private.
2018-07-23 22:29:07 -04:00
bunnei
f6657bc8d7 Merge pull request #787 from bunnei/tlds
gl_shader_decompiler: Implement shader instruction TLDS.
2018-07-23 19:14:01 -07:00
bunnei
25635907a2 Merge pull request #786 from lioncash/exclusive
exclusive_monitor: Use consistent type alias for u64
2018-07-23 19:11:05 -07:00
bunnei
84cc5dd35d Merge pull request #784 from lioncash/loader
loader: Minor cleanup
2018-07-23 19:08:12 -07:00
bunnei
1ce5e04be8 Merge pull request #783 from lioncash/linker
linker: Remove unused parameter from WriteRelocations()
2018-07-23 19:07:22 -07:00
bunnei
10dd03dec5 Merge pull request #782 from lioncash/file
loader/nro: Minor changes
2018-07-23 19:06:30 -07:00
bunnei
9505283989 gl_shader_decompiler: Implement shader instruction TLDS. 2018-07-23 22:02:51 -04:00
bunnei
f6f6f3811e Merge pull request #781 from lioncash/decl
gl_shader_decompiler: Simplify GetCommonDeclarations()
2018-07-23 18:30:23 -07:00
bunnei
52ec1840f5 Merge pull request #780 from lioncash/move
vi: Minor changes
2018-07-23 18:29:11 -07:00
bunnei
e0b6771e25 Merge pull request #779 from lioncash/shared
hle: Remove unused config_mem and shared_page source files
2018-07-23 18:28:45 -07:00
bunnei
a27c0099ed gl_rasterizer_cache: Implement RenderTargetFormat RG32_FLOAT. 2018-07-23 21:22:54 -04:00
bunnei
3a19c1098d gl_rasterizer_cache: Implement RenderTargetFormat RGBA32_FLOAT. 2018-07-23 21:22:53 -04:00
bunnei
bcc184acfa gl_rasterizer_cache: Implement RenderTargetFormat BGRA8_UNORM. 2018-07-23 21:22:44 -04:00
bunnei
89db8c2171 gl_rasterizer_cache: Add missing log statements. 2018-07-23 21:20:09 -04:00
bunnei
c4322ce87e gl_shader_decompiler: Print instruction value in shader comments. 2018-07-23 21:11:05 -04:00
bunnei
81aa02424b gl_shader_decompiler: Check if SetRegister result is ZeroIndex. 2018-07-23 21:08:40 -04:00
Lioncash
e12c84d5c5 exclusive_monitor: Use consistent type alias for u64
Uses the same type aliases we use for virtual addresses, and converts
one lingering usage of std::array<uint64_t, 2> to u128 for consistency.
2018-07-23 20:54:57 -04:00
Lioncash
db48ebb9c9 partition_filesystem: Use std::move where applicable
Avoids copying a std::string instance and avoids unnecessary atomic
reference count incrementing and decrementing.
2018-07-23 20:27:11 -04:00
Lioncash
a147fa5825 loader: Remove unnecessary constructor call in IdentifyFile()
RealVfsFile inherits from VfsFile, the instance from std::make_shared is
already compatible with the function argument type, making the copy
constructor call unnecessary.
2018-07-23 17:44:58 -04:00
Lioncash
184c516182 linker: Remove unused parameter from WriteRelocations()
is_jump_relocation is never used within the function, so we can just
remove it.
2018-07-23 17:40:12 -04:00
Lioncash
1b4d0ac20e nro: Replace inclusion with a forward declaration
It's sufficient to use a forward declaration instead of a direct
inclusion here.
2018-07-23 17:29:02 -04:00
Lioncash
2b497e5830 nro: Make bracing consistent
Makes the code more uniform, and also braces cases where the body of an
unbraced conditional travels more than one line.
2018-07-23 17:24:29 -04:00
Lioncash
ac8133b9ee nro: Make constructor explicit
Makes it consistent with the other Apploader constructors, and prevents
implicit conversions.
2018-07-23 17:20:33 -04:00
Lioncash
1c16700372 nro: Remove unused forward declaration
This isn't used anywhere in the header.
2018-07-23 17:19:42 -04:00
bunnei
07e5319d55 Merge pull request #695 from DarkLordZach/nro-asset
NRO Assets and NACP File Format
2018-07-23 14:14:11 -07:00
bunnei
d787873b3b Merge pull request #778 from lioncash/log
set: Add missing log call in GetAvailableLanguageCodeCount()
2018-07-23 14:13:09 -07:00
Lioncash
3b88ce3dcb gl_shader_decompiler: Simplify GetCommonDeclarations() 2018-07-23 17:11:18 -04:00
Lioncash
1432912ae8 vi: Add std::is_trivially_copyable checks to Read and Write functions
It's undefined behavior to memcpy an object that isn't considered
trivially copyable, so put a compile-time check in to make sure this
doesn't occur.
2018-07-23 14:53:54 -04:00
Lioncash
344a0c91f2 vi: std::move std::vector in constructors where applicable
Allows avoiding unnecessary copies of the vector depending on the
calling code.

While we're at it, remove a redundant no-parameter base constructor call
2018-07-23 14:49:54 -04:00
Lioncash
cbe841c9c9 hle: Remove config_mem.h/.cpp
This is just an unused hold-over from citra, so we can get rid of this
to trim off an exposed global, among other things.
2018-07-23 12:57:34 -04:00
Lioncash
1f3889a290 hle: Remove shared_page.h/.cpp
This is a holdover from citra that's essentially unused.
2018-07-23 12:53:07 -04:00
Lioncash
e85308cd90 set: Add missing log call in GetAvailableLanguageCodeCount()
Forgot to include this in 22f448b632
2018-07-23 12:37:42 -04:00
bunnei
7138b99f21 Merge pull request #775 from lioncash/str
string_util: Minor changes
2018-07-23 09:34:41 -07:00
Zach Hilman
e8f641a52d NRO Assets and NACP file format
Cleanup

Review fixes
2018-07-23 12:34:26 -04:00
bunnei
a85366a40c Merge pull request #777 from lioncash/lang
set: Amend return value of GetAvailableLanguageCodes()
2018-07-23 09:34:08 -07:00
Lioncash
22f448b632 set: Implement GetAvailableLanguageCodeCount()
This just returns the size of the language code buffer.
2018-07-23 00:29:40 -04:00
Lioncash
37aeecd29f set: Correct return code size of value in GetAvailableLanguageCodes()
The return code should be 32-bit in size.
2018-07-23 00:29:22 -04:00
bunnei
e85a528bb9 Merge pull request #769 from bunnei/shader-mask-fixes
shader_bytecode: Implement other TEXS masks.
2018-07-22 18:03:31 -07:00
bunnei
a4b2af7382 Merge pull request #774 from Subv/atomic_signal
Kernel/SVC: Perform atomic accesses in SignalProcessWideKey as per the real kernel.
2018-07-22 12:26:03 -07:00
Subv
7841447cf0 Kernel/SVC: Perform atomic accesses in SignalProcessWideKey as per the real kernel. 2018-07-22 12:27:24 -05:00
bunnei
148a5bef7e shader_bytecode: Implement other TEXS masks. 2018-07-22 03:23:15 -04:00
48 changed files with 640 additions and 467 deletions

View File

@@ -826,7 +826,7 @@ std::string_view GetPathWithoutTop(std::string_view path) {
}
while (path[0] == '\\' || path[0] == '/') {
path.remove_suffix(1);
path.remove_prefix(1);
if (path.empty()) {
return path;
}
@@ -870,6 +870,15 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
return path;
}
std::string SanitizePath(std::string_view path_) {
std::string path(path_);
std::replace(path.begin(), path.end(), '\\', '/');
path.erase(std::unique(path.begin(), path.end(),
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
path.end());
return std::string(RemoveTrailingSlash(path));
}
IOFile::IOFile() {}
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {

View File

@@ -178,6 +178,9 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
std::string SanitizePath(std::string_view path);
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
// and make forgetting an fclose() harder

View File

@@ -12,6 +12,8 @@ add_library(core STATIC
core_timing.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
file_sys/control_metadata.h
file_sys/directory.h
file_sys/errors.h
file_sys/mode.h
@@ -38,8 +40,6 @@ add_library(core STATIC
frontend/input.h
gdbstub/gdbstub.cpp
gdbstub/gdbstub.h
hle/config_mem.cpp
hle/config_mem.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
@@ -249,8 +249,6 @@ add_library(core STATIC
hle/service/vi/vi_s.h
hle/service/vi/vi_u.cpp
hle/service/vi/vi_u.h
hle/shared_page.cpp
hle/shared_page.h
hw/hw.cpp
hw/hw.h
hw/lcd.cpp

View File

@@ -102,8 +102,8 @@ public:
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() {
const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
auto** const page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
@@ -257,7 +257,7 @@ void ARM_Dynarmic::PageTableChanged() {
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, u64 addr) {
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) {
// Size doesn't actually matter.
monitor.Mark(core_index, addr, 16);
}
@@ -266,28 +266,27 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
monitor.Clear();
}
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
[&] { Memory::Write8(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
[&] { Memory::Write16(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
[&] { Memory::Write32(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
[&] { Memory::Write64(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, u64 vaddr,
std::array<std::uint64_t, 2> value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
Memory::Write64(vaddr, value[0]);
Memory::Write64(vaddr, value[1]);

View File

@@ -50,7 +50,7 @@ public:
void PageTableChanged() override;
private:
std::unique_ptr<Dynarmic::A64::Jit> MakeJit();
std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const;
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
@@ -68,15 +68,14 @@ public:
explicit DynarmicExclusiveMonitor(size_t core_count);
~DynarmicExclusiveMonitor();
void SetExclusive(size_t core_index, u64 addr) override;
void SetExclusive(size_t core_index, VAddr addr) override;
void ClearExclusive() override;
bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) override;
bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) override;
bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) override;
bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) override;
bool ExclusiveWrite128(size_t core_index, u64 vaddr,
std::array<std::uint64_t, 2> value) override;
bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) override;
bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) override;
bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) override;
bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) override;
bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) override;
private:
friend class ARM_Dynarmic;

View File

@@ -4,20 +4,18 @@
#pragma once
#include <array>
#include "common/common_types.h"
class ExclusiveMonitor {
public:
virtual ~ExclusiveMonitor();
virtual void SetExclusive(size_t core_index, u64 addr) = 0;
virtual void SetExclusive(size_t core_index, VAddr addr) = 0;
virtual void ClearExclusive() = 0;
virtual bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) = 0;
virtual bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) = 0;
virtual bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(size_t core_index, u64 vaddr,
std::array<std::uint64_t, 2> value) = 0;
virtual bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) = 0;
virtual bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) = 0;
virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0;
};

View File

@@ -0,0 +1,42 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/string_util.h"
#include "common/swap.h"
#include "core/file_sys/control_metadata.h"
namespace FileSys {
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
}
std::string LanguageEntry::GetDeveloperName() const {
return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100);
}
NACP::NACP(VirtualFile file_) : file(std::move(file_)), raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
}
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
return raw->language_entries.at(static_cast<u8>(language));
}
std::string NACP::GetApplicationName(Language language) const {
return GetLanguageEntry(language).GetApplicationName();
}
std::string NACP::GetDeveloperName(Language language) const {
return GetLanguageEntry(language).GetDeveloperName();
}
u64 NACP::GetTitleId() const {
return raw->title_id;
}
std::string NACP::GetVersionString() const {
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10);
}
} // namespace FileSys

View File

@@ -0,0 +1,81 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
// A localized entry containing strings within the NACP.
// One for each language of type Language.
struct LanguageEntry {
std::array<char, 0x200> application_name;
std::array<char, 0x100> developer_name;
std::string GetApplicationName() const;
std::string GetDeveloperName() const;
};
static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.");
// The raw file format of a NACP file.
struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
INSERT_PADDING_BYTES(0x38);
u64_le title_id;
INSERT_PADDING_BYTES(0x20);
std::array<char, 0x10> version_string;
u64_le dlc_base_title_id;
u64_le title_id_2;
INSERT_PADDING_BYTES(0x28);
u64_le product_code;
u64_le title_id_3;
std::array<u64_le, 0x7> title_id_array;
INSERT_PADDING_BYTES(0x8);
u64_le title_id_update;
std::array<u8, 0x40> bcat_passphrase;
INSERT_PADDING_BYTES(0xEC0);
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
// A language on the NX. These are for names and icons.
enum class Language : u8 {
AmericanEnglish = 0,
BritishEnglish = 1,
Japanese = 2,
French = 3,
German = 4,
LatinAmericanSpanish = 5,
Spanish = 6,
Italian = 7,
Dutch = 8,
CanadianFrench = 9,
Portugese = 10,
Russian = 11,
Korean = 12,
Taiwanese = 13,
Chinese = 14,
};
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {
public:
explicit NACP(VirtualFile file);
const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const;
std::string GetApplicationName(Language language = Language::AmericanEnglish) const;
std::string GetDeveloperName(Language language = Language::AmericanEnglish) const;
u64 GetTitleId() const;
std::string GetVersionString() const;
private:
VirtualFile file;
std::unique_ptr<RawNACP> raw;
};
} // namespace FileSys

View File

@@ -11,7 +11,13 @@ namespace FileSys {
enum class Mode : u32 {
Read = 1,
Write = 2,
ReadWrite = 3,
Append = 4,
WriteAppend = 6,
};
inline u32 operator&(Mode lhs, Mode rhs) {
return static_cast<u32>(lhs) & static_cast<u32>(rhs);
}
} // namespace FileSys

View File

@@ -65,8 +65,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
std::string name(
reinterpret_cast<const char*>(&file_data[strtab_offset + entry.strtab_offset]));
pfs_files.emplace_back(
std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name));
pfs_files.emplace_back(std::make_shared<OffsetVfsFile>(
file, entry.size, content_offset + entry.offset, std::move(name)));
}
status = Loader::ResultStatus::Success;
@@ -109,7 +109,7 @@ bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualD
return false;
const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
pfs_files[offset] = pfs_files.back();
pfs_files[offset] = std::move(pfs_files.back());
pfs_files.pop_back();
pfs_dirs.emplace_back(std::move(dir));

View File

@@ -13,24 +13,31 @@
namespace FileSys {
static std::string PermissionsToCharArray(Mode perms) {
std::string out;
switch (perms) {
case Mode::Read:
out += "r";
break;
case Mode::Write:
out += "r+";
break;
case Mode::Append:
out += "a";
break;
static std::string ModeFlagsToString(Mode mode) {
std::string mode_str;
// Calculate the correct open mode for the file.
if (mode & Mode::Read && mode & Mode::Write) {
if (mode & Mode::Append)
mode_str = "a+";
else
mode_str = "r+";
} else {
if (mode & Mode::Read)
mode_str = "r";
else if (mode & Mode::Append)
mode_str = "a";
else if (mode & Mode::Write)
mode_str = "w";
}
return out + "b";
mode_str += "b";
return mode_str;
}
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
: backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
parent_path(FileUtil::GetParentPath(path_)),
path_components(FileUtil::SplitPathComponents(path_)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -53,11 +60,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
}
bool RealVfsFile::IsWritable() const {
return perms == Mode::Append || perms == Mode::Write;
return (perms & Mode::WriteAppend) != 0;
}
bool RealVfsFile::IsReadable() const {
return perms == Mode::Read || perms == Mode::Write;
return (perms & Mode::ReadWrite) != 0;
}
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
@@ -79,7 +86,7 @@ bool RealVfsFile::Rename(std::string_view name) {
path = (parent_path + DIR_SEP).append(name);
path_components = parent_components;
path_components.push_back(std::move(name_str));
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
return out;
}
@@ -93,7 +100,7 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
FileUtil::CreateDir(path);
if (perms == Mode::Append)
@@ -120,11 +127,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
}
bool RealVfsDirectory::IsWritable() const {
return perms == Mode::Write || perms == Mode::Append;
return (perms & Mode::WriteAppend) != 0;
}
bool RealVfsDirectory::IsReadable() const {
return perms == Mode::Read || perms == Mode::Write;
return (perms & Mode::ReadWrite) != 0;
}
std::string RealVfsDirectory::GetName() const {

View File

@@ -1,31 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "core/hle/config_mem.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace ConfigMem {
ConfigMemDef config_mem;
void Init() {
std::memset(&config_mem, 0, sizeof(config_mem));
// Values extracted from firmware 11.2.0-35E
config_mem.kernel_version_min = 0x34;
config_mem.kernel_version_maj = 0x2;
config_mem.ns_tid = 0x0004013000008002;
config_mem.sys_core_ver = 0x2;
config_mem.unit_info = 0x1; // Bit 0 set for Retail
config_mem.prev_firm = 0x1;
config_mem.ctr_sdk_ver = 0x0000F297;
config_mem.firm_version_min = 0x34;
config_mem.firm_version_maj = 0x2;
config_mem.firm_sys_core_ver = 0x2;
config_mem.firm_ctr_sdk_ver = 0x0000F297;
}
} // namespace ConfigMem

View File

@@ -1,56 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
// Configuration memory stores various hardware/kernel configuration settings. This memory page is
// read-only for ARM11 processes. I'm guessing this would normally be written to by the firmware/
// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
// putting this as a subset of HLE for now.
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace ConfigMem {
struct ConfigMemDef {
u8 kernel_unk; // 0
u8 kernel_version_rev; // 1
u8 kernel_version_min; // 2
u8 kernel_version_maj; // 3
u32_le update_flag; // 4
u64_le ns_tid; // 8
u32_le sys_core_ver; // 10
u8 unit_info; // 14
u8 boot_firm; // 15
u8 prev_firm; // 16
INSERT_PADDING_BYTES(0x1); // 17
u32_le ctr_sdk_ver; // 18
INSERT_PADDING_BYTES(0x30 - 0x1C); // 1C
u32_le app_mem_type; // 30
INSERT_PADDING_BYTES(0x40 - 0x34); // 34
u32_le app_mem_alloc; // 40
u32_le sys_mem_alloc; // 44
u32_le base_mem_alloc; // 48
INSERT_PADDING_BYTES(0x60 - 0x4C); // 4C
u8 firm_unk; // 60
u8 firm_version_rev; // 61
u8 firm_version_min; // 62
u8 firm_version_maj; // 63
u32_le firm_sys_core_ver; // 64
u32_le firm_ctr_sdk_ver; // 68
INSERT_PADDING_BYTES(0x1000 - 0x6C); // 6C
};
static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE,
"Config Memory structure size is wrong");
extern ConfigMemDef config_mem;
void Init();
} // namespace ConfigMem

View File

@@ -25,9 +25,9 @@ protected:
ptrdiff_t index = 0;
public:
RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
RequestHelperBase(Kernel::HLERequestContext& context)
explicit RequestHelperBase(Kernel::HLERequestContext& context)
: context(&context), cmdbuf(context.CommandBuffer()) {}
void Skip(unsigned size_in_words, bool set_to_null) {
@@ -56,13 +56,6 @@ public:
class ResponseBuilder : public RequestHelperBase {
public:
ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
/// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 {
None = 0,
@@ -71,9 +64,11 @@ public:
AlwaysMoveHandles = 1,
};
ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
: RequestHelperBase(context), normal_params_size(normal_params_size),
num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
@@ -206,6 +201,12 @@ public:
template <typename... O>
void PushCopyObjects(Kernel::SharedPtr<O>... pointers);
private:
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
};
/// Push ///
@@ -273,9 +274,9 @@ inline void ResponseBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
class RequestParser : public RequestHelperBase {
public:
RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
RequestParser(Kernel::HLERequestContext& context) : RequestHelperBase(context) {
explicit RequestParser(Kernel::HLERequestContext& context) : RequestHelperBase(context) {
ASSERT_MSG(context.GetDataPayloadOffset(), "context is incomplete");
Skip(context.GetDataPayloadOffset(), false);
// Skip the u64 command id, it's already stored in the context
@@ -285,8 +286,9 @@ public:
ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
u32 num_handles_to_move,
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) {
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move, flags};
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) const {
return ResponseBuilder{*context, normal_params_size, num_handles_to_copy,
num_handles_to_move, flags};
}
template <typename T>

View File

@@ -91,7 +91,7 @@ protected:
*/
class HLERequestContext {
public:
HLERequestContext(SharedPtr<Kernel::ServerSession> session);
explicit HLERequestContext(SharedPtr<ServerSession> session);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/config_mem.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
@@ -11,7 +10,6 @@
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/shared_page.h"
namespace Kernel {
@@ -19,9 +17,6 @@ unsigned int Object::next_object_id;
/// Initialize the kernel
void Init(u32 system_mode) {
ConfigMem::Init();
SharedPage::Init();
Kernel::MemoryInit(system_mode);
Kernel::ResourceLimitsInit();

View File

@@ -11,11 +11,9 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/config_mem.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/result.h"
#include "core/hle/shared_page.h"
#include "core/memory.h"
#include "core/memory_setup.h"
@@ -63,14 +61,6 @@ void MemoryInit(u32 mem_type) {
// We must've allocated the entire FCRAM by the end
ASSERT(base == Memory::FCRAM_SIZE);
using ConfigMem::config_mem;
config_mem.app_mem_type = mem_type;
// app_mem_malloc does not always match the configured size for memory_region[0]: in case the
// n3DS type override is in effect it reports the size the game expects, not the real one.
config_mem.app_mem_alloc = memory_region_sizes[mem_type][0];
config_mem.sys_mem_alloc = static_cast<u32_le>(memory_regions[1].size);
config_mem.base_mem_alloc = static_cast<u32_le>(memory_regions[2].size);
}
void MemoryShutdown() {

View File

@@ -19,7 +19,7 @@ namespace Kernel {
/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
/// those.
static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
SharedPtr<Thread> current_thread, VAddr mutex_addr) {
const SharedPtr<Thread>& current_thread, VAddr mutex_addr) {
SharedPtr<Thread> highest_priority_thread;
u32 num_waiters = 0;

View File

@@ -650,12 +650,27 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
ASSERT(thread->condvar_wait_address == condition_variable_addr);
// If the mutex is not yet acquired, acquire it.
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
auto& monitor = Core::System::GetInstance().Monitor();
// Atomically read the value of the mutex.
u32 mutex_val = 0;
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// If the mutex is not yet acquired, acquire it.
mutex_val = Memory::Read32(thread->mutex_wait_address);
if (mutex_val != 0) {
monitor.ClearExclusive();
break;
}
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
thread->wait_handle));
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
@@ -668,7 +683,19 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
} else {
// Couldn't acquire the mutex, block the thread.
// Atomically signal that the mutex now has a waiting thread.
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// Ensure that the mutex value is still what we expect.
u32 value = Memory::Read32(thread->mutex_wait_address);
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
// retries the initial read for this thread.
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = g_handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
@@ -676,9 +703,6 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->status = ThreadStatus::WaitMutex;
thread->wakeup_callback = nullptr;
// Signal that the mutex now has a waiting thread.
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
owner->AddMutexWaiter(thread);
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();

View File

@@ -5,7 +5,6 @@
#include <algorithm>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/config_mem.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
@@ -13,7 +12,6 @@
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/shared_page.h"
namespace Kernel {

View File

@@ -35,7 +35,7 @@ static constexpr u128 DEFAULT_USER_ID{1ull, 0ull};
class IProfile final : public ServiceFramework<IProfile> {
public:
IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
static const FunctionInfo functions[] = {
{0, nullptr, "Get"},
{1, &IProfile::GetBase, "GetBase"},

View File

@@ -19,7 +19,4 @@ private:
std::shared_ptr<Module> apm;
};
/// Registers all AM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Service::APM

View File

@@ -24,7 +24,8 @@ namespace Service::FileSystem {
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name) {
std::string_view dir_name_) {
std::string dir_name(FileUtil::SanitizePath(dir_name_));
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
return base;
@@ -38,7 +39,8 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
return backing->GetName();
}
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
auto file = dir->CreateFile(FileUtil::GetFilename(path));
if (file == nullptr) {
@@ -52,7 +54,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 s
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path == "/" || path == "\\") {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
@@ -60,14 +63,15 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const
}
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
dir = backing;
@@ -79,7 +83,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path)
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
@@ -88,7 +93,8 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path)
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
@@ -97,8 +103,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
const std::string& dest_path) const {
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
const std::string& dest_path_) const {
std::string src_path(FileUtil::SanitizePath(src_path_));
std::string dest_path(FileUtil::SanitizePath(dest_path_));
auto src = backing->GetFileRelative(src_path);
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
@@ -130,8 +138,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
const std::string& dest_path) const {
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
const std::string& dest_path_) const {
std::string src_path(FileUtil::SanitizePath(src_path_));
std::string dest_path(FileUtil::SanitizePath(dest_path_));
auto src = GetDirectoryRelativeWrapped(backing, src_path);
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
@@ -154,8 +164,9 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
return ResultCode(-1);
}
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
FileSys::Mode mode) const {
std::string path(FileUtil::SanitizePath(path_));
auto npath = path;
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
npath = npath.substr(1);
@@ -171,7 +182,8 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
return MakeResult<FileSys::VirtualFile>(file);
}
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
@@ -188,7 +200,8 @@ u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
}
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
const std::string& path) const {
const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
@@ -272,9 +285,9 @@ void RegisterFileSystems() {
sdmc_factory = nullptr;
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::Write);
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::Write);
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
save_data_factory = std::move(savedata);

View File

@@ -146,7 +146,7 @@ protected:
* @param max_sessions Maximum number of sessions that can be
* connected to this service at the same time.
*/
ServiceFramework(const char* service_name, u32 max_sessions = DefaultMaxSessions)
explicit ServiceFramework(const char* service_name, u32 max_sessions = DefaultMaxSessions)
: ServiceFrameworkBase(service_name, max_sessions, Invoker) {}
/// Registers handlers in the service.

View File

@@ -11,31 +11,40 @@
namespace Service::Set {
constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
LanguageCode::DE,
LanguageCode::IT,
LanguageCode::ES,
LanguageCode::ZH_CN,
LanguageCode::KO,
LanguageCode::NL,
LanguageCode::PT,
LanguageCode::RU,
LanguageCode::ZH_TW,
LanguageCode::EN_GB,
LanguageCode::FR_CA,
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
}};
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
static constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
LanguageCode::DE,
LanguageCode::IT,
LanguageCode::ES,
LanguageCode::ZH_CN,
LanguageCode::KO,
LanguageCode::NL,
LanguageCode::PT,
LanguageCode::RU,
LanguageCode::ZH_TW,
LanguageCode::EN_GB,
LanguageCode::FR_CA,
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
}};
ctx.WriteBuffer(available_language_codes);
IPC::ResponseBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(available_language_codes.size()));
rb.Push(static_cast<u32>(available_language_codes.size()));
LOG_DEBUG(Service_SET, "called");
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(available_language_codes.size()));
LOG_DEBUG(Service_SET, "called");
}
@@ -45,10 +54,10 @@ SET::SET() : ServiceFramework("set") {
{0, nullptr, "GetLanguageCode"},
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
{2, nullptr, "MakeLanguageCode"},
{3, nullptr, "GetAvailableLanguageCodeCount"},
{3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
{4, nullptr, "GetRegionCode"},
{5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"},
{6, nullptr, "GetAvailableLanguageCodeCount2"},
{6, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount2"},
{7, nullptr, "GetKeyCodeMap"},
{8, nullptr, "GetQuestFlag"},
};

View File

@@ -36,6 +36,7 @@ public:
private:
void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx);
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Set

View File

@@ -5,6 +5,8 @@
#include <algorithm>
#include <array>
#include <memory>
#include <type_traits>
#include <utility>
#include <boost/optional.hpp>
#include "common/alignment.h"
#include "common/math_util.h"
@@ -43,7 +45,9 @@ public:
template <typename T>
T Read() {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
ASSERT(read_index + sizeof(T) <= buffer.size());
T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
@@ -53,7 +57,9 @@ public:
template <typename T>
T ReadUnaligned() {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
ASSERT(read_index + sizeof(T) <= buffer.size());
T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
@@ -87,8 +93,12 @@ public:
template <typename T>
void Write(const T& val) {
if (buffer.size() < write_index + sizeof(T))
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
if (buffer.size() < write_index + sizeof(T)) {
buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
}
std::memcpy(buffer.data() + write_index, &val, sizeof(T));
write_index += sizeof(T);
write_index = Common::AlignUp(write_index, 4);
@@ -96,7 +106,9 @@ public:
template <typename T>
void WriteObject(const T& val) {
u32_le size = static_cast<u32>(sizeof(val));
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
const u32_le size = static_cast<u32>(sizeof(val));
Write(size);
// TODO(Subv): Support file descriptors.
Write<u32_le>(0); // Fd count.
@@ -176,7 +188,7 @@ private:
class IGBPConnectRequestParcel : public Parcel {
public:
explicit IGBPConnectRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
explicit IGBPConnectRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPConnectRequestParcel() override = default;
@@ -223,8 +235,8 @@ private:
class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
public:
explicit IGBPSetPreallocatedBufferRequestParcel(const std::vector<u8>& buffer)
: Parcel(buffer) {
explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
: Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPSetPreallocatedBufferRequestParcel() override = default;
@@ -256,7 +268,7 @@ protected:
class IGBPDequeueBufferRequestParcel : public Parcel {
public:
explicit IGBPDequeueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPDequeueBufferRequestParcel() override = default;
@@ -307,7 +319,7 @@ protected:
class IGBPRequestBufferRequestParcel : public Parcel {
public:
explicit IGBPRequestBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPRequestBufferRequestParcel() override = default;
@@ -322,8 +334,7 @@ public:
class IGBPRequestBufferResponseParcel : public Parcel {
public:
explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer)
: Parcel(), buffer(buffer) {}
explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer) : buffer(buffer) {}
~IGBPRequestBufferResponseParcel() override = default;
protected:
@@ -340,7 +351,7 @@ protected:
class IGBPQueueBufferRequestParcel : public Parcel {
public:
explicit IGBPQueueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPQueueBufferRequestParcel() override = default;
@@ -409,7 +420,7 @@ private:
class IGBPQueryRequestParcel : public Parcel {
public:
explicit IGBPQueryRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
explicit IGBPQueryRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
~IGBPQueryRequestParcel() override = default;

View File

@@ -1,86 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <chrono>
#include <cstring>
#include <ctime>
#include "core/core_timing.h"
#include "core/hle/shared_page.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace SharedPage {
SharedPageDef shared_page;
static CoreTiming::EventType* update_time_event;
/// Gets system time in 3DS format. The epoch is Jan 1900, and the unit is millisecond.
static u64 GetSystemTime() {
auto now = std::chrono::system_clock::now();
// 3DS system does't allow user to set a time before Jan 1 2000,
// so we use it as an auxiliary epoch to calculate the console time.
std::tm epoch_tm;
epoch_tm.tm_sec = 0;
epoch_tm.tm_min = 0;
epoch_tm.tm_hour = 0;
epoch_tm.tm_mday = 1;
epoch_tm.tm_mon = 0;
epoch_tm.tm_year = 100;
epoch_tm.tm_isdst = 0;
auto epoch = std::chrono::system_clock::from_time_t(std::mktime(&epoch_tm));
// 3DS console time uses Jan 1 1900 as internal epoch,
// so we use the milliseconds between 1900 and 2000 as base console time
u64 console_time = 3155673600000ULL;
// Only when system time is after 2000, we set it as 3DS system time
if (now > epoch) {
console_time += std::chrono::duration_cast<std::chrono::milliseconds>(now - epoch).count();
}
// If the system time is in daylight saving, we give an additional hour to console time
std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
std::tm* now_tm = std::localtime(&now_time_t);
if (now_tm && now_tm->tm_isdst > 0)
console_time += 60 * 60 * 1000;
return console_time;
}
static void UpdateTimeCallback(u64 userdata, int cycles_late) {
DateTime& date_time =
shared_page.date_time_counter % 2 ? shared_page.date_time_0 : shared_page.date_time_1;
date_time.date_time = GetSystemTime();
date_time.update_tick = CoreTiming::GetTicks();
date_time.tick_to_second_coefficient = CoreTiming::BASE_CLOCK_RATE;
date_time.tick_offset = 0;
++shared_page.date_time_counter;
// system time is updated hourly
CoreTiming::ScheduleEvent(CoreTiming::msToCycles(60 * 60 * 1000) - cycles_late,
update_time_event);
}
void Init() {
std::memset(&shared_page, 0, sizeof(shared_page));
shared_page.running_hw = 0x1; // product
// Some games wait until this value becomes 0x1, before asking running_hw
shared_page.unknown_value = 0x1;
// Set to a completely full battery
shared_page.battery_state.is_adapter_connected.Assign(1);
shared_page.battery_state.is_charging.Assign(1);
update_time_event =
CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback);
CoreTiming::ScheduleEvent(0, update_time_event);
}
} // namespace SharedPage

View File

@@ -1,69 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
/**
* The shared page stores various runtime configuration settings. This memory page is
* read-only for user processes (there is a bit in the header that grants the process
* write access, according to 3dbrew; this is not emulated)
*/
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace SharedPage {
// See http://3dbrew.org/wiki/Configuration_Memory#Shared_Memory_Page_For_ARM11_Processes
struct DateTime {
u64_le date_time; // 0
u64_le update_tick; // 8
u64_le tick_to_second_coefficient; // 10
u64_le tick_offset; // 18
};
static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong");
union BatteryState {
u8 raw;
BitField<0, 1, u8> is_adapter_connected;
BitField<1, 1, u8> is_charging;
BitField<2, 3, u8> charge_level;
};
struct SharedPageDef {
// Most of these names are taken from the 3dbrew page linked above.
u32_le date_time_counter; // 0
u8 running_hw; // 4
/// "Microcontroller hardware info"
u8 mcu_hw_info; // 5
INSERT_PADDING_BYTES(0x20 - 0x6); // 6
DateTime date_time_0; // 20
DateTime date_time_1; // 40
u8 wifi_macaddr[6]; // 60
u8 wifi_link_level; // 66
u8 wifi_unknown2; // 67
INSERT_PADDING_BYTES(0x80 - 0x68); // 68
float_le sliderstate_3d; // 80
u8 ledstate_3d; // 84
BatteryState battery_state; // 85
u8 unknown_value; // 86
INSERT_PADDING_BYTES(0xA0 - 0x87); // 87
u64_le menu_title_id; // A0
u64_le active_menu_title_id; // A8
INSERT_PADDING_BYTES(0x1000 - 0xB0); // B0
};
static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE,
"Shared page structure size is wrong");
extern SharedPageDef shared_page;
void Init();
} // namespace SharedPage

View File

@@ -190,7 +190,7 @@ private:
u32 entryPoint;
public:
ElfReader(void* ptr);
explicit ElfReader(void* ptr);
u32 Read32(int off) const {
return base32[off >> 2];

View File

@@ -49,8 +49,7 @@ struct Elf64_Sym {
static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
u64 relocation_offset, u64 size, bool is_jump_relocation,
VAddr load_base) {
u64 relocation_offset, u64 size, VAddr load_base) {
for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
Elf64_Rela rela;
std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
@@ -124,12 +123,11 @@ void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset
}
if (dynamic.find(DT_RELA) != dynamic.end()) {
WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], false,
load_base);
WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
}
if (dynamic.find(DT_JMPREL) != dynamic.end()) {
WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ], true,
WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
load_base);
}
}

View File

@@ -24,8 +24,7 @@ protected:
};
void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
u64 relocation_offset, u64 size, bool is_jump_relocation,
VAddr load_base);
u64 relocation_offset, u64 size, VAddr load_base);
void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
void ResolveImports();

View File

@@ -42,7 +42,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
}
FileType IdentifyFile(const std::string& file_name) {
return IdentifyFile(FileSys::VirtualFile(std::make_shared<FileSys::RealVfsFile>(file_name)));
return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name));
}
FileType GuessFromFilename(const std::string& name) {

View File

@@ -79,7 +79,7 @@ enum class ResultStatus {
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
explicit AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
virtual ~AppLoader() {}
/**

View File

@@ -6,10 +6,13 @@
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
@@ -49,7 +52,62 @@ struct ModHeader {
};
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
struct AssetSection {
u64_le offset;
u64_le size;
};
static_assert(sizeof(AssetSection) == 0x10, "AssetSection has incorrect size.");
struct AssetHeader {
u32_le magic;
u32_le format_version;
AssetSection icon;
AssetSection nacp;
AssetSection romfs;
};
static_assert(sizeof(AssetHeader) == 0x38, "AssetHeader has incorrect size.");
AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {
NroHeader nro_header{};
if (file->ReadObject(&nro_header) != sizeof(NroHeader)) {
return;
}
if (file->GetSize() >= nro_header.file_size + sizeof(AssetHeader)) {
const u64 offset = nro_header.file_size;
AssetHeader asset_header{};
if (file->ReadObject(&asset_header, offset) != sizeof(AssetHeader)) {
return;
}
if (asset_header.format_version != 0) {
LOG_WARNING(Loader,
"NRO Asset Header has format {}, currently supported format is 0. If "
"strange glitches occur with metadata, check NRO assets.",
asset_header.format_version);
}
if (asset_header.magic != Common::MakeMagic('A', 'S', 'E', 'T')) {
return;
}
if (asset_header.nacp.size > 0) {
nacp = std::make_unique<FileSys::NACP>(std::make_shared<FileSys::OffsetVfsFile>(
file, asset_header.nacp.size, offset + asset_header.nacp.offset, "Control.nacp"));
}
if (asset_header.romfs.size > 0) {
romfs = std::make_shared<FileSys::OffsetVfsFile>(
file, asset_header.romfs.size, offset + asset_header.romfs.offset, "game.romfs");
}
if (asset_header.icon.size > 0) {
icon_data = file->ReadBytes(asset_header.icon.size, offset + asset_header.icon.offset);
}
}
}
AppLoader_NRO::~AppLoader_NRO() = default;
FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
// Read NSO header
@@ -80,8 +138,9 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
if (program_image.size() != PageAlignSize(nro_header.file_size))
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
return {};
}
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
codeset->segments[i].addr = nro_header.segments[i].offset;
@@ -136,4 +195,39 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
return ResultStatus::Success;
}
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
if (icon_data.empty()) {
return ResultStatus::ErrorNotUsed;
}
buffer = icon_data;
return ResultStatus::Success;
}
ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
if (nacp == nullptr) {
return ResultStatus::ErrorNotUsed;
}
out_program_id = nacp->GetTitleId();
return ResultStatus::Success;
}
ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
if (romfs == nullptr) {
return ResultStatus::ErrorNotUsed;
}
dir = romfs;
return ResultStatus::Success;
}
ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
if (nacp == nullptr) {
return ResultStatus::ErrorNotUsed;
}
title = nacp->GetApplicationName();
return ResultStatus::Success;
}
} // namespace Loader

View File

@@ -10,12 +10,17 @@
#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace FileSys {
class NACP;
}
namespace Loader {
/// Loads an NRO file
class AppLoader_NRO final : public AppLoader, Linker {
public:
AppLoader_NRO(FileSys::VirtualFile file);
explicit AppLoader_NRO(FileSys::VirtualFile file);
~AppLoader_NRO() override;
/**
* Returns the type of the file
@@ -30,8 +35,17 @@ public:
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadTitle(std::string& title) override;
private:
bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
std::vector<u8> icon_data;
std::unique_ptr<FileSys::NACP> nacp;
FileSys::VirtualFile romfs;
};
} // namespace Loader

View File

@@ -32,7 +32,7 @@ public:
* Recorder constructor
* @param initial_state Initial recorder state
*/
Recorder(const InitialState& initial_state);
explicit Recorder(const InitialState& initial_state);
/// Finish recording of this Citrace and save it using the given filename.
void Finish(const std::string& filename);

View File

@@ -425,6 +425,7 @@ union Instruction {
union {
BitField<50, 3, u64> component_mask_selector;
BitField<0, 8, Register> gpr0;
BitField<28, 8, Register> gpr28;
bool HasTwoDestinations() const {
@@ -432,13 +433,16 @@ union Instruction {
}
bool IsComponentEnabled(size_t component) const {
static constexpr std::array<size_t, 5> one_dest_mask{0x1, 0x2, 0x4, 0x8, 0x3};
static constexpr std::array<size_t, 5> two_dest_mask{0x7, 0xb, 0xd, 0xe, 0xf};
const auto& mask{HasTwoDestinations() ? two_dest_mask : one_dest_mask};
static constexpr std::array<std::array<u32, 8>, 4> mask_lut{
{{},
{0x1, 0x2, 0x4, 0x8, 0x3},
{0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
{0x7, 0xb, 0xd, 0xe, 0xf}}};
ASSERT(component_mask_selector < mask.size());
size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U};
index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0;
return ((1ull << component) & mask[component_mask_selector]) != 0;
return ((1ull << component) & mask_lut[index][component_mask_selector]) != 0;
}
} texs;

View File

@@ -35,9 +35,11 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
case RenderTargetFormat::RGBA32_FLOAT:
return 16;
case RenderTargetFormat::RGBA16_FLOAT:
case RenderTargetFormat::RG32_FLOAT:
return 8;
case RenderTargetFormat::RGBA8_UNORM:
case RenderTargetFormat::RGB10_A2_UNORM:
case RenderTargetFormat::BGRA8_UNORM:
return 4;
default:
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));

View File

@@ -18,6 +18,8 @@ enum class RenderTargetFormat : u32 {
RGBA32_FLOAT = 0xC0,
RGBA32_UINT = 0xC2,
RGBA16_FLOAT = 0xCA,
RG32_FLOAT = 0xCB,
BGRA8_UNORM = 0xCF,
RGB10_A2_UNORM = 0xD1,
RGBA8_UNORM = 0xD5,
RGBA8_SRGB = 0xD6,

View File

@@ -601,7 +601,6 @@ void RasterizerOpenGL::SamplerInfo::Create() {
sampler.Create();
mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap;
border_color_r = border_color_g = border_color_b = border_color_a = 0;
// default is GL_LINEAR_MIPMAP_LINEAR
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -630,8 +629,12 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
}
if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) {
// TODO(Subv): Implement border color
ASSERT(false);
const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
config.border_color_b, config.border_color_a}};
if (border_color != new_border_color) {
border_color = new_border_color;
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
}
}
}

View File

@@ -77,10 +77,7 @@ private:
Tegra::Texture::TextureFilter min_filter;
Tegra::Texture::WrapMode wrap_u;
Tegra::Texture::WrapMode wrap_v;
u32 border_color_r;
u32 border_color_g;
u32 border_color_b;
u32 border_color_a;
GLvec4 border_color;
};
/// Configures the color and depth framebuffer states and returns the dirty <Color, Depth>

View File

@@ -38,7 +38,8 @@ struct FormatTuple {
params.addr = config.tic.Address();
params.is_tiled = config.tic.IsTiled();
params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format);
params.pixel_format =
PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value());
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
params.type = GetFormatType(params.pixel_format);
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
@@ -106,6 +107,12 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
true}, // BC7U
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
{GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F
{GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F
{GL_R32F, GL_RED, GL_FLOAT, ComponentType::Float, false}, // R32F
{GL_R16F, GL_RED, GL_HALF_FLOAT, ComponentType::Float, false}, // R16F
{GL_R16, GL_RED, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // R16UNORM
// DepthStencil formats
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
@@ -197,7 +204,10 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::Z24S8>,
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>,
MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>,
MortonCopy<true, PixelFormat::R32F>, MortonCopy<true, PixelFormat::R16F>,
MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::Z24S8>,
MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::Z16>,
};
@@ -213,7 +223,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
MortonCopy<false, PixelFormat::RGBA16F>,
MortonCopy<false, PixelFormat::R11FG11FB10F>,
MortonCopy<false, PixelFormat::RGBA32UI>,
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1/BC7U formats is not yet supported
// TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/BC7U/ASTC_2D_4X4 formats is not supported
nullptr,
nullptr,
nullptr,
@@ -221,6 +231,12 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
nullptr,
nullptr,
MortonCopy<false, PixelFormat::G8R8>,
MortonCopy<false, PixelFormat::BGRA8>,
MortonCopy<false, PixelFormat::RGBA32F>,
MortonCopy<false, PixelFormat::RG32F>,
MortonCopy<false, PixelFormat::R32F>,
MortonCopy<false, PixelFormat::R16F>,
MortonCopy<false, PixelFormat::R16UNORM>,
MortonCopy<false, PixelFormat::Z24S8>,
MortonCopy<false, PixelFormat::S8Z24>,
MortonCopy<false, PixelFormat::Z32F>,

View File

@@ -37,14 +37,20 @@ struct SurfaceParams {
BC7U = 12,
ASTC_2D_4X4 = 13,
G8R8 = 14,
BGRA8 = 15,
RGBA32F = 16,
RG32F = 17,
R32F = 18,
R16F = 19,
R16UNORM = 20,
MaxColorFormat,
// DepthStencil formats
Z24S8 = 15,
S8Z24 = 16,
Z32F = 17,
Z16 = 18,
Z24S8 = 21,
S8Z24 = 22,
Z32F = 23,
Z16 = 24,
MaxDepthStencilFormat,
@@ -97,6 +103,12 @@ struct SurfaceParams {
4, // BC7U
4, // ASTC_2D_4X4
1, // G8R8
1, // BGRA8
1, // RGBA32F
1, // RG32F
1, // R32F
1, // R16F
1, // R16UNORM
1, // Z24S8
1, // S8Z24
1, // Z32F
@@ -127,6 +139,12 @@ struct SurfaceParams {
128, // BC7U
32, // ASTC_2D_4X4
16, // G8R8
32, // BGRA8
128, // RGBA32F
64, // RG32F
32, // R32F
16, // R16F
16, // R16UNORM
32, // Z24S8
32, // S8Z24
32, // Z32F
@@ -162,10 +180,16 @@ struct SurfaceParams {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
case Tegra::RenderTargetFormat::RGBA8_SRGB:
return PixelFormat::ABGR8;
case Tegra::RenderTargetFormat::BGRA8_UNORM:
return PixelFormat::BGRA8;
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
return PixelFormat::A2B10G10R10;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
return PixelFormat::RGBA16F;
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
return PixelFormat::RGBA32F;
case Tegra::RenderTargetFormat::RG32_FLOAT:
return PixelFormat::RG32F;
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
return PixelFormat::R11FG11FB10F;
case Tegra::RenderTargetFormat::RGBA32_UINT:
@@ -176,7 +200,8 @@ struct SurfaceParams {
}
}
static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format) {
static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
Tegra::Texture::ComponentType component_type) {
// TODO(Subv): Properly implement this
switch (format) {
case Tegra::Texture::TextureFormat::A8R8G8B8:
@@ -196,7 +221,29 @@ struct SurfaceParams {
case Tegra::Texture::TextureFormat::BF10GF11RF11:
return PixelFormat::R11FG11FB10F;
case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
return PixelFormat::RGBA32UI;
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::RGBA32F;
case Tegra::Texture::ComponentType::UINT:
return PixelFormat::RGBA32UI;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32_G32:
return PixelFormat::RG32F;
case Tegra::Texture::TextureFormat::R16:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
return PixelFormat::R16F;
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::R16UNORM;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::R32:
return PixelFormat::R32F;
case Tegra::Texture::TextureFormat::DXT1:
return PixelFormat::DXT1;
case Tegra::Texture::TextureFormat::DXT23:
@@ -210,7 +257,8 @@ struct SurfaceParams {
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
return PixelFormat::ASTC_2D_4X4;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}",
static_cast<u32>(format), static_cast<u32>(component_type));
UNREACHABLE();
}
}
@@ -248,7 +296,21 @@ struct SurfaceParams {
return Tegra::Texture::TextureFormat::BC7U;
case PixelFormat::ASTC_2D_4X4:
return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
case PixelFormat::BGRA8:
// TODO(bunnei): This is fine for unswizzling (since we just need the right component
// sizes), but could be a bug if we used this function in different ways.
return Tegra::Texture::TextureFormat::A8R8G8B8;
case PixelFormat::RGBA32F:
return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
case PixelFormat::RG32F:
return Tegra::Texture::TextureFormat::R32_G32;
case PixelFormat::R32F:
return Tegra::Texture::TextureFormat::R32;
case PixelFormat::R16F:
case PixelFormat::R16UNORM:
return Tegra::Texture::TextureFormat::R16;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
@@ -264,6 +326,7 @@ struct SurfaceParams {
case PixelFormat::Z16:
return Tegra::DepthFormat::Z16_UNORM;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
@@ -273,6 +336,8 @@ struct SurfaceParams {
switch (type) {
case Tegra::Texture::ComponentType::UNORM:
return ComponentType::UNorm;
case Tegra::Texture::ComponentType::FLOAT:
return ComponentType::Float;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
UNREACHABLE();
@@ -284,10 +349,13 @@ struct SurfaceParams {
switch (format) {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
case Tegra::RenderTargetFormat::RGBA8_SRGB:
case Tegra::RenderTargetFormat::BGRA8_UNORM:
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
return ComponentType::UNorm;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
case Tegra::RenderTargetFormat::RG32_FLOAT:
return ComponentType::Float;
case Tegra::RenderTargetFormat::RGBA32_UINT:
return ComponentType::UInt;

View File

@@ -6,6 +6,9 @@
#include <set>
#include <string>
#include <string_view>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/engines/shader_bytecode.h"
@@ -485,6 +488,12 @@ private:
*/
void SetRegister(const Register& reg, u64 elem, const std::string& value,
u64 dest_num_components, u64 value_num_components, u64 dest_elem) {
if (reg == Register::ZeroIndex) {
LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex");
UNREACHABLE();
return;
}
std::string dest = GetRegister(reg, static_cast<u32>(dest_elem));
if (dest_num_components > 1) {
dest += GetSwizzle(elem);
@@ -747,6 +756,38 @@ private:
}
}
void WriteTexsInstruction(const Instruction& instr, const std::string& coord,
const std::string& texture) {
// Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine('{');
++shader.scope;
shader.AddLine(coord);
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
// goes into gpr28+0 and gpr28+1
size_t texs_offset{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
for (unsigned elem = 0; elem < 2; ++elem) {
if (!instr.texs.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem);
}
if (!instr.texs.HasTwoDestinations()) {
// Skip the second destination
break;
}
texs_offset += 2;
}
--shader.scope;
shader.AddLine('}');
}
/**
* Compiles a single instruction from Tegra to GLSL.
* @param offset the offset of the Tegra shader instruction.
@@ -769,7 +810,8 @@ private:
return offset + 1;
}
shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName());
shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" +
std::to_string(instr.value) + ')');
using Tegra::Shader::Pred;
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -1345,36 +1387,18 @@ private:
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
const std::string sampler = GetSampler(instr.sampler);
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
// Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{");
++shader.scope;
shader.AddLine(coord);
const std::string texture = "texture(" + sampler + ", coords)";
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
// goes into gpr28+0 and gpr28+1
size_t texs_offset{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
for (unsigned elem = 0; elem < 2; ++elem) {
if (!instr.texs.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
elem);
}
if (!instr.texs.HasTwoDestinations()) {
// Skip the second destination
break;
}
texs_offset += 2;
}
--shader.scope;
shader.AddLine("}");
WriteTexsInstruction(instr, coord, texture);
break;
}
case OpCode::Id::TLDS: {
const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20);
const std::string sampler = GetSampler(instr.sampler);
const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");";
const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
WriteTexsInstruction(instr, coord, texture);
break;
}
default: {
@@ -1774,11 +1798,8 @@ private:
}; // namespace Decompiler
std::string GetCommonDeclarations() {
std::string declarations;
declarations += "#define MAX_CONSTBUFFER_ELEMENTS " +
std::to_string(RasterizerOpenGL::MaxConstbufferSize / (sizeof(GLvec4)));
declarations += '\n';
return declarations;
return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",
RasterizerOpenGL::MaxConstbufferSize / sizeof(GLvec4));
}
boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,

View File

@@ -56,6 +56,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
return {};
}
case Maxwell::VertexAttribute::Type::UnsignedInt:
return GL_UNSIGNED_INT;
case Maxwell::VertexAttribute::Type::Float:
return GL_FLOAT;
}
@@ -112,6 +115,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
return GL_MIRRORED_REPEAT;
case Tegra::Texture::WrapMode::ClampToEdge:
return GL_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::Border:
return GL_CLAMP_TO_BORDER;
case Tegra::Texture::WrapMode::ClampOGL:
// TODO(Subv): GL_CLAMP was removed as of OpenGL 3.1, to implement GL_CLAMP, we can use
// GL_CLAMP_TO_BORDER to get the border color of the texture, and then sample the edge to

View File

@@ -61,10 +61,12 @@ u32 BytesPerPixel(TextureFormat format) {
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::BF10GF11RF11:
case TextureFormat::R32:
return 4;
case TextureFormat::A1B5G5R5:
case TextureFormat::B5G6R5:
case TextureFormat::G8R8:
case TextureFormat::R16:
return 2;
case TextureFormat::R8:
return 1;
@@ -72,6 +74,8 @@ u32 BytesPerPixel(TextureFormat format) {
return 8;
case TextureFormat::R32_G32_B32_A32:
return 16;
case TextureFormat::R32_G32:
return 8;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
@@ -118,6 +122,9 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
case TextureFormat::G8R8:
case TextureFormat::R16_G16_B16_A16:
case TextureFormat::R32_G32_B32_A32:
case TextureFormat::R32_G32:
case TextureFormat::R32:
case TextureFormat::R16:
case TextureFormat::BF10GF11RF11:
case TextureFormat::ASTC_2D_4X4:
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
@@ -174,6 +181,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::G8R8:
case TextureFormat::BF10GF11RF11:
case TextureFormat::R32_G32_B32_A32:
case TextureFormat::R32_G32:
case TextureFormat::R32:
case TextureFormat::R16:
// TODO(Subv): For the time being just forward the same data without any decoding.
rgba_data = texture_data;
break;

View File

@@ -242,10 +242,10 @@ struct TSCEntry {
BitField<6, 2, TextureMipmapFilter> mip_filter;
};
INSERT_PADDING_BYTES(8);
u32 border_color_r;
u32 border_color_g;
u32 border_color_b;
u32 border_color_a;
float border_color_r;
float border_color_g;
float border_color_b;
float border_color_a;
};
static_assert(sizeof(TSCEntry) == 0x20, "TSCEntry has wrong size");