wiiu: working as a plugin

This commit is contained in:
Maschell 2023-11-19 16:44:10 +01:00
parent af2b1be4c8
commit 40ff376710
8 changed files with 368 additions and 133 deletions

1
.gitignore vendored
View File

@ -27,3 +27,4 @@ switch/romfs/shaders/*.dksh
build/
*.rpx
*.wuhb
*.wps

6
Dockerfile.wiiu Normal file
View File

@ -0,0 +1,6 @@
FROM ghcr.io/wiiu-env/devkitppc:20231112
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230719 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmocha:20230621 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -8,16 +8,10 @@ endif
TOPDIR ?= $(CURDIR)
#-------------------------------------------------------------------------------
# APP_NAME sets the long name of the application
# APP_SHORTNAME sets the short name of the application
# APP_AUTHOR sets the author of the application
#-------------------------------------------------------------------------------
#APP_NAME := Application Name
#APP_SHORTNAME := App Name
#APP_AUTHOR := Built with devkitPPC & wut
include $(DEVKITPRO)/wups/share/wups_rules
include $(DEVKITPRO)/wut/share/wut_rules
WUT_ROOT := $(DEVKITPRO)/wut
WUPS_ROOT := $(DEVKITPRO)/wups
#-------------------------------------------------------------------------------
# TARGET is the name of the output
@ -25,42 +19,44 @@ include $(DEVKITPRO)/wut/share/wut_rules
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# CONTENT is the path to the bundled folder that will be mounted as /vol/content/
# ICON is the game icon, leave blank to use default rule
# TV_SPLASH is the image displayed during bootup on the TV, leave blank to use default rule
# DRC_SPLASH is the image displayed during bootup on the DRC, leave blank to use default rule
#-------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
TARGET := ftpd
BUILD := build
SOURCES := source source/wiiu
DATA := data
INCLUDES := include
CONTENT :=
ICON :=
TV_SPLASH :=
DRC_SPLASH :=
INCLUDES := source include
#-------------------------------------------------------------------------------
# options for code generation
#-------------------------------------------------------------------------------
CFLAGS := -g -Wall -O0 -ffunction-sections \
CFLAGS := -Wall -O2 -ffunction-sections \
$(MACHDEP)
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ -DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
-DNO_IPV6 -DCLASSIC -DNO_CONSOLE -DFTPDCONFIG="\"/config/ftpd/ftpd.cfg\""
CXXFLAGS := $(CFLAGS) -std=c++20
CXXFLAGS := $(CFLAGS) -std=gnu++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
LIBS := -lwutd -lmocha
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -g
CFLAGS += -DDEBUG -g
endif
ifeq ($(DEBUG),VERBOSE)
CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
endif
LIBS := -lwups -lwutd -lmocha
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
# containing include and lib
#-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUT_ROOT)/usr
LIBDIRS := $(PORTLIBS) $(WUPS_ROOT) $(WUT_ROOT) $(WUT_ROOT)/usr
#-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
@ -107,34 +103,6 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifneq (,$(strip $(CONTENT)))
export APP_CONTENT := $(TOPDIR)/$(CONTENT)
endif
ifneq (,$(strip $(ICON)))
export APP_ICON := $(TOPDIR)/$(ICON)
else ifneq (,$(wildcard $(TOPDIR)/$(TARGET).png))
export APP_ICON := $(TOPDIR)/$(TARGET).png
else ifneq (,$(wildcard $(TOPDIR)/icon.png))
export APP_ICON := $(TOPDIR)/icon.png
endif
ifneq (,$(strip $(TV_SPLASH)))
export APP_TV_SPLASH := $(TOPDIR)/$(TV_SPLASH)
else ifneq (,$(wildcard $(TOPDIR)/tv-splash.png))
export APP_TV_SPLASH := $(TOPDIR)/tv-splash.png
else ifneq (,$(wildcard $(TOPDIR)/splash.png))
export APP_TV_SPLASH := $(TOPDIR)/splash.png
endif
ifneq (,$(strip $(DRC_SPLASH)))
export APP_DRC_SPLASH := $(TOPDIR)/$(DRC_SPLASH)
else ifneq (,$(wildcard $(TOPDIR)/drc-splash.png))
export APP_DRC_SPLASH := $(TOPDIR)/drc-splash.png
else ifneq (,$(wildcard $(TOPDIR)/splash.png))
export APP_DRC_SPLASH := $(TOPDIR)/splash.png
endif
.PHONY: $(BUILD) clean all
#-------------------------------------------------------------------------------
@ -147,7 +115,7 @@ $(BUILD):
#-------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).wuhb $(TARGET).rpx $(TARGET).elf
@rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf
#-------------------------------------------------------------------------------
else
@ -158,10 +126,9 @@ DEPENDS := $(OFILES:.o=.d)
#-------------------------------------------------------------------------------
# main targets
#-------------------------------------------------------------------------------
all : $(OUTPUT).wuhb
all : $(OUTPUT).wps
$(OUTPUT).wuhb : $(OUTPUT).rpx
$(OUTPUT).rpx : $(OUTPUT).elf
$(OUTPUT).wps : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -20,7 +20,6 @@ public:
static int closedir (DIR *dirp);
static DIR *opendir (const char *dirname);
static struct dirent *readdir (DIR *dirp);
@ -29,6 +28,16 @@ public:
static int lstat (const char *path, struct stat *buf);
static int mkdir (const char *path, mode_t mode);
static int rmdir (const char *path);
static int rename (const char *path, const char *path2);
static int unlink (const char *path);
static void addVirtualPath (const std::string &virtualPath,
const std::vector<std::string> &subDirectories);
static void clear ();
};

View File

@ -60,13 +60,7 @@ private:
/// \brief Command buffer size
constexpr static auto COMMAND_BUFFERSIZE = 4096;
#ifdef __WIIU__
/// \brief Response buffer size
constexpr static auto RESPONSE_BUFFERSIZE = 128 * 1024;
/// \brief Transfer buffersize
constexpr static auto XFER_BUFFERSIZE = 128 * 1024;
#elif defined(NDS)
#ifdef NDS
/// \brief Response buffer size
constexpr static auto RESPONSE_BUFFERSIZE = 4096;
@ -74,10 +68,10 @@ private:
constexpr static auto XFER_BUFFERSIZE = 8192;
#else
/// \brief Response buffer size
constexpr static auto RESPONSE_BUFFERSIZE = 32768;
constexpr static auto RESPONSE_BUFFERSIZE = 16 * 1024;
/// \brief Transfer buffersize
constexpr static auto XFER_BUFFERSIZE = 65536;
constexpr static auto XFER_BUFFERSIZE = 32 * 1024;
#endif
/// \brief File buffersize

View File

@ -3,6 +3,7 @@
#include <map>
#include <memory>
#include <sys/dirent.h>
#include <sys/unistd.h>
#include <vector>
class VirtualDirectory
@ -17,9 +18,9 @@ public:
mCurIterator = mDirectories.begin ();
}
[[nodiscard]] DIR *getAsDir () const
[[nodiscard]] const DIR *getAsDir () const
{
return (DIR *)this;
return &mDirPtr;
}
struct dirent *readdir ()
@ -31,10 +32,12 @@ public:
mDir = {};
snprintf (mDir.d_name, sizeof (mDir.d_name), "%s", mCurIterator->c_str ());
mCurIterator++;
mDirPtr.position++;
return &mDir;
}
private:
DIR mDirPtr = {};
std::vector<std::string> mDirectories;
struct dirent mDir = {};
std::vector<std::string>::iterator mCurIterator{};
@ -70,7 +73,7 @@ bool remove_locked_first_if (std::mutex &mutex, Container &container, Predicate
return remove_first_if (container, pred);
}
static DIR *getVirtualDir (const std::vector<std::string> &subDirectories)
static const DIR *getVirtualDir (const std::vector<std::string> &subDirectories)
{
auto virtDir = std::make_unique<VirtualDirectory> (subDirectories);
auto *result = virtDir->getAsDir ();
@ -124,12 +127,16 @@ int IOAbstraction::closedir (DIR *dirp)
DIR *IOAbstraction::opendir (const char *dirname)
{
auto convertedPath = convertPath (dirname);
auto * res = ::opendir (convertedPath.c_str ());
if(res == nullptr)
{
if (sVirtualDirs.count (convertedPath) > 0)
{
return getVirtualDir (sVirtualDirs[convertedPath]);
return (DIR*) getVirtualDir (sVirtualDirs[convertedPath]);
}
return ::opendir (convertedPath.c_str ());
}
return res;
}
FILE *IOAbstraction::fopen (const char *_name, const char *_type)
@ -195,3 +202,30 @@ void IOAbstraction::addVirtualPath (const std::string &virtualPath,
{
sVirtualDirs.insert (std::make_pair (virtualPath, subDirectories));
}
void IOAbstraction::clear ()
{
std::lock_guard lock (sOpenVirtualDirectoriesMutex);
sOpenVirtualDirectories.clear ();
sVirtualDirs.clear ();
}
int IOAbstraction::mkdir (const char *path, mode_t mode)
{
return ::mkdir (convertPath (path).c_str (), mode);
}
int IOAbstraction::rmdir (const char *path)
{
return ::rmdir (convertPath (path).c_str ());
}
int IOAbstraction::unlink (const char *path)
{
return ::unlink (convertPath (path).c_str ());
}
int IOAbstraction::rename (const char *path, const char *path2)
{
return ::rename (convertPath (path).c_str (), convertPath (path2).c_str ());
}

View File

@ -583,11 +583,21 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
}
else if (i.revents & (POLLIN | POLLOUT))
{
for (unsigned i = 0; i < 10; ++i)
auto start_time = std::chrono::high_resolution_clock::now ();
while (true)
{
if (!((*session).*(session->m_transfer)) ())
{
break;
}
if (std::chrono::duration_cast<std::chrono::microseconds> (
std::chrono::high_resolution_clock::now () - start_time) >
5000ms)
{
break;
}
}
}
break;
}
@ -1677,7 +1687,7 @@ bool FtpSession::listTransfer ()
{
// build the path
auto const fullPath = buildPath (m_lwd, dent->d_name);
struct stat st;
struct stat st = {};
#ifdef __3DS__
// the sdmc directory entry already has the type and size, so no need to do a slow stat
@ -1997,7 +2007,7 @@ void FtpSession::DELE (char const *args_)
}
// unlink the path
if (::unlink (path.c_str ()) != 0)
if (IOAbstraction::unlink (path.c_str ()) != 0)
{
sendResponse ("550 %s\r\n", std::strerror (errno));
return;
@ -2081,7 +2091,7 @@ void FtpSession::MKD (char const *args_)
}
// create the directory
if (::mkdir (path.c_str (), 0755) != 0)
if (IOAbstraction::mkdir (path.c_str (), 0755) != 0)
{
sendResponse ("550 %s\r\n", std::strerror (errno));
return;
@ -2498,7 +2508,7 @@ void FtpSession::RMD (char const *args_)
}
// remove the directory
if (::rmdir (path.c_str ()) != 0)
if (IOAbstraction::rmdir (path.c_str ()) != 0)
{
sendResponse ("550 %d %s\r\n", __LINE__, std::strerror (errno));
return;
@ -2566,7 +2576,7 @@ void FtpSession::RNTO (char const *args_)
}
// rename the file
if (::rename (m_rename.c_str (), path.c_str ()) != 0)
if (IOAbstraction::rename (m_rename.c_str (), path.c_str ()) != 0)
{
m_rename.clear ();
sendResponse ("550 %s\r\n", std::strerror (errno));

View File

@ -20,7 +20,8 @@
#include "platform.h"
#include "../IOAbstraction.h"
#include "IOAbstraction.h"
#include "ftpServer.h"
#include "log.h"
#include <mocha/mocha.h>
@ -28,12 +29,29 @@
#include <thread>
#include <coreinit/thread.h>
#include <sys/unistd.h>
#include <whb/proc.h>
#include <wups.h>
#include <wups/config/WUPSConfigCategory.h>
#include <wups/config/WUPSConfigItem.h>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/config/WUPSConfigItemStub.h>
#ifndef CLASSIC
#error "Wii U must be built in classic mode"
#endif
#define VERSION_FULL "0.1"
WUPS_PLUGIN_NAME ("ftpd");
WUPS_PLUGIN_DESCRIPTION ("FTP Server");
WUPS_PLUGIN_VERSION (VERSION_FULL);
WUPS_PLUGIN_AUTHOR ("mtheall, Maschell");
WUPS_PLUGIN_LICENSE ("GPL");
WUPS_USE_WUT_DEVOPTAB ();
WUPS_USE_STORAGE ("ftpd"); // Unqiue id for the storage api
#define FTPIIU_ENABLED_STRING "enabled"
#define SYSTEM_FILES_ALLOWED_STRING "systemFilesAllowed"
bool platform::networkVisible ()
{
@ -68,71 +86,262 @@ MochaUtilsStatus MountWrapper (const char *mount, const char *dev, const char *m
return res;
}
bool platform::init ()
UniqueFtpServer server = nullptr;
bool sSystemFilesAllowed = false;
bool sMochaPathsWereMounted = false;
bool sFTPServerEnabled = true;
void start_server ()
{
nn::ac::Initialize ();
nn::ac::ConnectAsync ();
WHBProcInit ();
if (server != nullptr)
{
return;
}
MochaUtilsStatus res;
if ((res = Mocha_InitLibrary ()) == MOCHA_RESULT_SUCCESS)
{
std::vector<std::string> virtualDirsInRoot;
if (sSystemFilesAllowed)
{
if (MountWrapper ("slccmpt01", "/dev/slccmpt01", "/vol/storage_slccmpt01") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("slccmpt01");
virtualDirsInRoot.emplace_back ("slccmpt01");
IOAbstraction::addVirtualPath ("slccmpt01:/", {});
}
if (MountWrapper ("storage_odd_tickets", nullptr, "/vol/storage_odd01") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("storage_odd_tickets");
virtualDirsInRoot.emplace_back ("storage_odd_tickets");
IOAbstraction::addVirtualPath ("storage_odd_tickets:/", {});
}
if (MountWrapper ("storage_odd_updates", nullptr, "/vol/storage_odd02") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("storage_odd_updates");
virtualDirsInRoot.emplace_back ("storage_odd_updates");
IOAbstraction::addVirtualPath ("storage_odd_updates:/", {});
}
if (MountWrapper ("storage_odd_content", nullptr, "/vol/storage_odd03") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("storage_odd_content");
virtualDirsInRoot.emplace_back ("storage_odd_content");
IOAbstraction::addVirtualPath ("storage_odd_content:/", {});
}
if (MountWrapper ("storage_odd_content2", nullptr, "/vol/storage_odd04") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("storage_odd_content2");
virtualDirsInRoot.emplace_back ("storage_odd_content2");
IOAbstraction::addVirtualPath ("storage_odd_content2:/", {});
}
if (MountWrapper ("storage_slc", "/dev/slc01", "/vol/storage_slc01") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("storage_slc");
virtualDirsInRoot.emplace_back ("storage_slc");
IOAbstraction::addVirtualPath ("storage_slc:/", {});
}
if (Mocha_MountFS ("storage_mlc", nullptr, "/vol/storage_mlc01") == MOCHA_RESULT_SUCCESS)
if (Mocha_MountFS ("storage_mlc", nullptr, "/vol/storage_mlc01") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("storage_mlc");
virtualDirsInRoot.emplace_back ("storage_mlc");
IOAbstraction::addVirtualPath ("storage_mlc:/", {});
}
if (Mocha_MountFS ("storage_usb", nullptr, "/vol/storage_usb01") == MOCHA_RESULT_SUCCESS)
if (Mocha_MountFS ("storage_usb", nullptr, "/vol/storage_usb01") ==
MOCHA_RESULT_SUCCESS)
{
virtualDirsInRoot.push_back ("storage_usb");
virtualDirsInRoot.emplace_back ("storage_usb");
IOAbstraction::addVirtualPath ("storage_usb:/", {});
}
virtualDirsInRoot.push_back ("fs");
}
virtualDirsInRoot.emplace_back ("fs");
IOAbstraction::addVirtualPath (":/", virtualDirsInRoot);
IOAbstraction::addVirtualPath ("fs:/", std::vector<std::string>{"vol"});
IOAbstraction::addVirtualPath ("fs:/vol", std::vector<std::string>{"external01"});
IOAbstraction::addVirtualPath ("storage_odd_tickets:/", {});
IOAbstraction::addVirtualPath ("storage_odd_updates:/", {});
IOAbstraction::addVirtualPath ("storage_odd_content:/", {});
IOAbstraction::addVirtualPath ("storage_odd_content2:/", {});
IOAbstraction::addVirtualPath ("storage_usb:/", {});
IOAbstraction::addVirtualPath (
"fs:/vol", std::vector<std::string>{"external01", "content", "save"});
IOAbstraction::addVirtualPath ("fs:/vol/content", {});
sMochaPathsWereMounted = true;
}
else
{
error ("Failed to init libmocha: %s [%d]", Mocha_GetStatusStr (res), res);
OSReport ("Failed to init libmocha: %s [%d]\n", Mocha_GetStatusStr (res), res);
}
::chdir ("fs:/vol/external01");
server = FtpServer::create ();
}
void stop_server ()
{
server.reset ();
if (sMochaPathsWereMounted)
{
Mocha_UnmountFS ("slccmpt01");
Mocha_UnmountFS ("storage_odd_tickets");
Mocha_UnmountFS ("storage_odd_updates");
Mocha_UnmountFS ("storage_odd_content");
Mocha_UnmountFS ("storage_odd_content2");
Mocha_UnmountFS ("storage_slc");
Mocha_UnmountFS ("storage_mlc");
Mocha_UnmountFS ("storage_usb");
sMochaPathsWereMounted = false;
}
IOAbstraction::clear ();
}
void gFTPServerRunningChanged (ConfigItemBoolean *item, bool newValue)
{
sFTPServerEnabled = newValue;
if (!sFTPServerEnabled)
{
stop_server ();
}
else
{
start_server ();
}
// If the value has changed, we store it in the storage.
auto res = WUPSStorageAPI::Store (FTPIIU_ENABLED_STRING, sFTPServerEnabled);
if (res != WUPS_STORAGE_ERROR_SUCCESS)
{
OSReport ("Failed to store gFTPServerEnabled: %s (%d)\n",
WUPSStorageAPI::GetStatusStr (res).data (),
res);
}
}
void gSystemFilesAllowedChanged (ConfigItemBoolean *item, bool newValue)
{
// DEBUG_FUNCTION_LINE("New value in gFTPServerEnabled: %d", newValue);
if (server != nullptr)
{ // If the server is already running we need to restart it.
stop_server ();
sSystemFilesAllowed = newValue;
start_server ();
}
else
{
sSystemFilesAllowed = newValue;
}
// If the value has changed, we store it in the storage.
auto res = WUPSStorageAPI::Store (SYSTEM_FILES_ALLOWED_STRING, sSystemFilesAllowed);
if (res != WUPS_STORAGE_ERROR_SUCCESS)
{
OSReport ("Failed to store gSystemFilesAllowed: %s (%d)\n",
WUPSStorageAPI::GetStatusStr (res).data (),
res);
}
}
WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback (WUPSConfigCategoryHandle rootHandle)
{
uint32_t hostIpAddress = 0;
nn::ac::GetAssignedAddress (&hostIpAddress);
try
{
WUPSConfigCategory root = WUPSConfigCategory (rootHandle);
root.add (WUPSConfigItemBoolean::Create (FTPIIU_ENABLED_STRING,
"Enable ftpd",
true,
sFTPServerEnabled,
&gFTPServerRunningChanged));
root.add (WUPSConfigItemBoolean::Create (SYSTEM_FILES_ALLOWED_STRING,
"Allow access to system files",
false,
sSystemFilesAllowed,
&gSystemFilesAllowedChanged));
root.add (WUPSConfigItemStub::Create ("==="));
char ipSettings[50];
if (hostIpAddress != 0)
{
snprintf (ipSettings,
50,
"IP of your console is %u.%u.%u.%u. Port %i",
(hostIpAddress >> 24) & 0xFF,
(hostIpAddress >> 16) & 0xFF,
(hostIpAddress >> 8) & 0xFF,
(hostIpAddress >> 0) & 0xFF,
5000);
}
else
{
snprintf (
ipSettings, sizeof (ipSettings), "The console is not connected to a network.");
}
root.add (WUPSConfigItemStub::Create (ipSettings));
root.add (WUPSConfigItemStub::Create ("You can connect with empty credentials"));
}
catch (std::exception &e)
{
OSReport ("Exception T_T : %s\n", e.what ());
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
}
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
}
void ConfigMenuClosedCallback ()
{
OSReport ("ConfigMenuClosedCallback\n");
WUPSStorageAPI::SaveStorage ();
}
INITIALIZE_PLUGIN ()
{
WUPSConfigAPIOptionsV1 configOptions = {.name = "ftpd"};
if (WUPSConfigAPI_Init (configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) !=
WUPSCONFIG_API_RESULT_SUCCESS)
{
OSFatal ("Failed to init config api");
}
if (WUPSStorageAPI::GetOrStoreDefault (FTPIIU_ENABLED_STRING, sFTPServerEnabled, true) !=
WUPS_STORAGE_ERROR_SUCCESS)
{
OSReport ("Failed\n");
}
if (WUPSStorageAPI::GetOrStoreDefault (
SYSTEM_FILES_ALLOWED_STRING, sSystemFilesAllowed, false) != WUPS_STORAGE_ERROR_SUCCESS)
{
OSReport ("Failed\n");
}
if (WUPSStorageAPI::SaveStorage () != WUPS_STORAGE_ERROR_SUCCESS)
{
OSReport ("Failed\n");
}
}
void wiiu_init ()
{
nn::ac::Initialize ();
nn::ac::ConnectAsync ();
if (sFTPServerEnabled)
{
start_server ();
}
}
ON_APPLICATION_START ()
{
nn::ac::Initialize ();
nn::ac::ConnectAsync ();
wiiu_init ();
}
ON_APPLICATION_ENDS ()
{
stop_server ();
}
bool platform::init ()
{
WHBProcInit ();
wiiu_init ();
return true;
}
@ -147,6 +356,7 @@ void platform::render ()
void platform::exit ()
{
IOAbstraction::clear ();
WHBProcShutdown ();
}
@ -162,11 +372,15 @@ public:
explicit privateData_t (std::function<void ()> &&func_) : thread (std::move (func_))
{
auto nativeHandle = (OSThread *)thread.native_handle ();
OSSetThreadName (nativeHandle, "ftpd_server");
OSSetThreadName (nativeHandle, "ftpd");
while (!OSSetThreadAffinity (nativeHandle, OS_THREAD_ATTRIB_AFFINITY_CPU2))
{
OSSleepTicks (OSMillisecondsToTicks (16));
}
while (!OSSetThreadPriority (nativeHandle, 16))
{
OSSleepTicks (OSMillisecondsToTicks (16));
}
}
/// \brief Underlying thread