diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7928ddd..5d6c90f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -31,6 +31,7 @@ jobs: run: | docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cpp/src + docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cmake/src docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2 build-examples: runs-on: ubuntu-22.04 @@ -44,6 +45,8 @@ jobs: docker run --rm -v ${PWD}:/project builder make cd ../example_plugin_cpp docker run --rm -v ${PWD}:/project builder make + cd ../example_plugin_cmake + docker run --rm -v ${PWD}:/project builder /bin/bash -c "/opt/devkitpro/portlibs/wiiu/bin/powerpc-eabi-cmake -S . -B build && cd build && make" cd ../storage_test_plugin docker run --rm -v ${PWD}:/project builder make - uses: actions/upload-artifact@master diff --git a/.github/workflows/push_image.yml b/.github/workflows/push_image.yml index 0ffa7a2..3aaa2f4 100644 --- a/.github/workflows/push_image.yml +++ b/.github/workflows/push_image.yml @@ -35,6 +35,7 @@ jobs: docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2 docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cpp/src + docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cmake/src build-examples: runs-on: ubuntu-22.04 needs: clang-format-examples @@ -47,6 +48,8 @@ jobs: docker run --rm -v ${PWD}:/project builder make cd ../example_plugin_cpp docker run --rm -v ${PWD}:/project builder make + cd ../example_plugin_cmake + docker run --rm -v ${PWD}:/project builder /bin/bash -c "/opt/devkitpro/portlibs/wiiu/bin/powerpc-eabi-cmake -S . -B build && cd build && make" cd ../storage_test_plugin docker run --rm -v ${PWD}:/project builder make build-and-push-image: diff --git a/.gitignore b/.gitignore index 7bbd68b..6bc76a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ loader/build/* loader/WiiUPluginLoader.cbp loader/*.elf -example_plugin/bin/* -example_plugin/build/* -example_plugin_pic/* portlib_repos/* plugins/*/bin/* plugins/*/build/* @@ -29,5 +26,4 @@ debug/ *.cbp lib/ cmake-build-debug/ -CMakeLists.txt _site/ diff --git a/plugins/example_plugin_cmake/CMakeLists.txt b/plugins/example_plugin_cmake/CMakeLists.txt new file mode 100644 index 0000000..33b23a1 --- /dev/null +++ b/plugins/example_plugin_cmake/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 20) + +include("$ENV{DEVKITPRO}/wups/share/wups.cmake" REQUIRED) + +project(example_cmake C CXX) + +if(DEFINED DEBUG) + add_compile_definitions(DEBUG) +endif() + +add_executable(example_cmake src/main.cpp) +add_subdirectory("src/utils") + +wups_create_plugin(example_cmake) diff --git a/plugins/example_plugin_cmake/Dockerfile b/plugins/example_plugin_cmake/Dockerfile new file mode 100644 index 0000000..29279a0 --- /dev/null +++ b/plugins/example_plugin_cmake/Dockerfile @@ -0,0 +1,5 @@ +FROM ghcr.io/wiiu-env/devkitppc:20230218 + +COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230215 /artifacts $DEVKITPRO + +WORKDIR project diff --git a/plugins/example_plugin_cmake/README.md b/plugins/example_plugin_cmake/README.md new file mode 100644 index 0000000..b4bc433 --- /dev/null +++ b/plugins/example_plugin_cmake/README.md @@ -0,0 +1,53 @@ +# Example plugin + +This is just a simple example plugin which can be used as a template. +The plugin logs the FSOpenFile calls via UDP (**Only when CMake is passed `-DDEBUG=1`**). + +The logging can be enabled/disabled via the WUPS Config menu (press L, DPAD Down and Minus on the GamePad, Pro Controller or Classic Controller). + +## Installation + +(`[ENVIRONMENT]` is a placeholder for the actual environment name.) + +1. Copy the file `ExamplePlugin.wps` into `sd:/wiiu/environments/[ENVIRONMENT]/plugins`. +2. Requires the [WiiUPluginLoaderBackend](https://github.com/wiiu-env/WiiUPluginLoaderBackend) in `sd:/wiiu/environments/[ENVIRONMENT]/modules`. + +Start the environment (e.g Aroma) and the backend should load the plugin. + +## Building + +For building you need: + +- [wups](https://github.com/Maschell/WiiUPluginSystem) +- [wut](https://github.com/devkitpro/wut) + +Install them (in this order) according to their README's. Don't forget the dependencies of the libs itself. + +Then you should be able to configure via `${DEVKITPRO}/portlibs/wiiu/bin/powerpc-eabi-cmake -S . -B build` (with no logging) or `${DEVKITPRO}/portlibs/wiiu/bin/powerpc-eabi-cmake -S . -B build -DDEBUG=1` (with logging), and build with `cd build && make`. + +## Buildflags + +### Logging + +Building only logs errors by default (via OSReport). To enable logging via the [LoggingModule](https://github.com/wiiu-env/LoggingModule) set `DEBUG` to `1` or `VERBOSE`. + +`DEBUG=1` Enables information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule). +`DEBUG=VERBOSE` Enables verbose information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule). + +If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present, it'll fallback to UDP (Port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging. + +## Building using the Dockerfile + +It's possible to use a docker image for building. This way you don't need anything installed on your host system. + +``` +# Build docker image (only needed once) +docker build . -t example-plugin-builder + +# Configure + Build +docker run -it --rm -v ${PWD}:/project example-plugin-builder /bin/bash -c "/opt/devkitpro/portlibs/wiiu/bin/powerpc-eabi-cmake -S . -B build && cd build && make" +``` + +## Format the code via docker + +`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src -i` diff --git a/plugins/example_plugin_cmake/src/main.cpp b/plugins/example_plugin_cmake/src/main.cpp new file mode 100644 index 0000000..7b15a7e --- /dev/null +++ b/plugins/example_plugin_cmake/src/main.cpp @@ -0,0 +1,286 @@ +#include "utils/logger.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Mandatory plugin information. + If not set correctly, the loader will refuse to use the plugin. +**/ +WUPS_PLUGIN_NAME("Example plugin C++"); +WUPS_PLUGIN_DESCRIPTION("This is just an example plugin written in C++"); +WUPS_PLUGIN_VERSION("v1.0"); +WUPS_PLUGIN_AUTHOR("Maschell"); +WUPS_PLUGIN_LICENSE("BSD"); + +#define LOG_FS_OPEN_CONFIG_ID "logFSOpen" +#define OTHER_EXAMPLE_BOOL_CONFIG_ID "otherBoolItem" +#define OTHER_EXAMPLE2_BOOL_CONFIG_ID "other2BoolItem" +#define INTEGER_RANGE_EXAMPLE_CONFIG_ID "intRangeExample" +#define MULTIPLE_VALUES_EXAMPLE_CONFIG_ID "multValueExample" + +/** + All of this defines can be used in ANY file. + It's possible to split it up into multiple files. + +**/ + +WUPS_USE_WUT_DEVOPTAB(); // Use the wut devoptabs +WUPS_USE_STORAGE("example_plugin_cmake"); // Unique id for the storage api + +enum ExampleOptions { + EXAMPLE_OPTION_1 = 0, + EXAMPLE_OPTION_2 = 1, + EXAMPLE_OPTION_3 = 2, +}; + +#define LOF_FS_OPEN_DEFAULT_VALUE true +#define INTEGER_RANGE_DEFAULT_VALUE 10 +#define MULTIPLE_VALUES_DEFAULT_VALUE EXAMPLE_OPTION_2 + +bool sLogFSOpen = LOF_FS_OPEN_DEFAULT_VALUE; +int sIntegerRangeValue = INTEGER_RANGE_DEFAULT_VALUE; +ExampleOptions sExampleOptionValue = MULTIPLE_VALUES_DEFAULT_VALUE; + +/** + * Callback that will be called if the config has been changed + */ +void boolItemChanged(ConfigItemBoolean *item, bool newValue) { + DEBUG_FUNCTION_LINE_INFO("New value in boolItemChanged: %d", newValue); + if (std::string_view(LOG_FS_OPEN_CONFIG_ID) == item->identifier) { + sLogFSOpen = newValue; + // If the value has changed, we store it in the storage. + WUPSStorageAPI::Store(item->identifier, newValue); + } else if (std::string_view(OTHER_EXAMPLE_BOOL_CONFIG_ID) == item->identifier) { + DEBUG_FUNCTION_LINE_ERR("Other bool value has changed to %d", newValue); + } else if (std::string_view(OTHER_EXAMPLE2_BOOL_CONFIG_ID) == item->identifier) { + DEBUG_FUNCTION_LINE_ERR("Other2 bool value has changed to %d", newValue); + } +} + +void integerRangeItemChanged(ConfigItemIntegerRange *item, int newValue) { + DEBUG_FUNCTION_LINE_INFO("New value in integerRangeItemChanged: %d", newValue); + // If the value has changed, we store it in the storage. + if (std::string_view(LOG_FS_OPEN_CONFIG_ID) == item->identifier) { + sIntegerRangeValue = newValue; + // If the value has changed, we store it in the storage. + WUPSStorageAPI::Store(item->identifier, newValue); + } +} + +void multipleValueItemChanged(ConfigItemIntegerRange *item, uint32_t newValue) { + DEBUG_FUNCTION_LINE_INFO("New value in multipleValueItemChanged: %d", newValue); + // If the value has changed, we store it in the storage. + if (std::string_view(MULTIPLE_VALUES_EXAMPLE_CONFIG_ID) == item->identifier) { + sExampleOptionValue = (ExampleOptions) newValue; + // If the value has changed, we store it in the storage. + WUPSStorageAPI::Store(item->identifier, sExampleOptionValue); + } +} + +WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) { + // To use the C++ API, we create new WUPSConfigCategory from the root handle! + WUPSConfigCategory root = WUPSConfigCategory(rootHandle); + + // The functions of the Config API come in two variants: One that throws an exception, and another one which doesn't + // To use the Config API without exception see the example below this try/catch block. + try { + // Then we can simply create a new category + auto functionPatchesCat = WUPSConfigCategory::Create("function patches"); + + // Add a boolean item to this newly created category + functionPatchesCat.add(WUPSConfigItemBoolean::Create(LOG_FS_OPEN_CONFIG_ID, "Log FSOpen calls", + LOF_FS_OPEN_DEFAULT_VALUE, sLogFSOpen, + boolItemChanged)); + + // And finally move that category to the root category. + // Note: "functionPatchesCat" can NOT be changed after adding it to root. + root.add(std::move(functionPatchesCat)); + + // We can also add items directly to root! + root.add(WUPSConfigItemBoolean::Create(OTHER_EXAMPLE_BOOL_CONFIG_ID, "Just another bool item", + false, false, + boolItemChanged)); + + // You can also add an item which just displays any text. + root.add(WUPSConfigItemStub::Create("This item is just displaying some text")); + + // It's also possible to create and item to select an integer from a range. + root.add(WUPSConfigItemIntegerRange::Create(INTEGER_RANGE_EXAMPLE_CONFIG_ID, "Item for selecting an integer between 0 and 50", + INTEGER_RANGE_DEFAULT_VALUE, sIntegerRangeValue, + 0, 50, + &integerRangeItemChanged)); + + + // To select value from an enum WUPSConfigItemMultipleValues fits the best. + constexpr WUPSConfigItemMultipleValues::ValuePair possibleValues[] = { + {EXAMPLE_OPTION_1, "Option 1"}, + {EXAMPLE_OPTION_2, "Option 2"}, + {EXAMPLE_OPTION_3, "Option 3"}, + }; + + // It comes in two variants. + // - "WUPSConfigItemMultipleValues::CreateFromValue" will take a default and current **value** + // - "WUPSConfigItemMultipleValues::CreateFromIndex" will take a default and current **index** + root.add(WUPSConfigItemMultipleValues::CreateFromValue(MULTIPLE_VALUES_EXAMPLE_CONFIG_ID, "Select an option!", + MULTIPLE_VALUES_DEFAULT_VALUE, sExampleOptionValue, + possibleValues, + nullptr)); + + // It's also possible to have nested categories + auto nc1 = WUPSConfigCategory::Create("Category inside root"); + auto nc2 = WUPSConfigCategory::Create("Category inside subcategory 1"); + auto nc3 = WUPSConfigCategory::Create("Category inside subcategory 2"); + + nc3.add(WUPSConfigItemStub::Create("Item inside subcategory 3")); + nc2.add(WUPSConfigItemStub::Create("Item inside subcategory 2")); + nc1.add(WUPSConfigItemStub::Create("Item inside subcategory 1")); + + nc2.add(std::move(nc3)); + nc1.add(std::move(nc2)); + root.add(std::move(nc1)); + } catch (std::exception &e) { + DEBUG_FUNCTION_LINE_ERR("Creating config menu failed: %s", e.what()); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + // In case we don't like exception, we can use the API as well. + // If we add a "WUPSConfigAPIStatus" reference to the API calls, the function won't throw an exception. + // Instead it will return std::optionals and write the result into the WUPSConfigAPIStatus. + WUPSConfigAPIStatus err; + auto categoryOpt = WUPSConfigCategory::Create("Just another Category", err); + if (!categoryOpt) { + DEBUG_FUNCTION_LINE_ERR("Failed to create category: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + auto boolItemOpt = WUPSConfigItemBoolean::Create(OTHER_EXAMPLE2_BOOL_CONFIG_ID, "Just another bool item", + false, false, + boolItemChanged, + err); + if (!boolItemOpt) { + DEBUG_FUNCTION_LINE_ERR("Failed to create bool item: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + // Add bool item to category + if (!categoryOpt->add(std::move(*boolItemOpt), err)) { + DEBUG_FUNCTION_LINE_ERR("Failed to add bool item to category: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + // Add category to root. + if (!root.add(std::move(*categoryOpt), err)) { + DEBUG_FUNCTION_LINE_ERR("Failed to add category to root: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + + return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS; +} + +void ConfigMenuClosedCallback() { + WUPSStorageAPI::SaveStorage(); +} + +/** + Gets called ONCE when the plugin was loaded. +**/ +INITIALIZE_PLUGIN() { + // Logging only works when compiled with `make DEBUG=1`. See the README for more information. + initLogging(); + DEBUG_FUNCTION_LINE("INITIALIZE_PLUGIN of example_plugin!"); + + WUPSConfigAPIOptionsV1 configOptions = {.name = "example_plugin_cmake"}; + if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to init config api"); + } + + WUPSStorageError storageRes; + if ((storageRes = WUPSStorageAPI::GetOrStoreDefault(LOG_FS_OPEN_CONFIG_ID, sLogFSOpen, LOF_FS_OPEN_DEFAULT_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("GetOrStoreDefault failed: %s (%d)", WUPSStorageAPI_GetStatusStr(storageRes), storageRes); + } + if ((storageRes = WUPSStorageAPI::SaveStorage()) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("GetOrStoreDefault failed: %s (%d)", WUPSStorageAPI_GetStatusStr(storageRes), storageRes); + } + + deinitLogging(); +} + +/** + Gets called when the plugin will be unloaded. +**/ +DEINITIALIZE_PLUGIN() { + DEBUG_FUNCTION_LINE("DEINITIALIZE_PLUGIN of example_plugin!"); +} + +/** + Gets called when an application starts. +**/ +ON_APPLICATION_START() { + initLogging(); + + DEBUG_FUNCTION_LINE("ON_APPLICATION_START of example_plugin!"); +} + +/** + * Gets called when an application actually ends + */ +ON_APPLICATION_ENDS() { + deinitLogging(); +} + +/** + Gets called when an application request to exit. +**/ +ON_APPLICATION_REQUESTS_EXIT() { + DEBUG_FUNCTION_LINE_INFO("ON_APPLICATION_REQUESTS_EXIT of example_plugin!"); +} + +/** + This defines a function replacement. + It allows to replace the system function with an own function. + So whenever a game / application calls an overridden function, your function gets called instead. + + Currently it's only possible to override functions that are loaded from .rpl files of OSv10 (00050010-1000400A). + + Signature of this macro: + DECL_FUNCTION( RETURN_TYPE, ARBITRARY_NAME_OF_FUNCTION , ARGS_SEPERATED_BY_COMMA){ + //Your code goes here. + } + + Within this macro, two more function get declare you can use. + my_ARBITRARY_NAME_OF_FUNCTION and real_ARBITRARY_NAME_OF_FUNCTION + + RETURN_TYPE my_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA): + is just name of the function that gets declared in this macro. + It has the same effect as calling the overridden function directly. + + RETURN_TYPE real_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA): + is the name of the function, that leads to function that was overridden. + Use this to call the original function that will be overridden. + CAUTION: Other plugins may already have manipulated the return value or arguments. + + Use this macro for each function you want to override +**/ +DECL_FUNCTION(int, FSOpenFile, FSClient *pClient, FSCmdBlock *pCmd, const char *path, const char *mode, int *handle, int error) { + int result = real_FSOpenFile(pClient, pCmd, path, mode, handle, error); + if (sLogFSOpen) { + DEBUG_FUNCTION_LINE_INFO("FSOpenFile called for folder %s! Result %d", path, result); + } + return result; +} + +/** +This tells the loader which functions from which library (.rpl) should be replaced with which function from this file. +The list of possible libraries can be found in the wiki. (In general it's WUPS_LOADER_LIBRARY_ + the name of the RPL in caps lock) + +WUPS_MUST_REPLACE(FUNCTION_NAME_IN_THIS_FILE, NAME_OF_LIB_WHICH_CONTAINS_THIS_FUNCTION, NAME_OF_FUNCTION_TO_OVERRIDE) + +Define this for each function you want to override. +**/ +WUPS_MUST_REPLACE(FSOpenFile, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile); diff --git a/plugins/example_plugin_cmake/src/utils/CMakeLists.txt b/plugins/example_plugin_cmake/src/utils/CMakeLists.txt new file mode 100644 index 0000000..0717760 --- /dev/null +++ b/plugins/example_plugin_cmake/src/utils/CMakeLists.txt @@ -0,0 +1,5 @@ +if(POLICY CMP0076) + cmake_policy(SET CMP0076 OLD) +endif() + +target_sources(example_cmake PRIVATE src/utils/logger.c) diff --git a/plugins/example_plugin_cmake/src/utils/logger.c b/plugins/example_plugin_cmake/src/utils/logger.c new file mode 100644 index 0000000..f700806 --- /dev/null +++ b/plugins/example_plugin_cmake/src/utils/logger.c @@ -0,0 +1,36 @@ +#ifdef DEBUG +#include +#include +#include +#include + +uint32_t moduleLogInit = false; +uint32_t cafeLogInit = false; +uint32_t udpLogInit = false; +#endif // DEBUG + +void initLogging() { +#ifdef DEBUG + if (!(moduleLogInit = WHBLogModuleInit())) { + cafeLogInit = WHBLogCafeInit(); + udpLogInit = WHBLogUdpInit(); + } +#endif // DEBUG +} + +void deinitLogging() { +#ifdef DEBUG + if (moduleLogInit) { + WHBLogModuleDeinit(); + moduleLogInit = false; + } + if (cafeLogInit) { + WHBLogCafeDeinit(); + cafeLogInit = false; + } + if (udpLogInit) { + WHBLogUdpDeinit(); + udpLogInit = false; + } +#endif // DEBUG +} \ No newline at end of file diff --git a/plugins/example_plugin_cmake/src/utils/logger.h b/plugins/example_plugin_cmake/src/utils/logger.h new file mode 100644 index 0000000..5f195ba --- /dev/null +++ b/plugins/example_plugin_cmake/src/utils/logger.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_APP_TYPE "P" +#define LOG_APP_NAME "ExamplePlugin" + +#define __FILENAME__ ({ \ + const char *__filename = __FILE__; \ + const char *__pos = strrchr(__filename, '/'); \ + if (!__pos) __pos = strrchr(__filename, '\\'); \ + __pos ? __pos + 1 : __filename; \ +}) + +#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", FMT, ##ARGS) + +#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS) + +#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \ + do { \ + LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \ + } while (0) + +#ifdef DEBUG + +#ifdef VERBOSE_DEBUG +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "", "", FMT, ##ARGS); +#else +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) +#endif + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS); + +#else + +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##INFO ## ", "\n", FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS); + +#endif + +void initLogging(); + +void deinitLogging(); + +#ifdef __cplusplus +} +#endif diff --git a/share/wups.cmake b/share/wups.cmake new file mode 100644 index 0000000..d20412d --- /dev/null +++ b/share/wups.cmake @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.2) + +if(NOT DEFINED ENV{DEVKITPRO}) + message(FATAL_ERROR "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitPro") +endif() + +if(NOT DEFINED ENV{DEVKITPPC}) + message(FATAL_ERROR "Please set DEVKITPPC in your environment. export DEVKITPPC=/devkitPro/devkitPPC") +endif() + +# create a WUPS plugin +function(wups_create_plugin target) + set(WUPS_ROOT "$ENV{DEVKITPRO}/wups") + + target_include_directories(${target} PRIVATE "${WUPS_ROOT}/include") + target_link_directories(${target} PRIVATE "${WUPS_ROOT}/lib") + target_link_libraries(${target} wups) + target_link_options(${target} PRIVATE "-T${WUPS_ROOT}/share/wups.ld" "-specs=${WUPS_ROOT}/share/wups.specs") + + get_target_property(WUPS_OUTPUT_NAME ${target} OUTPUT_NAME) + get_target_property(WUPS_BINARY_DIR ${target} BINARY_DIR) + if(NOT WUPS_OUTPUT_NAME) + set(WUPS_OUTPUT_NAME "${target}") + endif() + set(WUPS_OUTPUT "${WUPS_BINARY_DIR}/${WUPS_OUTPUT_NAME}.wps") + + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${WUT_ELF2RPL_EXE} "$" "${WUPS_OUTPUT}" + COMMAND echo PL | dd of=${WUPS_OUTPUT} bs=1 seek=9 count=2 conv=notrunc status=none + BYPRODUCTS "${WUPS_OUTPUT}" + COMMENT "Converting ${target} to .wps format" + VERBATIM + ) +endfunction()