mirror of
https://github.com/wiiu-env/ftpiiu_plugin.git
synced 2024-11-25 14:16:55 +01:00
wiiu: working as a plugin
This commit is contained in:
parent
af2b1be4c8
commit
40ff376710
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,3 +27,4 @@ switch/romfs/shaders/*.dksh
|
|||||||
build/
|
build/
|
||||||
*.rpx
|
*.rpx
|
||||||
*.wuhb
|
*.wuhb
|
||||||
|
*.wps
|
||||||
|
6
Dockerfile.wiiu
Normal file
6
Dockerfile.wiiu
Normal 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
|
@ -8,16 +8,10 @@ endif
|
|||||||
|
|
||||||
TOPDIR ?= $(CURDIR)
|
TOPDIR ?= $(CURDIR)
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
include $(DEVKITPRO)/wups/share/wups_rules
|
||||||
# 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)/wut/share/wut_rules
|
WUT_ROOT := $(DEVKITPRO)/wut
|
||||||
|
WUPS_ROOT := $(DEVKITPRO)/wups
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# TARGET is the name of the output
|
# 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
|
# SOURCES is a list of directories containing source code
|
||||||
# DATA is a list of directories containing data files
|
# DATA is a list of directories containing data files
|
||||||
# INCLUDES is a list of directories containing header 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
|
BUILD := build
|
||||||
SOURCES := source source/wiiu
|
SOURCES := source source/wiiu
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := include
|
INCLUDES := source include
|
||||||
CONTENT :=
|
|
||||||
ICON :=
|
|
||||||
TV_SPLASH :=
|
|
||||||
DRC_SPLASH :=
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
CFLAGS := -g -Wall -O0 -ffunction-sections \
|
CFLAGS := -Wall -O2 -ffunction-sections \
|
||||||
$(MACHDEP)
|
$(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\""
|
-DNO_IPV6 -DCLASSIC -DNO_CONSOLE -DFTPDCONFIG="\"/config/ftpd/ftpd.cfg\""
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -std=c++20
|
CXXFLAGS := $(CFLAGS) -std=gnu++20
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
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
|
# list of directories containing libraries, this must be the top level
|
||||||
# containing include and lib
|
# 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
|
# 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)
|
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
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
@ -147,7 +115,7 @@ $(BUILD):
|
|||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
clean:
|
clean:
|
||||||
@echo clean ...
|
@echo clean ...
|
||||||
@rm -fr $(BUILD) $(TARGET).wuhb $(TARGET).rpx $(TARGET).elf
|
@rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
else
|
else
|
||||||
@ -158,10 +126,9 @@ DEPENDS := $(OFILES:.o=.d)
|
|||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# main targets
|
# main targets
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
all : $(OUTPUT).wuhb
|
all : $(OUTPUT).wps
|
||||||
|
|
||||||
$(OUTPUT).wuhb : $(OUTPUT).rpx
|
$(OUTPUT).wps : $(OUTPUT).elf
|
||||||
$(OUTPUT).rpx : $(OUTPUT).elf
|
|
||||||
$(OUTPUT).elf : $(OFILES)
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
$(OFILES_SRC) : $(HFILES_BIN)
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
@ -20,7 +20,6 @@ public:
|
|||||||
|
|
||||||
static int closedir (DIR *dirp);
|
static int closedir (DIR *dirp);
|
||||||
|
|
||||||
|
|
||||||
static DIR *opendir (const char *dirname);
|
static DIR *opendir (const char *dirname);
|
||||||
|
|
||||||
static struct dirent *readdir (DIR *dirp);
|
static struct dirent *readdir (DIR *dirp);
|
||||||
@ -29,6 +28,16 @@ public:
|
|||||||
|
|
||||||
static int lstat (const char *path, struct stat *buf);
|
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,
|
static void addVirtualPath (const std::string &virtualPath,
|
||||||
const std::vector<std::string> &subDirectories);
|
const std::vector<std::string> &subDirectories);
|
||||||
|
|
||||||
|
static void clear ();
|
||||||
};
|
};
|
@ -60,13 +60,7 @@ private:
|
|||||||
/// \brief Command buffer size
|
/// \brief Command buffer size
|
||||||
constexpr static auto COMMAND_BUFFERSIZE = 4096;
|
constexpr static auto COMMAND_BUFFERSIZE = 4096;
|
||||||
|
|
||||||
#ifdef __WIIU__
|
#ifdef NDS
|
||||||
/// \brief Response buffer size
|
|
||||||
constexpr static auto RESPONSE_BUFFERSIZE = 128 * 1024;
|
|
||||||
|
|
||||||
/// \brief Transfer buffersize
|
|
||||||
constexpr static auto XFER_BUFFERSIZE = 128 * 1024;
|
|
||||||
#elif defined(NDS)
|
|
||||||
/// \brief Response buffer size
|
/// \brief Response buffer size
|
||||||
constexpr static auto RESPONSE_BUFFERSIZE = 4096;
|
constexpr static auto RESPONSE_BUFFERSIZE = 4096;
|
||||||
|
|
||||||
@ -74,10 +68,10 @@ private:
|
|||||||
constexpr static auto XFER_BUFFERSIZE = 8192;
|
constexpr static auto XFER_BUFFERSIZE = 8192;
|
||||||
#else
|
#else
|
||||||
/// \brief Response buffer size
|
/// \brief Response buffer size
|
||||||
constexpr static auto RESPONSE_BUFFERSIZE = 32768;
|
constexpr static auto RESPONSE_BUFFERSIZE = 16 * 1024;
|
||||||
|
|
||||||
/// \brief Transfer buffersize
|
/// \brief Transfer buffersize
|
||||||
constexpr static auto XFER_BUFFERSIZE = 65536;
|
constexpr static auto XFER_BUFFERSIZE = 32 * 1024;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// \brief File buffersize
|
/// \brief File buffersize
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sys/dirent.h>
|
#include <sys/dirent.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class VirtualDirectory
|
class VirtualDirectory
|
||||||
@ -17,9 +18,9 @@ public:
|
|||||||
mCurIterator = mDirectories.begin ();
|
mCurIterator = mDirectories.begin ();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] DIR *getAsDir () const
|
[[nodiscard]] const DIR *getAsDir () const
|
||||||
{
|
{
|
||||||
return (DIR *)this;
|
return &mDirPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dirent *readdir ()
|
struct dirent *readdir ()
|
||||||
@ -31,10 +32,12 @@ public:
|
|||||||
mDir = {};
|
mDir = {};
|
||||||
snprintf (mDir.d_name, sizeof (mDir.d_name), "%s", mCurIterator->c_str ());
|
snprintf (mDir.d_name, sizeof (mDir.d_name), "%s", mCurIterator->c_str ());
|
||||||
mCurIterator++;
|
mCurIterator++;
|
||||||
|
mDirPtr.position++;
|
||||||
return &mDir;
|
return &mDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
DIR mDirPtr = {};
|
||||||
std::vector<std::string> mDirectories;
|
std::vector<std::string> mDirectories;
|
||||||
struct dirent mDir = {};
|
struct dirent mDir = {};
|
||||||
std::vector<std::string>::iterator mCurIterator{};
|
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);
|
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 virtDir = std::make_unique<VirtualDirectory> (subDirectories);
|
||||||
auto *result = virtDir->getAsDir ();
|
auto *result = virtDir->getAsDir ();
|
||||||
@ -124,12 +127,16 @@ int IOAbstraction::closedir (DIR *dirp)
|
|||||||
DIR *IOAbstraction::opendir (const char *dirname)
|
DIR *IOAbstraction::opendir (const char *dirname)
|
||||||
{
|
{
|
||||||
auto convertedPath = convertPath (dirname);
|
auto convertedPath = convertPath (dirname);
|
||||||
if (sVirtualDirs.count (convertedPath) > 0)
|
auto * res = ::opendir (convertedPath.c_str ());
|
||||||
|
if(res == nullptr)
|
||||||
{
|
{
|
||||||
return getVirtualDir (sVirtualDirs[convertedPath]);
|
if (sVirtualDirs.count (convertedPath) > 0)
|
||||||
}
|
{
|
||||||
|
return (DIR*) getVirtualDir (sVirtualDirs[convertedPath]);
|
||||||
|
}
|
||||||
|
|
||||||
return ::opendir (convertedPath.c_str ());
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *IOAbstraction::fopen (const char *_name, const char *_type)
|
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));
|
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 ());
|
||||||
|
}
|
||||||
|
@ -583,10 +583,20 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
|
|||||||
}
|
}
|
||||||
else if (i.revents & (POLLIN | POLLOUT))
|
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)) ())
|
if (!((*session).*(session->m_transfer)) ())
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::chrono::duration_cast<std::chrono::microseconds> (
|
||||||
|
std::chrono::high_resolution_clock::now () - start_time) >
|
||||||
|
5000ms)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1677,7 +1687,7 @@ bool FtpSession::listTransfer ()
|
|||||||
{
|
{
|
||||||
// build the path
|
// build the path
|
||||||
auto const fullPath = buildPath (m_lwd, dent->d_name);
|
auto const fullPath = buildPath (m_lwd, dent->d_name);
|
||||||
struct stat st;
|
struct stat st = {};
|
||||||
|
|
||||||
#ifdef __3DS__
|
#ifdef __3DS__
|
||||||
// the sdmc directory entry already has the type and size, so no need to do a slow stat
|
// 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
|
// unlink the path
|
||||||
if (::unlink (path.c_str ()) != 0)
|
if (IOAbstraction::unlink (path.c_str ()) != 0)
|
||||||
{
|
{
|
||||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||||
return;
|
return;
|
||||||
@ -2081,7 +2091,7 @@ void FtpSession::MKD (char const *args_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the directory
|
// 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));
|
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||||
return;
|
return;
|
||||||
@ -2498,7 +2508,7 @@ void FtpSession::RMD (char const *args_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove the directory
|
// 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));
|
sendResponse ("550 %d %s\r\n", __LINE__, std::strerror (errno));
|
||||||
return;
|
return;
|
||||||
@ -2566,7 +2576,7 @@ void FtpSession::RNTO (char const *args_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rename the file
|
// 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 ();
|
m_rename.clear ();
|
||||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include "../IOAbstraction.h"
|
#include "IOAbstraction.h"
|
||||||
|
#include "ftpServer.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include <mocha/mocha.h>
|
#include <mocha/mocha.h>
|
||||||
@ -28,12 +29,29 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include <coreinit/thread.h>
|
#include <coreinit/thread.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <whb/proc.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
|
#ifndef CLASSIC
|
||||||
#error "Wii U must be built in classic mode"
|
#error "Wii U must be built in classic mode"
|
||||||
#endif
|
#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 ()
|
bool platform::networkVisible ()
|
||||||
{
|
{
|
||||||
@ -68,71 +86,262 @@ MochaUtilsStatus MountWrapper (const char *mount, const char *dev, const char *m
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool platform::init ()
|
UniqueFtpServer server = nullptr;
|
||||||
|
bool sSystemFilesAllowed = false;
|
||||||
|
bool sMochaPathsWereMounted = false;
|
||||||
|
bool sFTPServerEnabled = true;
|
||||||
|
|
||||||
|
void start_server ()
|
||||||
{
|
{
|
||||||
nn::ac::Initialize ();
|
if (server != nullptr)
|
||||||
nn::ac::ConnectAsync ();
|
{
|
||||||
WHBProcInit ();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MochaUtilsStatus res;
|
MochaUtilsStatus res;
|
||||||
if ((res = Mocha_InitLibrary ()) == MOCHA_RESULT_SUCCESS)
|
if ((res = Mocha_InitLibrary ()) == MOCHA_RESULT_SUCCESS)
|
||||||
{
|
{
|
||||||
std::vector<std::string> virtualDirsInRoot;
|
std::vector<std::string> virtualDirsInRoot;
|
||||||
if (MountWrapper ("slccmpt01", "/dev/slccmpt01", "/vol/storage_slccmpt01") ==
|
if (sSystemFilesAllowed)
|
||||||
MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
{
|
||||||
virtualDirsInRoot.push_back ("slccmpt01");
|
if (MountWrapper ("slccmpt01", "/dev/slccmpt01", "/vol/storage_slccmpt01") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("slccmpt01");
|
||||||
|
IOAbstraction::addVirtualPath ("slccmpt01:/", {});
|
||||||
|
}
|
||||||
|
if (MountWrapper ("storage_odd_tickets", nullptr, "/vol/storage_odd01") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("storage_odd_tickets");
|
||||||
|
IOAbstraction::addVirtualPath ("storage_odd_tickets:/", {});
|
||||||
|
}
|
||||||
|
if (MountWrapper ("storage_odd_updates", nullptr, "/vol/storage_odd02") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("storage_odd_updates");
|
||||||
|
IOAbstraction::addVirtualPath ("storage_odd_updates:/", {});
|
||||||
|
}
|
||||||
|
if (MountWrapper ("storage_odd_content", nullptr, "/vol/storage_odd03") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("storage_odd_content");
|
||||||
|
IOAbstraction::addVirtualPath ("storage_odd_content:/", {});
|
||||||
|
}
|
||||||
|
if (MountWrapper ("storage_odd_content2", nullptr, "/vol/storage_odd04") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("storage_odd_content2");
|
||||||
|
IOAbstraction::addVirtualPath ("storage_odd_content2:/", {});
|
||||||
|
}
|
||||||
|
if (MountWrapper ("storage_slc", "/dev/slc01", "/vol/storage_slc01") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("storage_slc");
|
||||||
|
IOAbstraction::addVirtualPath ("storage_slc:/", {});
|
||||||
|
}
|
||||||
|
if (Mocha_MountFS ("storage_mlc", nullptr, "/vol/storage_mlc01") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("storage_mlc");
|
||||||
|
IOAbstraction::addVirtualPath ("storage_mlc:/", {});
|
||||||
|
}
|
||||||
|
if (Mocha_MountFS ("storage_usb", nullptr, "/vol/storage_usb01") ==
|
||||||
|
MOCHA_RESULT_SUCCESS)
|
||||||
|
{
|
||||||
|
virtualDirsInRoot.emplace_back ("storage_usb");
|
||||||
|
IOAbstraction::addVirtualPath ("storage_usb:/", {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (MountWrapper ("storage_odd_tickets", nullptr, "/vol/storage_odd01") ==
|
virtualDirsInRoot.emplace_back ("fs");
|
||||||
MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
|
||||||
virtualDirsInRoot.push_back ("storage_odd_tickets");
|
|
||||||
}
|
|
||||||
if (MountWrapper ("storage_odd_updates", nullptr, "/vol/storage_odd02") ==
|
|
||||||
MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
|
||||||
virtualDirsInRoot.push_back ("storage_odd_updates");
|
|
||||||
}
|
|
||||||
if (MountWrapper ("storage_odd_content", nullptr, "/vol/storage_odd03") ==
|
|
||||||
MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
|
||||||
virtualDirsInRoot.push_back ("storage_odd_content");
|
|
||||||
}
|
|
||||||
if (MountWrapper ("storage_odd_content2", nullptr, "/vol/storage_odd04") ==
|
|
||||||
MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
|
||||||
virtualDirsInRoot.push_back ("storage_odd_content2");
|
|
||||||
}
|
|
||||||
if (MountWrapper ("storage_slc", "/dev/slc01", "/vol/storage_slc01") ==
|
|
||||||
MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
|
||||||
virtualDirsInRoot.push_back ("storage_slc");
|
|
||||||
}
|
|
||||||
if (Mocha_MountFS ("storage_mlc", nullptr, "/vol/storage_mlc01") == MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
|
||||||
virtualDirsInRoot.push_back ("storage_mlc");
|
|
||||||
}
|
|
||||||
if (Mocha_MountFS ("storage_usb", nullptr, "/vol/storage_usb01") == MOCHA_RESULT_SUCCESS)
|
|
||||||
{
|
|
||||||
virtualDirsInRoot.push_back ("storage_usb");
|
|
||||||
}
|
|
||||||
virtualDirsInRoot.push_back ("fs");
|
|
||||||
IOAbstraction::addVirtualPath (":/", virtualDirsInRoot);
|
IOAbstraction::addVirtualPath (":/", virtualDirsInRoot);
|
||||||
IOAbstraction::addVirtualPath ("fs:/", std::vector<std::string>{"vol"});
|
IOAbstraction::addVirtualPath ("fs:/", std::vector<std::string>{"vol"});
|
||||||
IOAbstraction::addVirtualPath ("fs:/vol", std::vector<std::string>{"external01"});
|
IOAbstraction::addVirtualPath (
|
||||||
IOAbstraction::addVirtualPath ("storage_odd_tickets:/", {});
|
"fs:/vol", std::vector<std::string>{"external01", "content", "save"});
|
||||||
IOAbstraction::addVirtualPath ("storage_odd_updates:/", {});
|
|
||||||
IOAbstraction::addVirtualPath ("storage_odd_content:/", {});
|
IOAbstraction::addVirtualPath ("fs:/vol/content", {});
|
||||||
IOAbstraction::addVirtualPath ("storage_odd_content2:/", {});
|
sMochaPathsWereMounted = true;
|
||||||
IOAbstraction::addVirtualPath ("storage_usb:/", {});
|
|
||||||
}
|
}
|
||||||
else
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +356,7 @@ void platform::render ()
|
|||||||
|
|
||||||
void platform::exit ()
|
void platform::exit ()
|
||||||
{
|
{
|
||||||
|
IOAbstraction::clear ();
|
||||||
WHBProcShutdown ();
|
WHBProcShutdown ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,11 +372,15 @@ public:
|
|||||||
explicit privateData_t (std::function<void ()> &&func_) : thread (std::move (func_))
|
explicit privateData_t (std::function<void ()> &&func_) : thread (std::move (func_))
|
||||||
{
|
{
|
||||||
auto nativeHandle = (OSThread *)thread.native_handle ();
|
auto nativeHandle = (OSThread *)thread.native_handle ();
|
||||||
OSSetThreadName (nativeHandle, "ftpd_server");
|
OSSetThreadName (nativeHandle, "ftpd");
|
||||||
while (!OSSetThreadAffinity (nativeHandle, OS_THREAD_ATTRIB_AFFINITY_CPU2))
|
while (!OSSetThreadAffinity (nativeHandle, OS_THREAD_ATTRIB_AFFINITY_CPU2))
|
||||||
{
|
{
|
||||||
OSSleepTicks (OSMillisecondsToTicks (16));
|
OSSleepTicks (OSMillisecondsToTicks (16));
|
||||||
}
|
}
|
||||||
|
while (!OSSetThreadPriority (nativeHandle, 16))
|
||||||
|
{
|
||||||
|
OSSleepTicks (OSMillisecondsToTicks (16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Underlying thread
|
/// \brief Underlying thread
|
||||||
|
Loading…
Reference in New Issue
Block a user