From fb0a3388c348588ae2ae2f7645595cd981d066ac Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Sat, 31 Jan 2026 07:22:49 +0000 Subject: [PATCH 01/12] split out platform thread implementation --- CMakeLists.txt | 1 + source/common/thread.cpp | 99 ++++++++++++++++++++++++++++++++++++++ source/switch/platform.cpp | 93 ----------------------------------- 3 files changed, 100 insertions(+), 93 deletions(-) create mode 100644 source/common/thread.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f8a523..1f8b3b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ if(NINTENDO_SWITCH) target_sources(${FTPD_TARGET} PRIVATE source/switch/init.c source/switch/platform.cpp + source/common/thread.cpp ) if(FTPD_CLASSIC) diff --git a/source/common/thread.cpp b/source/common/thread.cpp new file mode 100644 index 0000000..70ff51e --- /dev/null +++ b/source/common/thread.cpp @@ -0,0 +1,99 @@ +#include "platform.h" + +#include +#include + + +/////////////////////////////////////////////////////////////////////////// +/// \brief Platform thread pimpl +class platform::Thread::privateData_t +{ +public: + privateData_t () = default; + + /// \brief Parameterized constructor + /// \param func_ Thread entry point + privateData_t (std::function &&func_) : thread (std::move (func_)) + { + } + + /// \brief Underlying thread + std::thread thread; +}; + +/////////////////////////////////////////////////////////////////////////// +platform::Thread::~Thread () = default; + +platform::Thread::Thread () : m_d (new privateData_t ()) +{ +} + +platform::Thread::Thread (std::function &&func_) + : m_d (new privateData_t (std::move (func_))) +{ +} + +platform::Thread::Thread (Thread &&that_) : m_d (new privateData_t ()) +{ + std::swap (m_d, that_.m_d); +} + +platform::Thread &platform::Thread::operator= (Thread &&that_) +{ + std::swap (m_d, that_.m_d); + return *this; +} + +void platform::Thread::join () +{ + m_d->thread.join (); +} + +void platform::Thread::sleep (std::chrono::milliseconds const timeout_) +{ + std::this_thread::sleep_for (timeout_); +} + +/////////////////////////////////////////////////////////////////////////// +#define USE_STD_MUTEX 1 + +/// \brief Platform mutex pimpl +class platform::Mutex::privateData_t +{ +public: +#if USE_STD_MUTEX + /// \brief Underlying mutex + std::mutex mutex; +#else + /// \brief Underlying mutex + ::Mutex mutex; +#endif +}; + +/////////////////////////////////////////////////////////////////////////// +platform::Mutex::~Mutex () = default; + +platform::Mutex::Mutex () : m_d (new privateData_t ()) +{ +#if !USE_STD_MUTEX + mutexInit (&m_d->mutex); +#endif +} + +void platform::Mutex::lock () +{ +#if USE_STD_MUTEX + m_d->mutex.lock (); +#else + mutexLock (&m_d->mutex); +#endif +} + +void platform::Mutex::unlock () +{ +#if USE_STD_MUTEX + m_d->mutex.unlock (); +#else + mutexUnlock (&m_d->mutex); +#endif +} \ No newline at end of file diff --git a/source/switch/platform.cpp b/source/switch/platform.cpp index 4a6a41a..3471fcf 100644 --- a/source/switch/platform.cpp +++ b/source/switch/platform.cpp @@ -922,96 +922,3 @@ void platform::exit () } } -/////////////////////////////////////////////////////////////////////////// -/// \brief Platform thread pimpl -class platform::Thread::privateData_t -{ -public: - privateData_t () = default; - - /// \brief Parameterized constructor - /// \param func_ Thread entry point - privateData_t (std::function &&func_) : thread (std::move (func_)) - { - } - - /// \brief Underlying thread - std::thread thread; -}; - -/////////////////////////////////////////////////////////////////////////// -platform::Thread::~Thread () = default; - -platform::Thread::Thread () : m_d (new privateData_t ()) -{ -} - -platform::Thread::Thread (std::function &&func_) - : m_d (new privateData_t (std::move (func_))) -{ -} - -platform::Thread::Thread (Thread &&that_) : m_d (new privateData_t ()) -{ - std::swap (m_d, that_.m_d); -} - -platform::Thread &platform::Thread::operator= (Thread &&that_) -{ - std::swap (m_d, that_.m_d); - return *this; -} - -void platform::Thread::join () -{ - m_d->thread.join (); -} - -void platform::Thread::sleep (std::chrono::milliseconds const timeout_) -{ - std::this_thread::sleep_for (timeout_); -} - -/////////////////////////////////////////////////////////////////////////// -#define USE_STD_MUTEX 1 - -/// \brief Platform mutex pimpl -class platform::Mutex::privateData_t -{ -public: -#if USE_STD_MUTEX - /// \brief Underlying mutex - std::mutex mutex; -#else - /// \brief Underlying mutex - ::Mutex mutex; -#endif -}; - -/////////////////////////////////////////////////////////////////////////// -platform::Mutex::~Mutex () = default; - -platform::Mutex::Mutex () : m_d (new privateData_t ()) -{ -#if !USE_STD_MUTEX - mutexInit (&m_d->mutex); -#endif -} - -void platform::Mutex::lock () -{ -#if USE_STD_MUTEX - m_d->mutex.lock (); -#else - mutexLock (&m_d->mutex); -#endif -} - -void platform::Mutex::unlock () -{ -#if USE_STD_MUTEX - m_d->mutex.unlock (); -#else - mutexUnlock (&m_d->mutex); -#endif -} From 8eb8c326262507cf0c16519d2b4356b7ec849f17 Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Fri, 30 Jan 2026 23:39:07 +0000 Subject: [PATCH 02/12] move log print behind mutex --- source/log.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/source/log.cpp b/source/log.cpp index 63d85a4..96a5c97 100644 --- a/source/log.cpp +++ b/source/log.cpp @@ -243,16 +243,19 @@ void addLog (LogLevel const level_, char const *const fmt_, va_list ap_) return; #endif -#ifndef __NDS__ - thread_local +#if HAVE_MUTEX + auto const lock = std::scoped_lock (s_lock); #endif - static char buffer[1024]; - - std::vsnprintf (buffer, sizeof (buffer), fmt_, ap_); #ifndef __NDS__ auto const lock = std::scoped_lock (s_lock); + #define BUFFER_SIZE 1024 +#else + #define BUFFER_SIZE 256 #endif + static char buffer[BUFFER_SIZE]; + std::vsnprintf (buffer, sizeof (buffer), fmt_, ap_); + #ifndef NDEBUG // std::fprintf (stderr, "%s", s_prefix[level_]); // std::fputs (buffer, stderr); From f39c7ae12ff24c6820be74793b40d8cc0ea90df7 Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Mon, 19 Feb 2024 17:10:35 +0000 Subject: [PATCH 03/12] build for wii & cube --- CMakeLists.txt | 29 +++++++++- include/ftpConfig.h | 6 +- include/ftpServer.h | 2 +- include/ftpSession.h | 8 ++- include/platform.h | 10 +++- source/fs.cpp | 2 +- source/ftpConfig.cpp | 3 +- source/ftpServer.cpp | 49 +++++++++-------- source/ftpSession.cpp | 43 +++++++++------ source/licenses.cpp | 2 +- source/log.cpp | 28 +++++----- source/ogc/platform.cpp | 119 ++++++++++++++++++++++++++++++++++++++++ source/sockAddr.cpp | 6 +- source/socket.cpp | 6 +- 14 files changed, 239 insertions(+), 74 deletions(-) create mode 100644 source/ogc/platform.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f8b3b4..c6a26f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,17 +86,23 @@ if(IPO_SUPPORTED) ) endif() -if(FTPD_CLASSIC OR NINTENDO_DS) +if(FTPD_CLASSIC OR NINTENDO_DS OR NINTENDO_WII OR NINTENDO_GAMECUBE) target_compile_definitions(${FTPD_TARGET} PRIVATE CLASSIC) endif() -if(NINTENDO_SWITCH OR NINTENDO_3DS OR NINTENDO_DS) +if(NINTENDO_SWITCH OR NINTENDO_3DS OR NINTENDO_DS OR NINTENDO_WII OR NINTENDO_GAMECUBE) target_compile_definitions(${FTPD_TARGET} PRIVATE NO_IPV6 FTPDCONFIG="/config/${PROJECT_NAME}/${PROJECT_NAME}.cfg" ) endif() +if(NINTENDO_WII OR NINTENDO_GAMECUBE OR NINTENDO_DS) + target_compile_definitions(${FTPD_TARGET} PRIVATE HAVE_MUTEX=1 HAVE_TLS=0) +else() + target_compile_definitions(${FTPD_TARGET} PRIVATE HAVE_MUTEX=1 HAVE_TLS=1) +endif() + target_sources(${FTPD_TARGET} PRIVATE include/fs.h include/ftpConfig.h @@ -371,6 +377,25 @@ elseif(NINTENDO_DS) SUBTITLE2 "(c) 2025 mtheall" ICON nds/icon.bmp ) +elseif(NINTENDO_WII OR NINTENDO_GAMECUBE) + + target_link_libraries(${FTPD_TARGET} PRIVATE + fat + ) + + if(NINTENDO_GAMECUBE) + target_link_libraries(${FTPD_TARGET} PRIVATE + bba + ) + endif() + + target_sources(${FTPD_TARGET} PRIVATE + source/ogc/platform.cpp + source/common/thread.cpp + ) + + ogc_create_dol(${PROJECT_NAME}) + else() find_package(PkgConfig REQUIRED) find_package(glfw3 REQUIRED) diff --git a/include/ftpConfig.h b/include/ftpConfig.h index d92d4c2..b0c3305 100644 --- a/include/ftpConfig.h +++ b/include/ftpConfig.h @@ -48,10 +48,9 @@ class FtpConfig /// \param path_ Path to config file static UniqueFtpConfig load (gsl::not_null path_); -#ifndef __NDS__ +#if HAVE_MUTEX std::scoped_lock lockGuard (); #endif - /// \brief Save config /// \param path_ Path to config file bool save (gsl::not_null path_); @@ -139,11 +138,10 @@ class FtpConfig private: FtpConfig (); -#ifndef __NDS__ +#if HAVE_MUTEX /// \brief Mutex mutable platform::Mutex m_lock; #endif - /// \brief Username std::string m_user; diff --git a/include/ftpServer.h b/include/ftpServer.h index bf980dc..215073a 100644 --- a/include/ftpServer.h +++ b/include/ftpServer.h @@ -98,7 +98,7 @@ class FtpServer /// \brief Thread entry point void threadFunc (); -#ifndef __NDS__ +#if HAVE_MUTEX /// \brief Thread platform::Thread m_thread; diff --git a/include/ftpSession.h b/include/ftpSession.h index 7d4fa62..be8ef89 100644 --- a/include/ftpSession.h +++ b/include/ftpSession.h @@ -106,6 +106,11 @@ class FtpSession /// \brief Socket buffer size constexpr static auto SOCK_BUFFERSIZE = 32768; + /// \brief Amount of file position history to keep + constexpr static auto POSITION_HISTORY = 100; +#elif defined(__wii__) || defined(__gamecube__) + /// \brief Socket buffer size + constexpr static auto SOCK_BUFFERSIZE = 16384; /// \brief Amount of file position history to keep constexpr static auto POSITION_HISTORY = 100; #else @@ -246,11 +251,10 @@ class FtpSession /// \brief Transfer upload bool storeTransfer (); -#ifndef __NDS__ +#if HAVE_MUTEX /// \brief Mutex platform::Mutex m_lock; #endif - /// \brief FTP config FtpConfig &m_config; diff --git a/include/platform.h b/include/platform.h index a265136..b269ce8 100644 --- a/include/platform.h +++ b/include/platform.h @@ -32,13 +32,19 @@ #include #endif +#if defined (__wii__) || defined(__gamecube__) +#include +#include +#include +#endif + #include #include #include #include #include -#ifdef CLASSIC +#if defined(CLASSIC) extern PrintConsole g_statusConsole; extern PrintConsole g_logConsole; extern PrintConsole g_sessionConsole; @@ -113,7 +119,7 @@ struct steady_clock using steady_clock = std::chrono::steady_clock; #endif -#ifndef __NDS__ +#if HAVE_MUTEX /// \brief Platform thread class Thread { diff --git a/source/fs.cpp b/source/fs.cpp index fc69c93..5b4fca1 100644 --- a/source/fs.cpp +++ b/source/fs.cpp @@ -40,7 +40,7 @@ #include #include -#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__) +#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__) || defined(__wii__) || defined (__gamecube__) #define getline __getline #endif diff --git a/source/ftpConfig.cpp b/source/ftpConfig.cpp index d2a45f6..c246bac 100644 --- a/source/ftpConfig.cpp +++ b/source/ftpConfig.cpp @@ -43,6 +43,7 @@ using stat_t = struct stat; #include #include #include +#include namespace { @@ -196,7 +197,7 @@ UniqueFtpConfig FtpConfig::load (gsl::not_null const path_) return config; } -#ifndef __NDS__ +#if HAVE_MUTEX std::scoped_lock FtpConfig::lockGuard () { return std::scoped_lock (m_lock); diff --git a/source/ftpServer.cpp b/source/ftpServer.cpp index 531bc1e..4799b5d 100644 --- a/source/ftpServer.cpp +++ b/source/ftpServer.cpp @@ -75,15 +75,15 @@ using statvfs_t = struct statvfs; #include using namespace std::chrono_literals; -#ifdef __NDS__ -#define LOCKED(x) x -#else +#if HAVE_MUTEX #define LOCKED(x) \ do \ { \ auto const lock = std::scoped_lock (m_lock); \ x; \ } while (0) +#else +#define LOCKED(x) x #endif namespace @@ -96,11 +96,10 @@ auto const s_startTime = std::time (nullptr); int s_tzOffset = 0; #endif -#ifndef __NDS__ +#if HAVE_MUTEX /// \brief Mutex for s_freeSpace platform::Mutex s_lock; #endif - /// \brief Free space string std::string s_freeSpace; @@ -210,7 +209,7 @@ FtpServer::~FtpServer () { m_quit = true; -#ifndef __NDS__ +#if HAVE_MUTEX m_thread.join (); #endif @@ -234,9 +233,12 @@ FtpServer::FtpServer (UniqueFtpConfig config_) m_hostnameSetting (m_config->hostname ()) #endif { + #ifndef __NDS__ mdns::setHostname (m_config->hostname ()); +#endif +#if HAVE_MUTEX m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this)); #endif @@ -249,14 +251,15 @@ FtpServer::FtpServer (UniqueFtpConfig config_) void FtpServer::draw () { -#ifdef __NDS__ - loop (); +#if HAVE_MUTEX +#else + loop(); #endif #ifdef CLASSIC { char port[7]; -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (m_lock); #endif if (m_socket) @@ -281,7 +284,7 @@ void FtpServer::draw () } { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (s_lock); #endif if (!s_freeSpace.empty ()) @@ -295,7 +298,7 @@ void FtpServer::draw () } { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (m_lock); #endif consoleSelect (&g_sessionConsole); @@ -397,7 +400,7 @@ UniqueFtpServer FtpServer::create () std::string FtpServer::getFreeSpace () { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (s_lock); #endif return s_freeSpace; @@ -415,7 +418,7 @@ void FtpServer::updateFreeSpace () auto freeSpace = fs::printSize (static_cast (st.f_bsize) * st.f_bfree); -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (s_lock); #endif if (freeSpace != s_freeSpace) @@ -443,7 +446,7 @@ void FtpServer::handleNetworkFound () std::uint16_t port; { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config->lockGuard (); #endif port = m_config->port (); @@ -525,7 +528,7 @@ void FtpServer::showMenu () if (ImGui::MenuItem ("Upload Log")) { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (m_lock); #endif if (!m_uploadLogCurlM) @@ -589,10 +592,10 @@ void FtpServer::showMenu () { if (!prevShowSettings) { -#ifndef __NDS__ + +#if HAVE_MUTEX auto const lock = m_config->lockGuard (); #endif - m_userSetting = m_config->user (); m_userSetting.resize (32); @@ -735,10 +738,9 @@ void FtpServer::showSettings () m_showSettings = false; ImGui::CloseCurrentPopup (); -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config->lockGuard (); #endif - m_config->setUser (m_userSetting); m_config->setPass (m_passSetting); m_config->setHostname (m_hostnameSetting); @@ -764,7 +766,7 @@ void FtpServer::showSettings () if (save) { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config->lockGuard (); #endif if (!m_config->save (FTPDCONFIG)) @@ -852,7 +854,7 @@ void FtpServer::showAbout () ImGui::TreePop (); } -#if defined(__NDS__) +#if defined(__NDS__) || defined(__wii__) || defined(__gamecube__) #elif defined(__3DS__) if (ImGui::TreeNode (g_libctruVersion)) { @@ -1019,6 +1021,7 @@ void FtpServer::loop () if (rc > 0 && (info.revents & POLLIN)) { + printf("POLLIN on listen socket\n"); auto socket = m_socket->accept (); if (socket) { @@ -1043,7 +1046,7 @@ void FtpServer::loop () std::vector deadSessions; { // remove dead sessions -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (m_lock); #endif auto it = std::begin (m_sessions); @@ -1067,7 +1070,7 @@ void FtpServer::loop () if (!FtpSession::poll (m_sessions)) handleNetworkLost (); } -#ifndef __NDS__ +#if HAVE_MUTEX // avoid busy polling in background thread else platform::Thread::sleep (16ms); diff --git a/source/ftpSession.cpp b/source/ftpSession.cpp index 0070428..9160870 100644 --- a/source/ftpSession.cpp +++ b/source/ftpSession.cpp @@ -55,15 +55,15 @@ using namespace std::chrono_literals; #define lstat stat #endif -#ifdef __NDS__ -#define LOCKED(x) x -#else +#if HAVE_MUTEX #define LOCKED(x) \ do \ { \ auto const lock = std::scoped_lock (m_lock); \ x; \ } while (0) +#else +#define LOCKED(x) x #endif namespace @@ -400,7 +400,7 @@ FtpSession::FtpSession (FtpConfig &config_, UniqueSocket commandSocket_) m_devZero (false) { { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif if (m_config.user ().empty ()) @@ -423,7 +423,7 @@ FtpSession::FtpSession (FtpConfig &config_, UniqueSocket commandSocket_) bool FtpSession::dead () { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (m_lock); #endif if (m_commandSocket || m_pasvSocket || m_dataSocket) @@ -434,7 +434,7 @@ bool FtpSession::dead () void FtpSession::draw () { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (m_lock); #endif @@ -784,10 +784,9 @@ void FtpSession::setState (State const state_, bool const closePasv_, bool const if (state_ == State::COMMAND) { { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (m_lock); #endif - m_restartPosition = 0; m_fileSize = 0; m_filePosition = 0; @@ -1008,7 +1007,7 @@ int FtpSession::fillDirent (stat_t const &st_, std::string_view const path_, cha type_ = "file"; else if (S_ISDIR (st_.st_mode)) type_ = "dir"; -#if !defined(__3DS__) && !defined(__SWITCH__) +#if !defined(__3DS__) && !defined(__SWITCH__) && !defined (__wii__) && !defined (__gamecube__) else if (S_ISLNK (st_.st_mode)) type_ = "os.unix=symlink"; else if (S_ISCHR (st_.st_mode)) @@ -1191,6 +1190,12 @@ int FtpSession::fillDirent (stat_t const &st_, std::string_view const path_, cha #elif defined(__SWITCH__) auto const owner = "Switch"; auto const group = "Switch"; +#elif defined (__gamecube__) + auto const owner = "gamecube"; + auto const group = "gamecube"; +#elif defined (__wii__) + auto const owner = "wii"; + auto const group = "wii"; #else char owner[32]; char group[32]; @@ -1645,7 +1650,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool void FtpSession::readCommand (int const events_) { -#ifndef __NDS__ +#if !defined(__NDS__) && !defined (__wii__) && !defined(__gamecube__) // check out-of-band data if (events_ & POLLPRI) { @@ -2791,7 +2796,7 @@ void FtpSession::PASS (char const *args_) std::string pass; { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif user = m_config.user (); @@ -3194,7 +3199,7 @@ void FtpSession::SITE (char const *args_) if (compare (command, "USER") == 0) { { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif m_config.setUser (std::string (arg)); @@ -3206,7 +3211,7 @@ void FtpSession::SITE (char const *args_) else if (compare (command, "PASS") == 0) { { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif m_config.setPass (std::string (arg)); @@ -3220,7 +3225,7 @@ void FtpSession::SITE (char const *args_) bool error = false; { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif error = !m_config.setPort (arg); @@ -3250,7 +3255,9 @@ void FtpSession::SITE (char const *args_) else if (compare (command, "HOST") == 0) { { +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); +#endif m_config.setHostname (std::string (arg)); mdns::setHostname (std::string (arg)); } @@ -3262,14 +3269,14 @@ void FtpSession::SITE (char const *args_) { if (arg == "0") { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif m_config.setGetMTime (false); } else if (arg == "1") { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif m_config.setGetMTime (true); @@ -3286,7 +3293,7 @@ void FtpSession::SITE (char const *args_) bool error; { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif error = !m_config.save (FTPDCONFIG); @@ -3451,7 +3458,7 @@ void FtpSession::USER (char const *args_) std::string pass; { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = m_config.lockGuard (); #endif user = m_config.user (); diff --git a/source/licenses.cpp b/source/licenses.cpp index c8aecbf..2f298b0 100644 --- a/source/licenses.cpp +++ b/source/licenses.cpp @@ -28,7 +28,7 @@ #include #endif -#if !defined(__NDS__) && !defined(__3DS__) && !defined(__SWITCH__) +#if !defined(__NDS__) && !defined(__3DS__) && !defined(__SWITCH__) && !defined (__wii__) && !defined(__gamecube__) #include #endif diff --git a/source/log.cpp b/source/log.cpp index 96a5c97..362884a 100644 --- a/source/log.cpp +++ b/source/log.cpp @@ -75,7 +75,7 @@ struct Message /// \brief Log messages std::vector s_messages; -#ifndef __NDS__ +#if HAVE_MUTEX /// \brief Log lock platform::Mutex s_lock; #endif @@ -83,7 +83,7 @@ platform::Mutex s_lock; void drawLog () { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (s_lock); #endif @@ -157,10 +157,9 @@ void drawLog () #ifndef CLASSIC std::string getLog () { -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (s_lock); #endif - if (s_messages.empty ()) return {}; @@ -243,22 +242,22 @@ void addLog (LogLevel const level_, char const *const fmt_, va_list ap_) return; #endif -#if HAVE_MUTEX - auto const lock = std::scoped_lock (s_lock); -#endif - #ifndef __NDS__ - auto const lock = std::scoped_lock (s_lock); #define BUFFER_SIZE 1024 #else #define BUFFER_SIZE 256 #endif + + +#if HAVE_MUTEX + auto const lock = std::scoped_lock (s_lock); +#endif static char buffer[BUFFER_SIZE]; std::vsnprintf (buffer, sizeof (buffer), fmt_, ap_); #ifndef NDEBUG - // std::fprintf (stderr, "%s", s_prefix[level_]); - // std::fputs (buffer, stderr); + std::fprintf (stderr, "%s", s_prefix[level_]); + std::fputs (buffer, stderr); #endif s_messages.emplace_back (level_, buffer); #ifdef CLASSIC @@ -281,12 +280,13 @@ void addLog (LogLevel const level_, std::string_view const message_) c = '?'; } -#ifndef __NDS__ +#if HAVE_MUTEX auto const lock = std::scoped_lock (s_lock); #endif + #ifndef NDEBUG - // std::fprintf (stderr, "%s", s_prefix[level_]); - // std::fwrite (msg.data (), 1, msg.size (), stderr); + std::fprintf (stderr, "%s", s_prefix[level_]); + std::fwrite (msg.data (), 1, msg.size (), stderr); #endif s_messages.emplace_back (level_, msg); #ifdef CLASSIC diff --git a/source/ogc/platform.cpp b/source/ogc/platform.cpp new file mode 100644 index 0000000..e5aadc7 --- /dev/null +++ b/source/ogc/platform.cpp @@ -0,0 +1,119 @@ +// ftpd is a server implementation based on the following: +// - RFC 959 (https://tools.ietf.org/html/rfc959) +// - RFC 3659 (https://tools.ietf.org/html/rfc3659) +// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html +// +// Copyright (C) 2020 Michael Theall +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#ifndef CLASSIC +#error "Wii and GameCube must be built in classic mode" +#endif + +#include "platform.h" + +#include "log.h" + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include + + +PrintConsole g_statusConsole; +PrintConsole g_logConsole; +PrintConsole g_sessionConsole; + + +static bool networkUp = false; + +namespace +{ + +static struct in_addr loc_ip, loc_netmask, loc_gateway; + +} + + +bool platform::networkVisible () +{ + return networkUp; +} + +bool platform::networkAddress (SockAddr &addr_) +{ + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr = loc_ip; + + addr_ = addr; + return true; +} + +bool platform::init () +{ + fatInitDefault (); + + VIDEO_Init(); + PAD_Init(); + + consoleInit (&g_statusConsole); + consoleInit (&g_logConsole); + consoleInit (&g_sessionConsole); + + consoleSetWindow (&g_statusConsole, 1, 1, 80, 1); + consoleSetWindow (&g_logConsole, 1, 2, 80, 21); + consoleSetWindow (&g_sessionConsole, 1, 23, 80, 8); + consoleSelect(&g_sessionConsole); + printf(CONSOLE_ESC(46;1m) CONSOLE_ESC(2J)); + + int ret = if_configex ( &loc_ip, &loc_netmask, &loc_gateway, TRUE, 20); + if (ret>=0) { + networkUp = true; + } + + return true; +} + +bool platform::loop () +{ + return SYS_MainLoop(); +} + +void platform::render () +{ + VIDEO_WaitVSync(); +} + +void platform::exit () +{ +} + +std::string const &platform::hostname () +{ + static std::string const hostname = "wii-ftpd"; + return hostname; +} diff --git a/source/sockAddr.cpp b/source/sockAddr.cpp index 52f435e..2244ff6 100644 --- a/source/sockAddr.cpp +++ b/source/sockAddr.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef __3DS__ static_assert (sizeof (sockaddr_storage) == 0x1c); @@ -345,6 +346,7 @@ socklen_t SockAddr::size () const char const *SockAddr::name (char *buffer_, std::size_t size_) const { + printf("SockAddr::name, m_addr.ss_family=%d, m_addr.sin_addr=%s\n", m_addr.ss_family, inet_ntoa (reinterpret_cast (&m_addr)->sin_addr)); switch (m_addr.ss_family) { case AF_INET: @@ -370,8 +372,8 @@ char const *SockAddr::name (char *buffer_, std::size_t size_) const char const *SockAddr::name () const { -#ifdef __NDS__ - return inet_ntoa (reinterpret_cast (&m_addr)->sin_addr); +#if defined(__NDS__) || defined (__wii__) || defined (__gamecube__) + return inet_ntoa (reinterpret_cast (&m_addr)->sin_addr); #else #ifdef NO_IPV6 thread_local static char buffer[INET_ADDRSTRLEN]; diff --git a/source/socket.cpp b/source/socket.cpp index ba340b1..391f2f4 100644 --- a/source/socket.cpp +++ b/source/socket.cpp @@ -83,7 +83,7 @@ UniqueSocket Socket::accept () int Socket::atMark () { -#ifdef __NDS__ +#if defined(__NDS__) || defined(__wii__) || defined(__gamecube__) errno = ENOSYS; return -1; #else @@ -163,7 +163,7 @@ bool Socket::shutdown (int const how_) bool Socket::setLinger (bool const enable_, std::chrono::seconds const time_) { -#ifdef __NDS__ +#if defined(__NDS__) || defined(__wii__) || defined(__gamecube__) (void)enable_; (void)time_; errno = ENOSYS; @@ -195,7 +195,7 @@ bool Socket::setNonBlocking (bool const nonBlocking_) auto const rc = ::ioctl (m_fd, FIONBIO, &enable); if (rc != 0) { - error ("fcntl(FIONBIO, %d): %s\n", nonBlocking_, std::strerror (errno)); + error ("iocntl(FIONBIO, %d): %s\n", nonBlocking_, std::strerror (errno)); return false; } #else From 99a21928b36ae4f75072d5537d040e6644ecb4d6 Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Sat, 26 Oct 2024 19:33:08 +0100 Subject: [PATCH 04/12] set classic mode for ds, wii & gamecube --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6a26f6..bc1a585 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,10 @@ find_package(ZLIB REQUIRED) option(FTPD_CLASSIC "Build ${PROJECT_NAME} classic" OFF) +if(NINTENDO_DS OR NINTENDO_WII OR NINTENDO_GAMECUBE) + set(FTPD_CLASSIC ON) +endif() + if(FTPD_CLASSIC AND (NINTENDO_SWITCH OR NINTENDO_3DS)) set(FTPD_TARGET "${PROJECT_NAME}-classic") else() @@ -86,7 +90,7 @@ if(IPO_SUPPORTED) ) endif() -if(FTPD_CLASSIC OR NINTENDO_DS OR NINTENDO_WII OR NINTENDO_GAMECUBE) +if(FTPD_CLASSIC) target_compile_definitions(${FTPD_TARGET} PRIVATE CLASSIC) endif() From 7b6a999db44df08054661143ae7637f7b252b5fe Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Sat, 26 Oct 2024 19:36:01 +0100 Subject: [PATCH 05/12] reduce buffer sizes for wii/cube --- include/ftpSession.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/ftpSession.h b/include/ftpSession.h index be8ef89..54aa33d 100644 --- a/include/ftpSession.h +++ b/include/ftpSession.h @@ -85,6 +85,12 @@ class FtpSession /// \brief Transfer buffersize constexpr static auto XFER_BUFFERSIZE = 8192; +#elif defined (__wii__) || defined (__gamecube__) + /// \brief Response buffer size + constexpr static auto RESPONSE_BUFFERSIZE = 8192; + + /// \brief Transfer buffersize + constexpr static auto XFER_BUFFERSIZE = 16384; #else /// \brief Response buffer size constexpr static auto RESPONSE_BUFFERSIZE = 32768; From 9af7cb7e2ebd92c8771d5f77fe0c21268280603d Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Mon, 19 Jan 2026 17:50:13 +0000 Subject: [PATCH 06/12] use wiimote on wii --- source/ogc/platform.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/ogc/platform.cpp b/source/ogc/platform.cpp index e5aadc7..769eea5 100644 --- a/source/ogc/platform.cpp +++ b/source/ogc/platform.cpp @@ -37,12 +37,15 @@ #include #include +#ifdef __wii__ +#include +#endif + #include #include #include - PrintConsole g_statusConsole; PrintConsole g_logConsole; PrintConsole g_sessionConsole; @@ -78,8 +81,9 @@ bool platform::init () fatInitDefault (); VIDEO_Init(); - PAD_Init(); - +#ifdef __wii__ + WPAD_Init(); +#endif consoleInit (&g_statusConsole); consoleInit (&g_logConsole); consoleInit (&g_sessionConsole); From d6894468a9d0b525b53ffa3450efd2c2d5cb59e9 Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Wed, 21 Jan 2026 17:40:32 +0000 Subject: [PATCH 07/12] don't close connection on POLLHUP --- source/ftpSession.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/ftpSession.cpp b/source/ftpSession.cpp index 9160870..5d06cc2 100644 --- a/source/ftpSession.cpp +++ b/source/ftpSession.cpp @@ -693,7 +693,7 @@ bool FtpSession::poll (std::vector const &sessions_) if (i.revents & (POLLIN | POLLPRI)) session->readCommand (i.revents); - if (i.revents & (POLLERR | POLLHUP)) + if (i.revents & POLLERR) session->closeCommand (); } @@ -711,7 +711,7 @@ bool FtpSession::poll (std::vector const &sessions_) if (i.revents & ~(POLLIN | POLLPRI | POLLOUT)) debug ("Data revents 0x%X\n", i.revents); - if (i.revents & (POLLERR | POLLHUP)) + if (i.revents & POLLERR) { session->sendResponse ("426 Data connection failed\r\n"); session->setState (State::COMMAND, true, true); @@ -737,7 +737,7 @@ bool FtpSession::poll (std::vector const &sessions_) debug ("Data revents 0x%X\n", i.revents); // we need to transfer data - if (i.revents & (POLLERR | POLLHUP)) + if (i.revents & POLLERR) { session->sendResponse ("426 Data connection failed\r\n"); session->setState (State::COMMAND, true, true); From 38a2ba4445df5c01c527421b198178b51475e5ad Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Sat, 24 Jan 2026 14:38:28 +0000 Subject: [PATCH 08/12] implement threading on nds --- CMakeLists.txt | 1 + source/nds/platform.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc1a585..6cc0686 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -373,6 +373,7 @@ elseif(NINTENDO_DS) target_sources(${FTPD_TARGET} PRIVATE source/nds/platform.cpp + source/common/thread.cpp ) nds_create_rom(${FTPD_TARGET} diff --git a/source/nds/platform.cpp b/source/nds/platform.cpp index 99314c4..cdb596c 100644 --- a/source/nds/platform.cpp +++ b/source/nds/platform.cpp @@ -30,6 +30,8 @@ #include #include +#include +#include #ifndef CLASSIC #error "NDS must be built in classic mode" @@ -168,3 +170,4 @@ void platform::exit () { powerOn (POWER_LCD); } + From 09591967ebd574db55ed51b0b52305e090f7720f Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Tue, 27 Jan 2026 04:32:51 +0000 Subject: [PATCH 09/12] exit when home pressed on wiimote --- source/ogc/platform.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/ogc/platform.cpp b/source/ogc/platform.cpp index 769eea5..fbd9222 100644 --- a/source/ogc/platform.cpp +++ b/source/ogc/platform.cpp @@ -104,6 +104,13 @@ bool platform::init () bool platform::loop () { +#ifdef __wii__ + WPAD_ScanPads(); + + u32 pressed = WPAD_ButtonsDown(0); + + if ( pressed & WPAD_BUTTON_HOME ) return false; +#endif return SYS_MainLoop(); } From d26ee36fc0dcf9fd5bc8da470e2f6f57cd303d71 Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Fri, 30 Jan 2026 19:22:03 +0000 Subject: [PATCH 10/12] make thread_local optional --- source/sockAddr.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/sockAddr.cpp b/source/sockAddr.cpp index 2244ff6..c5d4691 100644 --- a/source/sockAddr.cpp +++ b/source/sockAddr.cpp @@ -375,10 +375,13 @@ char const *SockAddr::name () const #if defined(__NDS__) || defined (__wii__) || defined (__gamecube__) return inet_ntoa (reinterpret_cast (&m_addr)->sin_addr); #else +#if HAVE_TLS +thread_local +#endif #ifdef NO_IPV6 - thread_local static char buffer[INET_ADDRSTRLEN]; + static char buffer[INET_ADDRSTRLEN]; #else - thread_local static char buffer[INET6_ADDRSTRLEN]; + static char buffer[INET6_ADDRSTRLEN]; #endif return name (buffer, sizeof (buffer)); From 5cf8f4e336f5e7a4687feda0280bac4f61952a4f Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Fri, 30 Jan 2026 23:38:07 +0000 Subject: [PATCH 11/12] wii/cube have inet_pton/inet_ptoa --- source/sockAddr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/sockAddr.cpp b/source/sockAddr.cpp index c5d4691..4fd40f4 100644 --- a/source/sockAddr.cpp +++ b/source/sockAddr.cpp @@ -372,7 +372,7 @@ char const *SockAddr::name (char *buffer_, std::size_t size_) const char const *SockAddr::name () const { -#if defined(__NDS__) || defined (__wii__) || defined (__gamecube__) +#if defined(__NDS__) return inet_ntoa (reinterpret_cast (&m_addr)->sin_addr); #else #if HAVE_TLS From c042a52b7e31b763ddd94c6bc9db5b4337996139 Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Sun, 1 Mar 2026 17:40:52 +0000 Subject: [PATCH 12/12] remove debug printfs --- source/ftpServer.cpp | 1 - source/sockAddr.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/source/ftpServer.cpp b/source/ftpServer.cpp index 4799b5d..415d974 100644 --- a/source/ftpServer.cpp +++ b/source/ftpServer.cpp @@ -1021,7 +1021,6 @@ void FtpServer::loop () if (rc > 0 && (info.revents & POLLIN)) { - printf("POLLIN on listen socket\n"); auto socket = m_socket->accept (); if (socket) { diff --git a/source/sockAddr.cpp b/source/sockAddr.cpp index 4fd40f4..657bb75 100644 --- a/source/sockAddr.cpp +++ b/source/sockAddr.cpp @@ -346,7 +346,6 @@ socklen_t SockAddr::size () const char const *SockAddr::name (char *buffer_, std::size_t size_) const { - printf("SockAddr::name, m_addr.ss_family=%d, m_addr.sin_addr=%s\n", m_addr.ss_family, inet_ntoa (reinterpret_cast (&m_addr)->sin_addr)); switch (m_addr.ss_family) { case AF_INET: