├── .gitignore ├── source ├── utils │ ├── LogHandler.hpp │ ├── FSUtils.hpp │ ├── LogHandler.cpp │ └── FSUtils.cpp ├── ntag_crypt.h ├── re_nfpii │ ├── Cabinet.hpp │ ├── Lock.hpp │ ├── Utils.hpp │ ├── TagStream.hpp │ ├── Tag.hpp │ ├── re_nfpii.hpp │ ├── TagStream.cpp │ ├── TagManager.hpp │ ├── Cabinet.cpp │ ├── Utils.cpp │ └── Tag.cpp ├── debug │ ├── logger.h │ └── logger.c ├── main.cpp └── ntag_crypt.c ├── plugin ├── source │ ├── utils │ │ ├── input.h │ │ ├── DrawUtils.hpp │ │ ├── schrift.h │ │ ├── input.c │ │ └── DrawUtils.cpp │ ├── nfc.cpp │ ├── debug │ │ ├── logger.h │ │ └── logger.c │ ├── config │ │ ├── ConfigItemLog.hpp │ │ ├── ConfigItemDumpAmiibo.hpp │ │ ├── ConfigItemSelectAmiibo.hpp │ │ ├── WUPSConfigItemButtonCombo.h │ │ ├── ConfigItemLog.cpp │ │ ├── WUPSConfigItemButtonCombo.cpp │ │ ├── fav_icon.inc │ │ ├── dir_icon.inc │ │ └── ConfigItemDumpAmiibo.cpp │ ├── nn_nfp.def │ ├── quick_select.cpp │ └── main.cpp └── Makefile ├── .github └── workflows │ └── build.yml ├── Dockerfile ├── include └── nfpii.h ├── README.md ├── Makefile └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | dist/ 4 | *.elf 5 | *.wps 6 | *.wms 7 | -------------------------------------------------------------------------------- /source/utils/LogHandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class LogHandler { 4 | public: 5 | static void Init(); 6 | static void Info(const char* fmt, ...); 7 | static void Warn(const char* fmt, ...); 8 | static void Error(const char* fmt, ...); 9 | }; 10 | -------------------------------------------------------------------------------- /source/ntag_crypt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | int NTAGDecrypt(NTAGDataT2T* data, NTAGRawDataT2T* raw); 10 | 11 | int NTAGEncrypt(NTAGRawDataT2T* raw, NTAGDataT2T* data); 12 | 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | -------------------------------------------------------------------------------- /plugin/source/utils/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define VPAD_BUTTON_RESERVED_BIT 0x80000 11 | 12 | uint32_t remapWiiMoteButtons(uint32_t buttons); 13 | uint32_t remapClassicButtons(uint32_t buttons); 14 | uint32_t remapProButtons(uint32_t buttons); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /source/utils/FSUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class FSUtils { 6 | public: 7 | static int Initialize(); 8 | static int Finalize(); 9 | 10 | static int WriteToFile(const char* path, const void* data, uint32_t size); 11 | static int ReadFromFile(const char* path, void* data, uint32_t size); 12 | 13 | private: 14 | static inline FSAClientHandle clientHandle = -1; 15 | }; 16 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-binary: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - name: Build binary 11 | run: | 12 | docker build -t re_nfpii_builder . 13 | docker run --rm -v ${PWD}:/project re_nfpii_builder make dist 14 | - uses: actions/upload-artifact@v3 15 | with: 16 | name: re_nfpii 17 | path: dist/* 18 | if-no-files-found: error 19 | -------------------------------------------------------------------------------- /source/re_nfpii/Cabinet.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace re::nfpii { 6 | using nn::Result; 7 | using namespace nn::nfp; 8 | 9 | namespace Cabinet { 10 | 11 | Result GetArgs(AmiiboSettingsArgs* args); 12 | 13 | Result GetResult(AmiiboSettingsResult* result, SYSArgDataBlock const& block); 14 | 15 | Result InitializeArgsIn(AmiiboSettingsArgsIn* args); 16 | 17 | Result ReturnToCallerWithResult(AmiiboSettingsResult const& result); 18 | 19 | Result SwitchToCabinet(AmiiboSettingsArgsIn const& args, const char* standardArg, uint32_t standardArgSize); 20 | 21 | }; 22 | 23 | } // namespace re::nfpii 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/wiiu-env/devkitppc:20230621 2 | 3 | COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20230719 /artifacts $DEVKITPRO 4 | COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230719 /artifacts $DEVKITPRO 5 | COPY --from=ghcr.io/wiiu-env/libnotifications:20230621 /artifacts $DEVKITPRO 6 | 7 | RUN \ 8 | mkdir wut && \ 9 | cd wut && \ 10 | git init . && \ 11 | git remote add origin https://github.com/GaryOderNichts/wut.git && \ 12 | git fetch --depth 1 origin 09b3556e3dcde3516015ee964ac7fa51586190cb && \ 13 | git checkout FETCH_HEAD 14 | WORKDIR /wut 15 | RUN make -j$(nproc) 16 | RUN make install 17 | 18 | WORKDIR /project 19 | -------------------------------------------------------------------------------- /source/re_nfpii/Lock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Simple RAII style lock guard class Nintendo seems to be using 6 | // The compiler inlines most of this so it's hard to tell how this looks exactly 7 | class Lock { 8 | public: 9 | Lock(OSMutex* mutex, bool locked = false) 10 | : mutex(mutex), isLocked(locked) 11 | { 12 | if (!isLocked) { 13 | OSLockMutex(mutex); 14 | isLocked = true; 15 | } 16 | } 17 | 18 | ~Lock() 19 | { 20 | if (mutex && isLocked) { 21 | OSUnlockMutex(mutex); 22 | } 23 | } 24 | 25 | private: 26 | OSMutex* mutex; 27 | bool isLocked; 28 | }; 29 | -------------------------------------------------------------------------------- /plugin/source/nfc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "debug/logger.h" 8 | 9 | /* Some games call NFCGetTagInfo for amiibo detection instead of using nn_nfp for some reason. 10 | Notably Amiibo Festival and Mario Party 10 are doing this. 11 | This function replacement passes the callback to the module, which calls it on the next nfc proc. 12 | */ 13 | 14 | DECL_FUNCTION(NFCError, NFCGetTagInfo, uint32_t index, uint32_t timeout, NFCGetTagInfoCallbackFn callback, void* arg) 15 | { 16 | // DEBUG_FUNCTION_LINE("NFCGetTagInfo"); 17 | 18 | if (!NfpiiIsInitialized()) { 19 | return real_NFCGetTagInfo(index, timeout, callback, arg); 20 | } 21 | 22 | if (index != 0) { 23 | return -0x1385; 24 | } 25 | 26 | return NfpiiQueueNFCGetTagInfo(callback, arg); 27 | } 28 | 29 | WUPS_MUST_REPLACE(NFCGetTagInfo, WUPS_LOADER_LIBRARY_NFC, NFCGetTagInfo); 30 | -------------------------------------------------------------------------------- /source/debug/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | #define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) 11 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) 12 | 13 | #define DEBUG_FUNCTION_LINE(FMT, ARGS...) \ 14 | do { \ 15 | WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \ 16 | } while (0); 17 | 18 | #define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \ 19 | do { \ 20 | WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \ 21 | } while (0); 22 | 23 | void DumpHex(const void* data, size_t size); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /plugin/source/debug/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | #define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) 11 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) 12 | 13 | #define DEBUG_FUNCTION_LINE(FMT, ARGS...) \ 14 | do { \ 15 | WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \ 16 | } while (0); 17 | 18 | #define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \ 19 | do { \ 20 | WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \ 21 | } while (0); 22 | 23 | void DumpHex(const void* data, size_t size); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /plugin/source/config/ConfigItemLog.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | enum LogType { 4 | LOG_TYPE_NORMAL, 5 | LOG_TYPE_WARN, 6 | LOG_TYPE_ERROR, 7 | }; 8 | 9 | struct ConfigItemLog { 10 | char* configID; 11 | WUPSConfigItemHandle handle; 12 | }; 13 | 14 | void ConfigItemLog_Init(void); 15 | 16 | void ConfigItemLog_PrintType(LogType type, const char* text); 17 | 18 | bool ConfigItemLog_AddToCategory(WUPSConfigCategoryHandle cat, const char* configID, const char* displayName); 19 | 20 | #define ConfigItemLog_AddToCategoryHandled(__config__, __cat__, __configID__, __displayName__) \ 21 | do { \ 22 | if (!ConfigItemLog_AddToCategory(__cat__, __configID__, __displayName__)) { \ 23 | WUPSConfig_Destroy(__config__); \ 24 | return 0; \ 25 | } \ 26 | } while (0) 27 | -------------------------------------------------------------------------------- /source/re_nfpii/Utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace re::nfpii { 7 | using nn::Result; 8 | using namespace nn::nfp; 9 | 10 | bool CheckZero(const void* data, uint32_t size); 11 | void GetRandom(void* data, uint32_t size); 12 | uint16_t IncreaseCount(uint16_t count, bool overflow); 13 | 14 | void ReadTagInfo(TagInfo* info, const NTAGDataT2T* data); 15 | void ReadCommonInfo(CommonInfo* info, const NTAGDataT2T* data); 16 | void ReadRegisterInfo(RegisterInfo* info, const NTAGDataT2T* data); 17 | void ReadReadOnlyInfo(ReadOnlyInfo* info, const NTAGDataT2T* data); 18 | void ReadAdminInfo(AdminInfo* info, const NTAGDataT2T* data); 19 | 20 | void ClearApplicationArea(NTAGDataT2T* data); 21 | void ClearRegisterInfo(NTAGDataT2T* data); 22 | 23 | Result ReadCountryRegion(uint8_t* outCountryCode); 24 | uint16_t OSTimeToAmiiboTime(OSTime time); 25 | void ConvertAmiiboDate(Date* date, uint16_t time); 26 | bool CheckAmiiboMagic(NTAGDataT2T* data); 27 | 28 | bool CheckUuidCRC(NTAGInfoT2T* info); 29 | void SetUuidCRC(uint32_t* crc); 30 | 31 | Result UpdateMii(FFLStoreData* data); 32 | 33 | } // namespace re::nfpii 34 | -------------------------------------------------------------------------------- /source/re_nfpii/TagStream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Tag.hpp" 3 | 4 | #include 5 | 6 | namespace re::nfpii { 7 | using nn::Result; 8 | using namespace nn::nfp; 9 | 10 | // What the actual point of having this many different classes is, is beyond me 11 | // For rw to the tag that's now TagManager -> TagStream -> TagManager -> TagStreamImpl -> Tag 12 | class TagStream { 13 | public: 14 | TagStream(); 15 | virtual ~TagStream(); 16 | 17 | bool IsOpened(); 18 | bool CheckOpened(uint32_t id); 19 | 20 | Result Close(); 21 | Result Open(uint32_t id); 22 | 23 | Result Read(void* data, uint32_t size); 24 | Result Write(const void* data, uint32_t size); 25 | 26 | struct TagStreamImpl { 27 | Result Open(uint32_t id); 28 | Result Close(); 29 | 30 | Result Read(void* data, int32_t size); 31 | Result Write(const void* data, uint32_t size); 32 | 33 | Result Clear(); 34 | 35 | // +0x0 36 | Tag* tag; 37 | Tag::AppAreaInfo info; 38 | bool isOpened; 39 | }; 40 | 41 | private: 42 | // +0x0 43 | uint32_t appId; 44 | 45 | // +0x64 46 | bool isOpened; 47 | }; 48 | 49 | } // namespace re::nfpii 50 | -------------------------------------------------------------------------------- /plugin/source/config/ConfigItemDumpAmiibo.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | typedef enum { 6 | DUMP_STATE_INIT, 7 | DUMP_STATE_ERROR, 8 | DUMP_STATE_WAITING, 9 | DUMP_STATE_COMPLETED, 10 | } DumpState; 11 | 12 | struct ConfigItemDumpAmiibo { 13 | char* configID; 14 | WUPSConfigItemHandle handle; 15 | DumpState state; 16 | bool wasInit; 17 | std::string dumpFolder; 18 | std::string lastDumpPath; 19 | }; 20 | 21 | bool ConfigItemDumpAmiibo_AddToCategory(WUPSConfigCategoryHandle cat, const char* configID, const char* displayName, const char* dumpFolder); 22 | 23 | #define ConfigItemDumpAmiibo_AddToCategoryHandled(__config__, __cat__, __configID__, __displayName__, __dumpFolder__) \ 24 | do { \ 25 | if (!ConfigItemDumpAmiibo_AddToCategory(__cat__, __configID__, __displayName__, __dumpFolder__)) { \ 26 | WUPSConfig_Destroy(__config__); \ 27 | return 0; \ 28 | } \ 29 | } while (0) 30 | -------------------------------------------------------------------------------- /source/debug/logger.c: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include 3 | 4 | void DumpHex(const void* data, size_t size) 5 | { 6 | char buffer[512]; 7 | char* ptr = buffer; 8 | #define my_printf(x, ...) ptr += snprintf(ptr, (buffer + 512) - ptr, x, ##__VA_ARGS__) 9 | 10 | char ascii[17]; 11 | size_t i, j; 12 | ascii[16] = '\0'; 13 | for (i = 0; i < size; ++i) { 14 | my_printf("%02X ", ((unsigned char*)data)[i]); 15 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { 16 | ascii[i % 16] = ((unsigned char*)data)[i]; 17 | } else { 18 | ascii[i % 16] = '.'; 19 | } 20 | if ((i+1) % 8 == 0 || i+1 == size) { 21 | my_printf(" "); 22 | if ((i+1) % 16 == 0) { 23 | my_printf("| %s \n", ascii); 24 | DEBUG_FUNCTION_LINE_WRITE("%s", buffer); 25 | ptr = buffer; 26 | } else if (i+1 == size) { 27 | ascii[(i+1) % 16] = '\0'; 28 | if ((i+1) % 16 <= 8) { 29 | my_printf(" "); 30 | } 31 | for (j = (i+1) % 16; j < 16; ++j) { 32 | my_printf(" "); 33 | } 34 | my_printf("| %s \n", ascii); 35 | DEBUG_FUNCTION_LINE_WRITE("%s", buffer); 36 | ptr = buffer; 37 | } 38 | } 39 | } 40 | 41 | #undef my_printf 42 | } 43 | -------------------------------------------------------------------------------- /plugin/source/debug/logger.c: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include 3 | 4 | void DumpHex(const void* data, size_t size) 5 | { 6 | char buffer[512]; 7 | char* ptr = buffer; 8 | #define my_printf(x, ...) ptr += snprintf(ptr, (buffer + 512) - ptr, x, ##__VA_ARGS__) 9 | 10 | char ascii[17]; 11 | size_t i, j; 12 | ascii[16] = '\0'; 13 | for (i = 0; i < size; ++i) { 14 | my_printf("%02X ", ((unsigned char*)data)[i]); 15 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { 16 | ascii[i % 16] = ((unsigned char*)data)[i]; 17 | } else { 18 | ascii[i % 16] = '.'; 19 | } 20 | if ((i+1) % 8 == 0 || i+1 == size) { 21 | my_printf(" "); 22 | if ((i+1) % 16 == 0) { 23 | my_printf("| %s \n", ascii); 24 | DEBUG_FUNCTION_LINE_WRITE("%s", buffer); 25 | ptr = buffer; 26 | } else if (i+1 == size) { 27 | ascii[(i+1) % 16] = '\0'; 28 | if ((i+1) % 16 <= 8) { 29 | my_printf(" "); 30 | } 31 | for (j = (i+1) % 16; j < 16; ++j) { 32 | my_printf(" "); 33 | } 34 | my_printf("| %s \n", ascii); 35 | DEBUG_FUNCTION_LINE_WRITE("%s", buffer); 36 | ptr = buffer; 37 | } 38 | } 39 | } 40 | 41 | #undef my_printf 42 | } 43 | -------------------------------------------------------------------------------- /source/utils/LogHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "LogHandler.hpp" 2 | #include "debug/logger.h" 3 | #include "re_nfpii/Lock.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | static NfpiiLogHandler logHandler; 13 | static char logBuf[0x200]; 14 | static OSMutex logMutex; 15 | 16 | static void Log(NfpiiLogVerbosity verb, const char* fmt, va_list arg) 17 | { 18 | Lock lock(&logMutex); 19 | 20 | std::vsnprintf(logBuf, sizeof(logBuf), fmt, arg); 21 | 22 | if (logHandler) { 23 | logHandler(verb, logBuf); 24 | } 25 | } 26 | 27 | void LogHandler::Init(void) 28 | { 29 | OSInitMutex(&logMutex); 30 | } 31 | 32 | void LogHandler::Info(const char* fmt, ...) 33 | { 34 | va_list args; 35 | va_start(args, fmt); 36 | Log(NFPII_LOG_VERBOSITY_INFO, fmt, args); 37 | va_end(args); 38 | } 39 | 40 | void LogHandler::Warn(const char* fmt, ...) 41 | { 42 | va_list args; 43 | va_start(args, fmt); 44 | Log(NFPII_LOG_VERBOSITY_WARN, fmt, args); 45 | va_end(args); 46 | } 47 | 48 | void LogHandler::Error(const char* fmt, ...) 49 | { 50 | va_list args; 51 | va_start(args, fmt); 52 | Log(NFPII_LOG_VERBOSITY_ERROR, fmt, args); 53 | va_end(args); 54 | } 55 | 56 | void NfpiiSetLogHandler(NfpiiLogHandler handler) 57 | { 58 | logHandler = handler; 59 | 60 | LogHandler::Info("Module: Log Handler %s", handler ? "set" : "cleared"); 61 | LogHandler::Info("Module: Version %d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); 62 | } 63 | 64 | WUMS_EXPORT_FUNCTION(NfpiiSetLogHandler); 65 | -------------------------------------------------------------------------------- /include/nfpii.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | #define NFPII_VERSION_MAJOR(v) ((v >> 16) & 0xff) 10 | #define NFPII_VERSION_MINOR(v) ((v >> 8) & 0xff) 11 | #define NFPII_VERSION_PATCH(v) (v & 0xff) 12 | #define NFPII_VERSION(major, minor, patch) ((major << 16) | (minor << 8) | patch) 13 | 14 | typedef enum NfpiiEmulationState { 15 | NFPII_EMULATION_OFF, 16 | NFPII_EMULATION_ON 17 | } EmulationState; 18 | 19 | typedef enum NfpiiUUIDRandomizationState { 20 | NFPII_RANDOMIZATION_OFF, 21 | NFPII_RANDOMIZATION_ONCE, 22 | NFPII_RANDOMIZATION_EVERY_READ 23 | } UUIDRandomizationState; 24 | 25 | typedef enum NfpiiLogVerbosity { 26 | NFPII_LOG_VERBOSITY_INFO, 27 | NFPII_LOG_VERBOSITY_WARN, 28 | NFPII_LOG_VERBOSITY_ERROR, 29 | } NfpiiLogVerbosity; 30 | 31 | typedef void (*NfpiiLogHandler)(NfpiiLogVerbosity verb, const char* message); 32 | 33 | uint32_t NfpiiGetVersion(void); 34 | 35 | bool NfpiiIsInitialized(void); 36 | 37 | void NfpiiSetEmulationState(NfpiiEmulationState state); 38 | 39 | NfpiiEmulationState NfpiiGetEmulationState(void); 40 | 41 | void NfpiiSetUUIDRandomizationState(NfpiiUUIDRandomizationState state); 42 | 43 | NfpiiUUIDRandomizationState NfpiiGetUUIDRandomizationState(void); 44 | 45 | void NfpiiSetRemoveAfterSeconds(float seconds); 46 | 47 | void NfpiiSetTagEmulationPath(const char* path); 48 | 49 | const char* NfpiiGetTagEmulationPath(void); 50 | 51 | NFCError NfpiiQueueNFCGetTagInfo(NFCGetTagInfoCallbackFn callback, void* arg); 52 | 53 | void NfpiiSetLogHandler(NfpiiLogHandler handler); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /plugin/source/config/ConfigItemSelectAmiibo.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | typedef void (*AmiiboSelectedCallback)(struct ConfigItemSelectAmiibo*, const char* fileName); 7 | 8 | struct ConfigItemSelectAmiibo { 9 | char* configID; 10 | WUPSConfigItemHandle handle; 11 | 12 | AmiiboSelectedCallback callback; 13 | 14 | std::string rootPath; 15 | std::string currentPath; 16 | std::string selectedAmiibo; 17 | }; 18 | 19 | std::vector& ConfigItemSelectAmiibo_GetFavorites(void); 20 | 21 | void ConfigItemSelectAmiibo_Init(std::string rootPath, bool favoritesPerTitle); 22 | 23 | bool ConfigItemSelectAmiibo_AddToCategory(WUPSConfigCategoryHandle cat, const char* configID, const char* displayName, const char* amiiboFolder, const char* currentAmiibo, AmiiboSelectedCallback callback); 24 | 25 | #define ConfigItemSelectAmiibo_AddToCategoryHandled(__config__, __cat__, __configID__, __displayName__, __amiiboFolder__, __currentAmiibo__, __callback__) \ 26 | do { \ 27 | if (!ConfigItemSelectAmiibo_AddToCategory(__cat__, __configID__, __displayName__, __amiiboFolder__, __currentAmiibo__, __callback__)) { \ 28 | WUPSConfig_Destroy(__config__); \ 29 | return 0; \ 30 | } \ 31 | } while (0) 32 | -------------------------------------------------------------------------------- /plugin/source/utils/DrawUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // visible screen sizes 6 | #define SCREEN_WIDTH 854 7 | #define SCREEN_HEIGHT 480 8 | 9 | union Color { 10 | explicit Color(uint32_t color) { 11 | this->color = color; 12 | } 13 | 14 | Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 15 | this->r = r; 16 | this->g = g; 17 | this->b = b; 18 | this->a = a; 19 | } 20 | 21 | uint32_t color{}; 22 | struct { 23 | uint8_t r; 24 | uint8_t g; 25 | uint8_t b; 26 | uint8_t a; 27 | }; 28 | }; 29 | 30 | class DrawUtils { 31 | public: 32 | static void initBuffers(); 33 | 34 | static void beginDraw(); 35 | 36 | static void endDraw(); 37 | 38 | static void clear(Color col); 39 | 40 | static void drawPixel(uint32_t x, uint32_t y, Color col) { drawPixel(x, y, col.r, col.g, col.b, col.a); } 41 | 42 | static void drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a); 43 | 44 | static void drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col); 45 | 46 | static void drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col); 47 | 48 | static void drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t *data); 49 | 50 | static void drawPNG(uint32_t x, uint32_t y, const uint8_t *data); 51 | 52 | static bool initFont(); 53 | 54 | static void deinitFont(); 55 | 56 | static void setFontSize(uint32_t size); 57 | 58 | static void setFontColor(Color col); 59 | 60 | static void print(uint32_t x, uint32_t y, const char *string, bool alignRight = false); 61 | 62 | static void print(uint32_t x, uint32_t y, const wchar_t *string, bool alignRight = false); 63 | 64 | static uint32_t getTextWidth(const char *string); 65 | 66 | static uint32_t getTextWidth(const wchar_t *string); 67 | 68 | private: 69 | static uint8_t *tvBuffer; 70 | static uint32_t tvSize; 71 | static uint8_t *drcBuffer; 72 | static uint32_t drcSize; 73 | }; 74 | -------------------------------------------------------------------------------- /source/re_nfpii/Tag.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace re::nfpii { 9 | using nn::Result; 10 | using namespace nn::nfp; 11 | 12 | class Tag { 13 | public: 14 | Tag(); 15 | virtual ~Tag(); 16 | 17 | struct State { 18 | // +0x0 19 | int32_t state; 20 | int32_t result; 21 | Tag* tag; 22 | }; 23 | 24 | struct AppAreaInfo { 25 | // +0x0 26 | uint32_t id; 27 | // +0x8 28 | uint32_t offset; 29 | uint32_t size; 30 | }; 31 | 32 | Result SetData(NTAGDataT2T* data); 33 | NTAGDataT2T* GetData(); 34 | 35 | Result Mount(bool backup); 36 | Result Mount(); 37 | 38 | Result Unmount(); 39 | 40 | Result GetAppAreaInfo(AppAreaInfo* info, uint32_t* outNumAreas); 41 | Result GetAppAreaInfo(AppAreaInfo* info, uint32_t* outNumAreas, int32_t maxAreas); 42 | 43 | Result Write(TagInfo* info, bool update); 44 | 45 | Result InitializeDataBuffer(const NTAGDataT2T* data); 46 | Result WriteDataBuffer(const void* data, int32_t offset, uint32_t size); 47 | Result ReadDataBuffer(void* out, int32_t offset, uint32_t size); 48 | 49 | void ClearTagData(); 50 | 51 | Result CreateApplicationArea(NTAGDataT2T* data, ApplicationAreaCreateInfo const& createInfo); 52 | Result SetRegisterInfo(RegisterInfoSet const& info); 53 | 54 | Result DeleteApplicationArea(); 55 | Result DeleteRegisterInfo(); 56 | Result Format(NTAGDataT2T* data, const void* appData, int32_t appDataSize); 57 | 58 | bool HasRegisterInfo(); 59 | bool IsExistApplicationArea(); 60 | 61 | Result UpdateAppAreaInfo(bool unk); 62 | 63 | Result WriteTag(bool backup); 64 | 65 | public: // custom 66 | void SetPath(std::string& path) { 67 | this->path = path; 68 | } 69 | 70 | private: 71 | // +0x4 72 | uint8_t dataBuffer[0x800]; 73 | NTAGDataT2T ntagData; 74 | uint32_t numAppAreas; 75 | 76 | // +0xdec 77 | AppAreaInfo appAreaInfo[16]; 78 | uint32_t dataBufferCapacity; 79 | 80 | // +0xf58 81 | bool updateAppWriteCount; 82 | bool updateTitleId; 83 | 84 | private: // custom 85 | std::string path; 86 | }; 87 | 88 | } // namespace re::nfpii 89 | -------------------------------------------------------------------------------- /plugin/source/config/WUPSConfigItemButtonCombo.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | typedef enum ButtonComboState { 10 | BUTTON_COMBO_STATE_NONE, 11 | BUTTON_COMBO_STATE_PREPARE_FOR_HOLD, 12 | BUTTON_COMBO_STATE_WAIT_FOR_HOLD, 13 | } ButtonComboState; 14 | 15 | typedef struct ConfigItemButtonCombo { 16 | char *configId; 17 | WUPSConfigItemHandle handle; 18 | uint32_t defaultValue; 19 | uint32_t value; 20 | uint32_t holdDurationInMs; 21 | VPADButtons abortButton; 22 | uint32_t abortButtonHoldDurationInMs; 23 | void *callback; 24 | ButtonComboState state; 25 | } ConfigItemButtonCombo; 26 | 27 | typedef void (*ButtonComboValueChangedCallback)(ConfigItemButtonCombo *item, uint32_t buttonComboInVPADButtons); 28 | 29 | bool WUPSConfigItemButtonComboAddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, uint32_t defaultComboInVPADButtons, ButtonComboValueChangedCallback callback); 30 | 31 | bool WUPSConfigItemButtonComboAddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, uint32_t defaultComboInVPADButtons, uint32_t holdDurationInMs, VPADButtons abortButton, uint32_t abortButtonHoldDurationInMs, ButtonComboValueChangedCallback callback); 32 | 33 | #define WUPSConfigItemButtonCombo_AddToCategoryHandled(__config__, __cat__, __configID__, __displayName__, __defaultComboInVPADButtons__, __callback__) \ 34 | do { \ 35 | if (!WUPSConfigItemButtonComboAddToCategory(__cat__, __configID__, __displayName__, __defaultComboInVPADButtons__, __callback__)) { \ 36 | WUPSConfig_Destroy(__config__); \ 37 | return 0; \ 38 | } \ 39 | } while (0) 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /source/re_nfpii/re_nfpii.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "TagManager.hpp" 7 | 8 | #include "debug/logger.h" 9 | 10 | namespace re::nfpii { 11 | using nn::Result; 12 | using namespace nn::nfp; 13 | 14 | extern TagManager tagManager; 15 | 16 | // creates a result directly from the value (should not use this unless lazy, or unsure) 17 | #define RESULT(x) Result(NNResult{(int32_t)x}) 18 | 19 | #define NFP_SUCCESS Result(Result::LEVEL_SUCCESS, Result::RESULT_MODULE_COMMON, 0) // 0x0 20 | #define NFP_FATAL_RESULT(desc) Result(Result::LEVEL_FATAL, Result::RESULT_MODULE_NN_NFP, desc) 21 | #define NFP_STATUS_RESULT(desc) Result(Result::LEVEL_STATUS, Result::RESULT_MODULE_NN_NFP, desc) 22 | #define NFP_USAGE_RESULT(desc) Result(Result::LEVEL_USAGE, Result::RESULT_MODULE_NN_NFP, desc) 23 | 24 | #define NFP_OUT_OF_RANGE NFP_USAGE_RESULT(RESULT_OUT_OF_RANGE) // 0xc1b03700 25 | #define NFP_INVALID_PARAM NFP_USAGE_RESULT(RESULT_INVALID_PARAM) // 0xc1b03780 26 | #define NFP_INVALID_ALIGNMENT NFP_USAGE_RESULT(RESULT_INVALID_ALIGNMENT) // 0xc1b03800 27 | #define NFP_INVALID_STATE NFP_STATUS_RESULT(RESULT_INVALID_STATE) // 0xa1b06400 28 | #define NFP_INVALID_TAG NFP_STATUS_RESULT(RESULT_INVALID_TAG) // 0xa1b0c800 29 | #define NFP_INVALID_TAG_INFO NFP_STATUS_RESULT(RESULT_INVALID_TAG_INFO) // 0xa1b0ca80 30 | #define NFP_NO_BACKUPENTRY NFP_STATUS_RESULT(RESULT_NO_BACKUPENTRY) // 0xa1b0e580 31 | #define NFP_NO_REGISTER_INFO NFP_STATUS_RESULT(RESULT_NO_REGISTER_INFO) // 0xa1b10900 32 | #define NFP_APP_AREA_MISING NFP_STATUS_RESULT(RESULT_APP_AREA_MISSING) // 0xa1b10400 33 | #define NFP_APP_AREA_TAGID_MISMATCH NFP_STATUS_RESULT(RESULT_APP_AREA_TAGID_MISMATCH) // 0xa1b11d00 34 | #define NFP_APP_AREA_ALREADY_EXISTS NFP_STATUS_RESULT(RESULT_APP_AREA_ALREADY_EXISTS) // 0xa1b10e00 35 | #define NFP_APP_AREA_ACCESS_ID_MISMATCH NFP_STATUS_RESULT(RESULT_APP_AREA_ACCESS_ID_MISMATCH) // 0xa1b11300 36 | #define NFP_NO_BACKUP_SAVEDATA NFP_STATUS_RESULT(RESULT_NO_BACKUP_SAVEDATA) // 0xa1b38880 37 | #define NFP_SYSTEM_ERROR NFP_STATUS_RESULT(RESULT_SYSTEM_ERROR) // 0xa1b3e880 38 | #define NFP_FATAL NFP_FATAL_RESULT(RESULT_FATAL) // 0xe1b5db00 39 | 40 | } // namespace re::nfpii 41 | -------------------------------------------------------------------------------- /plugin/source/utils/schrift.h: -------------------------------------------------------------------------------- 1 | /* This file is part of libschrift. 2 | * 3 | * © 2019-2022 Thomas Oltmann and contributors 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 16 | 17 | #ifndef SCHRIFT_H 18 | #define SCHRIFT_H 1 19 | 20 | #include /* size_t */ 21 | #include /* uint_fast32_t, uint_least32_t */ 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #define SFT_DOWNWARD_Y 0x01 28 | 29 | typedef struct SFT SFT; 30 | typedef struct SFT_Font SFT_Font; 31 | typedef uint_least32_t SFT_UChar; /* Guaranteed to be compatible with char32_t. */ 32 | typedef uint_fast32_t SFT_Glyph; 33 | typedef struct SFT_LMetrics SFT_LMetrics; 34 | typedef struct SFT_GMetrics SFT_GMetrics; 35 | typedef struct SFT_Kerning SFT_Kerning; 36 | typedef struct SFT_Image SFT_Image; 37 | 38 | struct SFT { 39 | SFT_Font *font; 40 | double xScale; 41 | double yScale; 42 | double xOffset; 43 | double yOffset; 44 | int flags; 45 | }; 46 | 47 | struct SFT_LMetrics { 48 | double ascender; 49 | double descender; 50 | double lineGap; 51 | }; 52 | 53 | struct SFT_GMetrics { 54 | double advanceWidth; 55 | double leftSideBearing; 56 | int yOffset; 57 | int minWidth; 58 | int minHeight; 59 | }; 60 | 61 | struct SFT_Kerning { 62 | double xShift; 63 | double yShift; 64 | }; 65 | 66 | struct SFT_Image { 67 | void *pixels; 68 | int width; 69 | int height; 70 | }; 71 | 72 | const char *sft_version(void); 73 | 74 | SFT_Font *sft_loadmem(const void *mem, size_t size); 75 | void sft_freefont(SFT_Font *font); 76 | 77 | int sft_lmetrics(const SFT *sft, SFT_LMetrics *metrics); 78 | int sft_lookup(const SFT *sft, SFT_UChar codepoint, SFT_Glyph *glyph); 79 | int sft_gmetrics(const SFT *sft, SFT_Glyph glyph, SFT_GMetrics *metrics); 80 | int sft_kerning(const SFT *sft, SFT_Glyph leftGlyph, SFT_Glyph rightGlyph, 81 | SFT_Kerning *kerning); 82 | int sft_render(const SFT *sft, SFT_Glyph glyph, SFT_Image image); 83 | 84 | #ifdef __cplusplus 85 | } 86 | #endif 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /plugin/source/nn_nfp.def: -------------------------------------------------------------------------------- 1 | :NAME nn_nfp 2 | 3 | :TEXT 4 | // custom added exports 5 | NfpiiGetVersion 6 | NfpiiIsInitialized 7 | NfpiiSetEmulationState 8 | NfpiiGetEmulationState 9 | NfpiiSetUUIDRandomizationState 10 | NfpiiGetUUIDRandomizationState 11 | NfpiiSetRemoveAfterSeconds 12 | NfpiiSetTagEmulationPath 13 | NfpiiGetTagEmulationPath 14 | NfpiiQueueNFCGetTagInfo 15 | NfpiiSetLogHandler 16 | 17 | // nn_nfp exports 18 | AntennaCheck__Q2_2nn3nfpFv 19 | CheckMovable__Q2_2nn3nfpFRCQ3_2nn3nfp8MoveInfo 20 | CreateApplicationArea__Q2_2nn3nfpFRCQ3_2nn3nfp25ApplicationAreaCreateInfo 21 | DeleteApplicationArea__Q2_2nn3nfpFv 22 | DeleteNfpRegisterInfo__Q2_2nn3nfpFv 23 | Finalize__Q2_2nn3nfpFv 24 | FindCharacterImage__Q2_2nn3nfpFPUiUs 25 | Flush__Q2_2nn3nfpFv 26 | FormatForMove__Q2_2nn3nfpFRCQ3_2nn3nfp8MoveInfoT1 27 | Format__Q2_2nn3nfpFPCUci 28 | GetAmiiboSettingsArgs__Q2_2nn3nfpFPQ3_2nn3nfp18AmiiboSettingsArgs 29 | GetAmiiboSettingsResult__Q2_2nn3nfpFPQ3_2nn3nfp20AmiiboSettingsResultRC15SysArgDataBlock 30 | GetBackupEntryFromMemory__Q2_2nn3nfpFPQ3_2nn3nfp15BackupEntryInfoUsPCvUi 31 | GetBackupHeaderFromMemory__Q2_2nn3nfpFPQ3_2nn3nfp16BackupHeaderInfoPCvUi 32 | GetBackupSaveDataSize__Q2_2nn3nfpFv 33 | GetCharacterImage__Q2_2nn3nfpFPvUiUs 34 | GetCharacterName__Q2_2nn3nfpFPUsUiPCUcUc 35 | GetConnectionStatus__Q2_2nn3nfpFPQ3_2nn3nfp16ConnectionStatus 36 | GetErrorCode__Q2_2nn3nfpFRCQ2_2nn6Result 37 | GetNfpAdminInfo__Q2_2nn3nfpFPQ3_2nn3nfp9AdminInfo 38 | GetNfpCommonInfo__Q2_2nn3nfpFPQ3_2nn3nfp10CommonInfo 39 | GetNfpInfoForMove__Q2_2nn3nfpFPQ3_2nn3nfp8MoveInfo 40 | GetNfpReadOnlyInfo__Q2_2nn3nfpFPQ3_2nn3nfp12ReadOnlyInfo 41 | GetNfpRegisterInfo__Q2_2nn3nfpFPQ3_2nn3nfp12RegisterInfo 42 | GetNfpRomInfo__Q2_2nn3nfpFPQ3_2nn3nfp7RomInfo 43 | GetNfpState__Q2_2nn3nfpFv 44 | GetTagInfo__Q2_2nn3nfpFPQ3_2nn3nfp7TagInfo 45 | InitializeAmiiboSettingsArgsIn__Q2_2nn3nfpFPQ3_2nn3nfp20AmiiboSettingsArgsIn 46 | InitializeCreateInfo__Q2_2nn3nfpFPQ3_2nn3nfp25ApplicationAreaCreateInfo 47 | InitializeRegisterInfoSet__Q2_2nn3nfpFPQ3_2nn3nfp15RegisterInfoSet 48 | Initialize__Q2_2nn3nfpFv 49 | IsExistApplicationArea__Q2_2nn3nfpFv 50 | MountReadOnly__Q2_2nn3nfpFv 51 | MountRom__Q2_2nn3nfpFv 52 | Mount__Q2_2nn3nfpFv 53 | Move__Q2_2nn3nfpFv 54 | OpenApplicationArea__Q2_2nn3nfpFUi 55 | ReadAllBackupSaveData__Q2_2nn3nfpFPvUi 56 | ReadApplicationArea__Q2_2nn3nfpFPvUi 57 | Restore__Q2_2nn3nfpFv 58 | ReturnToCallerWithAmiiboSettingsResult__Q2_2nn3nfpFRCQ3_2nn3nfp20AmiiboSettingsResult 59 | SetActivateEvent__Q2_2nn3nfpFP7OSEvent 60 | SetDeactivateEvent__Q2_2nn3nfpFP7OSEvent 61 | SetNfpRegisterInfo__Q2_2nn3nfpFRCQ3_2nn3nfp15RegisterInfoSet 62 | StartDetection__Q2_2nn3nfpFv 63 | StopDetection__Q2_2nn3nfpFv 64 | SwitchToAmiiboSettings__Q2_2nn3nfpFRCQ3_2nn3nfp20AmiiboSettingsArgsIn 65 | SwitchToAmiiboSettings__Q2_2nn3nfpFRCQ3_2nn3nfp20AmiiboSettingsArgsInPCcUi 66 | Unmount__Q2_2nn3nfpFv 67 | WriteApplicationArea__Q2_2nn3nfpFPCvUiRCQ3_2nn3nfp5TagId 68 | -------------------------------------------------------------------------------- /source/utils/FSUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "FSUtils.hpp" 2 | #include 3 | 4 | int FSUtils::Initialize() 5 | { 6 | if (clientHandle >= 0) { 7 | return clientHandle; 8 | } 9 | 10 | FSAInit(); 11 | clientHandle = FSAAddClient(nullptr); 12 | if (clientHandle < 0) { 13 | DEBUG_FUNCTION_LINE("FSAAddClient: %x", clientHandle); 14 | return clientHandle; 15 | } 16 | 17 | FSError err = FSAMount(clientHandle, "/dev/sdcard01", "/vol/external01", FSA_MOUNT_FLAG_LOCAL_MOUNT, nullptr, 0); 18 | if (err < 0 && err != FS_ERROR_ALREADY_EXISTS) { 19 | DEBUG_FUNCTION_LINE("FSAMount: %x", err); 20 | 21 | FSADelClient(clientHandle); 22 | clientHandle = -1; 23 | return err; 24 | } 25 | 26 | return 0; 27 | } 28 | 29 | int FSUtils::Finalize() 30 | { 31 | if (clientHandle < 0) { 32 | return clientHandle; 33 | } 34 | 35 | FSAUnmount(clientHandle, "/vol/external01", FSA_UNMOUNT_FLAG_BIND_MOUNT); 36 | FSADelClient(clientHandle); 37 | clientHandle = -1; 38 | 39 | return 0; 40 | } 41 | 42 | int FSUtils::WriteToFile(const char* path, const void* data, uint32_t size) 43 | { 44 | if (clientHandle < 0) { 45 | return clientHandle; 46 | } 47 | 48 | FSAFileHandle fileHandle; 49 | FSError err = FSAOpenFileEx(clientHandle, path, "wb", (FSMode) 0x666, FS_OPEN_FLAG_NONE, 0, &fileHandle); 50 | if (err < 0) { 51 | return err; 52 | } 53 | 54 | __attribute__((aligned(0x40))) uint8_t buf[0x40]; 55 | 56 | uint32_t bytesWritten = 0; 57 | while (bytesWritten < size) { 58 | uint32_t toWrite = size - bytesWritten; 59 | if (toWrite > sizeof(buf)) { 60 | toWrite = sizeof(buf); 61 | } 62 | 63 | memcpy(buf, (uint8_t*) data + bytesWritten, toWrite); 64 | err = FSAWriteFile(clientHandle, buf, 1, toWrite, fileHandle, 0); 65 | if (err < 0) { 66 | break; 67 | } 68 | 69 | bytesWritten += err; 70 | 71 | if ((uint32_t) err != toWrite) { 72 | break; 73 | } 74 | } 75 | 76 | FSACloseFile(clientHandle, fileHandle); 77 | return bytesWritten; 78 | } 79 | 80 | int FSUtils::ReadFromFile(const char* path, void* data, uint32_t size) 81 | { 82 | if (clientHandle < 0) { 83 | return clientHandle; 84 | } 85 | 86 | FSAFileHandle fileHandle; 87 | FSError err = FSAOpenFileEx(clientHandle, path, "rb", (FSMode) 0x666, FS_OPEN_FLAG_NONE, 0, &fileHandle); 88 | if (err < 0) { 89 | return err; 90 | } 91 | 92 | __attribute__((aligned(0x40))) uint8_t buf[0x40]; 93 | 94 | uint32_t bytesRead = 0; 95 | while (bytesRead < size) { 96 | uint32_t toRead = size - bytesRead; 97 | if (toRead > sizeof(buf)) { 98 | toRead = sizeof(buf); 99 | } 100 | 101 | err = FSAReadFile(clientHandle, buf, 1, toRead, fileHandle, 0); 102 | if (err < 0) { 103 | break; 104 | } 105 | 106 | memcpy((uint8_t*) data + bytesRead, buf, err); 107 | bytesRead += err; 108 | 109 | if ((uint32_t) err != toRead) { 110 | break; 111 | } 112 | } 113 | 114 | FSACloseFile(clientHandle, fileHandle); 115 | return bytesRead; 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # re_nfpii 2 | [![](https://dcbadge.vercel.app/api/server/geY4G2NZK9?style=flat&compact=true)](https://discord.gg/geY4G2NZK9) 3 | 4 | A more or less accurate reimplementation of the Wii U's nn_nfp.rpl library, with the goal of research and amiibo emulation. 5 | Requires the [Aroma](https://github.com/wiiu-env/Aroma) environment. 6 | 7 | > :warning: This is still really experimental and work-in-progress! 8 | Check the [compatibility list](https://github.com/GaryOderNichts/re_nfpii/wiki/Compatibility-List) for confirmed working games. 9 | 10 | re_nfpii is split into a module and a plugin. 11 | The module completely replaces all exports of nn_nfp and adds additional exports for configuration. A majority of the code tries to be accurate to what nn_nfp is doing, so don't question some of the design choices. 12 | The plugin handles the configuration menu for the module and some additional things which can't be added to the module. 13 | 14 | ## Usage 15 | - Download the [latest release](https://github.com/GaryOderNichts/re_nfpii/releases) or the artifacts of latest nightly build from [here](https://nightly.link/GaryOderNichts/re_nfpii/workflows/build/main/re_nfpii.zip). 16 | - Copy the contents of the downloaded *`.zip`* file to your target environment. 17 | - Copy your **encrypted** amiibo dumps to `wiiu/re_nfpii`. Subfolders are also supported and can be browsed from the configuration menu. 18 | Folders in the `re_nfpii` folder starting with a [Title ID](https://wiiubrew.org/wiki/Title_database) (without the `-`) will be automatically opened for that game (for example a folder named `0005000010144F00 - Smash Bros` would automatically open for Smash Bros's USA version). 19 | - Open the plugin configuration menu with L + Down + SELECT. 20 | - Select one of your amiibo and enable emulation. 21 | 22 | > :information_source: Official Amiibo figures can currently not be used while the module is loaded. 23 | > To use the figures again delete the `re_nfpii.wms` file from your `modules` folder. 24 | 25 | ### Remove after 26 | The remove after feature "removes" the virtual tag from the reader after the specified amount of time. 27 | 28 | ### Quick selecting favorites 29 | You can mark Amiibo as favorites using X. By setting a Quick Select button combination, you can quickly cycle through your favorites. 30 | 31 | ### Dumping Amiibo 32 | re_nfpii comes with an Amiibo dumper in the configuration menu. This allows you to dump your tags directly to the `wiiu/re_nfpii/dumps` folder. 33 | 34 | ### Amiibo Settings 35 | The Wii U Plugin System does not work in applets, such as the Amiibo Settings, yet. 36 | This means you can't open the re_nfpii configuration while in the Amiibo Settings. 37 | To use the Amiibo Settings, select the Amiibo you want to configure before entering them, and everything else will be handled automatically. 38 | After returning from the Amiibo Settings, re-enable Amiibo emulation. 39 | 40 | ## Planned features 41 | This currently just reimplements the major parts of nn_nfp and redirects tag reads and writes to the SD Card. 42 | For future releases it is planned to have additional features and a custom format which does not require encryption. 43 | 44 | ## Building 45 | Building re_nfpii using the Dockerfile is recommended: 46 | ``` 47 | # Build docker image (only needed once) 48 | docker build . -t re_nfpii_builder 49 | 50 | # make 51 | docker run -it --rm -v ${PWD}:/project re_nfpii_builder make 52 | 53 | # make clean 54 | docker run -it --rm -v ${PWD}:/project re_nfpii_builder make clean 55 | ``` 56 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "utils/LogHandler.hpp" 12 | 13 | #define STR_VALUE(arg) #arg 14 | #define VERSION_STRING(x, y, z) "v" STR_VALUE(x) "." STR_VALUE(y) "." STR_VALUE(z) 15 | 16 | WUMS_MODULE_EXPORT_NAME("nn_nfp"); 17 | WUMS_MODULE_DESCRIPTION("A nn_nfp reimplementation with support for Amiibo emulation"); 18 | WUMS_MODULE_AUTHOR("GaryOderNichts"); 19 | WUMS_MODULE_VERSION(VERSION_STRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)); 20 | WUMS_MODULE_LICENSE("GPLv2"); 21 | 22 | WUMS_INITIALIZE(myargs) 23 | { 24 | if (!WHBLogModuleInit()) { 25 | WHBLogCafeInit(); 26 | WHBLogUdpInit(); 27 | } 28 | 29 | LogHandler::Init(); 30 | } 31 | 32 | WUMS_APPLICATION_STARTS() 33 | { 34 | if (!WHBLogModuleInit()) { 35 | WHBLogCafeInit(); 36 | WHBLogUdpInit(); 37 | } 38 | } 39 | 40 | WUMS_APPLICATION_ENDS() 41 | { 42 | // Call finalize in case the application doesn't 43 | re::nfpii::tagManager.Finalize(); 44 | } 45 | 46 | uint32_t NfpiiGetVersion(void) 47 | { 48 | return NFPII_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); 49 | } 50 | 51 | // custom exports for the configuration plugin 52 | bool NfpiiIsInitialized(void) 53 | { 54 | return re::nfpii::tagManager.IsInitialized(); 55 | } 56 | 57 | void NfpiiSetEmulationState(NfpiiEmulationState state) 58 | { 59 | LogHandler::Info("Module: Updated emulation state to: %d", state); 60 | 61 | re::nfpii::tagManager.SetEmulationState(state); 62 | } 63 | 64 | NfpiiEmulationState NfpiiGetEmulationState(void) 65 | { 66 | return re::nfpii::tagManager.GetEmulationState(); 67 | } 68 | 69 | void NfpiiSetUUIDRandomizationState(NfpiiUUIDRandomizationState state) 70 | { 71 | LogHandler::Info("Module: Updated uuid random state to: %d", state); 72 | 73 | re::nfpii::tagManager.SetUUIDRandomizationState(state); 74 | } 75 | 76 | NfpiiUUIDRandomizationState NfpiiGetUUIDRandomizationState(void) 77 | { 78 | return re::nfpii::tagManager.GetUUIDRandomizationState(); 79 | } 80 | 81 | void NfpiiSetRemoveAfterSeconds(float seconds) 82 | { 83 | LogHandler::Info("Module: Updated remove after seconds to: %.1fs", seconds); 84 | 85 | re::nfpii::tagManager.SetRemoveAfterSeconds(seconds); 86 | } 87 | 88 | void NfpiiSetTagEmulationPath(const char* path) 89 | { 90 | LogHandler::Info("Module: Update tag emulation path to: '%s'", path); 91 | 92 | re::nfpii::tagManager.SetTagEmulationPath(path); 93 | } 94 | 95 | const char* NfpiiGetTagEmulationPath(void) 96 | { 97 | return re::nfpii::tagManager.GetTagEmulationPath().c_str(); 98 | } 99 | 100 | NFCError NfpiiQueueNFCGetTagInfo(NFCGetTagInfoCallbackFn callback, void* arg) 101 | { 102 | LogHandler::Info("Module: Queued NFCGetTagInfo"); 103 | 104 | return re::nfpii::tagManager.QueueNFCGetTagInfo(callback, arg); 105 | } 106 | 107 | WUMS_EXPORT_FUNCTION(NfpiiIsInitialized); 108 | WUMS_EXPORT_FUNCTION(NfpiiSetEmulationState); 109 | WUMS_EXPORT_FUNCTION(NfpiiGetEmulationState); 110 | WUMS_EXPORT_FUNCTION(NfpiiSetUUIDRandomizationState); 111 | WUMS_EXPORT_FUNCTION(NfpiiGetUUIDRandomizationState); 112 | WUMS_EXPORT_FUNCTION(NfpiiSetRemoveAfterSeconds); 113 | WUMS_EXPORT_FUNCTION(NfpiiSetTagEmulationPath); 114 | WUMS_EXPORT_FUNCTION(NfpiiGetTagEmulationPath); 115 | WUMS_EXPORT_FUNCTION(NfpiiQueueNFCGetTagInfo); 116 | -------------------------------------------------------------------------------- /source/re_nfpii/TagStream.cpp: -------------------------------------------------------------------------------- 1 | #include "TagStream.hpp" 2 | #include "Utils.hpp" 3 | #include "re_nfpii.hpp" 4 | 5 | namespace re::nfpii { 6 | 7 | TagStream::TagStream() 8 | { 9 | appId = 0; 10 | isOpened = false; 11 | } 12 | 13 | TagStream::~TagStream() 14 | { 15 | Close(); 16 | } 17 | 18 | bool TagStream::IsOpened() 19 | { 20 | return isOpened; 21 | } 22 | 23 | bool TagStream::CheckOpened(uint32_t id) 24 | { 25 | if (IsOpened()) { 26 | // Not sure what nintendo is actually doing here 27 | // this is much simpler and does the same 28 | return id == appId; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | Result TagStream::Close() 35 | { 36 | if (!IsOpened()) { 37 | return NFP_INVALID_STATE; 38 | } 39 | 40 | Result res = tagManager.CloseStream(); 41 | if (res.IsFailure()) { 42 | return res; 43 | } 44 | 45 | isOpened = false; 46 | return NFP_SUCCESS; 47 | } 48 | 49 | Result TagStream::Open(uint32_t id) 50 | { 51 | if (IsOpened()) { 52 | return NFP_INVALID_STATE; 53 | } 54 | 55 | Result res = tagManager.OpenStream(id); 56 | if (res.IsFailure()) { 57 | return res; 58 | } 59 | 60 | isOpened = true; 61 | appId = id; 62 | 63 | return res; 64 | } 65 | 66 | Result TagStream::Read(void* data, uint32_t size) 67 | { 68 | if (!IsOpened()) { 69 | return NFP_INVALID_STATE; 70 | } 71 | 72 | return tagManager.ReadStream(data, size); 73 | } 74 | 75 | Result TagStream::Write(const void* data, uint32_t size) 76 | { 77 | if (!IsOpened()) { 78 | return NFP_INVALID_STATE; 79 | } 80 | 81 | return tagManager.WriteStream(data, size); 82 | } 83 | 84 | Result TagStream::TagStreamImpl::Open(uint32_t id) 85 | { 86 | if (isOpened) { 87 | return NFP_INVALID_STATE; 88 | } 89 | 90 | Tag::AppAreaInfo appInfo[16]; 91 | uint32_t numAreas; 92 | tag->GetAppAreaInfo(appInfo, &numAreas); 93 | 94 | if (numAreas < 1) { 95 | return NFP_INVALID_TAG; 96 | } 97 | 98 | for (uint32_t i = 0; i < numAreas; i++) { 99 | if (appInfo[i].id == id) { 100 | if (appInfo[i].size != 0xd8) { 101 | return NFP_INVALID_TAG; 102 | } 103 | 104 | memcpy(&info, &appInfo[i], sizeof(info)); 105 | isOpened = true; 106 | 107 | return NFP_SUCCESS; 108 | } 109 | } 110 | 111 | return NFP_INVALID_TAG; 112 | } 113 | 114 | Result TagStream::TagStreamImpl::Close() 115 | { 116 | if (!isOpened) { 117 | return NFP_INVALID_STATE; 118 | } 119 | 120 | memset(&info, 0, sizeof(info)); 121 | isOpened = false; 122 | 123 | return NFP_SUCCESS; 124 | } 125 | 126 | Result TagStream::TagStreamImpl::Read(void* data, int32_t size) 127 | { 128 | if (!isOpened) { 129 | return NFP_INVALID_STATE; 130 | } 131 | 132 | uint32_t* pSize = &info.size; 133 | if (size <= (int32_t) info.size) { 134 | pSize = (uint32_t*) &size; 135 | } 136 | 137 | return tag->ReadDataBuffer(data, info.offset, *pSize); 138 | } 139 | 140 | Result TagStream::TagStreamImpl::Write(const void* data, uint32_t size) 141 | { 142 | if (!isOpened) { 143 | return NFP_INVALID_STATE; 144 | } 145 | 146 | // nfp checks byte 0x10 of appareainfo here, not sure what that's for 147 | 148 | if (size > info.size) { 149 | return NFP_OUT_OF_RANGE; 150 | } 151 | 152 | Result res = tag->WriteDataBuffer(data, info.offset, size); 153 | if (res.IsSuccess() && size < info.size) { 154 | uint8_t randomness[0x21c]; 155 | GetRandom(randomness, sizeof(randomness)); 156 | 157 | res = tag->WriteDataBuffer(randomness, info.offset + size, info.size - size); 158 | } 159 | 160 | return res; 161 | } 162 | 163 | Result TagStream::TagStreamImpl::Clear() 164 | { 165 | if (!isOpened) { 166 | return NFP_INVALID_STATE; 167 | } 168 | 169 | // nfp checks byte 0x10 of appareainfo here, not sure what that's for 170 | 171 | uint8_t zeroes[0x21c]; 172 | memset(zeroes, 0, sizeof(zeroes)); 173 | 174 | return tag->WriteDataBuffer(zeroes, info.offset, info.size); 175 | } 176 | 177 | } // namespace re::nfpii 178 | -------------------------------------------------------------------------------- /plugin/source/utils/input.c: -------------------------------------------------------------------------------- 1 | #include "input.h" 2 | 3 | uint32_t remapWiiMoteButtons(uint32_t buttons) { 4 | uint32_t conv_buttons = 0; 5 | 6 | if (buttons & WPAD_BUTTON_LEFT) { 7 | conv_buttons |= VPAD_BUTTON_LEFT; 8 | } 9 | 10 | if (buttons & WPAD_BUTTON_RIGHT) { 11 | conv_buttons |= VPAD_BUTTON_RIGHT; 12 | } 13 | 14 | if (buttons & WPAD_BUTTON_DOWN) { 15 | conv_buttons |= VPAD_BUTTON_DOWN; 16 | } 17 | 18 | if (buttons & WPAD_BUTTON_UP) { 19 | conv_buttons |= VPAD_BUTTON_UP; 20 | } 21 | 22 | if (buttons & WPAD_BUTTON_PLUS) { 23 | conv_buttons |= VPAD_BUTTON_PLUS; 24 | } 25 | 26 | if (buttons & WPAD_BUTTON_B) { 27 | conv_buttons |= VPAD_BUTTON_B; 28 | } 29 | 30 | if (buttons & WPAD_BUTTON_A) { 31 | conv_buttons |= VPAD_BUTTON_A; 32 | } 33 | 34 | if (buttons & WPAD_BUTTON_MINUS) { 35 | conv_buttons |= VPAD_BUTTON_MINUS; 36 | } 37 | 38 | if (buttons & WPAD_BUTTON_HOME) { 39 | conv_buttons |= VPAD_BUTTON_HOME; 40 | } 41 | return conv_buttons; 42 | } 43 | 44 | uint32_t remapClassicButtons(uint32_t buttons) { 45 | uint32_t conv_buttons = 0; 46 | 47 | if (buttons & WPAD_CLASSIC_BUTTON_LEFT) { 48 | conv_buttons |= VPAD_BUTTON_LEFT; 49 | } 50 | if (buttons & WPAD_CLASSIC_BUTTON_RIGHT) { 51 | conv_buttons |= VPAD_BUTTON_RIGHT; 52 | } 53 | if (buttons & WPAD_CLASSIC_BUTTON_DOWN) { 54 | conv_buttons |= VPAD_BUTTON_DOWN; 55 | } 56 | if (buttons & WPAD_CLASSIC_BUTTON_UP) { 57 | conv_buttons |= VPAD_BUTTON_UP; 58 | } 59 | if (buttons & WPAD_CLASSIC_BUTTON_PLUS) { 60 | conv_buttons |= VPAD_BUTTON_PLUS; 61 | } 62 | if (buttons & WPAD_CLASSIC_BUTTON_X) { 63 | conv_buttons |= VPAD_BUTTON_X; 64 | } 65 | if (buttons & WPAD_CLASSIC_BUTTON_Y) { 66 | conv_buttons |= VPAD_BUTTON_Y; 67 | } 68 | if (buttons & WPAD_CLASSIC_BUTTON_B) { 69 | conv_buttons |= VPAD_BUTTON_B; 70 | } 71 | if (buttons & WPAD_CLASSIC_BUTTON_A) { 72 | conv_buttons |= VPAD_BUTTON_A; 73 | } 74 | if (buttons & WPAD_CLASSIC_BUTTON_MINUS) { 75 | conv_buttons |= VPAD_BUTTON_MINUS; 76 | } 77 | if (buttons & WPAD_CLASSIC_BUTTON_HOME) { 78 | conv_buttons |= VPAD_BUTTON_HOME; 79 | } 80 | if (buttons & WPAD_CLASSIC_BUTTON_ZR) { 81 | conv_buttons |= VPAD_BUTTON_ZR; 82 | } 83 | if (buttons & WPAD_CLASSIC_BUTTON_ZL) { 84 | conv_buttons |= VPAD_BUTTON_ZL; 85 | } 86 | if (buttons & WPAD_CLASSIC_BUTTON_R) { 87 | conv_buttons |= VPAD_BUTTON_R; 88 | } 89 | if (buttons & WPAD_CLASSIC_BUTTON_L) { 90 | conv_buttons |= VPAD_BUTTON_L; 91 | } 92 | return conv_buttons; 93 | } 94 | 95 | uint32_t remapProButtons(uint32_t buttons) { 96 | uint32_t conv_buttons = 0; 97 | 98 | if (buttons & WPAD_PRO_BUTTON_LEFT) { 99 | conv_buttons |= VPAD_BUTTON_LEFT; 100 | } 101 | if (buttons & WPAD_PRO_BUTTON_RIGHT) { 102 | conv_buttons |= VPAD_BUTTON_RIGHT; 103 | } 104 | if (buttons & WPAD_PRO_BUTTON_DOWN) { 105 | conv_buttons |= VPAD_BUTTON_DOWN; 106 | } 107 | if (buttons & WPAD_PRO_BUTTON_UP) { 108 | conv_buttons |= VPAD_BUTTON_UP; 109 | } 110 | if (buttons & WPAD_PRO_BUTTON_PLUS) { 111 | conv_buttons |= VPAD_BUTTON_PLUS; 112 | } 113 | if (buttons & WPAD_PRO_BUTTON_X) { 114 | conv_buttons |= VPAD_BUTTON_X; 115 | } 116 | if (buttons & WPAD_PRO_BUTTON_Y) { 117 | conv_buttons |= VPAD_BUTTON_Y; 118 | } 119 | if (buttons & WPAD_PRO_BUTTON_B) { 120 | conv_buttons |= VPAD_BUTTON_B; 121 | } 122 | if (buttons & WPAD_PRO_BUTTON_A) { 123 | conv_buttons |= VPAD_BUTTON_A; 124 | } 125 | if (buttons & WPAD_PRO_BUTTON_MINUS) { 126 | conv_buttons |= VPAD_BUTTON_MINUS; 127 | } 128 | if (buttons & WPAD_PRO_BUTTON_HOME) { 129 | conv_buttons |= VPAD_BUTTON_HOME; 130 | } 131 | if (buttons & WPAD_PRO_TRIGGER_ZR) { 132 | conv_buttons |= VPAD_BUTTON_ZR; 133 | } 134 | if (buttons & WPAD_PRO_TRIGGER_ZL) { 135 | conv_buttons |= VPAD_BUTTON_ZL; 136 | } 137 | if (buttons & WPAD_PRO_TRIGGER_R) { 138 | conv_buttons |= VPAD_BUTTON_R; 139 | } 140 | if (buttons & WPAD_PRO_TRIGGER_L) { 141 | conv_buttons |= VPAD_BUTTON_L; 142 | } 143 | if (buttons & WPAD_PRO_BUTTON_STICK_L) { 144 | conv_buttons |= VPAD_BUTTON_STICK_L; 145 | } 146 | if (buttons & WPAD_PRO_BUTTON_STICK_R) { 147 | conv_buttons |= VPAD_BUTTON_STICK_R; 148 | } 149 | if (buttons & WPAD_PRO_RESERVED) { 150 | conv_buttons |= VPAD_BUTTON_RESERVED_BIT; 151 | } 152 | return conv_buttons; 153 | } 154 | -------------------------------------------------------------------------------- /source/re_nfpii/TagManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Tag.hpp" 3 | #include "TagStream.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace re::nfpii { 14 | using nn::Result; 15 | using namespace nn::nfp; 16 | 17 | class TagManager { 18 | public: 19 | TagManager(); 20 | virtual ~TagManager(); 21 | 22 | bool IsInitialized(); 23 | 24 | Result Initialize(); 25 | Result Finalize(); 26 | 27 | Result GetNfpState(NfpState& state); 28 | 29 | Result SetActivateEvent(OSEvent* event); 30 | Result SetDeactivateEvent(OSEvent* event); 31 | 32 | Result StartDetection(); 33 | Result StopDetection(); 34 | 35 | Result Mount(); 36 | Result MountReadOnly(); 37 | Result MountRom(); 38 | Result Unmount(); 39 | 40 | Result Flush(); 41 | Result Restore(); 42 | Result Format(const void* data, int32_t size); 43 | 44 | bool IsExistApplicationArea(); 45 | Result CreateApplicationArea(ApplicationAreaCreateInfo const& createInfo); 46 | Result WriteApplicationArea(const void* data, uint32_t size, const TagId* tagId); 47 | Result OpenApplicationArea(uint32_t id); 48 | Result ReadApplicationArea(void* outData, uint32_t size); 49 | Result DeleteApplicationArea(); 50 | 51 | Result GetTagInfo(TagInfo* outTagInfo); 52 | Result GetNfpCommonInfo(CommonInfo* outCommonInfo); 53 | Result GetNfpRegisterInfo(RegisterInfo* outRegisterInfo); 54 | Result DeleteNfpRegisterInfo(); 55 | Result GetNfpReadOnlyInfo(ReadOnlyInfo* outReadOnlyInfo); 56 | Result GetNfpRomInfo(RomInfo* outRomInfo); 57 | Result GetNfpAdminInfo(AdminInfo* outAdminInfo); 58 | Result SetNfpRegisterInfo(RegisterInfoSet const& info); 59 | 60 | Result OpenStream(uint32_t id); 61 | Result CloseStream(); 62 | Result ReadStream(void* data, uint32_t size); 63 | Result WriteStream(const void* data, uint32_t size); 64 | 65 | private: 66 | void Reset(); 67 | 68 | bool UpdateInternal(); 69 | void SetNfpState(NfpState state); 70 | 71 | void Activate(); 72 | void Deactivate(); 73 | 74 | Result VerifyTagInfo(); 75 | Result GetTagInfo(TagInfo* outTagInfo, uint8_t index); 76 | 77 | Result MountTag(); 78 | 79 | Result GetAppAreaInfo(Tag::AppAreaInfo* outInfo, uint32_t* outNumAreas, int32_t maxAreas); 80 | 81 | bool CheckRegisterInfo(); 82 | 83 | static void NfcProcCallback(OSAlarm* alarm, OSContext* context); 84 | 85 | public: // custom 86 | void SetEmulationState(NfpiiEmulationState state) 87 | { 88 | emulationState = state; 89 | pendingRemove = state == NFPII_EMULATION_OFF; 90 | } 91 | 92 | NfpiiEmulationState GetEmulationState() const 93 | { 94 | return emulationState; 95 | } 96 | 97 | void SetUUIDRandomizationState(NfpiiUUIDRandomizationState state) 98 | { 99 | uuidRandomizationState = state; 100 | } 101 | 102 | NfpiiUUIDRandomizationState GetUUIDRandomizationState() const 103 | { 104 | return uuidRandomizationState; 105 | } 106 | 107 | void SetTagEmulationPath(std::string path) 108 | { 109 | tagEmulationPath = path; 110 | } 111 | 112 | std::string const& GetTagEmulationPath() const 113 | { 114 | return tagEmulationPath; 115 | } 116 | 117 | void SetRemoveAfterSeconds(float secs) 118 | { 119 | this->removeAfterSeconds = secs; 120 | } 121 | 122 | void SetAmiiboSettings(bool inAmiiboSettings) 123 | { 124 | this->inAmiiboSettings = inAmiiboSettings; 125 | } 126 | 127 | Result LoadTag(); 128 | void HandleTagUpdates(); 129 | 130 | NFCError QueueNFCGetTagInfo(NFCGetTagInfoCallbackFn callback, void* arg); 131 | void HandleNFCGetTagInfo(); 132 | 133 | private: 134 | // +0x0 135 | OSMutex mutex; 136 | 137 | // +0xe0 138 | OSAlarm nfcProcAlarm; 139 | 140 | // +0x15c 141 | OSEvent* activateEvent; 142 | OSEvent* deactivateEvent; 143 | 144 | // +0x164 145 | NfpState nfpState; 146 | // +0x170 147 | Tag::State tagStates[3]; 148 | 149 | // +0x1d0 150 | bool hasTag; 151 | uint8_t currentTagIndex; 152 | // +0x1d6 153 | bool readOnly; 154 | 155 | // +0x1d8 156 | TagStream::TagStreamImpl tagStreamImpl; 157 | Tag* currentTag; 158 | Tag tag; 159 | TagStream tagStream; 160 | 161 | private: // custom 162 | NfpiiEmulationState emulationState; 163 | NfpiiUUIDRandomizationState uuidRandomizationState; 164 | std::string tagEmulationPath; 165 | float removeAfterSeconds; 166 | bool pendingRemove; 167 | OSTime pendingTagRemoveTime; 168 | 169 | bool pendingTagInfo; 170 | NFCGetTagInfoCallbackFn nfcTagInfoCallback; 171 | void* nfcTagInfoArg; 172 | 173 | bool inAmiiboSettings; 174 | OSTime amiiboSettingsReattachTimeout; 175 | }; 176 | 177 | } // namespace re::nfpii 178 | -------------------------------------------------------------------------------- /plugin/source/quick_select.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "config/ConfigItemSelectAmiibo.hpp" 8 | #include "utils/input.h" 9 | #include "debug/logger.h" 10 | 11 | extern "C" uint32_t VPADGetButtonProcMode(VPADChan chan); 12 | 13 | extern uint32_t currentQuickSelectCombination; 14 | static uint32_t currentQuickSelectIndex = 0; 15 | 16 | extern uint32_t currentToggleEmulationCombination; 17 | 18 | static uint32_t sWPADLastButtonHold[4]; 19 | static uint32_t sWasHoldForXFrame[4]; 20 | static uint32_t sWasHoldForXFrameGamePad; 21 | 22 | static void cycleQuickSelect() 23 | { 24 | if (ConfigItemSelectAmiibo_GetFavorites().size() == 0) { 25 | return; 26 | } 27 | 28 | currentQuickSelectIndex++; 29 | if (currentQuickSelectIndex >= ConfigItemSelectAmiibo_GetFavorites().size()) { 30 | currentQuickSelectIndex = 0; 31 | } 32 | 33 | std::string path = ConfigItemSelectAmiibo_GetFavorites()[currentQuickSelectIndex]; 34 | NfpiiSetTagEmulationPath(path.c_str()); 35 | NfpiiSetEmulationState(NFPII_EMULATION_ON); 36 | 37 | std::string name = path.substr(path.find_last_of("/") + 1); 38 | std::string notifText = "re_nfpii: Selected \"" + name + "\""; 39 | 40 | if (NotificationModule_InitLibrary() == NOTIFICATION_MODULE_RESULT_SUCCESS) { 41 | NotificationModule_AddInfoNotification(notifText.c_str()); 42 | } 43 | } 44 | 45 | 46 | static void toggleEmulation() 47 | { 48 | NfpiiEmulationState state = NfpiiGetEmulationState(); 49 | std::string notifText; 50 | if (state == NFPII_EMULATION_ON) { 51 | NfpiiSetEmulationState(NFPII_EMULATION_OFF); 52 | notifText = "re_nfpii: Disabled emulation"; 53 | } else { 54 | NfpiiSetEmulationState(NFPII_EMULATION_ON); 55 | notifText = "re_nfpii: Enabled emulation"; 56 | }; 57 | 58 | if (NotificationModule_InitLibrary() == NOTIFICATION_MODULE_RESULT_SUCCESS) { 59 | NotificationModule_AddInfoNotification(notifText.c_str()); 60 | } 61 | } 62 | 63 | DECL_FUNCTION(int32_t, VPADRead, VPADChan chan, VPADStatus* buffer, uint32_t buffer_size, VPADReadError* error) 64 | { 65 | VPADReadError real_error; 66 | int32_t result = real_VPADRead(chan, buffer, buffer_size, &real_error); 67 | 68 | if (result > 0 && real_error == VPAD_READ_SUCCESS) { 69 | uint32_t end = 1; 70 | // Fix games like TP HD 71 | if (VPADGetButtonProcMode(chan) == 1) { 72 | end = result; 73 | } 74 | bool found = false; 75 | bool foundTe = false; 76 | 77 | for (uint32_t i = 0; i < end; i++) { 78 | if (currentQuickSelectCombination != 0 && (((buffer[i].hold & 0x000FFFFF) & currentQuickSelectCombination) == currentQuickSelectCombination)) { 79 | found = true; 80 | break; 81 | } else if (currentToggleEmulationCombination != 0 && (((buffer[i].hold & 0x000FFFFF) & currentToggleEmulationCombination) == currentToggleEmulationCombination)) { 82 | foundTe = true; 83 | break; 84 | } 85 | } 86 | if (found) { 87 | if (sWasHoldForXFrameGamePad == 0) { 88 | cycleQuickSelect(); 89 | } 90 | sWasHoldForXFrameGamePad++; 91 | } else if (foundTe) { 92 | if (sWasHoldForXFrameGamePad == 0) { 93 | toggleEmulation(); 94 | } 95 | sWasHoldForXFrameGamePad++; 96 | } else { 97 | sWasHoldForXFrameGamePad = 0; 98 | } 99 | } 100 | 101 | if (error) { 102 | *error = real_error; 103 | } 104 | return result; 105 | } 106 | 107 | DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController* data) 108 | { 109 | real_WPADRead(chan, data); 110 | 111 | if (chan >= 0 && chan < 4) { 112 | if (data[0].err == 0) { 113 | if (data[0].extensionType != 0xFF) { 114 | uint32_t curButtonHold = 0; 115 | if (data[0].extensionType == WPAD_EXT_CORE || data[0].extensionType == WPAD_EXT_NUNCHUK) { 116 | // button data is in the first 2 bytes for wiimotes 117 | curButtonHold = remapWiiMoteButtons(((uint16_t *) data)[0]); 118 | } else if (data[0].extensionType == WPAD_EXT_CLASSIC) { 119 | curButtonHold = remapClassicButtons(((uint32_t *) data)[10] & 0xFFFF); 120 | } else if (data[0].extensionType == WPAD_EXT_PRO_CONTROLLER) { 121 | curButtonHold = remapProButtons(data[0].buttons); 122 | } 123 | 124 | if ((currentQuickSelectCombination != 0 && (curButtonHold & currentQuickSelectCombination) == currentQuickSelectCombination)) { 125 | if (sWasHoldForXFrame[chan] == 0) { 126 | cycleQuickSelect(); 127 | } 128 | sWasHoldForXFrame[chan]++; 129 | } else if ((currentToggleEmulationCombination != 0 && (curButtonHold & currentToggleEmulationCombination) == currentToggleEmulationCombination)) { 130 | if (sWasHoldForXFrame[chan] == 0) { 131 | toggleEmulation(); 132 | } 133 | sWasHoldForXFrame[chan]++; 134 | } else { 135 | sWasHoldForXFrame[chan] = 0; 136 | } 137 | 138 | sWPADLastButtonHold[chan] = curButtonHold; 139 | } 140 | } 141 | } 142 | } 143 | 144 | WUPS_MUST_REPLACE(VPADRead, WUPS_LOADER_LIBRARY_VPAD, VPADRead); 145 | WUPS_MUST_REPLACE(WPADRead, WUPS_LOADER_LIBRARY_PADSCORE, WPADRead); 146 | -------------------------------------------------------------------------------- /plugin/Makefile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | 11 | include $(DEVKITPRO)/wups/share/wups_rules 12 | 13 | WUT_ROOT := $(DEVKITPRO)/wut 14 | WUMS_ROOT := $(DEVKITPRO)/wums 15 | 16 | #------------------------------------------------------------------------------- 17 | # TARGET is the name of the output 18 | # BUILD is the directory where object files & intermediate files will be placed 19 | # SOURCES is a list of directories containing source code 20 | # DATA is a list of directories containing data files 21 | # INCLUDES is a list of directories containing header files 22 | #------------------------------------------------------------------------------- 23 | TARGET := re_nfpii 24 | BUILD := build 25 | SOURCES := source \ 26 | source/config \ 27 | source/debug \ 28 | source/utils 29 | DATA := data 30 | INCLUDES := include \ 31 | ../include \ 32 | source 33 | 34 | #------------------------------------------------------------------------------- 35 | # options for code generation 36 | #------------------------------------------------------------------------------- 37 | CFLAGS := -Wall -O2 -ffunction-sections \ 38 | $(MACHDEP) 39 | 40 | CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ \ 41 | -DVERSION_MAJOR=$(VERSION_MAJOR) \ 42 | -DVERSION_MINOR=$(VERSION_MINOR) \ 43 | -DVERSION_PATCH=$(VERSION_PATCH) 44 | 45 | CFLAGS += -Wno-address-of-packed-member 46 | 47 | CXXFLAGS := $(CFLAGS) -std=gnu++20 48 | CFLAGS += -std=gnu11 49 | 50 | ASFLAGS := $(ARCH) 51 | LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS) 52 | 53 | LIBS := -lnotifications -lwups -lwut 54 | 55 | #------------------------------------------------------------------------------- 56 | # list of directories containing libraries, this must be the top level 57 | # containing include and lib 58 | #------------------------------------------------------------------------------- 59 | LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT) 60 | 61 | #------------------------------------------------------------------------------- 62 | # no real need to edit anything past this point unless you need to add additional 63 | # rules for different file extensions 64 | #------------------------------------------------------------------------------- 65 | ifneq ($(BUILD),$(notdir $(CURDIR))) 66 | #------------------------------------------------------------------------------- 67 | 68 | export OUTPUT := $(CURDIR)/$(TARGET) 69 | export TOPDIR := $(CURDIR) 70 | 71 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 72 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 73 | 74 | export DEPSDIR := $(CURDIR)/$(BUILD) 75 | 76 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 77 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 78 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 79 | DEFFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.def))) 80 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 81 | 82 | #------------------------------------------------------------------------------- 83 | # use CXX for linking C++ projects, CC for standard C 84 | #------------------------------------------------------------------------------- 85 | ifeq ($(strip $(CPPFILES)),) 86 | #------------------------------------------------------------------------------- 87 | export LD := $(CC) 88 | #------------------------------------------------------------------------------- 89 | else 90 | #------------------------------------------------------------------------------- 91 | export LD := $(CXX) 92 | #------------------------------------------------------------------------------- 93 | endif 94 | #------------------------------------------------------------------------------- 95 | 96 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 97 | export OFILES_SRC := $(DEFFILES:.def=.o) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 98 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 99 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 100 | 101 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 102 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 103 | -I$(CURDIR)/$(BUILD) 104 | 105 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 106 | 107 | .PHONY: $(BUILD) clean all 108 | 109 | #------------------------------------------------------------------------------- 110 | all: $(BUILD) 111 | 112 | $(BUILD): 113 | @[ -d $@ ] || mkdir -p $@ 114 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 115 | 116 | #------------------------------------------------------------------------------- 117 | clean: 118 | @echo clean ... 119 | @rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf 120 | 121 | #------------------------------------------------------------------------------- 122 | else 123 | .PHONY: all 124 | 125 | DEPENDS := $(OFILES:.o=.d) 126 | 127 | #------------------------------------------------------------------------------- 128 | # main targets 129 | #------------------------------------------------------------------------------- 130 | all : $(OUTPUT).wps 131 | 132 | $(OUTPUT).wps : $(OUTPUT).elf 133 | $(OUTPUT).elf : $(OFILES) 134 | 135 | $(OFILES_SRC) : $(HFILES_BIN) 136 | 137 | #------------------------------------------------------------------------------- 138 | # you need a rule like this for each extension you use as binary data 139 | #------------------------------------------------------------------------------- 140 | %.bin.o %_bin.h : %.bin 141 | #------------------------------------------------------------------------------- 142 | @echo $(notdir $<) 143 | @$(bin2o) 144 | #------------------------------------------------------------------------------- 145 | %.o: %.def 146 | #------------------------------------------------------------------------------- 147 | $(SILENTMSG) $(notdir $<) 148 | $(SILENTCMD)rplimportgen $< $*.s $*.ld $(ERROR_FILTER) 149 | $(SILENTCMD)$(CC) -x assembler-with-cpp $(ASFLAGS) -c $*.s -o $@ $(ERROR_FILTER) 150 | 151 | -include $(DEPENDS) 152 | 153 | #------------------------------------------------------------------------------- 154 | endif 155 | #------------------------------------------------------------------------------- 156 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | 11 | include $(DEVKITPRO)/wums/share/wums_rules 12 | 13 | WUMS_ROOT := $(DEVKITPRO)/wums 14 | WUT_ROOT := $(DEVKITPRO)/wut 15 | 16 | #------------------------------------------------------------------------------- 17 | # Version used for the module and plugin 18 | #------------------------------------------------------------------------------- 19 | export VERSION_MAJOR := 0 20 | export VERSION_MINOR := 3 21 | export VERSION_PATCH := 0 22 | 23 | #------------------------------------------------------------------------------- 24 | # TARGET is the name of the output 25 | # BUILD is the directory where object files & intermediate files will be placed 26 | # SOURCES is a list of directories containing source code 27 | # DATA is a list of directories containing data files 28 | # INCLUDES is a list of directories containing header files 29 | #------------------------------------------------------------------------------- 30 | TARGET := re_nfpii 31 | BUILD := build 32 | SOURCES := source \ 33 | source/config \ 34 | source/debug \ 35 | source/re_nfpii \ 36 | source/utils 37 | DATA := data 38 | INCLUDES := include \ 39 | source 40 | 41 | #------------------------------------------------------------------------------- 42 | # options for code generation 43 | #------------------------------------------------------------------------------- 44 | CFLAGS := -Wall -Os -ffunction-sections \ 45 | $(MACHDEP) 46 | 47 | CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ \ 48 | -DVERSION_MAJOR=$(VERSION_MAJOR) \ 49 | -DVERSION_MINOR=$(VERSION_MINOR) \ 50 | -DVERSION_PATCH=$(VERSION_PATCH) 51 | 52 | CFLAGS += -Wno-address-of-packed-member 53 | 54 | CXXFLAGS := $(CFLAGS) -std=gnu++20 55 | CFLAGS += -std=gnu11 56 | 57 | ASFLAGS := $(ARCH) 58 | LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUMSSPECS) 59 | 60 | LIBS := -lwums -lwut 61 | 62 | #------------------------------------------------------------------------------- 63 | # list of directories containing libraries, this must be the top level 64 | # containing include and lib 65 | #------------------------------------------------------------------------------- 66 | LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUT_ROOT) 67 | 68 | #------------------------------------------------------------------------------- 69 | # no real need to edit anything past this point unless you need to add additional 70 | # rules for different file extensions 71 | #------------------------------------------------------------------------------- 72 | ifneq ($(BUILD),$(notdir $(CURDIR))) 73 | #------------------------------------------------------------------------------- 74 | 75 | export OUTPUT := $(CURDIR)/$(TARGET) 76 | export TOPDIR := $(CURDIR) 77 | 78 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 79 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 80 | 81 | export DEPSDIR := $(CURDIR)/$(BUILD) 82 | 83 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 84 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 85 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 86 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 87 | 88 | #------------------------------------------------------------------------------- 89 | # use CXX for linking C++ projects, CC for standard C 90 | #------------------------------------------------------------------------------- 91 | ifeq ($(strip $(CPPFILES)),) 92 | #------------------------------------------------------------------------------- 93 | export LD := $(CC) 94 | #------------------------------------------------------------------------------- 95 | else 96 | #------------------------------------------------------------------------------- 97 | export LD := $(CXX) 98 | #------------------------------------------------------------------------------- 99 | endif 100 | #------------------------------------------------------------------------------- 101 | 102 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 103 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 104 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 105 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 106 | 107 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 108 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 109 | -I$(CURDIR)/$(BUILD) 110 | 111 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 112 | 113 | .PHONY: $(BUILD) clean all 114 | 115 | #------------------------------------------------------------------------------- 116 | all: $(BUILD) 117 | 118 | dist: all 119 | mkdir -p dist/modules/ 120 | cp *.wms dist/modules/ 121 | mkdir -p dist/plugins/ 122 | cp plugin/*.wps dist/plugins/ 123 | 124 | $(BUILD): 125 | @[ -d $@ ] || mkdir -p $@ 126 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 127 | @$(MAKE) --no-print-directory -C $(CURDIR)/plugin -f $(CURDIR)/plugin/Makefile 128 | 129 | #------------------------------------------------------------------------------- 130 | clean: 131 | @echo clean ... 132 | @rm -fr dist $(BUILD) $(TARGET).wms $(TARGET).elf 133 | @$(MAKE) --no-print-directory -C $(CURDIR)/plugin -f $(CURDIR)/plugin/Makefile clean 134 | 135 | #------------------------------------------------------------------------------- 136 | else 137 | .PHONY: all 138 | 139 | DEPENDS := $(OFILES:.o=.d) 140 | 141 | #------------------------------------------------------------------------------- 142 | # main targets 143 | #------------------------------------------------------------------------------- 144 | all : $(OUTPUT).wms 145 | 146 | $(OUTPUT).wms : $(OUTPUT).elf 147 | $(OUTPUT).elf : $(OFILES) 148 | 149 | $(OFILES_SRC) : $(HFILES_BIN) 150 | 151 | #------------------------------------------------------------------------------- 152 | # you need a rule like this for each extension you use as binary data 153 | #------------------------------------------------------------------------------- 154 | %.bin.o %_bin.h : %.bin 155 | #------------------------------------------------------------------------------- 156 | @echo $(notdir $<) 157 | @$(bin2o) 158 | 159 | -include $(DEPENDS) 160 | 161 | #------------------------------------------------------------------------------- 162 | endif 163 | #------------------------------------------------------------------------------- 164 | -------------------------------------------------------------------------------- /source/re_nfpii/Cabinet.cpp: -------------------------------------------------------------------------------- 1 | #include "Cabinet.hpp" 2 | #include "re_nfpii.hpp" 3 | #include "Utils.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace re::nfpii::Cabinet { 11 | 12 | // The imported functions for dynamic modules won't be correct while in an applet, 13 | // so we dynload them as a workaround. 14 | int32_t (*dyn_SYSDeserializeSysArgs)(SYSDeserializeCallback callback, void *userArg); 15 | int32_t (*dyn__SYSDirectlyReturnToCaller)(SYSStandardArgsOut *arg); 16 | BOOL (*dyn__SYSDeserializeStandardArg)(SYSDeserializeArg *deserializeArg, SYSStandardArgs *standardArg); 17 | 18 | static void loadSysappFunctionPointers() 19 | { 20 | OSDynLoad_Module sysappModule; 21 | OSDynLoad_Acquire("sysapp.rpl", &sysappModule); 22 | 23 | OSDynLoad_FindExport(sysappModule, OS_DYNLOAD_EXPORT_FUNC, "SYSDeserializeSysArgs", (void**) &dyn_SYSDeserializeSysArgs); 24 | OSDynLoad_FindExport(sysappModule, OS_DYNLOAD_EXPORT_FUNC, "_SYSDirectlyReturnToCaller", (void**) &dyn__SYSDirectlyReturnToCaller); 25 | OSDynLoad_FindExport(sysappModule, OS_DYNLOAD_EXPORT_FUNC, "_SYSDeserializeStandardArg", (void**) &dyn__SYSDeserializeStandardArg); 26 | } 27 | 28 | static void OnCabinetEnter() 29 | { 30 | // Need to dynload sysapp pointers while in an applet 31 | loadSysappFunctionPointers(); 32 | 33 | tagManager.SetAmiiboSettings(true); 34 | } 35 | 36 | static void OnCabinetLeave() 37 | { 38 | tagManager.SetAmiiboSettings(false); 39 | } 40 | 41 | static void AmiiboSettingsDeserializationCallback(SYSDeserializeArg* arg, void* usrarg) 42 | { 43 | AmiiboSettingsArgs* amiiboArgs = (AmiiboSettingsArgs*) usrarg; 44 | if (!amiiboArgs) { 45 | return; 46 | } 47 | 48 | if (dyn__SYSDeserializeStandardArg(arg, &amiiboArgs->standardArgs)) { 49 | return; 50 | } 51 | 52 | if (strcmp(arg->argName, "amb:mode") == 0) { 53 | OSBlockMove(&amiiboArgs->argsIn.mode, arg->data, sizeof(amiiboArgs->argsIn.mode), TRUE); 54 | return; 55 | } 56 | 57 | if (strcmp(arg->argName, "amb:tag_info") == 0) { 58 | OSBlockMove(&amiiboArgs->argsIn.tag_info, arg->data, sizeof(amiiboArgs->argsIn.tag_info), TRUE); 59 | return; 60 | } 61 | 62 | if (strcmp(arg->argName, "amb:is_registered") == 0) { 63 | OSBlockMove(&amiiboArgs->argsIn.is_registered, arg->data, sizeof(amiiboArgs->argsIn.is_registered), TRUE); 64 | return; 65 | } 66 | 67 | if (strcmp(arg->argName, "amb:register_info") == 0) { 68 | OSBlockMove(&amiiboArgs->argsIn.register_info, arg->data, sizeof(amiiboArgs->argsIn.register_info), TRUE); 69 | return; 70 | } 71 | 72 | if (strcmp(arg->argName, "amb:common_info") == 0) { 73 | OSBlockMove(&amiiboArgs->argsIn.common_info, arg->data, sizeof(amiiboArgs->argsIn.common_info), TRUE); 74 | return; 75 | } 76 | } 77 | 78 | Result GetArgs(AmiiboSettingsArgs* args) 79 | { 80 | OnCabinetEnter(); 81 | 82 | if (!args) { 83 | return NFP_INVALID_PARAM; 84 | } 85 | 86 | memset(args, 0, sizeof(*args)); 87 | if (dyn_SYSDeserializeSysArgs(AmiiboSettingsDeserializationCallback, args) != 0) { 88 | return NFP_SYSTEM_ERROR; 89 | } 90 | 91 | return NFP_SUCCESS; 92 | } 93 | 94 | struct AmiiboSettingsReturnArg { 95 | char argName[0x10]; 96 | AmiiboSettingsResult result; 97 | }; 98 | 99 | static bool VerifyResultValueBlock(SYSArgDataBlock const& block) 100 | { 101 | if (block.type != SYS_ARG_TYPE_DATA) { 102 | return false; 103 | } 104 | 105 | if (block.data.size != sizeof(AmiiboSettingsResult) + sizeof("ambResultValue:")) { 106 | return false; 107 | } 108 | 109 | if (memcmp(block.data.ptr, "ambResultValue:", sizeof("ambResultValue:")) != 0) { 110 | return false; 111 | } 112 | 113 | return true; 114 | } 115 | 116 | Result GetResult(AmiiboSettingsResult* result, SYSArgDataBlock const& block) 117 | { 118 | if (!result) { 119 | return NFP_INVALID_PARAM; 120 | } 121 | 122 | Initialize(); 123 | 124 | if (!VerifyResultValueBlock(block)) { 125 | return RESULT(0xa1b12c00); 126 | } 127 | 128 | AmiiboSettingsReturnArg* returnArg = (AmiiboSettingsReturnArg*) block.data.ptr; 129 | OSBlockMove(result, &returnArg->result, sizeof(returnArg->result), TRUE); 130 | 131 | return NFP_SUCCESS; 132 | } 133 | 134 | Result InitializeArgsIn(AmiiboSettingsArgsIn* args) 135 | { 136 | if (!args) { 137 | return NFP_INVALID_PARAM; 138 | } 139 | 140 | args->mode = AmiiboSettingsMode::Register; 141 | memset(&args->tag_info, 0, sizeof(args->tag_info)); 142 | args->is_registered = false; 143 | memset(&args->padding, 0, sizeof(args->padding)); 144 | memset(&args->register_info, 0, sizeof(args->register_info)); 145 | memset(&args->common_info, 0, sizeof(args->common_info)); 146 | memset(args->reserved, 0, sizeof(args->reserved)); 147 | 148 | return NFP_SUCCESS; 149 | } 150 | 151 | Result ReturnToCallerWithResult(AmiiboSettingsResult const& result) 152 | { 153 | AmiiboSettingsReturnArg returnArg; 154 | OSBlockMove(returnArg.argName, "ambResultValue:", sizeof("ambResultValue:"), TRUE); 155 | OSBlockMove(&returnArg.result, &result, sizeof(result), TRUE); 156 | memset(returnArg.result.reserved, 0, sizeof(returnArg.result.reserved)); 157 | 158 | SYSStandardArgsOut args; 159 | args.data = &returnArg; 160 | args.size = sizeof(returnArg); 161 | 162 | Finalize(); 163 | 164 | OnCabinetLeave(); 165 | 166 | if (dyn__SYSDirectlyReturnToCaller(&args) != 0) { 167 | return NFP_SYSTEM_ERROR; 168 | } 169 | 170 | return NFP_SUCCESS; 171 | } 172 | 173 | static bool VerifyAmiiboSettingsArgs(AmiiboSettingsArgsIn const& args) 174 | { 175 | if (args.mode != AmiiboSettingsMode::Register && 176 | args.mode != AmiiboSettingsMode::DeleteGameData && 177 | args.mode != AmiiboSettingsMode::Restore && 178 | args.mode != (AmiiboSettingsMode) 0x64) { 179 | return false; 180 | } 181 | 182 | if (!CheckZero(&args.tag_info.reserved1, sizeof(args.tag_info.reserved1))) { 183 | return false; 184 | } 185 | if (!CheckZero(&args.padding, sizeof(args.padding))) { 186 | return false; 187 | } 188 | if (!CheckZero(&args.common_info.reserved, sizeof(args.common_info.reserved))) { 189 | return false; 190 | } 191 | if (!CheckZero(&args.register_info.reserved, sizeof(args.register_info.reserved))) { 192 | return false; 193 | } 194 | if (!CheckZero(&args.reserved, sizeof(args.reserved))) { 195 | return false; 196 | } 197 | 198 | return true; 199 | } 200 | 201 | Result SwitchToCabinet(AmiiboSettingsArgsIn const& args, const char* standardArg, uint32_t standardArgSize) 202 | { 203 | if (!VerifyAmiiboSettingsArgs(args)) { 204 | return NFP_INVALID_PARAM; 205 | } 206 | 207 | SYSClearSysArgs(); 208 | SYSStandardArgsIn standardArgs; 209 | standardArgs.argString = standardArg; 210 | standardArgs.size = standardArgSize; 211 | if (_SYSSerializeStandardArgsIn(&standardArgs) < 0) { 212 | return NFP_INVALID_PARAM; 213 | } 214 | 215 | if (SYSSerializeSysArgs("amb:mode", &args.mode, sizeof(args.mode)) < 0) { 216 | return NFP_INVALID_PARAM; 217 | } 218 | 219 | if (SYSSerializeSysArgs("amb:tag_info", &args.tag_info, sizeof(args.tag_info)) < 0) { 220 | return NFP_INVALID_PARAM; 221 | } 222 | 223 | if (SYSSerializeSysArgs("amb:is_registered", &args.is_registered, sizeof(args.is_registered)) < 0) { 224 | return NFP_INVALID_PARAM; 225 | } 226 | 227 | if (SYSSerializeSysArgs("amb:register_info", &args.register_info, sizeof(args.register_info)) < 0) { 228 | return NFP_INVALID_PARAM; 229 | } 230 | 231 | if (SYSSerializeSysArgs("amb:common_info", &args.common_info, sizeof(args.common_info)) < 0) { 232 | return NFP_INVALID_PARAM; 233 | } 234 | 235 | Finalize(); 236 | 237 | // Switch to amiibo settings 238 | if (_SYSDirectlySwitchTo(SYSAPP_PFID_CABINETU) != 0) { 239 | return NFP_SYSTEM_ERROR; 240 | } 241 | 242 | return NFP_SUCCESS; 243 | } 244 | 245 | } // namespace re::nfpii::Cabinet 246 | -------------------------------------------------------------------------------- /plugin/source/config/ConfigItemLog.cpp: -------------------------------------------------------------------------------- 1 | #include "ConfigItemLog.hpp" 2 | #include "utils/DrawUtils.hpp" 3 | #include "utils/input.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #define COLOR_BACKGROUND Color(238, 238, 238, 255) 16 | #define COLOR_TEXT Color(51, 51, 51, 255) 17 | #define COLOR_TEXT2 Color(72, 72, 72, 255) 18 | #define COLOR_TEXT_WARN Color(255, 204, 0, 255) 19 | #define COLOR_TEXT_ERROR Color(204, 51, 0, 255) 20 | #define COLOR_WHITE Color(0xFFFFFFFF) 21 | #define COLOR_BLACK Color(0, 0, 0, 255) 22 | 23 | // max log entries which can be stored 24 | #define MAX_LOG_ENTRIES 100 25 | 26 | // max log entries displayed on page 27 | #define MAX_ENTRIES_PER_PAGE 20 28 | 29 | struct LogEntry { 30 | LogType type; 31 | std::string text; 32 | }; 33 | 34 | static OSMutex logMutex; 35 | static std::vector logEntries; 36 | 37 | void ConfigItemLog_Init(void) 38 | { 39 | OSInitMutex(&logMutex); 40 | } 41 | 42 | void ConfigItemLog_PrintType(LogType type, const char* text) 43 | { 44 | OSLockMutex(&logMutex); 45 | 46 | LogEntry entry; 47 | entry.type = type; 48 | entry.text = text; 49 | logEntries.push_back(entry); 50 | 51 | if (logEntries.size() > MAX_LOG_ENTRIES) { 52 | logEntries.erase(logEntries.begin(), logEntries.end() - MAX_LOG_ENTRIES); 53 | logEntries[0] = LogEntry(LOG_TYPE_NORMAL, "..."); 54 | } 55 | 56 | OSUnlockMutex(&logMutex); 57 | } 58 | 59 | static std::string getLogStats(ConfigItemLog* item) 60 | { 61 | uint32_t numErrors = 0; 62 | uint32_t numWarns = 0; 63 | for (LogEntry& e : logEntries) { 64 | if (e.type == LOG_TYPE_ERROR) { 65 | numErrors++; 66 | } else if (e.type == LOG_TYPE_WARN) { 67 | numWarns++; 68 | } 69 | } 70 | 71 | return std::to_string(numErrors) + " Error(s), " + std::to_string(numWarns) + " Warning(s)"; 72 | } 73 | 74 | static void enterLogViewer(ConfigItemLog* item) 75 | { 76 | // Init DrawUtils 77 | DrawUtils::initBuffers(); 78 | if (!DrawUtils::initFont()) { 79 | return; 80 | } 81 | 82 | uint32_t start = 0; 83 | uint32_t end = std::min(logEntries.size(), (uint32_t) MAX_ENTRIES_PER_PAGE); 84 | 85 | bool redraw = true; 86 | 87 | VPADStatus vpad{}; 88 | VPADReadError vpadError; 89 | KPADStatus kpad{}; 90 | KPADError kpadError; 91 | 92 | while (true) { 93 | uint32_t buttonsTriggered = 0; 94 | 95 | VPADRead(VPAD_CHAN_0, &vpad, 1, &vpadError); 96 | if (vpadError == VPAD_READ_SUCCESS) { 97 | buttonsTriggered = vpad.trigger; 98 | } 99 | 100 | // read kpads and remap the buttons we need 101 | for (int i = 0; i < 4; i++) { 102 | if (KPADReadEx((KPADChan) i, &kpad, 1, &kpadError) > 0) { 103 | if (kpadError != KPAD_ERROR_OK) { 104 | continue; 105 | } 106 | 107 | if (kpad.extensionType == WPAD_EXT_CORE || kpad.extensionType == WPAD_EXT_NUNCHUK || 108 | kpad.extensionType == WPAD_EXT_MPLUS || kpad.extensionType == WPAD_EXT_MPLUS_NUNCHUK) { 109 | buttonsTriggered |= remapWiiMoteButtons(kpad.trigger); 110 | } else if (kpad.extensionType == WPAD_EXT_CLASSIC) { 111 | buttonsTriggered |= remapClassicButtons(kpad.classic.trigger); 112 | } else if (kpad.extensionType == WPAD_EXT_PRO_CONTROLLER) { 113 | buttonsTriggered |= remapProButtons(kpad.pro.trigger); 114 | } 115 | } 116 | } 117 | 118 | if (buttonsTriggered & VPAD_BUTTON_DOWN) { 119 | end = std::min(end + MAX_ENTRIES_PER_PAGE, logEntries.size()); 120 | start = std::max((int) end - MAX_ENTRIES_PER_PAGE, 0); 121 | redraw = true; 122 | } 123 | 124 | if (buttonsTriggered & VPAD_BUTTON_UP) { 125 | start = std::max(0, (int) start - MAX_ENTRIES_PER_PAGE); 126 | end = std::min(start + MAX_ENTRIES_PER_PAGE, logEntries.size()); 127 | redraw = true; 128 | } 129 | 130 | if (buttonsTriggered & (VPAD_BUTTON_B | VPAD_BUTTON_HOME)) { 131 | return; 132 | } 133 | 134 | if (redraw) { 135 | DrawUtils::beginDraw(); 136 | DrawUtils::clear(COLOR_BACKGROUND); 137 | 138 | // draw entries 139 | uint32_t index = 8 + 24 + 8 + 4; 140 | for (uint32_t i = start; i < end; i++) { 141 | LogEntry& entry = logEntries[i]; 142 | 143 | if (entry.type == LOG_TYPE_WARN) { 144 | DrawUtils::setFontColor(COLOR_TEXT_WARN); 145 | } else if (entry.type == LOG_TYPE_ERROR) { 146 | DrawUtils::setFontColor(COLOR_TEXT_ERROR); 147 | } else { 148 | DrawUtils::setFontColor(COLOR_TEXT2); 149 | } 150 | 151 | DrawUtils::setFontSize(16); 152 | DrawUtils::print(16, index + 16, entry.text.c_str()); 153 | 154 | index += 16 + 4; 155 | } 156 | 157 | DrawUtils::setFontColor(COLOR_TEXT); 158 | 159 | // draw top bar 160 | DrawUtils::setFontSize(24); 161 | DrawUtils::print(16, 6 + 24, "re_nfpii - Log viewer"); 162 | DrawUtils::setFontSize(18); 163 | DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, getLogStats(item).c_str(), true); 164 | DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK); 165 | 166 | // draw bottom bar 167 | DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK); 168 | DrawUtils::setFontSize(18); 169 | DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Scroll "); 170 | DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue001 Back", true); 171 | 172 | // draw scroll indicators 173 | DrawUtils::setFontSize(24); 174 | if (end < logEntries.size()) { 175 | DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true); 176 | } 177 | if (start > 0) { 178 | DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true); 179 | } 180 | 181 | DrawUtils::endDraw(); 182 | redraw = false; 183 | } 184 | } 185 | } 186 | 187 | static bool ConfigItemLog_callCallback(void* context) 188 | { 189 | return true; 190 | } 191 | 192 | static void ConfigItemLog_onButtonPressed(void* context, WUPSConfigButtons buttons) 193 | { 194 | ConfigItemLog* item = (ConfigItemLog*) context; 195 | 196 | if (buttons & WUPS_CONFIG_BUTTON_A) { 197 | enterLogViewer(item); 198 | } 199 | } 200 | 201 | static bool ConfigItemLog_isMovementAllowed(void* context) 202 | { 203 | return true; 204 | } 205 | 206 | static int32_t ConfigItemLog_getCurrentValueDisplay(void* context, char* out_buf, int32_t out_size) 207 | { 208 | ConfigItemLog* item = (ConfigItemLog*) context; 209 | 210 | strncpy(out_buf, getLogStats(item).c_str(), out_size); 211 | return 0; 212 | } 213 | 214 | static void ConfigItemLog_restoreDefault(void* context) 215 | { 216 | } 217 | 218 | static void ConfigItemLog_onSelected(void* context, bool isSelected) 219 | { 220 | } 221 | 222 | static void ConfigItemLog_onDelete(void* context) 223 | { 224 | ConfigItemLog* item = (ConfigItemLog*) context; 225 | 226 | free(item->configID); 227 | 228 | delete item; 229 | } 230 | 231 | bool ConfigItemLog_AddToCategory(WUPSConfigCategoryHandle cat, const char* configID, const char* displayName) 232 | { 233 | if (!cat) { 234 | return false; 235 | } 236 | 237 | ConfigItemLog* item = new ConfigItemLog; 238 | if (!item) { 239 | return false; 240 | } 241 | 242 | if (configID) { 243 | item->configID = strdup(configID); 244 | } else { 245 | item->configID = nullptr; 246 | } 247 | 248 | WUPSConfigCallbacks_t callbacks = { 249 | .getCurrentValueDisplay = &ConfigItemLog_getCurrentValueDisplay, 250 | .getCurrentValueSelectedDisplay = &ConfigItemLog_getCurrentValueDisplay, 251 | .onSelected = &ConfigItemLog_onSelected, 252 | .restoreDefault = &ConfigItemLog_restoreDefault, 253 | .isMovementAllowed = &ConfigItemLog_isMovementAllowed, 254 | .callCallback = &ConfigItemLog_callCallback, 255 | .onButtonPressed = &ConfigItemLog_onButtonPressed, 256 | .onDelete = &ConfigItemLog_onDelete 257 | }; 258 | 259 | if (WUPSConfigItem_Create(&item->handle, configID, displayName, callbacks, item) < 0) { 260 | delete item; 261 | return false; 262 | } 263 | 264 | if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) { 265 | return false; 266 | } 267 | 268 | return true; 269 | } 270 | -------------------------------------------------------------------------------- /plugin/source/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include "debug/logger.h" 14 | #include "config/ConfigItemSelectAmiibo.hpp" 15 | #include "config/ConfigItemLog.hpp" 16 | #include "config/ConfigItemDumpAmiibo.hpp" 17 | #include "config/WUPSConfigItemButtonCombo.h" 18 | 19 | #define STR_VALUE(arg) #arg 20 | #define VERSION_STRING(x, y, z) "v" STR_VALUE(x) "." STR_VALUE(y) "." STR_VALUE(z) 21 | 22 | WUPS_PLUGIN_NAME("re_nfpii"); 23 | WUPS_PLUGIN_DESCRIPTION("A nn_nfp reimplementation with support for Amiibo emulation"); 24 | WUPS_PLUGIN_VERSION(VERSION_STRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)); 25 | WUPS_PLUGIN_AUTHOR("GaryOderNichts"); 26 | WUPS_PLUGIN_LICENSE("GPLv2"); 27 | 28 | WUPS_USE_STORAGE("re_nfpii"); 29 | WUPS_USE_WUT_DEVOPTAB(); 30 | 31 | // This is in .5 steps, i.e. 9 would be 4.5s 32 | #define MAX_REMOVE_AFTER_SECONDS 20 33 | 34 | #define TAG_EMULATION_PATH std::string("/vol/external01/wiiu/re_nfpii/") 35 | 36 | uint32_t currentRemoveAfterOption = 0; 37 | 38 | uint32_t currentQuickSelectCombination = 0; 39 | uint32_t currentToggleEmulationCombination = 0; 40 | 41 | bool favoritesPerTitle = false; 42 | 43 | static void nfpiiLogHandler(NfpiiLogVerbosity verb, const char* message) 44 | { 45 | ConfigItemLog_PrintType((LogType) verb, message); 46 | } 47 | 48 | INITIALIZE_PLUGIN() 49 | { 50 | if (!WHBLogModuleInit()) { 51 | WHBLogCafeInit(); 52 | WHBLogUdpInit(); 53 | } 54 | 55 | ConfigItemLog_Init(); 56 | NfpiiSetLogHandler(nfpiiLogHandler); 57 | 58 | // Read values from config 59 | WUPSStorageError err = WUPS_OpenStorage(); 60 | if (err == WUPS_STORAGE_ERROR_SUCCESS) { 61 | int32_t emulationState = (int32_t) NfpiiGetEmulationState(); 62 | if ((err = WUPS_GetInt(nullptr, "emulationState", &emulationState)) == WUPS_STORAGE_ERROR_NOT_FOUND) { 63 | WUPS_StoreInt(nullptr, "emulationState", emulationState); 64 | } else if (err == WUPS_STORAGE_ERROR_SUCCESS) { 65 | NfpiiSetEmulationState((NfpiiEmulationState) emulationState); 66 | } 67 | 68 | if ((err = WUPS_GetInt(nullptr, "removeAfter", (int32_t*) ¤tRemoveAfterOption)) == WUPS_STORAGE_ERROR_NOT_FOUND) { 69 | WUPS_StoreInt(nullptr, "removeAfter", currentRemoveAfterOption); 70 | } else if (err == WUPS_STORAGE_ERROR_SUCCESS) { 71 | NfpiiSetRemoveAfterSeconds(currentRemoveAfterOption / 2.0f); 72 | } 73 | 74 | char path[PATH_MAX]; 75 | strncpy(path, NfpiiGetTagEmulationPath(), PATH_MAX - 1); 76 | if ((err = WUPS_GetString(nullptr, "currentPath", path, PATH_MAX)) == WUPS_STORAGE_ERROR_NOT_FOUND) { 77 | WUPS_StoreString(nullptr, "currentPath", path); 78 | } else if (err == WUPS_STORAGE_ERROR_SUCCESS) { 79 | // check that the stored path actually exists 80 | struct stat sb; 81 | if (stat(path, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFREG) { 82 | NfpiiSetTagEmulationPath(path); 83 | } 84 | } 85 | 86 | if ((err = WUPS_GetBool(nullptr, "favoritesPerTitle", &favoritesPerTitle)) == WUPS_STORAGE_ERROR_NOT_FOUND) { 87 | WUPS_StoreBool(nullptr, "favoritesPerTitle", favoritesPerTitle); 88 | } 89 | ConfigItemSelectAmiibo_Init(TAG_EMULATION_PATH, favoritesPerTitle); 90 | 91 | if ((err = WUPS_GetInt(nullptr, "quickSelectCombo", (int32_t*) ¤tQuickSelectCombination)) == WUPS_STORAGE_ERROR_NOT_FOUND) { 92 | WUPS_StoreInt(nullptr, "quickSelectCombo", currentQuickSelectCombination); 93 | } 94 | 95 | if ((err = WUPS_GetInt(nullptr, "toggleEmulationCombo", (int32_t*) ¤tToggleEmulationCombination)) == WUPS_STORAGE_ERROR_NOT_FOUND) { 96 | WUPS_StoreInt(nullptr, "toggleEmulationCombo", currentToggleEmulationCombination); 97 | } 98 | 99 | if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { 100 | DEBUG_FUNCTION_LINE("Failed to close storage"); 101 | } 102 | } else { 103 | DEBUG_FUNCTION_LINE("Failed to open storage"); 104 | } 105 | } 106 | 107 | DEINITIALIZE_PLUGIN() 108 | { 109 | NfpiiSetLogHandler(nullptr); 110 | } 111 | 112 | ON_APPLICATION_START() 113 | { 114 | if (!WHBLogModuleInit()) { 115 | WHBLogCafeInit(); 116 | WHBLogUdpInit(); 117 | } 118 | 119 | // Make sure favorites are refreshed for the new title 120 | if (WUPS_OpenStorage() == WUPS_STORAGE_ERROR_SUCCESS) { 121 | ConfigItemSelectAmiibo_Init(TAG_EMULATION_PATH, favoritesPerTitle); 122 | 123 | if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { 124 | DEBUG_FUNCTION_LINE("Failed to close storage"); 125 | } 126 | } else { 127 | DEBUG_FUNCTION_LINE("Failed to open storage"); 128 | } 129 | } 130 | 131 | static void stateChangedCallback(ConfigItemMultipleValues* values, uint32_t index) 132 | { 133 | WUPS_StoreInt(nullptr, "emulationState", (int32_t) index); 134 | NfpiiSetEmulationState((NfpiiEmulationState) index); 135 | } 136 | 137 | static void removeAfterChangedCallback(ConfigItemMultipleValues* values, uint32_t index) 138 | { 139 | currentRemoveAfterOption = index; 140 | WUPS_StoreInt(nullptr, "removeAfter", (int32_t) currentRemoveAfterOption); 141 | NfpiiSetRemoveAfterSeconds(index / 2.0f); 142 | } 143 | 144 | static void uuidRandomizationChangedCallback(ConfigItemMultipleValues* values, uint32_t index) 145 | { 146 | NfpiiSetUUIDRandomizationState((NfpiiUUIDRandomizationState) index); 147 | } 148 | 149 | static void amiiboSelectedCallback(ConfigItemSelectAmiibo* amiibos, const char* filePath) 150 | { 151 | WUPS_StoreString(nullptr, "currentPath", filePath); 152 | NfpiiSetTagEmulationPath(filePath); 153 | } 154 | 155 | static void favoritesPerTitleCallback(ConfigItemBoolean* item, bool enable) 156 | { 157 | favoritesPerTitle = enable; 158 | WUPS_StoreBool(nullptr, "favoritesPerTitle", favoritesPerTitle); 159 | 160 | // refresh favorites 161 | ConfigItemSelectAmiibo_Init(TAG_EMULATION_PATH, favoritesPerTitle); 162 | } 163 | 164 | static void quickSelectComboCallback(ConfigItemButtonCombo* item, uint32_t newValue) 165 | { 166 | currentQuickSelectCombination = newValue; 167 | WUPS_StoreInt(nullptr, "quickSelectCombo", (int32_t) currentQuickSelectCombination); 168 | } 169 | 170 | static void toggleEmulationComboCallback(ConfigItemButtonCombo* item, uint32_t newValue) 171 | { 172 | currentToggleEmulationCombination = newValue; 173 | WUPS_StoreInt(nullptr, "toggleEmulationCombo", (int32_t) currentToggleEmulationCombination); 174 | } 175 | 176 | WUPS_GET_CONFIG() 177 | { 178 | if (WUPS_OpenStorage() != WUPS_STORAGE_ERROR_SUCCESS) { 179 | DEBUG_FUNCTION_LINE("Failed to open storage"); 180 | return 0; 181 | } 182 | 183 | WUPSConfigHandle config; 184 | WUPSConfig_CreateHandled(&config, "re_nfpii"); 185 | 186 | WUPSConfigCategoryHandle cat; 187 | WUPSConfig_AddCategoryByNameHandled(config, "Settings", &cat); 188 | 189 | ConfigItemMultipleValuesPair emulationStateValues[2]; 190 | emulationStateValues[0].value = NFPII_EMULATION_OFF; 191 | emulationStateValues[0].valueName = (char*) "Emulation Disabled"; 192 | emulationStateValues[1].value = NFPII_EMULATION_ON; 193 | emulationStateValues[1].valueName = (char*) "Emulation Enabled"; 194 | WUPSConfigItemMultipleValues_AddToCategoryHandled(config, cat, "state", "Set State", NfpiiGetEmulationState(), emulationStateValues, 2, stateChangedCallback); 195 | 196 | ConfigItemMultipleValuesPair removeAfterValues[MAX_REMOVE_AFTER_SECONDS + 1]; 197 | removeAfterValues[0].value = 0; 198 | removeAfterValues[0].valueName = (char*) "Never"; 199 | for (int i = 1; i < MAX_REMOVE_AFTER_SECONDS + 1; i++) { 200 | removeAfterValues[i].value = i; 201 | char* fmt = (char*) malloc(32); 202 | snprintf(fmt, 32, "%.1fs", i / 2.0f); 203 | removeAfterValues[i].valueName = fmt; 204 | } 205 | WUPSConfigItemMultipleValues_AddToCategoryHandled(config, cat, "remove_after", "Remove after", currentRemoveAfterOption, removeAfterValues, MAX_REMOVE_AFTER_SECONDS + 1, removeAfterChangedCallback); 206 | for (int i = 1; i < 10; i++) { 207 | free(removeAfterValues[i].valueName); 208 | } 209 | 210 | #if 0 //TODO 211 | values[0].value = RANDOMIZATION_OFF; 212 | values[0].valueName = (char*) "Off"; 213 | values[1].value = RANDOMIZATION_ONCE; 214 | values[1].valueName = (char*) "Once"; 215 | values[2].value = RANDOMIZATION_EVERY_READ; 216 | values[2].valueName = (char*) "After reading"; 217 | WUPSConfigItemMultipleValues_AddToCategoryHandled(config, cat, "random_uuid", "Randomize UUID", NfpiiGetUUIDRandomizationState(), values, 3, uuidRandomizationChangedCallback); 218 | #endif 219 | 220 | std::string currentAmiiboPath = NfpiiGetTagEmulationPath(); 221 | ConfigItemSelectAmiibo_AddToCategoryHandled(config, cat, "select_amiibo", "Select Amiibo", TAG_EMULATION_PATH.c_str(), currentAmiiboPath.c_str(), amiiboSelectedCallback); 222 | 223 | WUPSConfigItemBoolean_AddToCategoryHandled(config, cat, "favorites_per_title", "Per-Title Favorites", favoritesPerTitle, favoritesPerTitleCallback); 224 | 225 | WUPSConfigItemButtonCombo_AddToCategoryHandled(config, cat, "quick_select_combination", "Quick Select Combo", currentQuickSelectCombination, quickSelectComboCallback); 226 | 227 | WUPSConfigItemButtonCombo_AddToCategoryHandled(config, cat, "quick_remove_combination", "Toggle Emulation Combo", currentToggleEmulationCombination, toggleEmulationComboCallback); 228 | 229 | ConfigItemDumpAmiibo_AddToCategoryHandled(config, cat, "dump_amiibo", "Dump Amiibo", (TAG_EMULATION_PATH + "dumps").c_str()); 230 | 231 | ConfigItemLog_AddToCategoryHandled(config, cat, "log", "Logs"); 232 | 233 | return config; 234 | } 235 | 236 | WUPS_CONFIG_CLOSED() 237 | { 238 | if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { 239 | DEBUG_FUNCTION_LINE("Failed to close storage"); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /source/re_nfpii/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.hpp" 2 | #include "re_nfpii.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace re::nfpii { 10 | 11 | bool CheckZero(const void* data, uint32_t size) 12 | { 13 | assert(data); 14 | 15 | for (uint32_t i = 0; i < size; i++) { 16 | if (((const uint8_t*) data)[i] != 0) { 17 | return false; 18 | } 19 | } 20 | 21 | return true; 22 | } 23 | 24 | void GetRandom(void* data, uint32_t size) 25 | { 26 | assert(data); 27 | 28 | for (uint32_t i = 0; i < size; i++) { 29 | ((uint8_t*) data)[i] = (uint8_t) rand(); 30 | } 31 | } 32 | 33 | uint16_t IncreaseCount(uint16_t count, bool overflow) 34 | { 35 | if (count == 0xffff) { 36 | if (overflow) { 37 | return 0; 38 | } 39 | 40 | return 0xffff; 41 | } 42 | 43 | return count + 1; 44 | } 45 | 46 | static TagType FindTagType(NFCTechnology technology, NFCProtocol protocol) 47 | { 48 | static TagType tt_lookup[] = { TagType::Type1Tag, TagType::Type2Tag, TagType::Type3Tag }; 49 | 50 | switch (technology) { 51 | case NFC_TECHNOLOGY_A: 52 | if (protocol != NFC_PROTOCOL_UNKNOWN && protocol <= NFC_PROTOCOL_T3T) { 53 | return tt_lookup[protocol - 1]; 54 | } 55 | break; 56 | case NFC_TECHNOLOGY_B: 57 | if (protocol != NFC_PROTOCOL_UNKNOWN && protocol <= NFC_PROTOCOL_T3T) { 58 | return tt_lookup[protocol - 1]; 59 | } 60 | break; 61 | case NFC_TECHNOLOGY_F: 62 | if (protocol != NFC_PROTOCOL_UNKNOWN && protocol <= NFC_PROTOCOL_T3T) { 63 | return tt_lookup[protocol - 1]; 64 | } 65 | break; 66 | case NFC_TECHNOLOGY_ISO15693: 67 | if (protocol == NFC_PROTOCOL_15693) { 68 | return TagType::Iso15693; 69 | } 70 | break; 71 | } 72 | 73 | return TagType::Unknown; 74 | } 75 | 76 | void ReadTagInfo(TagInfo* info, const NTAGDataT2T* data) 77 | { 78 | assert(info); 79 | 80 | memcpy(info->id.uid, data->tagInfo.uid, data->tagInfo.uidSize); 81 | info->id.size = data->tagInfo.uidSize; 82 | info->technology = data->tagInfo.technology; 83 | info->tag_type = FindTagType(data->tagInfo.technology, data->tagInfo.protocol); 84 | } 85 | 86 | void ReadCommonInfo(CommonInfo* info, const NTAGDataT2T* data) 87 | { 88 | assert(info); 89 | 90 | if (!(data->info.flags & (uint8_t) AdminFlags::IsRegistered) && !(data->info.flags & (uint8_t) AdminFlags::HasApplicationData)) { 91 | ConvertAmiiboDate(&info->lastWriteDate, 0); 92 | info->writes = data->info.writes; 93 | memcpy(&info->characterID, data->info.characterID, 3); 94 | info->numberingID = data->info.numberingID; 95 | info->figureType = data->info.figureType; 96 | info->seriesID = data->info.seriesID; 97 | info->applicationAreaSize = data->appData.size; 98 | info->figureVersion = data->info.figureVersion; 99 | memset(info->reserved, 0, sizeof(info->reserved)); 100 | } else { 101 | ConvertAmiiboDate(&info->lastWriteDate, data->info.lastWriteDate); 102 | info->writes = data->info.writes; 103 | memcpy(&info->characterID, data->info.characterID, 3); 104 | info->numberingID = data->info.numberingID; 105 | info->figureType = data->info.figureType; 106 | info->seriesID = data->info.seriesID; 107 | info->applicationAreaSize = data->appData.size; 108 | info->figureVersion = data->info.figureVersion; 109 | memset(info->reserved, 0, sizeof(info->reserved)); 110 | } 111 | } 112 | 113 | void ReadRegisterInfo(RegisterInfo* info, const NTAGDataT2T* data) 114 | { 115 | assert(info); 116 | 117 | memcpy(&info->mii, &data->info.mii, sizeof(info->mii)); 118 | memcpy(info->name, data->info.name, sizeof(data->info.name)); 119 | // null-terminate name 120 | info->name[10] = 0; 121 | ConvertAmiiboDate(&info->registerDate, data->info.setupDate); 122 | info->fontRegion = data->info.fontRegion; 123 | info->country = data->info.country; 124 | memset(info->reserved, 0, sizeof(info->reserved)); 125 | } 126 | 127 | void ReadReadOnlyInfo(ReadOnlyInfo* info, const NTAGDataT2T* data) 128 | { 129 | assert(info); 130 | 131 | memcpy(&info->characterID, data->info.characterID, 3); 132 | info->numberingID = data->info.numberingID; 133 | info->figureType = data->info.figureType; 134 | info->seriesID = data->info.seriesID; 135 | memset(info->reserved, 0, sizeof(info->reserved)); 136 | } 137 | 138 | static uint8_t GetPlatform(uint64_t titleId) { 139 | static uint8_t lookup[] = { 0, 1, 0 }; 140 | 141 | // This checks the first part of the low title id, I assume 0 = 3ds, 1 = U? 142 | // So this returns 1 if this is a Wii U title id? 143 | if ((titleId >> 0x1c & 0xf) < 3) { 144 | return lookup[titleId >> 0x1c & 0xf]; 145 | } 146 | 147 | return 0xff; 148 | } 149 | 150 | void ReadAdminInfo(AdminInfo* info, const NTAGDataT2T* data) 151 | { 152 | assert(info); 153 | 154 | if (!(data->info.flags & (uint8_t) AdminFlags::HasApplicationData)) { 155 | info->flags = (AdminFlags) data->info.flags; 156 | info->titleID = 0; 157 | info->platform = 0xff; 158 | info->accessID = 0; 159 | info->applicationAreaWrites = data->info.applicationAreaWrites; 160 | info->formatVersion = data->formatVersion; 161 | memset(info->reserved, 0, sizeof(info->reserved)); 162 | } else { 163 | info->titleID = data->info.titleID; 164 | info->accessID = data->info.accessID; 165 | info->platform = GetPlatform(data->info.titleID); 166 | info->flags = (AdminFlags) data->info.flags; 167 | info->applicationAreaWrites = data->info.applicationAreaWrites; 168 | info->formatVersion = data->formatVersion; 169 | memset(info->reserved, 0, sizeof(info->reserved)); 170 | } 171 | } 172 | 173 | void ClearApplicationArea(NTAGDataT2T* data) 174 | { 175 | GetRandom(&data->info.accessID, sizeof(data->info.accessID)); 176 | GetRandom(&data->info.titleID, sizeof(data->info.titleID)); 177 | GetRandom(&data->appData.data, sizeof(data->appData.data)); 178 | // clear the "has application area" bit 179 | data->info.flags &= ~(uint8_t) AdminFlags::HasApplicationData; 180 | } 181 | 182 | void ClearRegisterInfo(NTAGDataT2T* data) 183 | { 184 | data->info.fontRegion = 0; 185 | data->info.country = 0; 186 | data->info.setupDate = rand(); 187 | GetRandom(data->info.name, sizeof(data->info.name)); 188 | GetRandom(&data->info.mii, sizeof(data->info.mii)); 189 | // clear the "has register info" bit 190 | data->info.flags &= ~(uint8_t) AdminFlags::IsRegistered; 191 | } 192 | 193 | static bool ReadSysConfig(const char* name, UCDataType type, uint32_t size, void* data) 194 | { 195 | UCHandle handle = UCOpen(); 196 | if (handle < 0) { 197 | return false; 198 | } 199 | 200 | UCSysConfig config; 201 | strncpy(config.name, name, sizeof(config.name)); 202 | config.access = 0x777; 203 | config.error = 0; 204 | config.dataType = type; 205 | config.dataSize = size; 206 | config.data = data; 207 | UCError error = UCReadSysConfig(handle, 1, &config); 208 | if (error == UC_ERROR_OK) { 209 | UCClose(handle); 210 | return true; 211 | } 212 | 213 | UCClose(handle); 214 | return false; 215 | } 216 | 217 | Result ReadCountryRegion(uint8_t* outCountryCode) 218 | { 219 | uint32_t cntry_reg; 220 | if (!ReadSysConfig("cafe.cntry_reg", UC_DATATYPE_UNSIGNED_INT, sizeof(cntry_reg), &cntry_reg)) { 221 | *outCountryCode = 0xff; 222 | return NFP_SYSTEM_ERROR; 223 | } 224 | 225 | if (cntry_reg > 0xff) { 226 | cntry_reg = 0xff; 227 | } 228 | 229 | *outCountryCode = (uint8_t) cntry_reg; 230 | return NFP_SUCCESS; 231 | } 232 | 233 | uint16_t OSTimeToAmiiboTime(OSTime time) 234 | { 235 | OSCalendarTime ct; 236 | OSTicksToCalendarTime(time, &ct); 237 | 238 | return ((ct.tm_year << 9) - 0xfa000) | ((ct.tm_mon << 5) + 0x20) | (ct.tm_mday); 239 | } 240 | 241 | static bool VerifyDate(uint32_t y, uint32_t m, uint32_t d, uint32_t h, uint32_t min, uint32_t s, uint32_t ms) 242 | { 243 | // N does proper ymd verification, too lazy to implement this right now 244 | if (h < 24 && min < 60 && s < 60 && ms < 1000) { 245 | return true; 246 | } 247 | 248 | return false; 249 | } 250 | 251 | void ConvertAmiiboDate(Date* date, uint16_t time) 252 | { 253 | uint32_t day = time & 0x1f; 254 | uint32_t month = time >> 0x5 & 0xf; 255 | uint32_t year = (time >> 0x9 & 0x7f) + 0x7d0; 256 | if (!VerifyDate(year, month, day, 0, 0, 0, 0)) { 257 | // TODO what does nfp actually do here? 258 | date->year = 0; 259 | date->month = 0; 260 | date->day = 0; 261 | return; 262 | } 263 | 264 | date->day = day; 265 | date->month = month; 266 | date->year = year; 267 | } 268 | 269 | bool CheckAmiiboMagic(NTAGDataT2T* data) 270 | { 271 | return data->info.magic == 0xa5; 272 | } 273 | 274 | bool CheckUuidCRC(NTAGInfoT2T* info) 275 | { 276 | // TODO 277 | return true; 278 | } 279 | 280 | void SetUuidCRC(uint32_t* crc) 281 | { 282 | // TODO 283 | } 284 | 285 | static uint16_t crc16(uint16_t poly, const void* data, uint32_t size) 286 | { 287 | uint16_t crc = 0; 288 | for (uint32_t byteIndex = 0; byteIndex < size; byteIndex++) { 289 | for (int bitIndex = 7; bitIndex >= 0; bitIndex--) { 290 | crc = (((crc << 1) | ((((const uint8_t*) data)[byteIndex] >> bitIndex) & 0x1)) 291 | ^ (((crc & 0x8000) != 0) ? poly : 0)); 292 | } 293 | } 294 | 295 | for (int counter = 16; counter > 0; counter--) { 296 | crc = ((crc << 1) ^ (((crc & 0x8000) != 0) ? poly : 0)); 297 | } 298 | 299 | return crc; 300 | } 301 | 302 | Result UpdateMii(FFLStoreData* data) 303 | { 304 | // Make sure the checksum matches 305 | if (crc16(0x1021, data, 0x5e) != data->checksum) { 306 | return NFP_OUT_OF_RANGE; 307 | } 308 | 309 | // Not sure what this bit is used for 310 | data->data.core.unk_0x18_b1 = 0; 311 | 312 | // Update checksum 313 | data->checksum = crc16(0x1021, data, 0x5e); 314 | return NFP_SUCCESS; 315 | } 316 | 317 | } // namespace re::nfpii 318 | -------------------------------------------------------------------------------- /plugin/source/config/WUPSConfigItemButtonCombo.cpp: -------------------------------------------------------------------------------- 1 | #include "WUPSConfigItemButtonCombo.h" 2 | #include "utils/input.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | const char *getButtonChar(VPADButtons value) { 15 | std::string combo; 16 | if (value & VPAD_BUTTON_A) { 17 | return "\ue000"; 18 | } 19 | if (value & VPAD_BUTTON_B) { 20 | return "\ue001"; 21 | } 22 | if (value & VPAD_BUTTON_X) { 23 | return "\ue002"; 24 | } 25 | if (value & VPAD_BUTTON_Y) { 26 | return "\ue003"; 27 | } 28 | if (value & VPAD_BUTTON_L) { 29 | return "\ue083"; 30 | } 31 | if (value & VPAD_BUTTON_R) { 32 | return "\ue084"; 33 | } 34 | if (value & VPAD_BUTTON_ZL) { 35 | return "\ue085"; 36 | } 37 | if (value & VPAD_BUTTON_ZR) { 38 | return "\ue086"; 39 | } 40 | if (value & VPAD_BUTTON_UP) { 41 | return "\ue079"; 42 | } 43 | if (value & VPAD_BUTTON_DOWN) { 44 | return "\ue07A"; 45 | } 46 | if (value & VPAD_BUTTON_LEFT) { 47 | return "\ue07B"; 48 | } 49 | if (value & VPAD_BUTTON_RIGHT) { 50 | return "\ue07C"; 51 | } 52 | if (value & VPAD_BUTTON_STICK_L) { 53 | return "\ue081"; 54 | } 55 | if (value & VPAD_BUTTON_STICK_R) { 56 | return "\ue082"; 57 | } 58 | if (value & VPAD_BUTTON_PLUS) { 59 | return "\ue045"; 60 | } 61 | if (value & VPAD_BUTTON_MINUS) { 62 | return "\ue046"; 63 | } 64 | if (value & VPAD_BUTTON_TV) { 65 | return "\ue089"; 66 | } 67 | if (value & VPAD_BUTTON_RESERVED_BIT) { 68 | return "\ue01E"; 69 | } 70 | return ""; 71 | } 72 | 73 | std::string getComboAsString(uint32_t value) { 74 | char comboString[60]; 75 | memset(comboString, 0, sizeof(comboString)); 76 | 77 | for (uint32_t i = 0; i < 32; i++) { 78 | uint32_t bitMask = 1 << i; 79 | if (value & bitMask) { 80 | auto val = getButtonChar(static_cast(bitMask)); 81 | if (val[0] != '\0') { 82 | strcat(comboString, val); 83 | strcat(comboString, "+"); 84 | } 85 | } 86 | } 87 | std::string res(comboString); 88 | if (res.ends_with("+")) { 89 | res.pop_back(); 90 | } 91 | return res; 92 | } 93 | 94 | int32_t WUPSConfigItemButtonCombo_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) { 95 | auto *item = (ConfigItemButtonCombo *) context; 96 | if (item->value != 0) { 97 | snprintf(out_buf, out_size, "%s", getComboAsString(item->value).c_str()); 98 | } else { 99 | strncpy(out_buf, "None", out_size); 100 | } 101 | return 0; 102 | } 103 | 104 | bool WUPSConfigItemButtonCombo_callCallback(void *context) { 105 | auto *item = (ConfigItemButtonCombo *) context; 106 | if (item->callback != nullptr) { 107 | ((ButtonComboValueChangedCallback) (item->callback))(item, item->value); 108 | return true; 109 | } 110 | return false; 111 | } 112 | 113 | void checkForHold(ConfigItemButtonCombo *item) { 114 | uint32_t lastHold = 0; 115 | uint32_t holdFor = 0; 116 | uint32_t holdForTarget = item->holdDurationInMs >> 4; 117 | uint32_t holdAbortTarget = item->abortButtonHoldDurationInMs >> 4; 118 | 119 | auto mask = VPAD_BUTTON_A | VPAD_BUTTON_B | VPAD_BUTTON_X | VPAD_BUTTON_Y | VPAD_BUTTON_L | VPAD_BUTTON_R | 120 | VPAD_BUTTON_ZL | VPAD_BUTTON_ZR | VPAD_BUTTON_UP | VPAD_BUTTON_DOWN | VPAD_BUTTON_LEFT | VPAD_BUTTON_RIGHT | 121 | VPAD_BUTTON_STICK_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_PLUS | VPAD_BUTTON_MINUS | VPAD_BUTTON_TV | VPAD_BUTTON_RESERVED_BIT; 122 | 123 | KPADStatus kpad_data{}; 124 | KPADError kpad_error; 125 | 126 | while (true) { 127 | uint32_t buttonsHold = 0; 128 | VPADReadError vpad_error = VPAD_READ_UNINITIALIZED; 129 | VPADStatus vpad_data; 130 | VPADRead(VPAD_CHAN_0, &vpad_data, 1, &vpad_error); 131 | if (vpad_error == VPAD_READ_SUCCESS) { 132 | buttonsHold = vpad_data.hold; 133 | } 134 | 135 | for (int i = 0; i < 4; i++) { 136 | memset(&kpad_data, 0, sizeof(kpad_data)); 137 | if (KPADReadEx((KPADChan) i, &kpad_data, 1, &kpad_error) > 0) { 138 | if (kpad_error == KPAD_ERROR_OK && kpad_data.extensionType != 0xFF) { 139 | if (kpad_data.extensionType == WPAD_EXT_CORE || kpad_data.extensionType == WPAD_EXT_NUNCHUK) { 140 | buttonsHold |= remapWiiMoteButtons(kpad_data.hold); 141 | } else if (kpad_data.extensionType == WPAD_EXT_PRO_CONTROLLER) { 142 | buttonsHold |= remapProButtons(kpad_data.pro.hold); 143 | } else { 144 | buttonsHold |= remapClassicButtons(kpad_data.classic.hold); 145 | } 146 | } 147 | } 148 | } 149 | 150 | buttonsHold &= mask; 151 | 152 | if (buttonsHold == lastHold) { 153 | if (buttonsHold != 0) { 154 | holdFor++; 155 | } 156 | } else { 157 | holdFor = 0; 158 | } 159 | lastHold = buttonsHold; 160 | 161 | if (holdFor >= holdAbortTarget && lastHold == item->abortButton) { 162 | item->value = 0; 163 | break; 164 | } 165 | 166 | if (holdFor >= holdForTarget) { 167 | item->value = lastHold; 168 | break; 169 | } 170 | OSSleepTicks(OSMillisecondsToTicks(16)); 171 | } 172 | } 173 | 174 | void WUPSConfigItemButtonCombo_onButtonPressed(void *context, WUPSConfigButtons buttons) { 175 | auto *item = (ConfigItemButtonCombo *) context; 176 | if (item->state == BUTTON_COMBO_STATE_NONE) { 177 | if ((buttons & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A) { 178 | item->state = BUTTON_COMBO_STATE_PREPARE_FOR_HOLD; 179 | } 180 | } 181 | } 182 | 183 | bool WUPSConfigItemButtonCombo_isMovementAllowed(void *context) { 184 | return true; 185 | } 186 | 187 | int32_t WUPSConfigItemButtonCombo_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) { 188 | auto *item = (ConfigItemButtonCombo *) context; 189 | if (item->state == BUTTON_COMBO_STATE_PREPARE_FOR_HOLD || item->state == BUTTON_COMBO_STATE_WAIT_FOR_HOLD) { 190 | if (item->state == BUTTON_COMBO_STATE_PREPARE_FOR_HOLD) { 191 | item->state = BUTTON_COMBO_STATE_WAIT_FOR_HOLD; 192 | snprintf(out_buf, out_size, "", item->holdDurationInMs / 1000, getButtonChar(item->abortButton)); 193 | return 0; 194 | } else { 195 | checkForHold(item); 196 | item->state = BUTTON_COMBO_STATE_NONE; 197 | } 198 | } 199 | snprintf(out_buf, out_size, "(Press \ue000 to change) %s", getComboAsString(item->value).c_str()); 200 | return 0; 201 | } 202 | 203 | void WUPSConfigItemButtonCombo_restoreDefault(void *context) { 204 | auto *item = (ConfigItemButtonCombo *) context; 205 | item->value = item->defaultValue; 206 | } 207 | 208 | void WUPSConfigItemButtonCombo_onSelected(void *context, bool isSelected) { 209 | } 210 | 211 | void WUPSConfigItemButtonCombo_onDelete(void *context) { 212 | auto *item = (ConfigItemButtonCombo *) context; 213 | if (item->configId) { 214 | free(item->configId); 215 | } 216 | free(item); 217 | } 218 | 219 | extern "C" bool 220 | WUPSConfigItemButtonComboAddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName, uint32_t defaultComboInVPADButtons, uint32_t holdDurationInMs, VPADButtons abortButton, uint32_t abortButtonHoldDurationInMs, ButtonComboValueChangedCallback callback) { 221 | if (cat == 0) { 222 | return false; 223 | } 224 | auto *item = (ConfigItemButtonCombo *) malloc(sizeof(ConfigItemButtonCombo)); 225 | if (item == nullptr) { 226 | OSReport("WUPSConfigItemButtonComboAddToCategoryEx: Failed to allocate memory for item data.\n"); 227 | return false; 228 | } 229 | 230 | if (configID != nullptr) { 231 | item->configId = strdup(configID); 232 | } else { 233 | item->configId = nullptr; 234 | } 235 | 236 | item->abortButton = abortButton; 237 | item->abortButtonHoldDurationInMs = abortButtonHoldDurationInMs; 238 | item->holdDurationInMs = holdDurationInMs; 239 | item->defaultValue = defaultComboInVPADButtons; 240 | item->value = defaultComboInVPADButtons; 241 | item->callback = (void *) callback; 242 | item->state = BUTTON_COMBO_STATE_NONE; 243 | 244 | WUPSConfigCallbacks_t callbacks = { 245 | .getCurrentValueDisplay = &WUPSConfigItemButtonCombo_getCurrentValueDisplay, 246 | .getCurrentValueSelectedDisplay = &WUPSConfigItemButtonCombo_getCurrentValueSelectedDisplay, 247 | .onSelected = &WUPSConfigItemButtonCombo_onSelected, 248 | .restoreDefault = &WUPSConfigItemButtonCombo_restoreDefault, 249 | .isMovementAllowed = &WUPSConfigItemButtonCombo_isMovementAllowed, 250 | .callCallback = &WUPSConfigItemButtonCombo_callCallback, 251 | .onButtonPressed = &WUPSConfigItemButtonCombo_onButtonPressed, 252 | .onDelete = &WUPSConfigItemButtonCombo_onDelete}; 253 | 254 | if (WUPSConfigItem_Create(&item->handle, configID, displayName, callbacks, item) < 0) { 255 | OSReport("WUPSConfigItemButtonComboAddToCategoryEx: Failed to create config item.\n"); 256 | free(item); 257 | return false; 258 | } 259 | 260 | if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) { 261 | OSReport("WUPSConfigItemButtonComboAddToCategoryEx: Failed to add item to category.\n"); 262 | WUPSConfigItem_Destroy(item->handle); 263 | return false; 264 | } 265 | return true; 266 | } 267 | 268 | extern "C" bool 269 | WUPSConfigItemButtonComboAddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName, uint32_t defaultComboInVPADButtons, ButtonComboValueChangedCallback callback) { 270 | return WUPSConfigItemButtonComboAddToCategoryEx(cat, configID, displayName, defaultComboInVPADButtons, 2000, VPAD_BUTTON_B, 250, callback); 271 | } 272 | -------------------------------------------------------------------------------- /plugin/source/config/fav_icon.inc: -------------------------------------------------------------------------------- 1 | static const Color fav_icon[] = { 2 | Color(0, 0, 0, 0), 3 | Color(0, 0, 0, 0), 4 | Color(0, 0, 0, 0), 5 | Color(0, 0, 0, 0), 6 | Color(0, 0, 0, 0), 7 | Color(0, 0, 0, 0), 8 | Color(0, 0, 0, 0), 9 | Color(0, 0, 0, 0), 10 | Color(0, 0, 0, 0), 11 | Color(255, 218, 68, 16), 12 | Color(255, 159, 34, 16), 13 | Color(0, 0, 0, 0), 14 | Color(0, 0, 0, 0), 15 | Color(0, 0, 0, 0), 16 | Color(0, 0, 0, 0), 17 | Color(0, 0, 0, 0), 18 | Color(0, 0, 0, 0), 19 | Color(0, 0, 0, 0), 20 | Color(0, 0, 0, 0), 21 | Color(0, 0, 0, 0), 22 | Color(0, 0, 0, 0), 23 | Color(0, 0, 0, 0), 24 | Color(0, 0, 0, 0), 25 | Color(0, 0, 0, 0), 26 | Color(0, 0, 0, 0), 27 | Color(0, 0, 0, 0), 28 | Color(0, 0, 0, 0), 29 | Color(0, 0, 0, 0), 30 | Color(0, 0, 0, 0), 31 | Color(255, 218, 68, 127), 32 | Color(255, 159, 34, 127), 33 | Color(0, 0, 0, 0), 34 | Color(0, 0, 0, 0), 35 | Color(0, 0, 0, 0), 36 | Color(0, 0, 0, 0), 37 | Color(0, 0, 0, 0), 38 | Color(0, 0, 0, 0), 39 | Color(0, 0, 0, 0), 40 | Color(0, 0, 0, 0), 41 | Color(0, 0, 0, 0), 42 | Color(0, 0, 0, 0), 43 | Color(0, 0, 0, 0), 44 | Color(0, 0, 0, 0), 45 | Color(0, 0, 0, 0), 46 | Color(0, 0, 0, 0), 47 | Color(0, 0, 0, 0), 48 | Color(0, 0, 0, 0), 49 | Color(0, 0, 0, 0), 50 | Color(255, 218, 68, 16), 51 | Color(255, 218, 68, 239), 52 | Color(255, 159, 34, 239), 53 | Color(255, 159, 34, 16), 54 | Color(0, 0, 0, 0), 55 | Color(0, 0, 0, 0), 56 | Color(0, 0, 0, 0), 57 | Color(0, 0, 0, 0), 58 | Color(0, 0, 0, 0), 59 | Color(0, 0, 0, 0), 60 | Color(0, 0, 0, 0), 61 | Color(0, 0, 0, 0), 62 | Color(0, 0, 0, 0), 63 | Color(0, 0, 0, 0), 64 | Color(0, 0, 0, 0), 65 | Color(0, 0, 0, 0), 66 | Color(0, 0, 0, 0), 67 | Color(0, 0, 0, 0), 68 | Color(0, 0, 0, 0), 69 | Color(0, 0, 0, 0), 70 | Color(255, 218, 68, 127), 71 | Color(255, 218, 68, 255), 72 | Color(255, 159, 34, 255), 73 | Color(255, 159, 34, 127), 74 | Color(0, 0, 0, 0), 75 | Color(0, 0, 0, 0), 76 | Color(0, 0, 0, 0), 77 | Color(0, 0, 0, 0), 78 | Color(0, 0, 0, 0), 79 | Color(0, 0, 0, 0), 80 | Color(0, 0, 0, 0), 81 | Color(0, 0, 0, 0), 82 | Color(0, 0, 0, 0), 83 | Color(0, 0, 0, 0), 84 | Color(0, 0, 0, 0), 85 | Color(0, 0, 0, 0), 86 | Color(0, 0, 0, 0), 87 | Color(0, 0, 0, 0), 88 | Color(0, 0, 0, 0), 89 | Color(255, 218, 68, 16), 90 | Color(255, 218, 68, 238), 91 | Color(255, 218, 68, 255), 92 | Color(255, 159, 34, 255), 93 | Color(255, 159, 34, 238), 94 | Color(255, 159, 34, 16), 95 | Color(0, 0, 0, 0), 96 | Color(0, 0, 0, 0), 97 | Color(0, 0, 0, 0), 98 | Color(0, 0, 0, 0), 99 | Color(0, 0, 0, 0), 100 | Color(0, 0, 0, 0), 101 | Color(0, 0, 0, 0), 102 | Color(0, 0, 0, 0), 103 | Color(0, 0, 0, 0), 104 | Color(0, 0, 0, 0), 105 | Color(0, 0, 0, 0), 106 | Color(0, 0, 0, 0), 107 | Color(0, 0, 0, 0), 108 | Color(0, 0, 0, 0), 109 | Color(255, 218, 68, 125), 110 | Color(255, 218, 68, 255), 111 | Color(255, 218, 68, 255), 112 | Color(255, 159, 34, 255), 113 | Color(255, 159, 34, 255), 114 | Color(255, 159, 34, 125), 115 | Color(0, 0, 0, 0), 116 | Color(0, 0, 0, 0), 117 | Color(0, 0, 0, 0), 118 | Color(0, 0, 0, 0), 119 | Color(0, 0, 0, 0), 120 | Color(0, 0, 0, 0), 121 | Color(0, 0, 0, 0), 122 | Color(0, 0, 0, 0), 123 | Color(0, 0, 0, 0), 124 | Color(0, 0, 0, 0), 125 | Color(0, 0, 0, 0), 126 | Color(0, 0, 0, 0), 127 | Color(255, 219, 69, 3), 128 | Color(255, 218, 68, 58), 129 | Color(255, 218, 68, 238), 130 | Color(255, 218, 68, 255), 131 | Color(255, 218, 68, 255), 132 | Color(255, 159, 34, 255), 133 | Color(255, 159, 34, 255), 134 | Color(255, 159, 34, 238), 135 | Color(255, 159, 34, 58), 136 | Color(255, 160, 35, 3), 137 | Color(0, 0, 0, 0), 138 | Color(0, 0, 0, 0), 139 | Color(0, 0, 0, 0), 140 | Color(0, 0, 0, 0), 141 | Color(0, 0, 0, 0), 142 | Color(255, 218, 68, 65), 143 | Color(255, 218, 68, 126), 144 | Color(255, 218, 68, 152), 145 | Color(255, 218, 68, 191), 146 | Color(255, 218, 68, 233), 147 | Color(255, 218, 68, 255), 148 | Color(255, 218, 68, 255), 149 | Color(255, 218, 68, 255), 150 | Color(255, 218, 68, 255), 151 | Color(255, 218, 68, 255), 152 | Color(255, 159, 34, 255), 153 | Color(255, 159, 34, 255), 154 | Color(255, 159, 34, 255), 155 | Color(255, 159, 34, 255), 156 | Color(255, 159, 34, 255), 157 | Color(255, 159, 34, 234), 158 | Color(255, 159, 34, 191), 159 | Color(255, 159, 34, 153), 160 | Color(255, 159, 34, 126), 161 | Color(255, 159, 34, 66), 162 | Color(255, 218, 68, 65), 163 | Color(255, 218, 68, 242), 164 | Color(255, 218, 68, 255), 165 | Color(255, 218, 68, 255), 166 | Color(255, 218, 68, 255), 167 | Color(255, 218, 68, 255), 168 | Color(255, 218, 68, 255), 169 | Color(255, 218, 68, 255), 170 | Color(255, 218, 68, 255), 171 | Color(255, 218, 68, 255), 172 | Color(255, 159, 34, 255), 173 | Color(255, 159, 34, 255), 174 | Color(255, 159, 34, 255), 175 | Color(255, 159, 34, 255), 176 | Color(255, 159, 34, 255), 177 | Color(255, 159, 34, 255), 178 | Color(255, 159, 34, 255), 179 | Color(255, 159, 34, 255), 180 | Color(255, 159, 34, 243), 181 | Color(255, 159, 34, 66), 182 | Color(0, 0, 0, 0), 183 | Color(255, 218, 68, 52), 184 | Color(255, 218, 68, 239), 185 | Color(255, 218, 68, 255), 186 | Color(255, 218, 68, 255), 187 | Color(255, 218, 68, 255), 188 | Color(255, 218, 68, 255), 189 | Color(255, 218, 68, 255), 190 | Color(255, 218, 68, 255), 191 | Color(255, 218, 68, 255), 192 | Color(255, 159, 34, 255), 193 | Color(255, 159, 34, 255), 194 | Color(255, 159, 34, 255), 195 | Color(255, 159, 34, 255), 196 | Color(255, 159, 34, 255), 197 | Color(255, 159, 34, 255), 198 | Color(255, 159, 34, 255), 199 | Color(255, 159, 34, 239), 200 | Color(255, 159, 34, 52), 201 | Color(0, 0, 0, 0), 202 | Color(0, 0, 0, 0), 203 | Color(0, 0, 0, 0), 204 | Color(255, 218, 68, 49), 205 | Color(255, 218, 68, 239), 206 | Color(255, 218, 68, 255), 207 | Color(255, 218, 68, 255), 208 | Color(255, 218, 68, 255), 209 | Color(255, 218, 68, 255), 210 | Color(255, 218, 68, 255), 211 | Color(255, 218, 68, 255), 212 | Color(255, 159, 34, 255), 213 | Color(255, 159, 34, 255), 214 | Color(255, 159, 34, 255), 215 | Color(255, 159, 34, 255), 216 | Color(255, 159, 34, 255), 217 | Color(255, 159, 34, 255), 218 | Color(255, 159, 34, 239), 219 | Color(255, 159, 34, 49), 220 | Color(0, 0, 0, 0), 221 | Color(0, 0, 0, 0), 222 | Color(0, 0, 0, 0), 223 | Color(0, 0, 0, 0), 224 | Color(0, 0, 0, 0), 225 | Color(255, 218, 68, 48), 226 | Color(255, 218, 68, 239), 227 | Color(255, 218, 68, 255), 228 | Color(255, 218, 68, 255), 229 | Color(255, 218, 68, 255), 230 | Color(255, 218, 68, 255), 231 | Color(255, 218, 68, 255), 232 | Color(255, 159, 34, 255), 233 | Color(255, 159, 34, 255), 234 | Color(255, 159, 34, 255), 235 | Color(255, 159, 34, 255), 236 | Color(255, 159, 34, 255), 237 | Color(255, 159, 34, 239), 238 | Color(255, 159, 34, 48), 239 | Color(0, 0, 0, 0), 240 | Color(0, 0, 0, 0), 241 | Color(0, 0, 0, 0), 242 | Color(0, 0, 0, 0), 243 | Color(0, 0, 0, 0), 244 | Color(0, 0, 0, 0), 245 | Color(0, 0, 0, 0), 246 | Color(255, 218, 68, 48), 247 | Color(255, 218, 68, 255), 248 | Color(255, 218, 68, 255), 249 | Color(255, 218, 68, 255), 250 | Color(255, 218, 68, 255), 251 | Color(255, 218, 68, 255), 252 | Color(255, 159, 34, 255), 253 | Color(255, 159, 34, 255), 254 | Color(255, 159, 34, 255), 255 | Color(255, 159, 34, 255), 256 | Color(255, 159, 34, 255), 257 | Color(255, 159, 34, 48), 258 | Color(0, 0, 0, 0), 259 | Color(0, 0, 0, 0), 260 | Color(0, 0, 0, 0), 261 | Color(0, 0, 0, 0), 262 | Color(0, 0, 0, 0), 263 | Color(0, 0, 0, 0), 264 | Color(0, 0, 0, 0), 265 | Color(0, 0, 0, 0), 266 | Color(255, 218, 68, 41), 267 | Color(255, 218, 68, 255), 268 | Color(255, 218, 68, 255), 269 | Color(255, 218, 68, 255), 270 | Color(255, 218, 68, 255), 271 | Color(255, 218, 68, 255), 272 | Color(255, 159, 34, 255), 273 | Color(255, 159, 34, 255), 274 | Color(255, 159, 34, 255), 275 | Color(255, 159, 34, 255), 276 | Color(255, 159, 34, 255), 277 | Color(255, 159, 34, 41), 278 | Color(0, 0, 0, 0), 279 | Color(0, 0, 0, 0), 280 | Color(0, 0, 0, 0), 281 | Color(0, 0, 0, 0), 282 | Color(0, 0, 0, 0), 283 | Color(0, 0, 0, 0), 284 | Color(0, 0, 0, 0), 285 | Color(0, 0, 0, 0), 286 | Color(255, 218, 68, 76), 287 | Color(255, 218, 68, 255), 288 | Color(255, 218, 68, 255), 289 | Color(255, 218, 68, 255), 290 | Color(255, 218, 68, 255), 291 | Color(255, 218, 68, 255), 292 | Color(255, 159, 34, 255), 293 | Color(255, 159, 34, 255), 294 | Color(255, 159, 34, 255), 295 | Color(255, 159, 34, 255), 296 | Color(255, 159, 34, 255), 297 | Color(255, 159, 34, 76), 298 | Color(0, 0, 0, 0), 299 | Color(0, 0, 0, 0), 300 | Color(0, 0, 0, 0), 301 | Color(0, 0, 0, 0), 302 | Color(0, 0, 0, 0), 303 | Color(0, 0, 0, 0), 304 | Color(0, 0, 0, 0), 305 | Color(0, 0, 0, 0), 306 | Color(255, 218, 68, 127), 307 | Color(255, 218, 68, 255), 308 | Color(255, 218, 68, 255), 309 | Color(255, 218, 68, 255), 310 | Color(255, 218, 68, 255), 311 | Color(255, 218, 68, 255), 312 | Color(255, 159, 34, 255), 313 | Color(255, 159, 34, 255), 314 | Color(255, 159, 34, 255), 315 | Color(255, 159, 34, 255), 316 | Color(255, 159, 34, 255), 317 | Color(255, 159, 34, 127), 318 | Color(0, 0, 0, 0), 319 | Color(0, 0, 0, 0), 320 | Color(0, 0, 0, 0), 321 | Color(0, 0, 0, 0), 322 | Color(0, 0, 0, 0), 323 | Color(0, 0, 0, 0), 324 | Color(0, 0, 0, 0), 325 | Color(0, 0, 0, 0), 326 | Color(255, 218, 68, 174), 327 | Color(255, 218, 68, 255), 328 | Color(255, 218, 68, 255), 329 | Color(255, 218, 68, 255), 330 | Color(255, 218, 68, 243), 331 | Color(255, 218, 68, 128), 332 | Color(255, 159, 34, 128), 333 | Color(255, 159, 34, 242), 334 | Color(255, 159, 34, 255), 335 | Color(255, 159, 34, 255), 336 | Color(255, 159, 34, 255), 337 | Color(255, 159, 34, 174), 338 | Color(0, 0, 0, 0), 339 | Color(0, 0, 0, 0), 340 | Color(0, 0, 0, 0), 341 | Color(0, 0, 0, 0), 342 | Color(0, 0, 0, 0), 343 | Color(0, 0, 0, 0), 344 | Color(0, 0, 0, 0), 345 | Color(0, 0, 0, 0), 346 | Color(255, 218, 68, 209), 347 | Color(255, 218, 68, 255), 348 | Color(255, 218, 68, 254), 349 | Color(255, 218, 68, 149), 350 | Color(255, 218, 68, 20), 351 | Color(0, 0, 0, 0), 352 | Color(0, 0, 0, 0), 353 | Color(255, 159, 34, 20), 354 | Color(255, 159, 34, 149), 355 | Color(255, 159, 34, 254), 356 | Color(255, 159, 34, 255), 357 | Color(255, 159, 34, 209), 358 | Color(0, 0, 0, 0), 359 | Color(0, 0, 0, 0), 360 | Color(0, 0, 0, 0), 361 | Color(0, 0, 0, 0), 362 | Color(0, 0, 0, 0), 363 | Color(0, 0, 0, 0), 364 | Color(0, 0, 0, 0), 365 | Color(0, 0, 0, 0), 366 | Color(255, 218, 68, 255), 367 | Color(255, 218, 68, 160), 368 | Color(255, 218, 68, 31), 369 | Color(0, 0, 0, 0), 370 | Color(0, 0, 0, 0), 371 | Color(0, 0, 0, 0), 372 | Color(0, 0, 0, 0), 373 | Color(0, 0, 0, 0), 374 | Color(0, 0, 0, 0), 375 | Color(255, 159, 34, 31), 376 | Color(255, 159, 34, 160), 377 | Color(255, 159, 34, 255), 378 | Color(0, 0, 0, 0), 379 | Color(0, 0, 0, 0), 380 | Color(0, 0, 0, 0), 381 | Color(0, 0, 0, 0), 382 | Color(0, 0, 0, 0), 383 | Color(0, 0, 0, 0), 384 | Color(0, 0, 0, 0), 385 | Color(255, 218, 68, 20), 386 | Color(255, 218, 68, 35), 387 | Color(0, 0, 0, 0), 388 | Color(0, 0, 0, 0), 389 | Color(0, 0, 0, 0), 390 | Color(0, 0, 0, 0), 391 | Color(0, 0, 0, 0), 392 | Color(0, 0, 0, 0), 393 | Color(0, 0, 0, 0), 394 | Color(0, 0, 0, 0), 395 | Color(0, 0, 0, 0), 396 | Color(0, 0, 0, 0), 397 | Color(255, 159, 34, 35), 398 | Color(255, 159, 34, 20), 399 | Color(0, 0, 0, 0), 400 | Color(0, 0, 0, 0), 401 | Color(0, 0, 0, 0), 402 | }; -------------------------------------------------------------------------------- /plugin/source/config/dir_icon.inc: -------------------------------------------------------------------------------- 1 | static const Color dir_icon[] = { 2 | Color(0, 0, 0, 0), 3 | Color(0, 0, 0, 0), 4 | Color(0, 0, 0, 0), 5 | Color(0, 0, 0, 0), 6 | Color(0, 0, 0, 0), 7 | Color(0, 0, 0, 0), 8 | Color(0, 0, 0, 0), 9 | Color(0, 0, 0, 0), 10 | Color(0, 0, 0, 0), 11 | Color(0, 0, 0, 0), 12 | Color(0, 0, 0, 0), 13 | Color(0, 0, 0, 0), 14 | Color(0, 0, 0, 0), 15 | Color(0, 0, 0, 0), 16 | Color(0, 0, 0, 0), 17 | Color(0, 0, 0, 0), 18 | Color(0, 0, 0, 0), 19 | Color(0, 0, 0, 0), 20 | Color(0, 0, 0, 0), 21 | Color(0, 0, 0, 0), 22 | Color(0, 0, 0, 0), 23 | Color(0, 0, 0, 0), 24 | Color(0, 0, 0, 0), 25 | Color(0, 0, 0, 0), 26 | Color(0, 0, 0, 0), 27 | Color(0, 0, 0, 0), 28 | Color(0, 0, 0, 0), 29 | Color(0, 0, 0, 0), 30 | Color(0, 0, 0, 0), 31 | Color(0, 0, 0, 0), 32 | Color(0, 0, 0, 0), 33 | Color(0, 0, 0, 0), 34 | Color(0, 0, 0, 0), 35 | Color(0, 0, 0, 0), 36 | Color(0, 0, 0, 0), 37 | Color(0, 0, 0, 0), 38 | Color(0, 0, 0, 0), 39 | Color(0, 0, 0, 0), 40 | Color(0, 0, 0, 0), 41 | Color(0, 0, 0, 0), 42 | Color(253, 170, 75, 141), 43 | Color(253, 162, 34, 162), 44 | Color(253, 159, 0, 162), 45 | Color(253, 159, 0, 162), 46 | Color(253, 159, 0, 162), 47 | Color(253, 161, 17, 162), 48 | Color(253, 163, 31, 113), 49 | Color(0, 0, 0, 0), 50 | Color(0, 0, 0, 0), 51 | Color(0, 0, 0, 0), 52 | Color(0, 0, 0, 0), 53 | Color(0, 0, 0, 0), 54 | Color(0, 0, 0, 0), 55 | Color(0, 0, 0, 0), 56 | Color(0, 0, 0, 0), 57 | Color(0, 0, 0, 0), 58 | Color(0, 0, 0, 0), 59 | Color(0, 0, 0, 0), 60 | Color(0, 0, 0, 0), 61 | Color(0, 0, 0, 0), 62 | Color(253, 164, 50, 221), 63 | Color(253, 160, 21, 255), 64 | Color(253, 159, 0, 255), 65 | Color(253, 159, 0, 255), 66 | Color(253, 159, 0, 255), 67 | Color(253, 160, 8, 255), 68 | Color(253, 161, 20, 219), 69 | Color(252, 162, 25, 109), 70 | Color(0, 0, 0, 0), 71 | Color(0, 0, 0, 0), 72 | Color(0, 0, 0, 0), 73 | Color(0, 0, 0, 0), 74 | Color(0, 0, 0, 0), 75 | Color(0, 0, 0, 0), 76 | Color(0, 0, 0, 0), 77 | Color(0, 0, 0, 0), 78 | Color(0, 0, 0, 0), 79 | Color(0, 0, 0, 0), 80 | Color(0, 0, 0, 0), 81 | Color(0, 0, 0, 0), 82 | Color(253, 159, 0, 221), 83 | Color(253, 159, 0, 255), 84 | Color(253, 159, 0, 255), 85 | Color(253, 159, 0, 255), 86 | Color(253, 159, 0, 255), 87 | Color(253, 159, 0, 255), 88 | Color(253, 160, 5, 255), 89 | Color(253, 160, 15, 225), 90 | Color(253, 159, 0, 115), 91 | Color(253, 159, 0, 115), 92 | Color(253, 159, 0, 115), 93 | Color(253, 159, 0, 115), 94 | Color(253, 159, 0, 115), 95 | Color(253, 159, 0, 115), 96 | Color(253, 159, 0, 115), 97 | Color(253, 159, 0, 115), 98 | Color(253, 159, 0, 115), 99 | Color(253, 159, 0, 83), 100 | Color(0, 0, 0, 0), 101 | Color(0, 0, 0, 0), 102 | Color(253, 159, 0, 221), 103 | Color(253, 159, 0, 255), 104 | Color(253, 159, 0, 255), 105 | Color(253, 159, 0, 255), 106 | Color(253, 160, 1, 255), 107 | Color(253, 160, 1, 255), 108 | Color(253, 160, 1, 255), 109 | Color(253, 160, 1, 255), 110 | Color(253, 160, 1, 255), 111 | Color(253, 160, 1, 255), 112 | Color(253, 160, 1, 255), 113 | Color(253, 160, 1, 255), 114 | Color(253, 160, 1, 255), 115 | Color(253, 160, 1, 255), 116 | Color(253, 160, 1, 255), 117 | Color(253, 160, 1, 255), 118 | Color(253, 160, 1, 255), 119 | Color(253, 164, 45, 210), 120 | Color(252, 187, 127, 74), 121 | Color(0, 0, 0, 0), 122 | Color(253, 159, 0, 221), 123 | Color(253, 159, 0, 255), 124 | Color(253, 159, 0, 255), 125 | Color(253, 161, 3, 255), 126 | Color(253, 165, 8, 255), 127 | Color(253, 168, 12, 255), 128 | Color(253, 168, 13, 255), 129 | Color(253, 168, 13, 255), 130 | Color(253, 168, 13, 255), 131 | Color(253, 168, 13, 255), 132 | Color(253, 168, 13, 255), 133 | Color(253, 168, 13, 255), 134 | Color(253, 168, 13, 255), 135 | Color(253, 168, 13, 255), 136 | Color(253, 168, 13, 255), 137 | Color(253, 168, 13, 255), 138 | Color(253, 168, 13, 255), 139 | Color(253, 174, 66, 255), 140 | Color(252, 189, 119, 209), 141 | Color(0, 0, 0, 0), 142 | Color(253, 159, 0, 221), 143 | Color(253, 159, 0, 255), 144 | Color(253, 160, 2, 255), 145 | Color(253, 169, 14, 255), 146 | Color(254, 180, 25, 255), 147 | Color(254, 186, 30, 255), 148 | Color(254, 186, 30, 255), 149 | Color(254, 186, 30, 255), 150 | Color(254, 186, 30, 255), 151 | Color(254, 186, 30, 255), 152 | Color(254, 186, 30, 255), 153 | Color(254, 186, 30, 255), 154 | Color(254, 186, 30, 255), 155 | Color(254, 186, 30, 255), 156 | Color(254, 186, 30, 255), 157 | Color(254, 186, 30, 255), 158 | Color(254, 186, 30, 255), 159 | Color(254, 189, 53, 255), 160 | Color(253, 195, 85, 216), 161 | Color(254, 200, 39, 39), 162 | Color(253, 159, 0, 221), 163 | Color(253, 159, 0, 255), 164 | Color(253, 164, 8, 255), 165 | Color(254, 186, 30, 255), 166 | Color(254, 199, 39, 255), 167 | Color(254, 200, 39, 255), 168 | Color(254, 200, 39, 255), 169 | Color(254, 200, 39, 255), 170 | Color(254, 200, 39, 255), 171 | Color(254, 200, 39, 255), 172 | Color(254, 200, 39, 255), 173 | Color(254, 200, 39, 255), 174 | Color(254, 200, 39, 255), 175 | Color(254, 200, 39, 255), 176 | Color(254, 200, 39, 255), 177 | Color(254, 200, 39, 255), 178 | Color(254, 200, 39, 255), 179 | Color(254, 200, 39, 255), 180 | Color(254, 200, 39, 255), 181 | Color(254, 200, 39, 232), 182 | Color(253, 159, 0, 221), 183 | Color(253, 159, 0, 255), 184 | Color(253, 172, 17, 255), 185 | Color(254, 191, 33, 255), 186 | Color(254, 200, 39, 255), 187 | Color(254, 200, 39, 255), 188 | Color(254, 200, 39, 255), 189 | Color(254, 200, 39, 255), 190 | Color(254, 200, 39, 255), 191 | Color(254, 200, 39, 255), 192 | Color(254, 200, 39, 255), 193 | Color(254, 200, 39, 255), 194 | Color(254, 200, 39, 255), 195 | Color(254, 200, 39, 255), 196 | Color(254, 200, 39, 255), 197 | Color(254, 200, 39, 255), 198 | Color(254, 200, 39, 255), 199 | Color(254, 200, 39, 255), 200 | Color(254, 200, 39, 255), 201 | Color(254, 200, 39, 232), 202 | Color(253, 159, 0, 221), 203 | Color(253, 159, 0, 255), 204 | Color(253, 177, 22, 255), 205 | Color(254, 194, 35, 255), 206 | Color(254, 200, 39, 255), 207 | Color(254, 200, 39, 255), 208 | Color(254, 200, 39, 255), 209 | Color(254, 200, 39, 255), 210 | Color(254, 200, 39, 255), 211 | Color(254, 200, 39, 255), 212 | Color(254, 200, 39, 255), 213 | Color(254, 200, 39, 255), 214 | Color(254, 200, 39, 255), 215 | Color(254, 200, 39, 255), 216 | Color(254, 200, 39, 255), 217 | Color(254, 200, 39, 255), 218 | Color(254, 200, 39, 255), 219 | Color(254, 200, 39, 255), 220 | Color(254, 200, 39, 255), 221 | Color(254, 200, 39, 232), 222 | Color(253, 159, 0, 221), 223 | Color(253, 159, 0, 255), 224 | Color(254, 181, 26, 255), 225 | Color(254, 196, 37, 255), 226 | Color(254, 200, 39, 255), 227 | Color(254, 200, 39, 255), 228 | Color(254, 200, 39, 255), 229 | Color(254, 200, 39, 255), 230 | Color(254, 200, 39, 255), 231 | Color(254, 200, 39, 255), 232 | Color(254, 200, 39, 255), 233 | Color(254, 200, 39, 255), 234 | Color(254, 200, 39, 255), 235 | Color(254, 200, 39, 255), 236 | Color(254, 200, 39, 255), 237 | Color(254, 200, 39, 255), 238 | Color(254, 200, 39, 255), 239 | Color(254, 200, 39, 255), 240 | Color(254, 200, 39, 255), 241 | Color(254, 200, 39, 232), 242 | Color(253, 159, 0, 221), 243 | Color(253, 160, 1, 255), 244 | Color(254, 186, 29, 255), 245 | Color(254, 199, 38, 255), 246 | Color(254, 200, 39, 255), 247 | Color(254, 200, 39, 255), 248 | Color(254, 200, 39, 255), 249 | Color(254, 200, 39, 255), 250 | Color(254, 200, 39, 255), 251 | Color(254, 200, 39, 255), 252 | Color(254, 200, 39, 255), 253 | Color(254, 200, 39, 255), 254 | Color(254, 200, 39, 255), 255 | Color(254, 200, 39, 255), 256 | Color(254, 200, 39, 255), 257 | Color(254, 200, 39, 255), 258 | Color(254, 200, 39, 255), 259 | Color(254, 200, 39, 255), 260 | Color(254, 200, 39, 255), 261 | Color(254, 200, 39, 232), 262 | Color(253, 159, 0, 221), 263 | Color(253, 163, 5, 255), 264 | Color(254, 189, 32, 255), 265 | Color(254, 200, 39, 255), 266 | Color(254, 200, 39, 255), 267 | Color(254, 200, 39, 255), 268 | Color(254, 200, 39, 255), 269 | Color(254, 200, 39, 255), 270 | Color(254, 200, 39, 255), 271 | Color(254, 200, 39, 255), 272 | Color(254, 200, 39, 255), 273 | Color(254, 200, 39, 255), 274 | Color(254, 200, 39, 255), 275 | Color(254, 200, 39, 255), 276 | Color(254, 200, 39, 255), 277 | Color(254, 200, 39, 255), 278 | Color(254, 200, 39, 255), 279 | Color(254, 200, 39, 255), 280 | Color(254, 200, 41, 255), 281 | Color(254, 201, 48, 232), 282 | Color(253, 159, 0, 221), 283 | Color(253, 167, 11, 255), 284 | Color(254, 191, 33, 255), 285 | Color(254, 200, 39, 255), 286 | Color(254, 200, 39, 255), 287 | Color(254, 200, 39, 255), 288 | Color(254, 200, 39, 255), 289 | Color(254, 200, 39, 255), 290 | Color(254, 200, 39, 255), 291 | Color(254, 200, 39, 255), 292 | Color(254, 200, 39, 255), 293 | Color(254, 200, 39, 255), 294 | Color(254, 200, 39, 255), 295 | Color(254, 200, 39, 255), 296 | Color(254, 200, 39, 255), 297 | Color(254, 200, 39, 255), 298 | Color(254, 200, 39, 255), 299 | Color(254, 200, 39, 255), 300 | Color(254, 201, 53, 255), 301 | Color(253, 205, 91, 232), 302 | Color(253, 159, 0, 221), 303 | Color(253, 173, 18, 255), 304 | Color(254, 193, 34, 255), 305 | Color(254, 200, 39, 255), 306 | Color(254, 200, 39, 255), 307 | Color(254, 200, 39, 255), 308 | Color(254, 200, 39, 255), 309 | Color(254, 200, 39, 255), 310 | Color(254, 200, 39, 255), 311 | Color(254, 200, 39, 255), 312 | Color(254, 200, 39, 255), 313 | Color(254, 200, 39, 255), 314 | Color(254, 200, 39, 255), 315 | Color(254, 200, 39, 255), 316 | Color(254, 200, 39, 255), 317 | Color(254, 200, 39, 255), 318 | Color(254, 200, 39, 255), 319 | Color(254, 200, 39, 255), 320 | Color(254, 201, 54, 236), 321 | Color(253, 209, 114, 135), 322 | Color(253, 161, 35, 221), 323 | Color(253, 180, 38, 255), 324 | Color(253, 196, 45, 255), 325 | Color(254, 201, 48, 255), 326 | Color(254, 200, 48, 255), 327 | Color(254, 200, 48, 255), 328 | Color(254, 200, 48, 255), 329 | Color(254, 200, 48, 255), 330 | Color(254, 201, 48, 255), 331 | Color(254, 200, 48, 255), 332 | Color(254, 201, 48, 255), 333 | Color(254, 200, 48, 255), 334 | Color(254, 201, 48, 255), 335 | Color(254, 200, 48, 255), 336 | Color(254, 200, 48, 255), 337 | Color(254, 200, 48, 255), 338 | Color(254, 200, 48, 255), 339 | Color(254, 201, 50, 255), 340 | Color(254, 201, 55, 209), 341 | Color(0, 0, 0, 0), 342 | Color(253, 166, 64, 170), 343 | Color(253, 186, 59, 196), 344 | Color(253, 198, 60, 196), 345 | Color(253, 202, 62, 196), 346 | Color(253, 202, 62, 196), 347 | Color(253, 201, 62, 196), 348 | Color(253, 201, 62, 196), 349 | Color(253, 201, 62, 196), 350 | Color(253, 202, 62, 196), 351 | Color(253, 201, 62, 196), 352 | Color(253, 202, 62, 196), 353 | Color(253, 201, 62, 196), 354 | Color(253, 202, 62, 196), 355 | Color(253, 201, 62, 196), 356 | Color(253, 201, 62, 196), 357 | Color(253, 201, 62, 196), 358 | Color(253, 201, 62, 196), 359 | Color(254, 202, 67, 196), 360 | Color(254, 203, 78, 160), 361 | Color(0, 0, 0, 0), 362 | Color(0, 0, 0, 0), 363 | Color(0, 0, 0, 0), 364 | Color(0, 0, 0, 0), 365 | Color(0, 0, 0, 0), 366 | Color(0, 0, 0, 0), 367 | Color(0, 0, 0, 0), 368 | Color(0, 0, 0, 0), 369 | Color(0, 0, 0, 0), 370 | Color(0, 0, 0, 0), 371 | Color(0, 0, 0, 0), 372 | Color(0, 0, 0, 0), 373 | Color(0, 0, 0, 0), 374 | Color(0, 0, 0, 0), 375 | Color(0, 0, 0, 0), 376 | Color(0, 0, 0, 0), 377 | Color(0, 0, 0, 0), 378 | Color(0, 0, 0, 0), 379 | Color(0, 0, 0, 0), 380 | Color(0, 0, 0, 0), 381 | Color(0, 0, 0, 0), 382 | Color(0, 0, 0, 0), 383 | Color(0, 0, 0, 0), 384 | Color(0, 0, 0, 0), 385 | Color(0, 0, 0, 0), 386 | Color(0, 0, 0, 0), 387 | Color(0, 0, 0, 0), 388 | Color(0, 0, 0, 0), 389 | Color(0, 0, 0, 0), 390 | Color(0, 0, 0, 0), 391 | Color(0, 0, 0, 0), 392 | Color(0, 0, 0, 0), 393 | Color(0, 0, 0, 0), 394 | Color(0, 0, 0, 0), 395 | Color(0, 0, 0, 0), 396 | Color(0, 0, 0, 0), 397 | Color(0, 0, 0, 0), 398 | Color(0, 0, 0, 0), 399 | Color(0, 0, 0, 0), 400 | Color(0, 0, 0, 0), 401 | Color(0, 0, 0, 0), 402 | }; -------------------------------------------------------------------------------- /source/ntag_crypt.c: -------------------------------------------------------------------------------- 1 | #include "ntag_crypt.h" 2 | #include "debug/logger.h" 3 | 4 | #include 5 | #include 6 | 7 | /* This file handles converting nfc data and encrypting/decrypting the 8 | data using /dev/ccr_nfc. It's mainly based on what ntag.rpl does after 9 | reading or before writing tags. 10 | This should probably be replaced with a custom format without encryption 11 | at some point. */ 12 | 13 | typedef struct { 14 | uint32_t version; 15 | uint32_t offsets[10]; 16 | uint8_t data[0x21c]; 17 | } NFCCryptData; 18 | WUT_CHECK_SIZE(NFCCryptData, 0x248); 19 | 20 | static void rawDataToCryptData(NFCCryptData* raw, NFCCryptData* crypt) 21 | { 22 | memcpy(crypt, raw, sizeof(NFCCryptData)); 23 | crypt->version = raw->version; 24 | crypt->offsets[0] = 0x208; 25 | crypt->offsets[1] = 0x29; 26 | crypt->offsets[2] = 0x1e8; 27 | crypt->offsets[3] = 0x1d4; 28 | crypt->offsets[4] = 0x2c; 29 | crypt->offsets[5] = 0x188; 30 | crypt->offsets[6] = 0x1dc; 31 | crypt->offsets[7] = 0x0; 32 | crypt->offsets[8] = 0x8; 33 | crypt->offsets[9] = 0x1b4; 34 | 35 | uint8_t* src = (uint8_t*) raw->data; 36 | uint8_t* dst = (uint8_t*) crypt->data; 37 | memcpy(dst + 0x1d4, src, 0x8); 38 | memcpy(dst, src + 0x8, 0x8); 39 | memcpy(dst + 0x28, src + 0x10, 0x4); 40 | memcpy(dst + crypt->offsets[4], src + 0x14, 0x20); 41 | memcpy(dst + crypt->offsets[9], src + 0x34, 0x20); 42 | memcpy(dst + crypt->offsets[6], src + 0x54, 0xc); 43 | memcpy(dst + crypt->offsets[2], src + 0x60, 0x20); 44 | memcpy(dst + crypt->offsets[8], src + 0x80, 0x20); 45 | memcpy(dst + crypt->offsets[4] + 0x20, src + 0xa0, 0x168); 46 | memcpy(dst + 0x208, src + 0x208, 0x14); 47 | } 48 | 49 | static void cryptDataToRawData(NFCCryptData* crypt, NFCCryptData* raw) 50 | { 51 | memcpy(raw, crypt, sizeof(NFCCryptData)); 52 | raw->version = crypt->version; 53 | raw->offsets[0] = 0x208; 54 | raw->offsets[1] = 0x11; 55 | raw->offsets[2] = 0x60; 56 | raw->offsets[3] = 0x0; 57 | raw->offsets[4] = 0x14; 58 | raw->offsets[5] = 0x188; 59 | raw->offsets[6] = 0x54; 60 | raw->offsets[7] = 0xc; 61 | raw->offsets[8] = 0x80; 62 | raw->offsets[9] = 0x34; 63 | 64 | uint8_t* src = (uint8_t*) crypt->data; 65 | uint8_t* dst = (uint8_t*) raw->data; 66 | memcpy(dst + 0x8, src, 0x8); 67 | memcpy(dst + 0x10, src + 0x28, 0x4); 68 | memcpy(dst + 0xa0, src + 0x4c, 0x168); 69 | memcpy(dst + raw->offsets[8], src + 0x8, 0x20); 70 | memcpy(dst + raw->offsets[4], src + 0x2c, 0x20); 71 | memcpy(dst + raw->offsets[9], src + 0x1b4, 0x20); 72 | memcpy(dst + raw->offsets[3], src + 0x1d4, 0x8); 73 | memcpy(dst + raw->offsets[6], src + 0x1dc, 0xc); 74 | memcpy(dst + raw->offsets[2], src + 0x1e8, 0x20); 75 | memcpy(dst + 0x208, src + 0x208, 0x14); 76 | } 77 | 78 | static int decryptGameData(NTAGRawDataT2T* data) 79 | { 80 | // Only support version 2 81 | if (data->section1.formatVersion != 2) { 82 | return -1; 83 | } 84 | 85 | int ccrNfcHandle = IOS_Open("/dev/ccr_nfc", (IOSOpenMode) 0); 86 | if (ccrNfcHandle < 0) { 87 | return ccrNfcHandle; 88 | } 89 | 90 | NFCCryptData rawData; 91 | rawData.version = data->section1.formatVersion; 92 | memcpy(rawData.data, data, sizeof(NTAGRawDataT2T)); 93 | 94 | NFCCryptData inData; 95 | rawDataToCryptData(&rawData, &inData); 96 | 97 | // Let /dev/ccr_nfc do the actual decryption 98 | NFCCryptData outData; 99 | int res = IOS_Ioctl(ccrNfcHandle, 2, &inData, sizeof(inData), &outData, sizeof(outData)); 100 | IOS_Close(ccrNfcHandle); 101 | if (res < 0) { 102 | return res; 103 | } 104 | 105 | memset(&rawData, 0, sizeof(rawData)); 106 | cryptDataToRawData(&outData, &rawData); 107 | 108 | if (rawData.version != 2) { 109 | return -1; 110 | } 111 | 112 | memcpy(data, rawData.data, sizeof(NTAGRawDataT2T)); 113 | 114 | return 0; 115 | } 116 | 117 | static int encryptGameData(NTAGRawDataT2T* data) 118 | { 119 | // Only support version 2 120 | if (data->section1.formatVersion != 2) { 121 | return -1; 122 | } 123 | 124 | int ccrNfcHandle = IOS_Open("/dev/ccr_nfc", (IOSOpenMode) 0); 125 | if (ccrNfcHandle < 0) { 126 | return ccrNfcHandle; 127 | } 128 | 129 | NFCCryptData rawData; 130 | rawData.version = data->section1.formatVersion; 131 | memcpy(rawData.data, data, sizeof(NTAGRawDataT2T)); 132 | 133 | NFCCryptData inData; 134 | rawDataToCryptData(&rawData, &inData); 135 | 136 | // Let /dev/ccr_nfc do the actual encryption 137 | NFCCryptData outData; 138 | int res = IOS_Ioctl(ccrNfcHandle, 1, &inData, sizeof(inData), &outData, sizeof(outData)); 139 | IOS_Close(ccrNfcHandle); 140 | if (res < 0) { 141 | return res; 142 | } 143 | 144 | memset(&rawData, 0, sizeof(rawData)); 145 | cryptDataToRawData(&outData, &rawData); 146 | 147 | if (rawData.version != 2) { 148 | return -1; 149 | } 150 | 151 | memcpy(data, rawData.data, sizeof(NTAGRawDataT2T)); 152 | 153 | return 0; 154 | } 155 | 156 | int NTAGDecrypt(NTAGDataT2T* data, NTAGRawDataT2T* raw) 157 | { 158 | // Verify tag version 159 | if (raw->section1.formatVersion != 2) { 160 | DEBUG_FUNCTION_LINE("Invalid format version: %u", raw->section1.formatVersion); 161 | return -1; 162 | } 163 | 164 | // Copy UID, set proto and tag type 165 | data->tagInfo.uidSize = 0x7; 166 | memcpy(data->tagInfo.uid, raw->uid, data->tagInfo.uidSize); 167 | data->tagInfo.technology = NFC_TECHNOLOGY_A; 168 | data->tagInfo.protocol = NFC_PROTOCOL_T2T; 169 | 170 | // Verify lock and cfg bytes 171 | if (raw->lockBytes[0] != 0x0f || raw->lockBytes[1] != 0xe0 || raw->dynamicLock[0] != 0x01 172 | || raw->dynamicLock[1] != 0x00 || raw->dynamicLock[2] != 0x0f || raw->cfg0[0] != 0x00 173 | || raw->cfg0[1] != 0x00 || raw->cfg0[2] != 0x00 || raw->cfg0[3] != 0x04 || raw->cfg1[0] != 0x5f 174 | || raw->cfg1[2] != 0x00 || raw->cfg1[3] != 0x00) { 175 | DEBUG_FUNCTION_LINE("Invalid lock/cfg bit !"); 176 | return -1; 177 | } 178 | 179 | // Store raw data 180 | data->raw.size = sizeof(NTAGRawDataT2T); 181 | memcpy(&data->raw.data, raw, sizeof(NTAGRawDataT2T)); 182 | 183 | // Decrypt 184 | int res = decryptGameData(raw); 185 | if (res != 0) { 186 | DEBUG_FUNCTION_LINE("Failed to decrypt data"); 187 | return res; 188 | } 189 | 190 | // Convert 191 | data->info.figureVersion = raw->section0.figureVersion; 192 | data->info.setupDate = raw->section0.setupDate; 193 | data->info.magic = raw->section0.magic; 194 | data->info.writes = raw->section0.writes; 195 | data->info.flags = raw->section0.flags >> 4; 196 | data->info.country = raw->section0.country; 197 | data->info.fontRegion = raw->section0.flags & 0xf; 198 | data->info.crcCounter = raw->section0.crcCounter; 199 | data->info.lastWriteDate = raw->section0.lastWriteDate; 200 | memcpy(&data->info.crc, &raw->section0.crc, sizeof(raw->section0.crc)); 201 | memcpy(data->info.name, raw->section0.name, sizeof(raw->section0.name)); 202 | data->info.accessID = raw->section2.accessID; 203 | memcpy(data->info.characterID, raw->section1.characterID, sizeof(raw->section1.characterID)); 204 | data->info.figureType = raw->section1.figureType; 205 | data->info.numberingID = raw->section1.numberingID; 206 | data->info.seriesID = raw->section1.seriesID; 207 | memcpy(&data->info.unknown, &raw->section1.unknown, sizeof(raw->section1.unknown)); 208 | memcpy(&data->info.mii, &raw->section2.mii, sizeof(raw->section2.mii)); 209 | data->info.titleID = raw->section2.titleID; 210 | data->info.applicationAreaWrites = raw->section2.applicationAreaWrites; 211 | memset(data->info.reserved, 0, sizeof(data->info.reserved)); 212 | memcpy(data->info.reserved, raw->section2.reserved, sizeof(raw->section2.reserved)); 213 | memset(data->appData.data, 0, sizeof(data->appData.data)); 214 | data->appData.size = 0xd8; 215 | memcpy(data->appData.data, raw->applicationData, data->appData.size); 216 | data->formatVersion = raw->section1.formatVersion; 217 | 218 | return 0; 219 | } 220 | 221 | int NTAGEncrypt(NTAGRawDataT2T* raw, NTAGDataT2T* data) 222 | { 223 | #if 0 // not doing this anymore since NTAGConvertT2T does no error handling and also corrupts data sometimes? 224 | // To encrypt we can simply call NTAGConvertT2T 225 | NTAGDataT2T tmp; 226 | memset(&tmp, 0, sizeof(tmp)); 227 | 228 | int res = NTAGConvertT2T(&tmp, data); 229 | if (res != 0) { 230 | DEBUG_FUNCTION_LINE("NTAGConvertT2T failed"); 231 | return res; 232 | } 233 | 234 | if (tmp.section1.version != 2) { 235 | DEBUG_FUNCTION_LINE("Invalid format version"); 236 | return -1; 237 | } 238 | 239 | memcpy(raw, &data->rawData, sizeof(NTAGRawDataT2T)); 240 | 241 | // Copy over the individual modified sections 242 | memcpy(&raw->section0, &tmp.rawData.section0, sizeof(raw->section0)); 243 | memcpy(&raw->section1, &tmp.rawData.section1, sizeof(raw->section1)); 244 | memcpy(&raw->section2, &tmp.rawData.section2, sizeof(raw->section2)); 245 | memcpy(&raw->applicationData, &tmp.rawData.applicationData, sizeof(raw->applicationData)); 246 | #endif 247 | 248 | // Convert 249 | memcpy(raw, &data->raw.data, sizeof(data->raw.data)); 250 | raw->section0.crcCounter = data->info.crcCounter; 251 | raw->section0.magic = data->info.magic; 252 | raw->section0.writes = data->info.writes; 253 | raw->section0.country = data->info.country; 254 | raw->section0.flags = (data->info.fontRegion & 0xf) | (data->info.flags << 4); 255 | raw->section0.setupDate = data->info.setupDate; 256 | raw->section0.figureVersion = data->info.figureVersion; 257 | raw->section0.lastWriteDate = data->info.lastWriteDate; 258 | memcpy(&raw->section0.crc, &data->info.crc, sizeof(data->info.crc)); 259 | raw->section2.accessID = data->info.accessID; 260 | memcpy(raw->section0.name, data->info.name, sizeof(data->info.name)); 261 | memcpy(raw->section1.tagHmac, data->raw.data.section1.tagHmac, sizeof(data->raw.data.section1.tagHmac)); 262 | memcpy(raw->section1.characterID, data->info.characterID, sizeof(data->info.characterID)); 263 | raw->section1.numberingID = data->info.numberingID; 264 | raw->section1.seriesID = data->info.seriesID; 265 | raw->section1.figureType = data->info.figureType; 266 | raw->section1.formatVersion = data->formatVersion; 267 | memcpy(&raw->section1.unknown, &data->info.unknown, sizeof(data->info.unknown)); 268 | memcpy(raw->section1.keygenSalt, data->raw.data.section1.keygenSalt, sizeof(data->raw.data.section1.keygenSalt)); 269 | memcpy(raw->section1.dataHmac, data->raw.data.section1.dataHmac, sizeof(data->raw.data.section1.dataHmac)); 270 | memcpy(&raw->section2.mii, &data->info.mii, sizeof(data->info.mii)); 271 | raw->section2.titleID = data->info.titleID; 272 | raw->section2.applicationAreaWrites = data->info.applicationAreaWrites; 273 | memcpy(raw->section2.reserved, data->info.reserved, sizeof(raw->section2.reserved)); 274 | memcpy(raw, &data->raw.data, 0x10); 275 | if (data->appData.size > 0xd8) { 276 | return -1; 277 | } 278 | memcpy(raw->applicationData, data->appData.data, data->appData.size); 279 | 280 | // Encrypt 281 | int res = encryptGameData(raw); 282 | if (res != 0) { 283 | DEBUG_FUNCTION_LINE("Failed to encrypt data"); 284 | return res; 285 | } 286 | 287 | // Copy back ntag uid and lock bytes from original rawData 288 | //memcpy(raw, &data->rawData, 0x10); 289 | //memcpy(raw->dynamicLock, data->rawData.dynamicLock, 0x14); 290 | 291 | return 0; 292 | } 293 | -------------------------------------------------------------------------------- /plugin/source/utils/DrawUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "DrawUtils.hpp" 2 | #include "debug/logger.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include "schrift.h" 13 | 14 | // buffer width 15 | #define TV_WIDTH 0x500 16 | #define DRC_WIDTH 0x380 17 | 18 | uint8_t *DrawUtils::tvBuffer = nullptr; 19 | uint32_t DrawUtils::tvSize = 0; 20 | uint8_t *DrawUtils::drcBuffer = nullptr; 21 | uint32_t DrawUtils::drcSize = 0; 22 | static SFT pFont = {}; 23 | 24 | static Color font_col(0xFFFFFFFF); 25 | 26 | template 27 | inline typename std::unique_ptr make_unique_nothrow(size_t num) noexcept { 28 | return std::unique_ptr(new (std::nothrow) std::remove_extent_t[num]()); 29 | } 30 | 31 | void DrawUtils::initBuffers() { 32 | DrawUtils::tvSize = OSScreenGetBufferSizeEx(SCREEN_TV); 33 | DrawUtils::drcSize = OSScreenGetBufferSizeEx(SCREEN_DRC); 34 | } 35 | 36 | void DrawUtils::beginDraw() { 37 | void* (*__GetNextBufferEA)(OSScreenID screen) = (void* (*)(OSScreenID screen)) ((uint32_t) &OSScreenFlipBuffersEx + 0xac); 38 | DrawUtils::tvBuffer = (uint8_t*) __GetNextBufferEA(SCREEN_TV); 39 | DrawUtils::drcBuffer = (uint8_t*) __GetNextBufferEA(SCREEN_DRC); 40 | } 41 | 42 | void DrawUtils::endDraw() { 43 | OSScreenFlipBuffersEx(SCREEN_DRC); 44 | OSScreenFlipBuffersEx(SCREEN_TV); 45 | } 46 | 47 | void DrawUtils::clear(Color col) { 48 | OSScreenClearBufferEx(SCREEN_TV, col.color); 49 | OSScreenClearBufferEx(SCREEN_DRC, col.color); 50 | } 51 | 52 | void DrawUtils::drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 53 | float opacity = a / 255.0f; 54 | 55 | // put pixel in the drc buffer 56 | uint32_t i = (x + y * DRC_WIDTH) * 4; 57 | if (i + 3 < drcSize / 2) { 58 | if (a == 0xFF) { 59 | drcBuffer[i] = r; 60 | drcBuffer[i + 1] = g; 61 | drcBuffer[i + 2] = b; 62 | } else { 63 | drcBuffer[i] = r * opacity + drcBuffer[i] * (1 - opacity); 64 | drcBuffer[i + 1] = g * opacity + drcBuffer[i + 1] * (1 - opacity); 65 | drcBuffer[i + 2] = b * opacity + drcBuffer[i + 2] * (1 - opacity); 66 | } 67 | } 68 | 69 | uint32_t USED_TV_WIDTH = TV_WIDTH; 70 | float scale = 1.5f; 71 | if (DrawUtils::tvSize == 0x00FD2000) { 72 | USED_TV_WIDTH = 1920; 73 | scale = 2.25f; 74 | } 75 | 76 | // scale and put pixel in the tv buffer 77 | for (uint32_t yy = (y * scale); yy < ((y * scale) + (uint32_t) scale); yy++) { 78 | for (uint32_t xx = (x * scale); xx < ((x * scale) + (uint32_t) scale); xx++) { 79 | uint32_t i = (xx + yy * USED_TV_WIDTH) * 4; 80 | if (i + 3 < tvSize / 2) { 81 | if (a == 0xFF) { 82 | tvBuffer[i] = r; 83 | tvBuffer[i + 1] = g; 84 | tvBuffer[i + 2] = b; 85 | } else { 86 | tvBuffer[i] = r * opacity + tvBuffer[i] * (1 - opacity); 87 | tvBuffer[i + 1] = g * opacity + tvBuffer[i + 1] * (1 - opacity); 88 | tvBuffer[i + 2] = b * opacity + tvBuffer[i + 2] * (1 - opacity); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | void DrawUtils::drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col) { 96 | for (uint32_t yy = y; yy < y + h; yy++) { 97 | for (uint32_t xx = x; xx < x + w; xx++) { 98 | drawPixel(xx, yy, col); 99 | } 100 | } 101 | } 102 | 103 | void DrawUtils::drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col) { 104 | drawRectFilled(x, y, w, borderSize, col); 105 | drawRectFilled(x, y + h - borderSize, w, borderSize, col); 106 | drawRectFilled(x, y, borderSize, h, col); 107 | drawRectFilled(x + w - borderSize, y, borderSize, h, col); 108 | } 109 | 110 | void DrawUtils::drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t *data) { 111 | if (data[0] != 'B' || data[1] != 'M') { 112 | // invalid header 113 | return; 114 | } 115 | 116 | uint32_t dataPos = __builtin_bswap32(*(uint32_t *) &(data[0x0A])); 117 | uint32_t width = __builtin_bswap32(*(uint32_t *) &(data[0x12])); 118 | uint32_t height = __builtin_bswap32(*(uint32_t *) &(data[0x16])); 119 | 120 | if (dataPos == 0) { 121 | dataPos = 54; 122 | } 123 | 124 | data += dataPos; 125 | 126 | // TODO flip image since bitmaps are stored upside down 127 | 128 | for (uint32_t yy = y; yy < y + target_height; yy++) { 129 | for (uint32_t xx = x; xx < x + target_width; xx++) { 130 | uint32_t i = (((xx - x) * width / target_width) + ((yy - y) * height / target_height) * width) * 3; 131 | drawPixel(xx, yy, data[i + 2], data[i + 1], data[i], 0xFF); 132 | } 133 | } 134 | } 135 | 136 | static void png_read_data(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) { 137 | void **data = (void **) png_get_io_ptr(png_ptr); 138 | 139 | memcpy(outBytes, *data, byteCountToRead); 140 | *((uint8_t **) data) += byteCountToRead; 141 | } 142 | 143 | void DrawUtils::drawPNG(uint32_t x, uint32_t y, const uint8_t *data) { 144 | png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 145 | if (png_ptr == nullptr) { 146 | return; 147 | } 148 | 149 | png_infop info_ptr = png_create_info_struct(png_ptr); 150 | if (info_ptr == nullptr) { 151 | png_destroy_read_struct(&png_ptr, nullptr, nullptr); 152 | return; 153 | } 154 | 155 | png_set_read_fn(png_ptr, (void *) &data, png_read_data); 156 | 157 | png_read_info(png_ptr, info_ptr); 158 | 159 | uint32_t width = 0; 160 | uint32_t height = 0; 161 | int bitDepth = 0; 162 | int colorType = -1; 163 | uint32_t retval = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, nullptr, nullptr, nullptr); 164 | if (retval != 1) { 165 | return; 166 | } 167 | 168 | uint32_t bytesPerRow = png_get_rowbytes(png_ptr, info_ptr); 169 | auto *rowData = new uint8_t[bytesPerRow]; 170 | 171 | for (uint32_t yy = y; yy < y + height; yy++) { 172 | png_read_row(png_ptr, (png_bytep) rowData, nullptr); 173 | 174 | for (uint32_t xx = x; xx < x + width; xx++) { 175 | if (colorType == PNG_COLOR_TYPE_RGB_ALPHA) { 176 | uint32_t i = (xx - x) * 4; 177 | drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], rowData[i + 3]); 178 | } else if (colorType == PNG_COLOR_TYPE_RGB) { 179 | uint32_t i = (xx - x) * 3; 180 | drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], 0xFF); 181 | } 182 | } 183 | } 184 | 185 | delete[] rowData; 186 | png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); 187 | } 188 | 189 | bool DrawUtils::initFont() { 190 | void *font = nullptr; 191 | uint32_t size = 0; 192 | OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &font, &size); 193 | 194 | if (font && size) { 195 | pFont.xScale = 20; 196 | pFont.yScale = 20, 197 | pFont.flags = SFT_DOWNWARD_Y; 198 | pFont.font = sft_loadmem(font, size); 199 | if (!pFont.font) { 200 | return false; 201 | } 202 | OSMemoryBarrier(); 203 | return true; 204 | } 205 | return false; 206 | } 207 | 208 | void DrawUtils::deinitFont() { 209 | sft_freefont(pFont.font); 210 | pFont.font = nullptr; 211 | pFont = {}; 212 | } 213 | 214 | void DrawUtils::setFontSize(uint32_t size) { 215 | pFont.xScale = size; 216 | pFont.yScale = size; 217 | SFT_LMetrics metrics; 218 | sft_lmetrics(&pFont, &metrics); 219 | } 220 | 221 | void DrawUtils::setFontColor(Color col) { 222 | font_col = col; 223 | } 224 | 225 | static void draw_freetype_bitmap(SFT_Image *bmp, int32_t x, int32_t y) { 226 | int32_t i, j, p, q; 227 | 228 | int32_t x_max = x + bmp->width; 229 | int32_t y_max = y + bmp->height; 230 | 231 | auto *src = (uint8_t *) bmp->pixels; 232 | 233 | for (i = x, p = 0; i < x_max; i++, p++) { 234 | for (j = y, q = 0; j < y_max; j++, q++) { 235 | if (i < 0 || j < 0 || i >= SCREEN_WIDTH || j >= SCREEN_HEIGHT) { 236 | continue; 237 | } 238 | 239 | float opacity = src[q * bmp->width + p] / 255.0f; 240 | DrawUtils::drawPixel(i, j, font_col.r, font_col.g, font_col.b, font_col.a * opacity); 241 | } 242 | } 243 | } 244 | 245 | void DrawUtils::print(uint32_t x, uint32_t y, const char *string, bool alignRight) { 246 | auto *buffer = new wchar_t[strlen(string) + 1]; 247 | 248 | size_t num = mbstowcs(buffer, string, strlen(string)); 249 | if (num > 0) { 250 | buffer[num] = 0; 251 | } else { 252 | wchar_t *tmp = buffer; 253 | while ((*tmp++ = *string++)) 254 | ; 255 | } 256 | 257 | print(x, y, buffer, alignRight); 258 | delete[] buffer; 259 | } 260 | 261 | void DrawUtils::print(uint32_t x, uint32_t y, const wchar_t *string, bool alignRight) { 262 | auto penX = (int32_t) x; 263 | auto penY = (int32_t) y; 264 | 265 | if (alignRight) { 266 | penX -= getTextWidth(string); 267 | } 268 | 269 | uint16_t textureWidth = 0, textureHeight = 0; 270 | for (; *string; string++) { 271 | SFT_Glyph gid; // unsigned long gid; 272 | if (sft_lookup(&pFont, *string, &gid) >= 0) { 273 | SFT_GMetrics mtx; 274 | if (sft_gmetrics(&pFont, gid, &mtx) < 0) { 275 | DEBUG_FUNCTION_LINE("Failed to get glyph metrics"); 276 | return; 277 | } 278 | 279 | if (*string == '\n') { 280 | penY += mtx.minHeight; 281 | penX = x; 282 | continue; 283 | } 284 | 285 | textureWidth = (mtx.minWidth + 3) & ~3; 286 | textureHeight = mtx.minHeight; 287 | 288 | SFT_Image img = { 289 | .pixels = nullptr, 290 | .width = textureWidth, 291 | .height = textureHeight, 292 | }; 293 | 294 | if (textureWidth == 0) { 295 | textureWidth = 4; 296 | } 297 | if (textureHeight == 0) { 298 | textureHeight = 4; 299 | } 300 | 301 | auto buffer = make_unique_nothrow((uint32_t) (img.width * img.height)); 302 | if (!buffer) { 303 | DEBUG_FUNCTION_LINE("Failed to allocate memory for glyph"); 304 | return; 305 | } 306 | img.pixels = buffer.get(); 307 | if (sft_render(&pFont, gid, img) < 0) { 308 | DEBUG_FUNCTION_LINE("Failed to render glyph"); 309 | return; 310 | } else { 311 | draw_freetype_bitmap(&img, (int32_t) (penX + mtx.leftSideBearing), (int32_t) (penY + mtx.yOffset)); 312 | penX += (int32_t) mtx.advanceWidth; 313 | } 314 | } 315 | } 316 | } 317 | 318 | uint32_t DrawUtils::getTextWidth(const char *string) { 319 | auto *buffer = new wchar_t[strlen(string) + 1]; 320 | 321 | size_t num = mbstowcs(buffer, string, strlen(string)); 322 | if (num > 0) { 323 | buffer[num] = 0; 324 | } else { 325 | wchar_t *tmp = buffer; 326 | while ((*tmp++ = *string++)) 327 | ; 328 | } 329 | 330 | uint32_t width = getTextWidth(buffer); 331 | delete[] buffer; 332 | 333 | return width; 334 | } 335 | 336 | uint32_t DrawUtils::getTextWidth(const wchar_t *string) { 337 | uint32_t width = 0; 338 | 339 | for (; *string; string++) { 340 | SFT_Glyph gid; // unsigned long gid; 341 | if (sft_lookup(&pFont, *string, &gid) >= 0) { 342 | SFT_GMetrics mtx; 343 | if (sft_gmetrics(&pFont, gid, &mtx) < 0) { 344 | DEBUG_FUNCTION_LINE("bad glyph metrics"); 345 | } 346 | width += (int32_t) mtx.advanceWidth; 347 | } 348 | } 349 | 350 | return (uint32_t) width; 351 | } 352 | -------------------------------------------------------------------------------- /source/re_nfpii/Tag.cpp: -------------------------------------------------------------------------------- 1 | #include "Tag.hpp" 2 | #include "Utils.hpp" 3 | #include "re_nfpii.hpp" 4 | #include "ntag_crypt.h" 5 | #include "debug/logger.h" 6 | #include "utils/FSUtils.hpp" 7 | #include "utils/LogHandler.hpp" 8 | 9 | #include 10 | #include 11 | 12 | namespace re::nfpii { 13 | 14 | Tag::Tag() 15 | { 16 | numAppAreas = 0; 17 | memset(appAreaInfo, 0, sizeof(appAreaInfo)); 18 | dataBufferCapacity = 0; 19 | updateTitleId = false; 20 | updateAppWriteCount = false; 21 | memset(&ntagData, 0, sizeof(ntagData)); 22 | } 23 | 24 | Tag::~Tag() 25 | { 26 | } 27 | 28 | Result Tag::SetData(NTAGDataT2T* data) 29 | { 30 | memmove(&ntagData, data, sizeof(NTAGDataT2T)); 31 | return NFP_SUCCESS; 32 | } 33 | 34 | NTAGDataT2T* Tag::GetData() 35 | { 36 | return &ntagData; 37 | } 38 | 39 | Result Tag::Mount(bool backup) 40 | { 41 | if (backup) { 42 | // We currently don't support writing the backup data 43 | // Probably unnecessary anyways? 44 | } 45 | 46 | if (InitializeDataBuffer(&ntagData).IsFailure()) { 47 | return RESULT(0xa1b0c880); 48 | } 49 | 50 | numAppAreas = 1; 51 | dataBufferCapacity = ntagData.appData.size; 52 | UpdateAppAreaInfo(false); 53 | 54 | updateTitleId = false; 55 | updateAppWriteCount = false; 56 | 57 | return NFP_SUCCESS; 58 | } 59 | 60 | Result Tag::Mount() 61 | { 62 | return Mount(true); 63 | } 64 | 65 | Result Tag::Unmount() 66 | { 67 | numAppAreas = 0; 68 | return NFP_SUCCESS; 69 | } 70 | 71 | Result Tag::GetAppAreaInfo(AppAreaInfo* info, uint32_t* outNumAreas) 72 | { 73 | *outNumAreas = numAppAreas; 74 | memcpy(info, appAreaInfo, numAppAreas * sizeof(AppAreaInfo)); 75 | return NFP_SUCCESS; 76 | } 77 | 78 | Result Tag::GetAppAreaInfo(AppAreaInfo* info, uint32_t* outNumAreas, int32_t maxAreas) 79 | { 80 | // nfp implements this differently with another AppAreaInfo struct 81 | // I don't see the point in doing so though 82 | 83 | if (!info || !outNumAreas) { 84 | return NFP_INVALID_PARAM; 85 | } 86 | 87 | *outNumAreas = numAppAreas; 88 | 89 | if ((int32_t) numAppAreas > maxAreas || numAppAreas > 0x10) { 90 | return NFP_INVALID_PARAM; 91 | } 92 | 93 | memcpy(info, appAreaInfo, numAppAreas * sizeof(AppAreaInfo)); 94 | return NFP_SUCCESS; 95 | } 96 | 97 | Result Tag::Write(TagInfo* info, bool update) 98 | { 99 | memcpy(ntagData.appData.data, dataBuffer + 0x130, 0xd8); 100 | 101 | // Backup values we might restore if writing to tag fails 102 | uint64_t currentTitleId = ntagData.info.titleID; 103 | uint16_t currentWrites = ntagData.info.writes; 104 | uint16_t currentApplicationAreaWrites = ntagData.info.applicationAreaWrites; 105 | uint16_t currentCrcCounter = ntagData.info.crcCounter; 106 | 107 | if (update) { 108 | ntagData.info.writes = IncreaseCount(ntagData.info.writes, true); 109 | 110 | if (updateAppWriteCount) { 111 | ntagData.info.applicationAreaWrites = IncreaseCount(ntagData.info.applicationAreaWrites, false); 112 | } 113 | 114 | if (updateTitleId) { 115 | ntagData.info.titleID = OSGetTitleID(); 116 | } 117 | 118 | if (!CheckUuidCRC(&ntagData.info)) { 119 | ntagData.info.crcCounter = IncreaseCount(ntagData.info.crcCounter, false); 120 | SetUuidCRC(&ntagData.info.crc); 121 | } 122 | 123 | ntagData.info.lastWriteDate = OSTimeToAmiiboTime(OSGetTime()); 124 | } 125 | 126 | Result res = WriteTag(true); 127 | if (res.IsFailure()) { 128 | // Restore values if writing failed 129 | ntagData.info.crcCounter = currentCrcCounter; 130 | ntagData.info.titleID = currentTitleId; 131 | ntagData.info.writes = currentWrites; 132 | ntagData.info.applicationAreaWrites = currentApplicationAreaWrites; 133 | return res; 134 | } 135 | 136 | return NFP_SUCCESS; 137 | } 138 | 139 | Result Tag::InitializeDataBuffer(const NTAGDataT2T* data) 140 | { 141 | memset(dataBuffer, 0, sizeof(dataBuffer)); 142 | memcpy(dataBuffer + 0x130, data->appData.data, sizeof(data->appData.data)); 143 | return NFP_SUCCESS; 144 | } 145 | 146 | Result Tag::WriteDataBuffer(const void* data, int32_t offset, uint32_t size) 147 | { 148 | if (size < 0 || size > dataBufferCapacity) { 149 | return NFP_INVALID_PARAM; 150 | } 151 | 152 | memcpy(dataBuffer + offset, data, size); 153 | 154 | updateAppWriteCount = true; 155 | updateTitleId = true; 156 | 157 | return NFP_SUCCESS; 158 | } 159 | 160 | Result Tag::ReadDataBuffer(void* out, int32_t offset, uint32_t size) 161 | { 162 | if (size < 0 || size > dataBufferCapacity) { 163 | return NFP_INVALID_PARAM; 164 | } 165 | 166 | memcpy(out, dataBuffer + offset, size); 167 | 168 | return NFP_SUCCESS; 169 | } 170 | 171 | void Tag::ClearTagData() 172 | { 173 | memset(&ntagData, 0, sizeof(ntagData)); 174 | } 175 | 176 | Result Tag::CreateApplicationArea(NTAGDataT2T* data, ApplicationAreaCreateInfo const& createInfo) 177 | { 178 | TagInfo tagInfo; 179 | ReadTagInfo(&tagInfo, data); 180 | if (tagInfo.tag_type != TagType::Type2Tag) { 181 | return NFP_INVALID_TAG; 182 | } 183 | 184 | if (!createInfo.data) { 185 | return NFP_INVALID_PARAM; 186 | } 187 | 188 | if (createInfo.size > ntagData.appData.size) { 189 | return NFP_OUT_OF_RANGE; 190 | } 191 | 192 | if (!CheckZero(createInfo.reserved, sizeof(createInfo.reserved))) { 193 | return NFP_OUT_OF_RANGE; 194 | } 195 | 196 | // Backup current working buffer in case anything fails, to not modify state 197 | NTAGDataT2T backupData; 198 | memcpy(&backupData, &ntagData, sizeof(NTAGDataT2T)); 199 | 200 | // Move data into working buffer 201 | memmove(&ntagData, data, sizeof(NTAGDataT2T)); 202 | 203 | updateTitleId = true; 204 | ntagData.info.accessID = createInfo.accessID; 205 | 206 | memset(tagInfo.reserved1, 0, sizeof(tagInfo.reserved1)); 207 | 208 | uint8_t applicationData[0xd8]; 209 | memcpy(applicationData, createInfo.data, createInfo.size); 210 | 211 | // Rest will be padded with random bytes 212 | if (createInfo.size < ntagData.appData.size) { 213 | GetRandom(applicationData + createInfo.size, ntagData.appData.size - createInfo.size); 214 | } 215 | 216 | // Write the application data to the data buffer 217 | WriteDataBuffer(applicationData, 0x130, ntagData.appData.size); 218 | 219 | // Set the "has application area" flag 220 | ntagData.info.flags |= (uint8_t) AdminFlags::HasApplicationData; 221 | 222 | // Write the data to the tag 223 | Result res = Write(&tagInfo, true); 224 | if (res.IsFailure()) { 225 | // Restore the backed up state, if writing failed 226 | memcpy(&ntagData, &backupData, sizeof(NTAGDataT2T)); 227 | return res; 228 | } 229 | 230 | // Re-mount the tag 231 | Mount(); 232 | 233 | return NFP_SUCCESS; 234 | } 235 | 236 | Result Tag::SetRegisterInfo(RegisterInfoSet const& info) 237 | { 238 | if (!CheckZero(info.reserved, sizeof(info.reserved))) { 239 | // TODO this rarely happens in smash? 240 | DumpHex(info.reserved, sizeof(info.reserved)); 241 | return NFP_OUT_OF_RANGE; 242 | } 243 | 244 | ntagData.info.figureVersion = 0; 245 | ReadCountryRegion(&ntagData.info.country); 246 | memcpy(&ntagData.info.mii, &info.mii, sizeof(info.mii)); 247 | UpdateMii(&ntagData.info.mii); 248 | memcpy(ntagData.info.name, info.name, sizeof(ntagData.info.name)); 249 | ntagData.info.fontRegion = info.fontRegion; 250 | 251 | // If we don't have any register info yet, write setup date 252 | if (!HasRegisterInfo()) { 253 | ntagData.info.setupDate = OSTimeToAmiiboTime(OSGetTime()); 254 | updateAppWriteCount = true; 255 | } 256 | 257 | // Set "has register info" bit 258 | ntagData.info.flags |= (uint8_t) AdminFlags::IsRegistered; 259 | 260 | return NFP_SUCCESS; 261 | } 262 | 263 | Result Tag::DeleteApplicationArea() 264 | { 265 | TagInfo tagInfo; 266 | ReadTagInfo(&tagInfo, &ntagData); 267 | if (tagInfo.tag_type != TagType::Type2Tag) { 268 | return NFP_INVALID_TAG; 269 | } 270 | 271 | // Backup current working buffer in case anything fails, to not modify state 272 | NTAGDataT2T backupData; 273 | memcpy(&backupData, &ntagData, sizeof(NTAGDataT2T)); 274 | 275 | // Delete app area and increase write count 276 | ClearApplicationArea(&ntagData); 277 | ntagData.info.writes = IncreaseCount(ntagData.info.writes, true); 278 | 279 | // Write the data to the tag 280 | Result res = Write(&tagInfo, true); 281 | if (res.IsFailure()) { 282 | // Restore the backed up state, if writing failed 283 | memcpy(&ntagData, &backupData, sizeof(NTAGDataT2T)); 284 | return res; 285 | } 286 | 287 | // Re-mount the tag 288 | Mount(); 289 | 290 | return NFP_SUCCESS; 291 | } 292 | 293 | Result Tag::DeleteRegisterInfo() 294 | { 295 | TagInfo tagInfo; 296 | ReadTagInfo(&tagInfo, &ntagData); 297 | if (tagInfo.tag_type != TagType::Type2Tag) { 298 | return NFP_INVALID_TAG; 299 | } 300 | 301 | // Backup current working buffer in case anything fails, to not modify state 302 | NTAGDataT2T backupData; 303 | memcpy(&backupData, &ntagData, sizeof(NTAGDataT2T)); 304 | 305 | // Delete register info and increase write count 306 | ClearRegisterInfo(&ntagData); 307 | ntagData.info.writes = IncreaseCount(ntagData.info.writes, true); 308 | 309 | // Write the data to the tag 310 | Result res = Write(&tagInfo, true); 311 | if (res.IsFailure()) { 312 | // Restore the backed up state, if writing failed 313 | memcpy(&ntagData, &backupData, sizeof(NTAGDataT2T)); 314 | return res; 315 | } 316 | 317 | // Re-mount the tag 318 | Mount(); 319 | 320 | return NFP_SUCCESS; 321 | } 322 | 323 | Result Tag::Format(NTAGDataT2T* data, const void* appData, int32_t appDataSize) 324 | { 325 | if ((appDataSize > 1 && !appData) || appDataSize > 0xd8) { 326 | return NFP_INVALID_PARAM; 327 | } 328 | 329 | TagInfo tagInfo; 330 | ReadTagInfo(&tagInfo, data); 331 | if (tagInfo.tag_type != TagType::Type2Tag) { 332 | return NFP_INVALID_TAG; 333 | } 334 | 335 | // Move data into working buffer 336 | memmove(&ntagData, data, sizeof(NTAGDataT2T)); 337 | 338 | // Clear and randomize all data 339 | ntagData.info.magic = 0xa5; 340 | ntagData.info.flags = 0; 341 | ntagData.info.figureVersion = 0; 342 | ntagData.info.country = 0; 343 | ntagData.info.writes = rand() | 0x8000; 344 | ntagData.info.crcCounter = 0; 345 | ntagData.info.applicationAreaWrites = 0; 346 | ntagData.info.fontRegion = 0; 347 | ntagData.info.setupDate = rand(); 348 | ntagData.info.lastWriteDate = rand(); 349 | GetRandom(&ntagData.info.accessID, sizeof(ntagData.info.accessID)); 350 | GetRandom(&ntagData.info.titleID, sizeof(ntagData.info.titleID)); 351 | GetRandom(&ntagData.info.crc, sizeof(ntagData.info.crc)); 352 | GetRandom(&ntagData.info.name, sizeof(ntagData.info.name)); 353 | GetRandom(&ntagData.info.mii, sizeof(ntagData.info.mii)); 354 | if (appDataSize > 0) { 355 | memcpy(ntagData.appData.data, appData, appDataSize); 356 | } 357 | if (0xd8 - appDataSize > 0) { 358 | GetRandom(ntagData.appData.data + appDataSize, 0xd8 - appDataSize); 359 | } 360 | ntagData.appData.size = 0xd8; 361 | 362 | // nfp also updates some backup state here, but we don't emulate that yet 363 | return WriteTag(false); 364 | } 365 | 366 | bool Tag::HasRegisterInfo() 367 | { 368 | return ntagData.info.flags & (uint8_t) AdminFlags::IsRegistered; 369 | } 370 | 371 | bool Tag::IsExistApplicationArea() 372 | { 373 | return ntagData.info.flags & (uint8_t) AdminFlags::HasApplicationData; 374 | } 375 | 376 | Result Tag::UpdateAppAreaInfo(bool unk) 377 | { 378 | if (unk) { 379 | return NFP_INVALID_PARAM; 380 | } 381 | 382 | appAreaInfo[0].offset = 0x130; 383 | appAreaInfo[0].size = ntagData.appData.size; 384 | appAreaInfo[0].id = ntagData.info.accessID; 385 | 386 | return NFP_SUCCESS; 387 | } 388 | 389 | Result Tag::WriteTag(bool backup) 390 | { 391 | // nfp usually writes to the tag using NTAG here 392 | // this code is mostly custom and writes the data to the SD instead 393 | 394 | NTAGRawDataT2T raw; 395 | if (NTAGEncrypt(&raw, GetData()) != 0) { 396 | return NFP_STATUS_RESULT(0x12345); 397 | } 398 | 399 | int res = FSUtils::WriteToFile(path.c_str(), &raw, sizeof(raw)); 400 | if (res != sizeof(raw)) { 401 | DEBUG_FUNCTION_LINE("Failed to write tag data to %s: %x", path.c_str(), res); 402 | LogHandler::Error("Failed to write tag data to %s: %x", path.c_str(), res); 403 | 404 | return NFP_STATUS_RESULT(0x12345); 405 | } 406 | 407 | // copy the new encrypted raw data to the raw part 408 | memcpy(&ntagData.raw.data, &raw, sizeof(raw)); 409 | 410 | return NFP_SUCCESS; 411 | } 412 | 413 | } // namespace re::nfpii 414 | -------------------------------------------------------------------------------- /plugin/source/config/ConfigItemDumpAmiibo.cpp: -------------------------------------------------------------------------------- 1 | #include "ConfigItemDumpAmiibo.hpp" 2 | #include "utils/DrawUtils.hpp" 3 | #include "utils/input.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define COLOR_BACKGROUND Color(238, 238, 238, 255) 17 | #define COLOR_TEXT Color(51, 51, 51, 255) 18 | #define COLOR_TEXT2 Color(72, 72, 72, 255) 19 | #define COLOR_TEXT_WARN Color(255, 204, 0, 255) 20 | #define COLOR_TEXT_ERROR Color(204, 51, 0, 255) 21 | #define COLOR_WHITE Color(0xFFFFFFFF) 22 | #define COLOR_BLACK Color(0, 0, 0, 255) 23 | 24 | #define TIMEOUT_SECONDS 10 25 | 26 | static void _mkdir(const char *dir) 27 | { 28 | char tmp[PATH_MAX]; 29 | char *p = NULL; 30 | size_t len; 31 | 32 | strncpy(tmp, dir, sizeof(tmp) - 1); 33 | 34 | // remove trailing / 35 | len = strlen(tmp); 36 | if (tmp[len - 1] == '/') 37 | tmp[len - 1] = '\0'; 38 | 39 | for (p = tmp + 1; *p; p++) { 40 | if (*p == '/') { 41 | *p = '\0'; 42 | 43 | // we can't create /vol and /vol/external01 44 | if (strcmp(tmp, "/vol") != 0 && strcmp(tmp, "/vol/external01") != 0) { 45 | mkdir(tmp, S_IRWXU); 46 | } 47 | 48 | *p = '/'; 49 | } 50 | } 51 | 52 | mkdir(tmp, S_IRWXU); 53 | } 54 | 55 | static void ntagReadCallback(VPADChan chan, NTAGError error, NFCTagInfo *tagInfo, NTAGRawDataContainerT2T *rawData, void *userContext) 56 | { 57 | ConfigItemDumpAmiibo* item = (ConfigItemDumpAmiibo*) userContext; 58 | 59 | if (error == 0) { 60 | char filePath[PATH_MAX]; 61 | OSCalendarTime ct; 62 | 63 | OSTicksToCalendarTime(OSGetTime(), &ct); 64 | snprintf(filePath, PATH_MAX, "%s/%04d-%02d-%02d_%02d-%02d-%02d_%02X%02X%02X%02X%02X%02X%02X.bin", item->dumpFolder.c_str(), 65 | ct.tm_year, ct.tm_mon + 1, ct.tm_mday, ct.tm_hour, ct.tm_min, ct.tm_sec, 66 | tagInfo->uid[0], tagInfo->uid[1], tagInfo->uid[2], tagInfo->uid[3], tagInfo->uid[4], tagInfo->uid[5], tagInfo->uid[6]); 67 | 68 | item->lastDumpPath = filePath; 69 | 70 | FILE* f = fopen(filePath, "wb"); 71 | if (!f) { 72 | item->state = DUMP_STATE_ERROR; 73 | return; 74 | } 75 | 76 | fwrite(&rawData->data, 1, rawData->size, f); 77 | fclose(f); 78 | 79 | item->state = DUMP_STATE_COMPLETED; 80 | } else if (error == -0x3e5) { 81 | // timeout 82 | item->state = DUMP_STATE_INIT; 83 | return; 84 | } else { 85 | item->state = DUMP_STATE_ERROR; 86 | return; 87 | } 88 | } 89 | 90 | static void ntagAbortCallback(VPADChan chan, NTAGError error, void *userContext) 91 | { 92 | ConfigItemDumpAmiibo* item = (ConfigItemDumpAmiibo*) userContext; 93 | 94 | if (error == 0) { 95 | item->state = DUMP_STATE_INIT; 96 | } else { 97 | item->state = DUMP_STATE_ERROR; 98 | } 99 | } 100 | 101 | static void enterDumpMenu(ConfigItemDumpAmiibo* item) 102 | { 103 | // Init DrawUtils 104 | DrawUtils::initBuffers(); 105 | if (!DrawUtils::initFont()) { 106 | return; 107 | } 108 | 109 | item->state = DUMP_STATE_INIT; 110 | 111 | // create the dump folder 112 | _mkdir(item->dumpFolder.c_str()); 113 | 114 | VPADStatus vpad{}; 115 | VPADReadError vpadError; 116 | KPADStatus kpad{}; 117 | KPADError kpadError; 118 | 119 | OSTick readStart = 0; 120 | 121 | while (true) { 122 | refresh: ; 123 | 124 | uint32_t buttonsTriggered = 0; 125 | 126 | VPADRead(VPAD_CHAN_0, &vpad, 1, &vpadError); 127 | if (vpadError == VPAD_READ_SUCCESS) { 128 | buttonsTriggered = vpad.trigger; 129 | } 130 | 131 | // read kpads and remap the buttons we need 132 | for (int i = 0; i < 4; i++) { 133 | if (KPADReadEx((KPADChan) i, &kpad, 1, &kpadError) > 0) { 134 | if (kpadError != KPAD_ERROR_OK) { 135 | continue; 136 | } 137 | 138 | if (kpad.extensionType == WPAD_EXT_CORE || kpad.extensionType == WPAD_EXT_NUNCHUK || 139 | kpad.extensionType == WPAD_EXT_MPLUS || kpad.extensionType == WPAD_EXT_MPLUS_NUNCHUK) { 140 | buttonsTriggered |= remapWiiMoteButtons(kpad.trigger); 141 | } else if (kpad.extensionType == WPAD_EXT_CLASSIC) { 142 | buttonsTriggered |= remapClassicButtons(kpad.classic.trigger); 143 | } else if (kpad.extensionType == WPAD_EXT_PRO_CONTROLLER) { 144 | buttonsTriggered |= remapProButtons(kpad.pro.trigger); 145 | } 146 | } 147 | } 148 | 149 | if (item->state != DUMP_STATE_WAITING) { 150 | if (buttonsTriggered & (VPAD_BUTTON_B | VPAD_BUTTON_HOME)) { 151 | break; 152 | } 153 | } 154 | 155 | if (item->state == DUMP_STATE_INIT) { 156 | if (buttonsTriggered & VPAD_BUTTON_A) { 157 | if (NTAGIsInit(VPAD_CHAN_0)) { 158 | if (item->wasInit) { 159 | // if we were the one initializing it, everything is fine 160 | if (NTAGReadT2TRawData(VPAD_CHAN_0, TIMEOUT_SECONDS * 1000, nullptr, nullptr, ntagReadCallback, item) != 0) { 161 | NTAGShutdown(VPAD_CHAN_0); 162 | item->state = DUMP_STATE_ERROR; 163 | goto refresh; 164 | } 165 | 166 | readStart = OSGetSystemTick(); 167 | item->state = DUMP_STATE_WAITING; 168 | goto refresh; 169 | } 170 | 171 | // If NTAG is already initialized, we error out to not mess with the application using it 172 | item->state = DUMP_STATE_ERROR; 173 | goto refresh; 174 | } 175 | 176 | item->wasInit = true; 177 | 178 | if (NTAGInit(VPAD_CHAN_0) != 0) { 179 | item->state = DUMP_STATE_ERROR; 180 | goto refresh; 181 | } 182 | 183 | // Wait for NFC to be ready 184 | OSTick start = OSGetSystemTick(); 185 | while (!NTAGIsInit(VPAD_CHAN_0) && (OSGetSystemTick() - start) < (OSTick) OSSecondsToTicks(2)) { 186 | NTAGProc(VPAD_CHAN_0); 187 | OSYieldThread(); 188 | } 189 | NTAGProc(VPAD_CHAN_0); 190 | 191 | if (!NTAGIsInit(VPAD_CHAN_0)) { 192 | NTAGShutdown(VPAD_CHAN_0); 193 | item->state = DUMP_STATE_ERROR; 194 | goto refresh; 195 | } 196 | 197 | if (NTAGReadT2TRawData(VPAD_CHAN_0, TIMEOUT_SECONDS * 1000, nullptr, nullptr, ntagReadCallback, item) != 0) { 198 | NTAGShutdown(VPAD_CHAN_0); 199 | item->state = DUMP_STATE_ERROR; 200 | goto refresh; 201 | } 202 | 203 | readStart = OSGetSystemTick(); 204 | item->state = DUMP_STATE_WAITING; 205 | } 206 | } else if (item->state == DUMP_STATE_WAITING) { 207 | NTAGProc(VPAD_CHAN_0); 208 | if (buttonsTriggered & VPAD_BUTTON_B) { 209 | NTAGAbort(VPAD_CHAN_0, ntagAbortCallback, item); 210 | } 211 | } else if (item->state == DUMP_STATE_COMPLETED || item->state == DUMP_STATE_ERROR) { 212 | if (buttonsTriggered & VPAD_BUTTON_A) { 213 | item->state = DUMP_STATE_INIT; 214 | } 215 | } 216 | 217 | { 218 | DrawUtils::beginDraw(); 219 | DrawUtils::clear(COLOR_BACKGROUND); 220 | 221 | DrawUtils::setFontColor(COLOR_TEXT); 222 | 223 | DrawUtils::setFontSize(26); 224 | 225 | if (item->state == DUMP_STATE_INIT) { 226 | int yOff = SCREEN_HEIGHT / 2 - (6 * 26) / 2 - 8; 227 | const char* text = "Welcome to the Amiibo Dumper!"; 228 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 229 | yOff += 26; 230 | 231 | text = "After pressing \ue000 hold one of your Amiibo figures"; 232 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 233 | yOff += 26; 234 | 235 | text = "to the \ue099 symbol on your Gamepad."; 236 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 237 | yOff += 26 + 16; 238 | 239 | text = "The dump will be saved to the"; 240 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 241 | yOff += 26; 242 | 243 | std::string folderText = item->dumpFolder.substr(sizeof("/vol/external01/") - 1); 244 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(folderText.c_str()) / 2, yOff, folderText.c_str()); 245 | yOff += 26; 246 | 247 | text = "folder on your SD Card."; 248 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 249 | } else if (item->state == DUMP_STATE_WAITING) { 250 | int yOff = SCREEN_HEIGHT / 2 - (2 * 26) / 2; 251 | const char* text = "Waiting for Amiibo..."; 252 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 253 | yOff += 26; 254 | 255 | std::string timeoutText = std::to_string(TIMEOUT_SECONDS - OSTicksToSeconds(OSGetSystemTick() - readStart)) + " seconds remaining."; 256 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(timeoutText.c_str()) / 2, yOff, timeoutText.c_str()); 257 | } else if (item->state == DUMP_STATE_COMPLETED) { 258 | int yOff = SCREEN_HEIGHT / 2 - (26 + 22) / 2; 259 | const char* text = "Dump complete! Dump was saved to:"; 260 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 261 | yOff += 26; 262 | 263 | DrawUtils::setFontSize(22); 264 | std::string dumpPathText = item->lastDumpPath.substr(sizeof("/vol/external01/") - 1); 265 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(dumpPathText.c_str()) / 2, yOff, dumpPathText.c_str()); 266 | } else if (item->state == DUMP_STATE_ERROR) { 267 | int yOff = SCREEN_HEIGHT / 2 - (3 * 26) / 2; 268 | const char* text = "An error has occured!"; 269 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 270 | yOff += 26; 271 | 272 | text = "Make sure the Gamepad is properly connected"; 273 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 274 | yOff += 26; 275 | 276 | text = "and no other title is using NFC."; 277 | DrawUtils::print(SCREEN_WIDTH / 2 - DrawUtils::getTextWidth(text) / 2, yOff, text); 278 | } 279 | 280 | // draw top bar 281 | DrawUtils::setFontSize(24); 282 | DrawUtils::print(16, 6 + 24, "re_nfpii - Dump Amiibo"); 283 | DrawUtils::setFontSize(18); 284 | DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK); 285 | 286 | // draw bottom bar 287 | DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK); 288 | DrawUtils::setFontSize(18); 289 | 290 | if (item->state == DUMP_STATE_WAITING) { 291 | DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue001 Abort"); 292 | } else { 293 | DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue001 Back"); 294 | } 295 | 296 | if (item->state == DUMP_STATE_COMPLETED) { 297 | DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Continue", true); 298 | } else if (item->state == DUMP_STATE_ERROR) { 299 | DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Try again", true); 300 | } else if (item->state == DUMP_STATE_INIT) { 301 | DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Start", true); 302 | } 303 | 304 | DrawUtils::endDraw(); 305 | } 306 | } 307 | 308 | if (item->wasInit) { 309 | NTAGShutdown(VPAD_CHAN_0); 310 | item->wasInit = false; 311 | } 312 | } 313 | 314 | static bool ConfigItemDumpAmiibo_callCallback(void* context) 315 | { 316 | return true; 317 | } 318 | 319 | static void ConfigItemDumpAmiibo_onButtonPressed(void* context, WUPSConfigButtons buttons) 320 | { 321 | ConfigItemDumpAmiibo* item = (ConfigItemDumpAmiibo*) context; 322 | 323 | if (buttons & WUPS_CONFIG_BUTTON_A) { 324 | enterDumpMenu(item); 325 | } 326 | } 327 | 328 | static bool ConfigItemDumpAmiibo_isMovementAllowed(void* context) 329 | { 330 | return true; 331 | } 332 | 333 | static int32_t ConfigItemDumpAmiibo_getCurrentValueDisplay(void* context, char* out_buf, int32_t out_size) 334 | { 335 | *out_buf = '\0'; 336 | return 0; 337 | } 338 | 339 | static void ConfigItemDumpAmiibo_restoreDefault(void* context) 340 | { 341 | } 342 | 343 | static void ConfigItemDumpAmiibo_onSelected(void* context, bool isSelected) 344 | { 345 | } 346 | 347 | 348 | static void ConfigItemDumpAmiibo_onDelete(void* context) 349 | { 350 | ConfigItemDumpAmiibo* item = (ConfigItemDumpAmiibo*) context; 351 | 352 | free(item->configID); 353 | 354 | delete item; 355 | } 356 | 357 | bool ConfigItemDumpAmiibo_AddToCategory(WUPSConfigCategoryHandle cat, const char* configID, const char* displayName, const char* dumpFolder) 358 | { 359 | if (!cat) { 360 | return false; 361 | } 362 | 363 | ConfigItemDumpAmiibo* item = new ConfigItemDumpAmiibo; 364 | if (!item) { 365 | return false; 366 | } 367 | 368 | if (configID) { 369 | item->configID = strdup(configID); 370 | } else { 371 | item->configID = nullptr; 372 | } 373 | 374 | item->state = DUMP_STATE_INIT; 375 | item->wasInit = false; 376 | item->dumpFolder = dumpFolder; 377 | 378 | WUPSConfigCallbacks_t callbacks = { 379 | .getCurrentValueDisplay = &ConfigItemDumpAmiibo_getCurrentValueDisplay, 380 | .getCurrentValueSelectedDisplay = &ConfigItemDumpAmiibo_getCurrentValueDisplay, 381 | .onSelected = &ConfigItemDumpAmiibo_onSelected, 382 | .restoreDefault = &ConfigItemDumpAmiibo_restoreDefault, 383 | .isMovementAllowed = &ConfigItemDumpAmiibo_isMovementAllowed, 384 | .callCallback = &ConfigItemDumpAmiibo_callCallback, 385 | .onButtonPressed = &ConfigItemDumpAmiibo_onButtonPressed, 386 | .onDelete = &ConfigItemDumpAmiibo_onDelete 387 | }; 388 | 389 | if (WUPSConfigItem_Create(&item->handle, configID, displayName, callbacks, item) < 0) { 390 | delete item; 391 | return false; 392 | } 393 | 394 | if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) { 395 | return false; 396 | } 397 | 398 | return true; 399 | } 400 | 401 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------