mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-06-03 20:37:18 +12:00
Merge remote-tracking branch 'upstream/master' into io-file-cpp
This commit is contained in:
commit
f65d9480a5
17 changed files with 1002 additions and 420 deletions
165
include/crypto/aes_engine.hpp
Normal file
165
include/crypto/aes_engine.hpp
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,78 +1,101 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <SDL.h>
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include "crypto/aes_engine.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "opengl.hpp"
|
||||
#include "PICA/gpu.hpp"
|
||||
|
||||
enum class ROMType {
|
||||
None, ELF, NCSD
|
||||
};
|
||||
enum class ROMType { None, ELF, NCSD };
|
||||
|
||||
class Emulator {
|
||||
CPU cpu;
|
||||
GPU gpu;
|
||||
Memory memory;
|
||||
Kernel kernel;
|
||||
CPU cpu;
|
||||
GPU gpu;
|
||||
Memory memory;
|
||||
Kernel kernel;
|
||||
Crypto::AESEngine aesEngine;
|
||||
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
SDL_Window* window;
|
||||
SDL_GLContext glContext;
|
||||
SDL_GameController* gameController;
|
||||
int gameControllerID;
|
||||
|
||||
static constexpr u32 width = 400;
|
||||
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
|
||||
ROMType romType = ROMType::None;
|
||||
bool running = true;
|
||||
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
|
||||
// This is done so when a gamepad is connected, we won't automatically override the 3DS analog stick settings with the gamepad's state
|
||||
// And so the user can still use the keyboard to control the analog
|
||||
bool keyboardAnalogX = false;
|
||||
bool keyboardAnalogY = false;
|
||||
|
||||
// Keep the handle for the ROM here to reload when necessary and to prevent deleting it
|
||||
// This is currently only used for ELFs, NCSDs use the IOFile API instead
|
||||
std::ifstream loadedELF;
|
||||
NCSD loadedNCSD;
|
||||
static constexpr u32 width = 400;
|
||||
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
|
||||
ROMType romType = ROMType::None;
|
||||
bool running = true;
|
||||
|
||||
public:
|
||||
Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory), memory(cpu.getTicksRef()) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
||||
Helpers::panic("Failed to initialize SDL2");
|
||||
}
|
||||
// Keep the handle for the ROM here to reload when necessary and to prevent deleting it
|
||||
// This is currently only used for ELFs, NCSDs use the IOFile API instead
|
||||
std::ifstream loadedELF;
|
||||
NCSD loadedNCSD;
|
||||
|
||||
// Request OpenGL 4.1 Core (Max available on MacOS)
|
||||
// MacOS gets mad if we don't explicitly demand a core profile
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
|
||||
public:
|
||||
Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory), memory(cpu.getTicksRef()) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
||||
Helpers::panic("Failed to initialize SDL2");
|
||||
}
|
||||
|
||||
if (window == nullptr) {
|
||||
Helpers::panic("Window creation failed: %s", SDL_GetError());
|
||||
}
|
||||
// Make SDL use consistent positional button mapping
|
||||
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
|
||||
if (SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
Helpers::warn("Failed to initialize SDL2 GameController: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
glContext = SDL_GL_CreateContext(window);
|
||||
// Request OpenGL 4.1 Core (Max available on MacOS)
|
||||
// MacOS gets mad if we don't explicitly demand a core profile
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
|
||||
|
||||
if (glContext == nullptr) {
|
||||
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
|
||||
}
|
||||
if (window == nullptr) {
|
||||
Helpers::panic("Window creation failed: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
if(!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
|
||||
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
|
||||
}
|
||||
glContext = SDL_GL_CreateContext(window);
|
||||
if (glContext == nullptr) {
|
||||
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
if (!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
|
||||
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
void step();
|
||||
void render();
|
||||
void reset();
|
||||
void run();
|
||||
void runFrame();
|
||||
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) {
|
||||
gameController = SDL_GameControllerOpen(0);
|
||||
|
||||
bool loadROM(const std::filesystem::path& path);
|
||||
bool loadNCSD(const std::filesystem::path& path);
|
||||
bool loadELF(const std::filesystem::path& path);
|
||||
bool loadELF(std::ifstream& file);
|
||||
void initGraphicsContext() { gpu.initGraphicsContext(); }
|
||||
};
|
||||
if (gameController != nullptr) {
|
||||
SDL_Joystick* stick = SDL_GameControllerGetJoystick(gameController);
|
||||
gameControllerID = SDL_JoystickInstanceID(stick);
|
||||
}
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void step();
|
||||
void render();
|
||||
void reset();
|
||||
void run();
|
||||
void runFrame();
|
||||
|
||||
bool loadROM(const std::filesystem::path& path);
|
||||
bool loadNCSD(const std::filesystem::path& path);
|
||||
bool loadELF(const std::filesystem::path& path);
|
||||
bool loadELF(std::ifstream& file);
|
||||
void initGraphicsContext() { gpu.initGraphicsContext(); }
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
|
|
16
include/metaprogramming.hpp
Normal file
16
include/metaprogramming.hpp
Normal 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>{});
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
|
@ -43,6 +44,19 @@
|
|||
#include <span>
|
||||
#endif
|
||||
|
||||
#if defined(GPU_DEBUG_INFO)
|
||||
#define OPENGL_DEBUG_INFO
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <sal.h>
|
||||
#define OPENGL_PRINTF_FORMAT _Printf_format_string_
|
||||
#define OPENGL_PRINTF_FORMAT_ATTR(format_arg_index, dots_arg_index)
|
||||
#else
|
||||
#define OPENGL_PRINTF_FORMAT
|
||||
#define OPENGL_PRINTF_FORMAT_ATTR(format_arg_index, dots_arg_index) __attribute__((__format__(__printf__, format_arg_index, dots_arg_index)))
|
||||
#endif
|
||||
|
||||
// Uncomment the following define if you want GL objects to automatically free themselves when their lifetime ends
|
||||
// #define OPENGL_DESTRUCTORS
|
||||
|
||||
|
@ -53,7 +67,50 @@ namespace OpenGL {
|
|||
template <class...>
|
||||
constexpr std::false_type AlwaysFalse{};
|
||||
|
||||
struct VertexArray {
|
||||
OPENGL_PRINTF_FORMAT_ATTR(3, 4)
|
||||
static void setObjectLabel(GLenum identifier, GLuint name, OPENGL_PRINTF_FORMAT const char* format, ...) {
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
GLchar label[256] = {};
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const GLsizei length = vsnprintf(label, std::size(label), format, args);
|
||||
va_end(args);
|
||||
glObjectLabel(identifier, name, length, label);
|
||||
#endif
|
||||
}
|
||||
|
||||
class DebugScope {
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
inline static GLuint scopeDepth = 0;
|
||||
const GLuint m_scope_depth;
|
||||
#endif
|
||||
|
||||
public:
|
||||
OPENGL_PRINTF_FORMAT_ATTR(2, 3)
|
||||
DebugScope(OPENGL_PRINTF_FORMAT const char* format, ...)
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
: m_scope_depth(scopeDepth++) {
|
||||
GLchar message[256] = {};
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const GLsizei length = vsnprintf(message, std::size(message), format, args);
|
||||
va_end(args);
|
||||
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, m_scope_depth, length, message);
|
||||
}
|
||||
#else
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
~DebugScope() {
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
glPopDebugGroup();
|
||||
scopeDepth--;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
struct VertexArray {
|
||||
GLuint m_handle = 0;
|
||||
|
||||
void create() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
|
@ -22,12 +23,12 @@ namespace HID::Keys {
|
|||
X = 1 << 10,
|
||||
Y = 1 << 11,
|
||||
|
||||
GPIO0Inv = 1 << 12, // Inverted value of GPIO bit 0
|
||||
GPIO14Inv = 1 << 13, // Inverted value of GPIO bit 14
|
||||
GPIO0Inv = 1 << 12, // Inverted value of GPIO bit 0
|
||||
GPIO14Inv = 1 << 13, // Inverted value of GPIO bit 14
|
||||
|
||||
CirclePadRight = 1 << 28, // X >= 41
|
||||
CirclePadLeft = 1 << 29, // X <= -41
|
||||
CirclePadUp = 1 << 30, // Y >= 41
|
||||
CirclePadRight = 1 << 28, // X >= 41
|
||||
CirclePadLeft = 1 << 29, // X <= -41
|
||||
CirclePadUp = 1 << 30, // Y >= 41
|
||||
CirclePadDown = 1u << 31 // Y <= -41
|
||||
};
|
||||
}
|
||||
|
@ -39,18 +40,18 @@ class HIDService {
|
|||
Handle handle = KernelHandles::HID;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
u8* sharedMem = nullptr; // Pointer to HID shared memory
|
||||
u8* sharedMem = nullptr; // Pointer to HID shared memory
|
||||
|
||||
uint nextPadIndex;
|
||||
uint nextTouchscreenIndex;
|
||||
uint nextAccelerometerIndex;
|
||||
uint nextGyroIndex;
|
||||
|
||||
u32 newButtons; // The button state currently being edited
|
||||
u32 oldButtons; // The previous pad state
|
||||
u32 newButtons; // The button state currently being edited
|
||||
u32 oldButtons; // The previous pad state
|
||||
|
||||
s16 circlePadX, circlePadY; // Circlepad state
|
||||
s16 touchScreenX, touchScreenY; // Touchscreen state
|
||||
s16 circlePadX, circlePadY; // Circlepad state
|
||||
s16 touchScreenX, touchScreenY; // Touchscreen state
|
||||
|
||||
bool accelerometerEnabled;
|
||||
bool eventsInitialized;
|
||||
|
@ -79,7 +80,7 @@ class HIDService {
|
|||
*(T*)&sharedMem[offset] = value;
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
HIDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
@ -87,15 +88,18 @@ public:
|
|||
void pressKey(u32 mask) { newButtons |= mask; }
|
||||
void releaseKey(u32 mask) { newButtons &= ~mask; }
|
||||
|
||||
s16 getCirclepadX() { return circlePadX; }
|
||||
s16 getCirclepadY() { return circlePadY; }
|
||||
|
||||
void setCirclepadX(s16 x) {
|
||||
circlePadX = x;
|
||||
|
||||
// Turn bits 28 and 29 off in the new button state, which indicate whether the circlepad is steering left or right
|
||||
// Then, set them according to the new value of x
|
||||
newButtons &= ~0x3000'0000;
|
||||
if (x >= 41) // Pressing right
|
||||
if (x >= 41) // Pressing right
|
||||
newButtons |= 1 << 28;
|
||||
else if (x <= -41) // Pressing left
|
||||
else if (x <= -41) // Pressing left
|
||||
newButtons |= 1 << 29;
|
||||
}
|
||||
|
||||
|
@ -105,9 +109,9 @@ public:
|
|||
// Turn bits 30 and 31 off in the new button state, which indicate whether the circlepad is steering up or down
|
||||
// Then, set them according to the new value of y
|
||||
newButtons &= ~0xC000'0000;
|
||||
if (y >= 41) // Pressing up
|
||||
if (y >= 41) // Pressing up
|
||||
newButtons |= 1 << 30;
|
||||
else if (y <= -41) // Pressing down
|
||||
else if (y <= -41) // Pressing down
|
||||
newButtons |= 1 << 31;
|
||||
}
|
||||
|
||||
|
@ -115,7 +119,7 @@ public:
|
|||
|
||||
void setSharedMem(u8* ptr) {
|
||||
sharedMem = ptr;
|
||||
if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa
|
||||
if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa
|
||||
std::memset(ptr, 0, 0x2b0);
|
||||
}
|
||||
}
|
||||
|
@ -129,4 +133,4 @@ public:
|
|||
void releaseTouchScreen() {
|
||||
touchScreenPressed = false;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -90,9 +90,11 @@ class ServiceManager {
|
|||
// Input function wrappers
|
||||
void pressKey(u32 key) { hid.pressKey(key); }
|
||||
void releaseKey(u32 key) { hid.releaseKey(key); }
|
||||
void setCirclepadX(u16 x) { hid.setCirclepadX(x); }
|
||||
void setCirclepadY(u16 y) { hid.setCirclepadY(y); }
|
||||
s16 getCirclepadX() { return hid.getCirclepadX(); }
|
||||
s16 getCirclepadY() { return hid.getCirclepadY(); }
|
||||
void setCirclepadX(s16 x) { hid.setCirclepadX(x); }
|
||||
void setCirclepadY(s16 y) { hid.setCirclepadY(y); }
|
||||
void updateInputs(u64 currentTimestamp) { hid.updateInputs(currentTimestamp); }
|
||||
void setTouchScreenPress(u16 x, u16 y) { hid.setTouchScreenPress(x, y); }
|
||||
void releaseTouchScreen() { hid.releaseTouchScreen(); }
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue