diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30553e1d..68c0beb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: lint-result.html - path: app/build/reports/lint-results.html + path: app/build/reports/lint-results-debug.html - name: Android Assemble run: ./gradlew --stacktrace assemble diff --git a/app/build.gradle b/app/build.gradle index 65699aba..500d6a5c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -102,7 +102,7 @@ dependencies { kapt "com.google.dagger:hilt-android-compiler:$hilt_version" implementation 'com.google.android.flexbox:flexbox:3.0.0' - /* Kotlin */ + /* JetBrains */ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" /* Other Java */ diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 1d044059..cca42ab0 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -5,529 +5,21 @@ #include #include -#include #include #include -#include #include #include #include #include #include #include -#include -#include -#include #include #include -#include -#include -#include -#include -#include -#include #include -#include - -#define FORCE_INLINE __attribute__((always_inline)) // NOLINT(cppcoreguidelines-macro-usage) - -namespace fmt { - /** - * @brief A std::bitset formatter for {fmt} - */ - template - struct formatter> : formatter { - template - constexpr auto format(const std::bitset &s, FormatContext &ctx) { - return formatter::format(s.to_string(), ctx); - } - }; -} +#include +#include namespace skyline { - using u128 = __uint128_t; //!< Unsigned 128-bit integer - using u64 = __uint64_t; //!< Unsigned 64-bit integer - using u32 = __uint32_t; //!< Unsigned 32-bit integer - using u16 = __uint16_t; //!< Unsigned 16-bit integer - using u8 = __uint8_t; //!< Unsigned 8-bit integer - using i128 = __int128_t; //!< Signed 128-bit integer - using i64 = __int64_t; //!< Signed 64-bit integer - using i32 = __int32_t; //!< Signed 32-bit integer - using i16 = __int16_t; //!< Signed 16-bit integer - using i8 = __int8_t; //!< Signed 8-bit integer - - using KHandle = u32; //!< The type of a kernel handle - namespace frz = frozen; - - /** - * @brief The result of an operation in HOS - * @url https://switchbrew.org/wiki/Error_codes - */ - union Result { - u32 raw{}; - struct __attribute__((packed)) { - u16 module : 9; - u16 id : 12; - }; - - /** - * @note Success is 0, it's the only result that's not specific to a module - */ - constexpr Result() = default; - - constexpr explicit Result(u16 module, u16 id) : module(module), id(id) {} - - constexpr operator u32() const { - return raw; - } - }; - - /** - * @brief A wrapper around std::optional that also stores a HOS result code - */ - template - class ResultValue { - static_assert(!std::is_same::value); - - private: - std::optional value; - - public: - ResultType result{}; - - constexpr ResultValue(ValueType value) : value(value) {}; - - constexpr ResultValue(ResultType result) : result(result) {}; - - template - constexpr ResultValue(ResultValue result) : result(result) {}; - - constexpr operator ResultType() const { - return result; - } - - explicit constexpr operator bool() const { - return value.has_value(); - } - - constexpr ValueType& operator*() { - return *value; - } - - constexpr ValueType* operator->() { - return &*value; - } - }; - - namespace constant { - // Time - constexpr u64 NsInMicrosecond{1000}; //!< The amount of nanoseconds in a microsecond - constexpr u64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second - constexpr u64 NsInMillisecond{1000000}; //!< The amount of nanoseconds in a millisecond - constexpr u64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day - } - - namespace util { - /** - * @brief A way to implicitly cast all pointers to uintptr_t, this is used for {fmt} as we use 0x{:X} to print pointers - * @note There's the exception of signed char pointers as they represent C Strings - * @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly casted to uintptr_t or passed through fmt::ptr - */ - template - constexpr auto FmtCast(T object) { - if constexpr (std::is_pointer::value) - if constexpr (std::is_same::type>::type>::value) - return reinterpret_cast::type>(object); - else - return reinterpret_cast(object); - else - return object; - } - - /** - * @brief {fmt}::format but with FmtCast built into it - */ - template - auto Format(S formatString, Args &&... args) { - return fmt::format(fmt::runtime(formatString), FmtCast(args)...); - } - } - - /** - * @brief A wrapper over std::runtime_error with {fmt} formatting - */ - class exception : public std::runtime_error { - public: - template - exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(fmt::runtime(formatStr), util::FmtCast(args)...)) {} - }; - - namespace util { - /** - * @brief Returns the current time in nanoseconds - * @return The current time in nanoseconds - */ - inline u64 GetTimeNs() { - u64 frequency; - asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency)); - u64 ticks; - asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); - return ((ticks / frequency) * constant::NsInSecond) + (((ticks % frequency) * constant::NsInSecond + (frequency / 2)) / frequency); - } - - /** - * @brief Returns the current time in arbitrary ticks - * @return The current time in ticks - */ - inline u64 GetTimeTicks() { - u64 ticks; - asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); - return ticks; - } - - /** - * @brief A way to implicitly convert a pointer to uintptr_t and leave it unaffected if it isn't a pointer - */ - template - constexpr T PointerValue(T item) { - return item; - } - - template - uintptr_t PointerValue(T *item) { - return reinterpret_cast(item); - } - - /** - * @brief A way to implicitly convert an integral to a pointer, if the return type is a pointer - */ - template - constexpr Return ValuePointer(T item) { - if constexpr (std::is_pointer::value) - return reinterpret_cast(item); - else - return item; - } - - /** - * @return The value aligned up to the next multiple - * @note The multiple needs to be a power of 2 - */ - template - constexpr TypeVal AlignUp(TypeVal value, TypeMul multiple) { - multiple--; - return ValuePointer((PointerValue(value) + multiple) & ~(multiple)); - } - - /** - * @return The value aligned down to the previous multiple - * @note The multiple needs to be a power of 2 - */ - template - constexpr TypeVal AlignDown(TypeVal value, TypeMul multiple) { - return ValuePointer(PointerValue(value) & ~(multiple - 1)); - } - - /** - * @return If the address is aligned with the multiple - */ - template - constexpr bool IsAligned(TypeVal value, TypeMul multiple) { - if ((multiple & (multiple - 1)) == 0) - return !(PointerValue(value) & (multiple - 1U)); - else - return (PointerValue(value) % multiple) == 0; - } - - /** - * @return If the value is page aligned - */ - template - constexpr bool PageAligned(TypeVal value) { - return IsAligned(value, PAGE_SIZE); - } - - /** - * @return If the value is word aligned - */ - template - constexpr bool WordAligned(TypeVal value) { - return IsAligned(value, WORD_BIT / 8); - } - - /** - * @param string The string to create a magic from - * @return The magic of the supplied string - */ - template - constexpr Type MakeMagic(std::string_view string) { - Type object{}; - size_t offset{}; - - for (auto &character : string) { - object |= static_cast(character) << offset; - offset += sizeof(character) * 8; - } - - return object; - } - - constexpr u8 HexDigitToNibble(char digit) { - if (digit >= '0' && digit <= '9') - return digit - '0'; - else if (digit >= 'a' && digit <= 'f') - return digit - 'a' + 10; - else if (digit >= 'A' && digit <= 'F') - return digit - 'A' + 10; - throw exception("Invalid hex character: '{}'", digit); - } - - template - constexpr std::array HexStringToArray(std::string_view string) { - if (string.size() != Size * 2) - throw exception("String size: {} (Expected {})", string.size(), Size); - std::array result; - for (size_t i{}; i < Size; i++) { - size_t index{i * 2}; - result[i] = (HexDigitToNibble(string[index]) << 4) | HexDigitToNibble(string[index + 1]); - } - return result; - } - - template - constexpr Type HexStringToInt(std::string_view string) { - if (string.size() > sizeof(Type) * 2) - throw exception("String size larger than type: {} (sizeof(Type): {})", string.size(), sizeof(Type)); - Type result{}; - size_t offset{(sizeof(Type) * 8) - 4}; - for (size_t index{}; index < string.size(); index++, offset -= 4) { - char digit{string[index]}; - if (digit >= '0' && digit <= '9') - result |= static_cast(digit - '0') << offset; - else if (digit >= 'a' && digit <= 'f') - result |= static_cast(digit - 'a' + 10) << offset; - else if (digit >= 'A' && digit <= 'F') - result |= static_cast(digit - 'A' + 10) << offset; - else - break; - } - return result >> (offset + 4); - } - - template - constexpr std::array SwapEndianness(std::array in) { - std::reverse(in.begin(), in.end()); - return in; - } - - constexpr u64 SwapEndianness(u64 in) { - return __builtin_bswap64(in); - } - - constexpr u32 SwapEndianness(u32 in) { - return __builtin_bswap32(in); - } - - constexpr u16 SwapEndianness(u16 in) { - return __builtin_bswap16(in); - } - - /** - * @brief A compile-time hash function as std::hash isn't constexpr - */ - constexpr std::size_t Hash(std::string_view view) { - return frz::elsa{}(frz::string(view.data(), view.size()), 0); - } - - /** - * @brief Selects the largest possible integer type for representing an object alongside providing the size of the object in terms of the underlying type - */ - template - struct IntegerFor { - using type = std::conditional_t - > - >; - - static constexpr size_t count{sizeof(T) / sizeof(type)}; - }; - - namespace detail { - static thread_local std::mt19937_64 generator{GetTimeTicks()}; - } - - /** - * @brief Fills an array with random data from a Mersenne Twister pseudo-random generator - * @note The generator is seeded with the the current time in ticks - */ - template requires (std::is_integral_v) - void FillRandomBytes(std::span in) { - std::independent_bits_engine::digits, T> gen(detail::generator); - std::generate(in.begin(), in.end(), gen); - } - - template requires (!std::is_integral_v && std::is_trivially_copyable_v) - void FillRandomBytes(T &object) { - FillRandomBytes(std::span(reinterpret_cast::type *>(&object), IntegerFor::count)); - } - - /** - * @brief A temporary shim for C++ 20's bit_cast to make transitioning to it easier - */ - template - To BitCast(const From& from) { - return *reinterpret_cast(&from); - } - } - - /** - * @brief A custom wrapper over span that adds several useful methods to it - * @note This class is completely transparent, it implicitly converts from and to span - */ - template - class span : public std::span { - public: - using std::span::span; - using std::span::operator=; - - typedef typename std::span::element_type element_type; - typedef typename std::span::size_type size_type; - - constexpr span(const std::span &spn) : std::span(spn) {} - - /** - * @brief A single-element constructor for a span - */ - constexpr span(T &spn) : std::span(&spn, 1) {} - - /** - * @brief We want to support implicitly casting from std::string_view -> span as it's just a specialization of a data view which span is a generic form of, the opposite doesn't hold true as not all data held by a span is string data therefore the conversion isn't implicit there - */ - template - constexpr span(const std::basic_string_view &string) : std::span(const_cast(string.data()), string.size()) {} - - template - constexpr Out &as() { - if constexpr (Extent != std::dynamic_extent && sizeof(T) * Extent >= sizeof(Out)) - return *reinterpret_cast(span::data()); - - if (span::size_bytes() >= sizeof(Out)) - return *reinterpret_cast(span::data()); - throw exception("Span size is less than Out type size (0x{:X}/0x{:X})", span::size_bytes(), sizeof(Out)); - } - - /** - * @param nullTerminated If true and the string is null-terminated, a view of it will be returned (not including the null terminator itself), otherwise the entire span will be returned as a string view - */ - constexpr std::string_view as_string(bool nullTerminated = false) { - return std::string_view(reinterpret_cast(span::data()), nullTerminated ? (std::find(span::begin(), span::end(), 0) - span::begin()) : span::size_bytes()); - } - - template - constexpr span cast() { - if (SkipAlignmentCheck || util::IsAligned(span::size_bytes(), sizeof(Out))) - return span(reinterpret_cast(span::data()), span::size_bytes() / sizeof(Out)); - throw exception("Span size not aligned with Out type size (0x{:X}/0x{:X})", span::size_bytes(), sizeof(Out)); - } - - /** - * @brief Copies data from the supplied span into this one - * @param amount The amount of elements that need to be copied (in terms of the supplied span), 0 will try to copy the entirety of the other span - */ - template - constexpr void copy_from(const span spn, size_type amount = 0) { - auto size{amount ? amount * sizeof(In) : spn.size_bytes()}; - if (span::size_bytes() < size) - throw exception("Data being copied is larger than this span"); - std::memmove(span::data(), spn.data(), size); - } - - /** - * @brief Implicit type conversion for copy_from, this allows passing in std::vector/std::array in directly is automatically passed by reference which is important for any containers - */ - template - constexpr void copy_from(const In &in, size_type amount = 0) { - copy_from(span::type>(in), amount); - } - - /** - * @return If a supplied span is located entirely inside this span and is effectively a subspan - */ - constexpr bool contains(const span& other) const { - return this->begin() <= other.begin() && this->end() >= other.end(); - } - - /** Comparision operators for equality and binary searches **/ - - constexpr bool operator==(const span& other) const { - return this->data() == other.data() && this->size() == other.size(); - } - - constexpr bool operator<(const span &other) const { - return this->data() < other.data(); - } - - constexpr bool operator<(T* pointer) const { - return this->data() < pointer; - } - - constexpr bool operator<(typename std::span::const_iterator it) const { - return this->begin() < it; - } - - /** Base Class Functions that return an instance of it, we upcast them **/ - template - constexpr span first() const noexcept { - return std::span::template first(); - } - - template - constexpr span last() const noexcept { - return std::span::template last(); - } - - constexpr span first(size_type count) const noexcept { - return std::span::first(count); - } - - constexpr span last(size_type count) const noexcept { - return std::span::last(count); - } - - template - constexpr auto subspan() const noexcept -> span { - return std::span::template subspan(); - } - - constexpr span subspan(size_type offset, size_type count = std::dynamic_extent) const noexcept { - return std::span::subspan(offset, count); - } - }; - - /** - * @brief Deduction guides required for arguments to span, CTAD will fail for iterators, arrays and containers without this - */ - template - span(It, End) -> span::value_type, Extent>; - template - span(T (&)[Size]) -> span; - template - span(std::array &) -> span; - template - span(const std::array &) -> span; - template - span(Container &) -> span; - template - span(const Container &) -> span; - - /** - * @brief A deduction guide for overloads required for std::visit with std::variant - */ - template - struct VariantVisitor : Ts ... { using Ts::operator()...; }; - template VariantVisitor(Ts...) -> VariantVisitor; - /** * @brief A wrapper around writing logs into a log file and logcat using Android Log APIs */ @@ -586,91 +78,91 @@ namespace skyline { template void Error(FunctionString formatString, Args &&... args) { if (LogLevel::Error <= configLevel) - Write(LogLevel::Error, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Error, util::Format(*formatString, args...)); } template void Error(FunctionString formatString, Args &&... args) { if (LogLevel::Error <= configLevel) - Write(LogLevel::Error, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Error, util::Format(*formatString, args...)); } template void ErrorNoPrefix(S formatString, Args &&... args) { if (LogLevel::Error <= configLevel) - Write(LogLevel::Error, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...)); + Write(LogLevel::Error, util::Format(formatString, args...)); } template void Warn(FunctionString formatString, Args &&... args) { if (LogLevel::Warn <= configLevel) - Write(LogLevel::Warn, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Warn, util::Format(*formatString, args...)); } template void Warn(FunctionString formatString, Args &&... args) { if (LogLevel::Warn <= configLevel) - Write(LogLevel::Warn, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Warn, util::Format(*formatString, args...)); } template void WarnNoPrefix(S formatString, Args &&... args) { if (LogLevel::Warn <= configLevel) - Write(LogLevel::Warn, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...)); + Write(LogLevel::Warn, util::Format(formatString, args...)); } template void Info(FunctionString formatString, Args &&... args) { if (LogLevel::Info <= configLevel) - Write(LogLevel::Info, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Info, util::Format(*formatString, args...)); } template void Info(FunctionString formatString, Args &&... args) { if (LogLevel::Info <= configLevel) - Write(LogLevel::Info, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Info, util::Format(*formatString, args...)); } template void InfoNoPrefix(S formatString, Args &&... args) { if (LogLevel::Info <= configLevel) - Write(LogLevel::Info, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...)); + Write(LogLevel::Info, util::Format(formatString, args...)); } template void Debug(FunctionString formatString, Args &&... args) { if (LogLevel::Debug <= configLevel) - Write(LogLevel::Debug, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Debug, util::Format(*formatString, args...)); } template void Debug(FunctionString formatString, Args &&... args) { if (LogLevel::Debug <= configLevel) - Write(LogLevel::Debug, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Debug, util::Format(*formatString, args...)); } template void DebugNoPrefix(S formatString, Args &&... args) { if (LogLevel::Debug <= configLevel) - Write(LogLevel::Debug, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...)); + Write(LogLevel::Debug, util::Format(formatString, args...)); } template void Verbose(FunctionString formatString, Args &&... args) { if (LogLevel::Verbose <= configLevel) - Write(LogLevel::Verbose, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Verbose, util::Format(*formatString, args...)); } template void Verbose(FunctionString formatString, Args &&... args) { if (LogLevel::Verbose <= configLevel) - Write(LogLevel::Verbose, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...)); + Write(LogLevel::Verbose, util::Format(*formatString, args...)); } template void VerboseNoPrefix(S formatString, Args &&... args) { if (LogLevel::Verbose <= configLevel) - Write(LogLevel::Verbose, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...)); + Write(LogLevel::Verbose, util::Format(formatString, args...)); } }; diff --git a/app/src/main/cpp/skyline/common/base.h b/app/src/main/cpp/skyline/common/base.h new file mode 100644 index 00000000..d60a45eb --- /dev/null +++ b/app/src/main/cpp/skyline/common/base.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include +#include + +namespace fmt { + /** + * @brief A std::bitset formatter for {fmt} + */ + template + struct formatter> : formatter { + template + constexpr auto format(const std::bitset &s, FormatContext &ctx) { + return formatter::format(s.to_string(), ctx); + } + }; +} + +namespace skyline { + using u128 = __uint128_t; //!< Unsigned 128-bit integer + using u64 = __uint64_t; //!< Unsigned 64-bit integer + using u32 = __uint32_t; //!< Unsigned 32-bit integer + using u16 = __uint16_t; //!< Unsigned 16-bit integer + using u8 = __uint8_t; //!< Unsigned 8-bit integer + using i128 = __int128_t; //!< Signed 128-bit integer + using i64 = __int64_t; //!< Signed 64-bit integer + using i32 = __int32_t; //!< Signed 32-bit integer + using i16 = __int16_t; //!< Signed 16-bit integer + using i8 = __int8_t; //!< Signed 8-bit integer + + using KHandle = u32; //!< The type of a kernel handle + + namespace constant { + // Time + constexpr u64 NsInMicrosecond{1000}; //!< The amount of nanoseconds in a microsecond + constexpr u64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second + constexpr u64 NsInMillisecond{1000000}; //!< The amount of nanoseconds in a millisecond + constexpr u64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day + } + + namespace util { + /** + * @brief A way to implicitly cast all pointers to uintptr_t, this is used for {fmt} as we use 0x{:X} to print pointers + * @note There's the exception of signed char pointers as they represent C Strings + * @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly casted to uintptr_t or passed through fmt::ptr + */ + template + constexpr auto FmtCast(T object) { + if constexpr (std::is_pointer::value) + if constexpr (std::is_same::type>::type>::value) + return reinterpret_cast::type>(object); + else + return reinterpret_cast(object); + else + return object; + } + + /** + * @brief {fmt}::format but with FmtCast built into it + */ + template + constexpr auto Format(S formatString, Args &&... args) { + return fmt::format(fmt::runtime(formatString), FmtCast(args)...); + } + } + + /** + * @brief A wrapper over std::runtime_error with {fmt} formatting + */ + class exception : public std::runtime_error { + public: + template + exception(const S &formatStr, Args &&... args) : runtime_error(util::Format(formatStr, args...)) {} + }; + + /** + * @brief A deduction guide for overloads required for std::visit with std::variant + */ + template + struct VariantVisitor : Ts ... { using Ts::operator()...; }; + template VariantVisitor(Ts...) -> VariantVisitor; +} diff --git a/app/src/main/cpp/skyline/common/result.h b/app/src/main/cpp/skyline/common/result.h new file mode 100644 index 00000000..1e2f30a5 --- /dev/null +++ b/app/src/main/cpp/skyline/common/result.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "base.h" + +namespace skyline { + /** + * @brief The result of an operation in HOS + * @url https://switchbrew.org/wiki/Error_codes + */ + union Result { + u32 raw{}; + struct __attribute__((packed)) { + u16 module : 9; + u16 id : 12; + }; + + /** + * @note Success is 0, it's the only result that's not specific to a module + */ + constexpr Result() = default; + + constexpr explicit Result(u16 module, u16 id) : module(module), id(id) {} + + constexpr operator u32() const { + return raw; + } + }; + + /** + * @brief A wrapper around std::optional that also stores a HOS result code + */ + template + class ResultValue { + static_assert(!std::is_same::value); + + private: + std::optional value; + + public: + ResultType result{}; + + constexpr ResultValue(ValueType value) : value(value) {}; + + constexpr ResultValue(ResultType result) : result(result) {}; + + template + constexpr ResultValue(ResultValue result) : result(result) {}; + + constexpr operator ResultType() const { + return result; + } + + explicit constexpr operator bool() const { + return value.has_value(); + } + + constexpr ValueType &operator*() { + return *value; + } + + constexpr ValueType *operator->() { + return &*value; + } + }; +} diff --git a/app/src/main/cpp/skyline/common/span.h b/app/src/main/cpp/skyline/common/span.h new file mode 100644 index 00000000..67536b17 --- /dev/null +++ b/app/src/main/cpp/skyline/common/span.h @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include "utils.h" + +namespace skyline { + /** + * @brief A custom wrapper over span that adds several useful methods to it + * @note This class is completely transparent, it implicitly converts from and to span + */ + template + class span : public std::span { + public: + using std::span::span; + using std::span::operator=; + + typedef typename std::span::element_type element_type; + typedef typename std::span::size_type size_type; + + constexpr span(const std::span &spn) : std::span(spn) {} + + /** + * @brief A single-element constructor for a span + */ + constexpr span(T &spn) : std::span(&spn, 1) {} + + /** + * @brief We want to support implicitly casting from std::string_view -> span as it's just a specialization of a data view which span is a generic form of, the opposite doesn't hold true as not all data held by a span is string data therefore the conversion isn't implicit there + */ + template + constexpr span(const std::basic_string_view &string) : std::span(const_cast(string.data()), string.size()) {} + + template + constexpr Out &as() { + if constexpr (Extent != std::dynamic_extent && sizeof(T) * Extent >= sizeof(Out)) + return *reinterpret_cast(span::data()); + + if (span::size_bytes() >= sizeof(Out)) + return *reinterpret_cast(span::data()); + throw exception("Span size is less than Out type size (0x{:X}/0x{:X})", span::size_bytes(), sizeof(Out)); + } + + /** + * @param nullTerminated If true and the string is null-terminated, a view of it will be returned (not including the null terminator itself), otherwise the entire span will be returned as a string view + */ + constexpr std::string_view as_string(bool nullTerminated = false) { + return std::string_view(reinterpret_cast(span::data()), nullTerminated ? (std::find(span::begin(), span::end(), 0) - span::begin()) : span::size_bytes()); + } + + template + constexpr span cast() { + if (SkipAlignmentCheck || util::IsAligned(span::size_bytes(), sizeof(Out))) + return span(reinterpret_cast(span::data()), span::size_bytes() / sizeof(Out)); + throw exception("Span size not aligned with Out type size (0x{:X}/0x{:X})", span::size_bytes(), sizeof(Out)); + } + + /** + * @brief Copies data from the supplied span into this one + * @param amount The amount of elements that need to be copied (in terms of the supplied span), 0 will try to copy the entirety of the other span + */ + template + constexpr void copy_from(const span spn, size_type amount = 0) { + auto size{amount ? amount * sizeof(In) : spn.size_bytes()}; + if (span::size_bytes() < size) + throw exception("Data being copied is larger than this span"); + std::memmove(span::data(), spn.data(), size); + } + + /** + * @brief Implicit type conversion for copy_from, this allows passing in std::vector/std::array in directly is automatically passed by reference which is important for any containers + */ + template + constexpr void copy_from(const In &in, size_type amount = 0) { + copy_from(span::type>(in), amount); + } + + /** + * @return If a supplied span is located entirely inside this span and is effectively a subspan + */ + constexpr bool contains(const span& other) const { + return this->begin() <= other.begin() && this->end() >= other.end(); + } + + /** Comparision operators for equality and binary searches **/ + + constexpr bool operator==(const span& other) const { + return this->data() == other.data() && this->size() == other.size(); + } + + constexpr bool operator<(const span &other) const { + return this->data() < other.data(); + } + + constexpr bool operator<(T* pointer) const { + return this->data() < pointer; + } + + constexpr bool operator<(typename std::span::const_iterator it) const { + return this->begin() < it; + } + + /** Base Class Functions that return an instance of it, we upcast them **/ + template + constexpr span first() const noexcept { + return std::span::template first(); + } + + template + constexpr span last() const noexcept { + return std::span::template last(); + } + + constexpr span first(size_type count) const noexcept { + return std::span::first(count); + } + + constexpr span last(size_type count) const noexcept { + return std::span::last(count); + } + + template + constexpr auto subspan() const noexcept -> span { + return std::span::template subspan(); + } + + constexpr span subspan(size_type offset, size_type count = std::dynamic_extent) const noexcept { + return std::span::subspan(offset, count); + } + }; + + /** + * @brief Deduction guides required for arguments to span, CTAD will fail for iterators, arrays and containers without this + */ + template + span(It, End) -> span::value_type, Extent>; + template + span(T (&)[Size]) -> span; + template + span(std::array &) -> span; + template + span(const std::array &) -> span; + template + span(Container &) -> span; + template + span(const Container &) -> span; +} diff --git a/app/src/main/cpp/skyline/common/utils.h b/app/src/main/cpp/skyline/common/utils.h new file mode 100644 index 00000000..24da688e --- /dev/null +++ b/app/src/main/cpp/skyline/common/utils.h @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include +#include "base.h" + +namespace skyline::util { + /** + * @brief Returns the current time in nanoseconds + * @return The current time in nanoseconds + */ + inline u64 GetTimeNs() { + u64 frequency; + asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency)); + u64 ticks; + asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); + return ((ticks / frequency) * constant::NsInSecond) + (((ticks % frequency) * constant::NsInSecond + (frequency / 2)) / frequency); + } + + /** + * @brief Returns the current time in arbitrary ticks + * @return The current time in ticks + */ + inline u64 GetTimeTicks() { + u64 ticks; + asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); + return ticks; + } + + /** + * @brief A way to implicitly convert a pointer to uintptr_t and leave it unaffected if it isn't a pointer + */ + template + constexpr T PointerValue(T item) { + return item; + } + + template + uintptr_t PointerValue(T *item) { + return reinterpret_cast(item); + } + + /** + * @brief A way to implicitly convert an integral to a pointer, if the return type is a pointer + */ + template + constexpr Return ValuePointer(T item) { + if constexpr (std::is_pointer::value) + return reinterpret_cast(item); + else + return item; + } + + /** + * @return The value aligned up to the next multiple + * @note The multiple needs to be a power of 2 + */ + template + constexpr TypeVal AlignUp(TypeVal value, TypeMul multiple) { + multiple--; + return ValuePointer((PointerValue(value) + multiple) & ~(multiple)); + } + + /** + * @return The value aligned down to the previous multiple + * @note The multiple needs to be a power of 2 + */ + template + constexpr TypeVal AlignDown(TypeVal value, TypeMul multiple) { + return ValuePointer(PointerValue(value) & ~(multiple - 1)); + } + + /** + * @return If the address is aligned with the multiple + */ + template + constexpr bool IsAligned(TypeVal value, TypeMul multiple) { + if ((multiple & (multiple - 1)) == 0) + return !(PointerValue(value) & (multiple - 1U)); + else + return (PointerValue(value) % multiple) == 0; + } + + /** + * @return If the value is page aligned + */ + template + constexpr bool PageAligned(TypeVal value) { + return IsAligned(value, PAGE_SIZE); + } + + /** + * @return If the value is word aligned + */ + template + constexpr bool WordAligned(TypeVal value) { + return IsAligned(value, WORD_BIT / 8); + } + + /** + * @param string The string to create a magic from + * @return The magic of the supplied string + */ + template + constexpr Type MakeMagic(std::string_view string) { + Type object{}; + size_t offset{}; + + for (auto &character : string) { + object |= static_cast(character) << offset; + offset += sizeof(character) * 8; + } + + return object; + } + + constexpr u8 HexDigitToNibble(char digit) { + if (digit >= '0' && digit <= '9') + return digit - '0'; + else if (digit >= 'a' && digit <= 'f') + return digit - 'a' + 10; + else if (digit >= 'A' && digit <= 'F') + return digit - 'A' + 10; + throw exception("Invalid hex character: '{}'", digit); + } + + template + constexpr std::array HexStringToArray(std::string_view string) { + if (string.size() != Size * 2) + throw exception("String size: {} (Expected {})", string.size(), Size); + std::array result; + for (size_t i{}; i < Size; i++) { + size_t index{i * 2}; + result[i] = (HexDigitToNibble(string[index]) << 4) | HexDigitToNibble(string[index + 1]); + } + return result; + } + + template + constexpr Type HexStringToInt(std::string_view string) { + if (string.size() > sizeof(Type) * 2) + throw exception("String size larger than type: {} (sizeof(Type): {})", string.size(), sizeof(Type)); + Type result{}; + size_t offset{(sizeof(Type) * 8) - 4}; + for (size_t index{}; index < string.size(); index++, offset -= 4) { + char digit{string[index]}; + if (digit >= '0' && digit <= '9') + result |= static_cast(digit - '0') << offset; + else if (digit >= 'a' && digit <= 'f') + result |= static_cast(digit - 'a' + 10) << offset; + else if (digit >= 'A' && digit <= 'F') + result |= static_cast(digit - 'A' + 10) << offset; + else + break; + } + return result >> (offset + 4); + } + + template + constexpr std::array SwapEndianness(std::array in) { + std::reverse(in.begin(), in.end()); + return in; + } + + constexpr u64 SwapEndianness(u64 in) { + return __builtin_bswap64(in); + } + + constexpr u32 SwapEndianness(u32 in) { + return __builtin_bswap32(in); + } + + constexpr u16 SwapEndianness(u16 in) { + return __builtin_bswap16(in); + } + + /** + * @brief A compile-time hash function as std::hash isn't constexpr + */ + constexpr std::size_t Hash(std::string_view view) { + return frozen::elsa{}(frozen::string(view.data(), view.size()), 0); + } + + /** + * @brief Selects the largest possible integer type for representing an object alongside providing the size of the object in terms of the underlying type + */ + template + struct IntegerFor { + using type = std::conditional_t + > + >; + + static constexpr size_t count{sizeof(T) / sizeof(type)}; + }; + + namespace detail { + static thread_local std::mt19937_64 generator{GetTimeTicks()}; + } + + /** + * @brief Fills an array with random data from a Mersenne Twister pseudo-random generator + * @note The generator is seeded with the the current time in ticks + */ + template requires (std::is_integral_v) + void FillRandomBytes(std::span in) { + std::independent_bits_engine::digits, T> gen(detail::generator); + std::generate(in.begin(), in.end(), gen); + } + + template requires (!std::is_integral_v && std::is_trivially_copyable_v) + void FillRandomBytes(T &object) { + FillRandomBytes(std::span(reinterpret_cast::type *>(&object), IntegerFor::count)); + } + + /** + * @brief A temporary shim for C++ 20's bit_cast to make transitioning to it easier + */ + template + To BitCast(const From& from) { + return *reinterpret_cast(&from); + } +} diff --git a/app/src/main/cpp/skyline/gpu/presentation_engine.h b/app/src/main/cpp/skyline/gpu/presentation_engine.h index 7b12895d..9eeab661 100644 --- a/app/src/main/cpp/skyline/gpu/presentation_engine.h +++ b/app/src/main/cpp/skyline/gpu/presentation_engine.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include diff --git a/app/src/main/cpp/skyline/input/npad.h b/app/src/main/cpp/skyline/input/npad.h index 91572e6e..f172cd54 100644 --- a/app/src/main/cpp/skyline/input/npad.h +++ b/app/src/main/cpp/skyline/input/npad.h @@ -11,7 +11,7 @@ namespace skyline::input { */ struct GuestController { NpadControllerType type{}; - i8 partnerIndex{constant::NullIndex}; //!< The index of a Joy-Con partner, if this has one + i8 partnerIndex{NpadDevice::NullIndex}; //!< The index of a Joy-Con partner, if this has one NpadDevice *device{nullptr}; //!< A pointer to the NpadDevice that all events from this are redirected to }; diff --git a/app/src/main/cpp/skyline/input/npad_device.cpp b/app/src/main/cpp/skyline/input/npad_device.cpp index dee5a8de..82a1ea4b 100644 --- a/app/src/main/cpp/skyline/input/npad_device.cpp +++ b/app/src/main/cpp/skyline/input/npad_device.cpp @@ -353,13 +353,16 @@ namespace skyline::input { globalTimestamp++; } + constexpr jlong MsInSecond{1000}; //!< The amount of milliseconds in a single second of time + constexpr jint AmplitudeMax{std::numeric_limits::max()}; //!< The maximum amplitude for Android Vibration APIs + struct VibrationInfo { jlong period; jint amplitude; jlong start; jlong end; - VibrationInfo(float frequency, float amplitude) : period(constant::MsInSecond / frequency), amplitude(amplitude), start(0), end(period) {} + VibrationInfo(float frequency, float amplitude) : period(MsInSecond / frequency), amplitude(amplitude), start(0), end(period) {} }; template @@ -410,7 +413,7 @@ namespace skyline::input { timings[i] = time; totalTime += time; - amplitudes[i] = std::min(totalAmplitude, constant::AmplitudeMax); + amplitudes[i] = std::min(totalAmplitude, AmplitudeMax); } jvm->VibrateDevice(index, span(timings.begin(), timings.begin() + i), span(amplitudes.begin(), amplitudes.begin() + i)); @@ -418,8 +421,8 @@ namespace skyline::input { void VibrateDevice(const std::shared_ptr &jvm, i8 index, const NpadVibrationValue &value) { std::array vibrations{ - VibrationInfo{value.frequencyLow, value.amplitudeLow * (constant::AmplitudeMax / 2)}, - {value.frequencyHigh, value.amplitudeHigh * (constant::AmplitudeMax / 2)}, + VibrationInfo{value.frequencyLow, value.amplitudeLow * (AmplitudeMax / 2)}, + {value.frequencyHigh, value.amplitudeHigh * (AmplitudeMax / 2)}, }; VibrateDevice(jvm, index, vibrations); } @@ -448,12 +451,12 @@ namespace skyline::input { vibrationLeft = left; vibrationRight = right; - if (partnerIndex == constant::NullIndex) { + if (partnerIndex == NpadDevice::NullIndex) { std::array vibrations{ - VibrationInfo{left.frequencyLow, left.amplitudeLow * (constant::AmplitudeMax / 4)}, - {left.frequencyHigh, left.amplitudeHigh * (constant::AmplitudeMax / 4)}, - {right.frequencyLow, right.amplitudeLow * (constant::AmplitudeMax / 4)}, - {right.frequencyHigh, right.amplitudeHigh * (constant::AmplitudeMax / 4)}, + VibrationInfo{left.frequencyLow, left.amplitudeLow * (AmplitudeMax / 4)}, + {left.frequencyHigh, left.amplitudeHigh * (AmplitudeMax / 4)}, + {right.frequencyLow, right.amplitudeLow * (AmplitudeMax / 4)}, + {right.frequencyHigh, right.amplitudeHigh * (AmplitudeMax / 4)}, }; VibrateDevice<4>(manager.state.jvm, index, vibrations); } else { diff --git a/app/src/main/cpp/skyline/input/npad_device.h b/app/src/main/cpp/skyline/input/npad_device.h index 96816250..9e61baf1 100644 --- a/app/src/main/cpp/skyline/input/npad_device.h +++ b/app/src/main/cpp/skyline/input/npad_device.h @@ -6,12 +6,6 @@ #include #include "shared_mem.h" -namespace skyline::constant { - constexpr jlong MsInSecond{1000}; //!< The amount of milliseconds in a single second of time - constexpr jint AmplitudeMax{std::numeric_limits::max()}; //!< The maximum amplitude for Android Vibration APIs - constexpr i8 NullIndex{-1}; //!< The placeholder index value when there is no device present -} - namespace skyline::input { /** * @brief The orientations the Joy-Con(s) can be held in @@ -137,8 +131,9 @@ namespace skyline::input { public: NpadId id; - i8 index{constant::NullIndex}; //!< The index of the device assigned to this player - i8 partnerIndex{constant::NullIndex}; //!< The index of a partner device, if present + static constexpr i8 NullIndex{-1}; //!< The placeholder index value when there is no device present + i8 index{NullIndex}; //!< The index of the device assigned to this player + i8 partnerIndex{NullIndex}; //!< The index of a partner device, if present NpadVibrationValue vibrationLeft; //!< Vibration for the left Joy-Con (Handheld/Pair), left LRA in a Pro-Controller or individual Joy-Cons std::optional vibrationRight; //!< Vibration for the right Joy-Con (Handheld/Pair) or right LRA in a Pro-Controller NpadControllerType type{}; diff --git a/app/src/main/cpp/skyline/input/touch.h b/app/src/main/cpp/skyline/input/touch.h index 0a1504b0..a3c51065 100644 --- a/app/src/main/cpp/skyline/input/touch.h +++ b/app/src/main/cpp/skyline/input/touch.h @@ -3,6 +3,7 @@ #pragma once +#include #include "shared_mem.h" namespace skyline::input { diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h index 3a809928..bce1bfd6 100644 --- a/app/src/main/cpp/skyline/kernel/memory.h +++ b/app/src/main/cpp/skyline/kernel/memory.h @@ -4,6 +4,7 @@ #pragma once #include +#include namespace skyline { namespace memory { diff --git a/app/src/main/cpp/skyline/services/base_service.h b/app/src/main/cpp/skyline/services/base_service.h index d727933d..2e3a8867 100644 --- a/app/src/main/cpp/skyline/services/base_service.h +++ b/app/src/main/cpp/skyline/services/base_service.h @@ -15,7 +15,7 @@ template(this)->*BaseFunction)(session, request, response); \ } \ -SERVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \ +SERVICE_DECL_AUTO(functions, frozen::make_unordered_map({__VA_ARGS__})); \ protected: \ ServiceFunctionDescriptor GetServiceFunction(u32 id) override { \ auto& function{functions.at(id)}; \ diff --git a/app/src/main/cpp/skyline/services/lm/ILogger.cpp b/app/src/main/cpp/skyline/services/lm/ILogger.cpp index 34c5f01a..ec9fd12b 100644 --- a/app/src/main/cpp/skyline/services/lm/ILogger.cpp +++ b/app/src/main/cpp/skyline/services/lm/ILogger.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include #include "ILogger.h" namespace skyline::service::lm { diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/macro_interpreter.cpp b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/macro_interpreter.cpp index e889719d..340f51d3 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/macro_interpreter.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/macro_interpreter.cpp @@ -19,7 +19,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d { while (Step()); } - FORCE_INLINE bool MacroInterpreter::Step(Opcode *delayedOpcode) { + __attribute__((always_inline)) bool MacroInterpreter::Step(Opcode *delayedOpcode) { switch (opcode->operation) { case Opcode::Operation::AluRegister: { u32 result{HandleAlu(opcode->aluOperation, registers[opcode->srcA], registers[opcode->srcB])}; @@ -116,7 +116,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d { return true; } - FORCE_INLINE u32 MacroInterpreter::HandleAlu(Opcode::AluOperation operation, u32 srcA, u32 srcB) { + __attribute__((always_inline)) u32 MacroInterpreter::HandleAlu(Opcode::AluOperation operation, u32 srcA, u32 srcB) { switch (operation) { case Opcode::AluOperation::Add: { u64 result{static_cast(srcA) + srcB}; @@ -155,7 +155,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d { } } - FORCE_INLINE void MacroInterpreter::HandleAssignment(Opcode::AssignmentOperation operation, u8 reg, u32 result) { + __attribute__((always_inline)) void MacroInterpreter::HandleAssignment(Opcode::AssignmentOperation operation, u8 reg, u32 result) { switch (operation) { case Opcode::AssignmentOperation::IgnoreAndFetch: WriteRegister(reg, *argument++); @@ -192,12 +192,12 @@ namespace skyline::soc::gm20b::engine::maxwell3d { } } - FORCE_INLINE void MacroInterpreter::Send(u32 pArgument) { + __attribute__((always_inline)) void MacroInterpreter::Send(u32 pArgument) { maxwell3D.CallMethod(methodAddress.address, pArgument, true); methodAddress.address += methodAddress.increment; } - FORCE_INLINE void MacroInterpreter::WriteRegister(u8 reg, u32 value) { + __attribute__((always_inline)) void MacroInterpreter::WriteRegister(u8 reg, u32 value) { // Register 0 should always be zero so block writes to it if (reg == 0) [[unlikely]] return;