/* * Copyright (c) 2020 eliboa * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef RCMDEVICE_H #define RCMDEVICE_H #include #include #include #include #include "libusbk_int.h" #include #include "../../types.h" #define PACKET_SIZE 0x1000 #define PAYLOAD_MAX_SIZE 0x1ED58 static int RCM_VID = 0x0955; static int RCM_PID = 0x7321; static UCHAR READ_PIPE_ID = 0x81; static UCHAR WRITE_PIPE_ID = 0x01; typedef enum _RESULT : DWORD { SUCCESS = 0x000, LIBUSBK_WRONG_DRIVER = 0x001, DEVICE_NOT_FOUND = 0x002, DEVICE_HANDLE_FAILED = 0x003, MISSING_LIBUSBK_DRIVER = 0x004, OPEN_FILE_FAILED = 0x005, PAYLOAD_TOO_LARGE = 0x006, USB_WRITE_FAILED = 0x007, SW_HIGHBUFF_FAILED = 0x008, STACK_SMASH_FAILED = 0x009, DEVICEID_READ_FAILED = 0x00A, DEVICE_NOT_READY = 0x00B, DEVICE_NOT_SET = 0x00C, WRONG_DEVICE_VID_PID = 0x00D, DEVICE_DISCONNECTED = 0x00E } RRESULT; typedef enum _DEVICE_STATUS { DISCONNECTED, CONNECTED } DEVICE_STATUS; class RcmDevice { public: //! Rcm device constructor RcmDevice(); ~RcmDevice(); //////////////////////////////////////////// ///// PUBLIC METHODS ///// //////////////////////////////////////////// /*! * \brief Initialize a RCM device. This function must be called at least on time before any other method call * \param[in] deviceInfo * Pointer to a KLST_DEVINFO struct. If not provided, the system will try to detect a plugged RCM device (a.k.a Nintendo Switch in RCM mode) * * \returns * - on success, true. * - on failure, false (use GetLastError() to get error code) */ bool initDevice(KLST_DEVINFO_HANDLE deviceInfo = nullptr); /*! * \brief Constructs a RCM payload and smash the device stack (fusée gelée exploit) * \param payload_path * Path to the user payload file to build final payload with * \returns * - on success, SUCCESS (0) * - on failure, see enum RRESULT */ RRESULT hack(const char* payload_path); /*! * \brief Constructs a RCM payload and smash the device stack (fusée gelée exploit) * \param payload_buff * Pointer to the payload data * \param buff_size * Payload size * \returns * - on success, SUCCESS (0) * - on failure, see enum RRESULT */ RRESULT hack(u8 *payload_buff, u32 buff_size); /*! * \brief Read a buffer from USB pipe * \param buffer * \param bufferSize * \returns * - on success, number of bytes read from pipe * - on failure, error code < 0 */ int read(u8* buffer, size_t bufferSize); /*! * \brief Write a buffer to USB pipe * \param buffer * \param bufferSize * \returns * - on success, number of bytes written to pipe * - on failure, error code < 0 */ int write(const u8* buffer, size_t bufferSize); /*! * \brief Check whether the device is ready to receive RCM commands * \return true if device is ready */ bool deviceIsReady(); //! Get the RCM device status (CONNECTED / DISCONNECTED) DEVICE_STATUS getStatus() { return m_devStatus; } //! Set the RCM device status to DISCONNECTED void disconnect() { m_devStatus = DISCONNECTED; } // API Getters bool flushPipe(UCHAR pipeId) { return m_usbApi.FlushPipe(m_usbHandle, pipeId); } // Reset the current buffer to lower one. Use this with caution. // Current buffer should not be reset manually, except after a controlled RCM reboot or disconnection. // disconnect() method should be preferred void resetCurrentBuffer() { m_currentBuffer = 0; } private: bool m_is_devicePlugged; KLST_DEVINFO_HANDLE m_devInfo = nullptr; KUSB_HANDLE m_usbHandle; KUSB_DRIVER_API m_usbApi; KLST_HANDLE m_devList = nullptr; u32 m_currentBuffer = 0; bool m_devIsInitialized = false; bool m_usbAPI_loaded = false; DEVICE_STATUS m_devStatus; private: bool getPluggedDevice(KLST_DEVINFO_HANDLE *devinfo); int switchToHighBuffer(); u32 getCurrentBufferAddress() const { return m_currentBuffer == 0 ? 0x40005000u : 0x40009000u; } int Ioctl(DWORD ioctlCode, const void* inputBytes, size_t numInputBytes, void* outputBytes, size_t numOutputBytes); }; class WinHandle { public: WinHandle(HANDLE srcHandle = INVALID_HANDLE_VALUE) noexcept : _handle(srcHandle) {} ~WinHandle() { if (_handle != INVALID_HANDLE_VALUE) { ::CloseHandle(_handle); _handle = INVALID_HANDLE_VALUE; } } void swap(WinHandle&& other) noexcept { std::swap(_handle, other._handle); } WinHandle(WinHandle&& moved) noexcept : _handle(INVALID_HANDLE_VALUE) { swap(std::move(moved)); } WinHandle(const WinHandle& copied) = delete; WinHandle& operator=(WinHandle&& moved) { swap(std::move(moved)); return *this; } WinHandle& operator=(const WinHandle& copied) = delete; HANDLE get() const { return _handle; } HANDLE release() { HANDLE retHandle = _handle; _handle = INVALID_HANDLE_VALUE; return retHandle; } protected: HANDLE _handle; }; #endif // RCMDEVICE_H