Merge remote-tracking branch 'upstream/master' into feature/basic-controller-input

This commit is contained in:
wheremyfoodat 2023-06-28 00:20:54 +03:00
commit acd21c6908
14 changed files with 610 additions and 202 deletions

View file

@ -0,0 +1,165 @@
#pragma once
#include <array>
#include <cstring>
#include <cstdint>
#include <climits>
#include <filesystem>
#include <optional>
#include "helpers.hpp"
namespace Crypto {
constexpr std::size_t AesKeySize = 0x10;
using AESKey = std::array<u8, AesKeySize>;
template <std::size_t N>
static std::array<u8, N> rolArray(const std::array<u8, N>& value, std::size_t bits) {
const auto bitWidth = N * CHAR_BIT;
bits %= bitWidth;
const auto byteShift = bits / CHAR_BIT;
const auto bitShift = bits % CHAR_BIT;
std::array<u8, N> result;
for (std::size_t i = 0; i < N; i++) {
result[i] = ((value[(i + byteShift) % N] << bitShift) | (value[(i + byteShift + 1) % N] >> (CHAR_BIT - bitShift))) & UINT8_MAX;
}
return result;
}
template <std::size_t N>
static std::array<u8, N> addArray(const std::array<u8, N>& a, const std::array<u8, N>& b) {
std::array<u8, N> result;
std::size_t sum = 0;
std::size_t carry = 0;
for (std::int64_t i = N - 1; i >= 0; i--) {
sum = a[i] + b[i] + carry;
carry = sum >> CHAR_BIT;
result[i] = static_cast<u8>(sum & UINT8_MAX);
}
return result;
}
template <std::size_t N>
static std::array<u8, N> xorArray(const std::array<u8, N>& a, const std::array<u8, N>& b) {
std::array<u8, N> result;
for (std::size_t i = 0; i < N; i++) {
result[i] = a[i] ^ b[i];
}
return result;
}
static std::optional<AESKey> createKeyFromHex(const std::string& hex) {
if (hex.size() < 32) {
return {};
}
AESKey rawKey;
for (std::size_t i = 0; i < rawKey.size(); i++) {
rawKey[i] = static_cast<u8>(std::stoi(hex.substr(i * 2, 2), 0, 16));
}
return rawKey;
}
struct AESKeySlot {
std::optional<AESKey> keyX = std::nullopt;
std::optional<AESKey> keyY = std::nullopt;
std::optional<AESKey> normalKey = std::nullopt;
};
enum KeySlotId : std::size_t {
NCCHKey0 = 0x2C,
NCCHKey1 = 0x25,
NCCHKey2 = 0x18,
NCCHKey3 = 0x1B,
};
class AESEngine {
private:
constexpr static std::size_t AesKeySlotCount = 0x40;
std::optional<AESKey> m_generator = std::nullopt;
std::array<AESKeySlot, AesKeySlotCount> m_slots;
bool keysLoaded = false;
constexpr void updateNormalKey(std::size_t slotId) {
if (m_generator.has_value() && hasKeyX(slotId) && hasKeyY(slotId)) {
auto& keySlot = m_slots.at(slotId);
AESKey keyX = keySlot.keyX.value();
AESKey keyY = keySlot.keyY.value();
keySlot.normalKey = rolArray(addArray(xorArray(rolArray(keyX, 2), keyY), m_generator.value()), 87);
}
}
public:
AESEngine() {}
void loadKeys(const std::filesystem::path& path);
bool haveKeys() { return keysLoaded; }
constexpr bool hasKeyX(std::size_t slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
return m_slots.at(slotId).keyX.has_value();
}
constexpr AESKey getKeyX(std::size_t slotId) {
return m_slots.at(slotId).keyX.value_or(AESKey{});
}
constexpr void setKeyX(std::size_t slotId, const AESKey &key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).keyX = key;
updateNormalKey(slotId);
}
}
constexpr bool hasKeyY(std::size_t slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
return m_slots.at(slotId).keyY.has_value();
}
constexpr AESKey getKeyY(std::size_t slotId) {
return m_slots.at(slotId).keyY.value_or(AESKey{});
}
constexpr void setKeyY(std::size_t slotId, const AESKey &key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).keyY = key;
updateNormalKey(slotId);
}
}
constexpr bool hasNormalKey(std::size_t slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
return m_slots.at(slotId).normalKey.has_value();
}
constexpr AESKey getNormalKey(std::size_t slotId) {
return m_slots.at(slotId).normalKey.value_or(AESKey{});
}
constexpr void setNormalKey(std::size_t slotId, const AESKey &key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).normalKey = key;
}
}
};
}

View file

@ -6,6 +6,7 @@
#include <glad/gl.h>
#include "cpu.hpp"
#include "crypto/aes_engine.hpp"
#include "io_file.hpp"
#include "memory.hpp"
#include "opengl.hpp"
@ -20,6 +21,7 @@ class Emulator {
GPU gpu;
Memory memory;
Kernel kernel;
Crypto::AESEngine aesEngine;
SDL_Window* window;
SDL_GLContext glContext;

View file

@ -2,11 +2,10 @@
#include <climits>
#include <cstdarg>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <iterator>
#include <type_traits>
#include <utility>
#include <sstream>
#include <string>
#include <vector>
#include "termcolor.hpp"
@ -51,21 +50,6 @@ namespace Helpers {
va_end(args);
}
static std::vector<u8> loadROM(std::string directory) {
std::ifstream file(directory, std::ios::binary);
if (file.fail()) panic("Couldn't read %s", directory.c_str());
std::vector<u8> ROM;
file.unsetf(std::ios::skipws);
ROM.insert(ROM.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
file.close();
printf("%s loaded successfully\n", directory.c_str());
return ROM;
}
static constexpr bool buildingInDebugMode() {
#ifdef NDEBUG
return false;
@ -120,39 +104,6 @@ namespace Helpers {
return (value >> offset) & ones<T, bits>();
}
/// Check if a bit "bit" of value is set
static constexpr bool isBitSet(u32 value, int bit) { return (value >> bit) & 1; }
/// rotate number right
template <typename T>
static constexpr T rotr(T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value >> bits) | (value << (bitWidth - bits));
}
// rotate number left
template <typename T>
static constexpr T rotl(T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value << bits) | (value >> (bitWidth - bits));
}
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
template <typename T, T Begin, class Func, T... Is>
static constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>) {
(f(std::integral_constant<T, Begin + Is>{}), ...);
}
template <typename T, T Begin, T End, class Func>
static constexpr void static_for(Func&& f) {
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
}
// For values < 0x99
static constexpr inline u8 incBCDByte(u8 value) { return ((value & 0xf) == 0x9) ? value + 7 : value + 1; }
#ifdef HELPERS_APPLE_CLANG
template <class To, class From>
constexpr To bit_cast(const From& from) noexcept {
@ -164,6 +115,19 @@ namespace Helpers {
return std::bit_cast<To, From>(from);
}
#endif
static std::vector<std::string> split(const std::string& s, const char c) {
std::istringstream tmp(s);
std::vector<std::string> result(1);
while (std::getline(tmp, *result.rbegin(), c)) {
result.emplace_back();
}
// Remove temporary slot
result.pop_back();
return result;
}
}; // namespace Helpers
// UDLs for memory size values
@ -171,12 +135,3 @@ constexpr size_t operator""_KB(unsigned long long int x) { return 1024ULL * x; }
constexpr size_t operator""_MB(unsigned long long int x) { return 1024_KB * x; }
constexpr size_t operator""_GB(unsigned long long int x) { return 1024_MB * x; }
// useful macros
// likely/unlikely
#ifdef __GNUC__
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif

View file

@ -1,14 +1,22 @@
#pragma once
#include <array>
#include <optional>
#include <vector>
#include "io_file.hpp"
#include "helpers.hpp"
#include "crypto/aes_engine.hpp"
struct NCCH {
struct EncryptionInfo {
Crypto::AESKey normalKey;
Crypto::AESKey initialCounter;
};
struct FSInfo { // Info on the ExeFS/RomFS
u64 offset = 0;
u64 size = 0;
u64 hashRegionSize = 0;
std::optional<EncryptionInfo> encryptionInfo;
};
// Descriptions for .text, .data and .rodata sections
@ -34,6 +42,8 @@ struct NCCH {
bool mountRomFS = false;
bool encrypted = false;
bool fixedCryptoKey = false;
bool seedCrypto = false;
u8 secondaryKeySlot = 0;
static constexpr u64 mediaUnit = 0x200;
u64 size = 0; // Size of NCCH converted to bytes
@ -41,6 +51,7 @@ struct NCCH {
u32 bssSize = 0;
u32 exheaderSize = 0;
FSInfo exheaderInfo;
FSInfo exeFS;
FSInfo romFS;
CodeSetInfo text, data, rodata;
@ -50,10 +61,9 @@ struct NCCH {
// Contains of the cart's save data
std::vector<u8> saveData;
// Header: 0x200 + 0x800 byte NCCH header + exheadr
// Returns true on success, false on failure
// Partition index/offset/size must have been set before this
bool loadFromHeader(u8* header, IOFile& file);
bool loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSInfo &info);
bool hasExtendedHeader() { return exheaderSize != 0; }
bool hasExeFS() { return exeFS.size != 0; }
@ -61,7 +71,8 @@ struct NCCH {
bool hasCode() { return codeFile.size() != 0; }
bool hasSaveData() { return saveData.size() != 0; }
private:
std::array<u8, 16> primaryKey = {}; // For exheader, ExeFS header and icons
std::array<u8, 16> secondaryKey = {}; // For RomFS and some files in ExeFS
std::pair<bool, Crypto::AESKey> getPrimaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY);
std::pair<bool, Crypto::AESKey> getSecondaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY);
std::pair<bool, std::size_t> readFromFile(IOFile& file, const FSInfo &info, u8 *dst, std::size_t offset, std::size_t size);
};

View file

@ -5,6 +5,7 @@
#include <fstream>
#include <optional>
#include <vector>
#include "crypto/aes_engine.hpp"
#include "helpers.hpp"
#include "handles.hpp"
#include "loader/ncsd.hpp"
@ -146,7 +147,7 @@ public:
void* getReadPointer(u32 address);
void* getWritePointer(u32 address);
std::optional<u32> loadELF(std::ifstream& file);
std::optional<NCSD> loadNCSD(const std::filesystem::path& path);
std::optional<NCSD> loadNCSD(Crypto::AESEngine &aesEngine, const std::filesystem::path& path);
u8 read8(u32 vaddr);
u16 read16(u32 vaddr);

View file

@ -0,0 +1,16 @@
#pragma once
#include <type_traits>
#include <utility>
namespace Helpers {
/// Used to make the compiler evaluate beeg loops at compile time for things like generating compile-time tables
template <typename T, T Begin, class Func, T... Is>
static constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>) {
(f(std::integral_constant<T, Begin + Is>{}), ...);
}
template <typename T, T Begin, T End, class Func>
static constexpr void static_for(Func&& f) {
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
}
}