diff --git a/.gitignore b/.gitignore index e863b80..d6b7354 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -build/* +/lib +/release +/libcontrollerpatcher-1.0.0.tar.bz2 libcontrollerpatcher.cbp libcontrollerpatcher.depend \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index aaf4c70..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -cmake_minimum_required(VERSION 3.2) -project(controllerpatcherwut) -include("${WUT_ROOT}/share/wut.cmake" REQUIRED) - -file(GLOB_RECURSE SOURCE_FILES *.c *.cpp) -file(GLOB_RECURSE HEADER_FILES *.h) - -add_library(controllerpatcherwut STATIC ${SOURCE_FILES} ${HEADER_FILES}) - -target_link_libraries(controllerpatcherwut - utilswut) - -target_include_directories(controllerpatcherwut PUBLIC "include") -target_include_directories(controllerpatcherwut PRIVATE "src") - - -include_directories("${WUT_ROOT}/include/libutilswut" REQUIRED) - -wut_enable_stdcpp(controllerpatcherwut) -wut_default_malloc(controllerpatcherwut) - -target_include_directories(controllerpatcherwut PUBLIC "include") -target_compile_options(controllerpatcherwut PUBLIC "-D__LOGGING__") - - -install(TARGETS controllerpatcherwut - ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib") -install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ - DESTINATION "${CMAKE_INSTALL_PREFIX}/include" - FILES_MATCHING PATTERN "*.h*") \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1332e88 --- /dev/null +++ b/Makefile @@ -0,0 +1,171 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wut/share/wut_rules + +export VER_MAJOR := 1 +export VER_MINOR := 0 +export VER_PATCH := 0 + +VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) + +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# 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 +#------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source \ + source/config \ + source/fs \ + source/network \ + source/patcher \ + source/system \ + source/utils +DATA := data +INCLUDES := source \ + include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS := -Wall -save-temps \ + -ffunction-sections -fdata-sections \ + $(MACHDEP) \ + $(BUILD_CFLAGS) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__LOGGING__ + +CXXFLAGS := $(CFLAGS) -std=gnu++17 + +ASFLAGS := $(MACHDEP) + +LDFLAGS = $(ARCH) -Wl,--gc-sections + + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +DEFFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.def))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(DEFFILES:.def=.o) $(SFILES:.s=.o) $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) + +.PHONY: all dist-bin dist-src dist install clean + +#--------------------------------------------------------------------------------- +all: lib/libcontrollerpatcher.a + +dist-bin: all + @tar --exclude=*~ -cjf libcontrollerpatcher-$(VERSION).tar.bz2 include lib + +dist-src: + @tar --exclude=*~ -cjf libcontrollerpatcher-src-$(VERSION).tar.bz2 include source Makefile + +dist: dist-src dist-bin + +install: dist-bin + mkdir -p $(WUT_ROOT)/usr + bzip2 -cd libcontrollerpatcher-$(VERSION).tar.bz2 | tar -xf - -C $(WUT_ROOT)/usr + +lib: + @[ -d $@ ] || mkdir -p $@ + +share: + @[ -d $@ ] || mkdir -p $@ + +release: + @[ -d $@ ] || mkdir -p $@ + +lib/libcontrollerpatcher.a :$(SOURCES) $(INCLUDES) | lib release + @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ + BUILD_CFLAGS="-DNDEBUG=1 -O2 -s" \ + DEPSDIR=$(CURDIR)/release \ + --no-print-directory -C release \ + -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -rf release lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES_SRC) : $(HFILES) + +#--------------------------------------------------------------------------------- +%.o: %.def + $(SILENTMSG) $(notdir $<) + $(SILENTCMD)rplimportgen $< $*.s $*.ld $(ERROR_FILTER) + $(SILENTCMD)$(CC) -x assembler-with-cpp $(ASFLAGS) -c $*.s -o $@ $(ERROR_FILTER) + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/README.md b/README.md index c5b3e04..4c3c2a6 100644 --- a/README.md +++ b/README.md @@ -18,25 +18,22 @@ You need to install all dependencies first! Install this static library into your wut folder via: ``` -mkdir build && cd build -cmake -DCMAKE_TOOLCHAIN_FILE=$WUT_ROOT/share/wut.toolchain.cmake -DCMAKE_INSTALL_PREFIX=$WUT_ROOT ../ make install ``` Link the application with ``` --lutilswut -lcontrollerpatcherwut +-lcontrollerpatcher ``` You also need to add the include path to your Makefile. Example: ``` -export INCLUDE := [...] -I$(WUT_ROOT)/include +export INCLUDE := [...] -I$(WUT_ROOT)/usr/include ``` # Dependencies -- [libutils](https://github.com/Maschell/libutils/tree/wut) (WUT branch) for common functions. - [wut](https://github.com/decaf-emu/wut) (WUT branch) for common functions. # Example implementation diff --git a/source/fs/CFile.cpp b/source/fs/CFile.cpp new file mode 100644 index 0000000..c562f11 --- /dev/null +++ b/source/fs/CFile.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#include "CFile.hpp" + +CFile::CFile() { + iFd = -1; + mem_file = NULL; + filesize = 0; + pos = 0; +} + +CFile::CFile(const std::string &filepath, eOpenTypes mode) { + iFd = -1; + this->open(filepath, mode); +} + +CFile::CFile(const uint8_t *mem, int32_t size) { + iFd = -1; + this->open(mem, size); +} + +CFile::~CFile() { + this->close(); +} + +int32_t CFile::open(const std::string &filepath, eOpenTypes mode) { + this->close(); + int32_t openMode = 0; + + // This depend on the devoptab implementation. + // see https://github.com/devkitPro/wut/blob/master/libraries/wutdevoptab/devoptab_fs_open.c#L21 fpr reference + + switch (mode) { + default: + case ReadOnly: // file must exist + openMode = O_RDONLY; + break; + case WriteOnly: // file will be created / zerod + openMode = O_TRUNC | O_CREAT | O_WRONLY; + break; + case ReadWrite: // file must exist + openMode = O_RDWR; + break; + case Append: // append to file, file will be created if missing. write only + openMode = O_CREAT | O_APPEND | O_WRONLY; + break; + } + + //! Using fopen works only on the first launch as expected + //! on the second launch it causes issues because we don't overwrite + //! the .data sections which is needed for a normal application to re-init + //! this will be added with launching as RPX + iFd = ::open(filepath.c_str(), openMode); + if (iFd < 0) + return iFd; + + + filesize = ::lseek(iFd, 0, SEEK_END); + ::lseek(iFd, 0, SEEK_SET); + + return 0; +} + +int32_t CFile::open(const uint8_t *mem, int32_t size) { + this->close(); + + mem_file = mem; + filesize = size; + + return 0; +} + +void CFile::close() { + if (iFd >= 0) + ::close(iFd); + + iFd = -1; + mem_file = NULL; + filesize = 0; + pos = 0; +} + +int32_t CFile::read(uint8_t *ptr, size_t size) { + if (iFd >= 0) { + int32_t ret = ::read(iFd, ptr, size); + if (ret > 0) + pos += ret; + return ret; + } + + int32_t readsize = size; + + if (readsize > (int64_t) (filesize - pos)) + readsize = filesize - pos; + + if (readsize <= 0) + return readsize; + + if (mem_file != NULL) { + memcpy(ptr, mem_file + pos, readsize); + pos += readsize; + return readsize; + } + + return -1; +} + +int32_t CFile::write(const uint8_t *ptr, size_t size) { + if (iFd >= 0) { + size_t done = 0; + while (done < size) { + int32_t ret = ::write(iFd, ptr, size - done); + if (ret <= 0) + return ret; + + ptr += ret; + done += ret; + pos += ret; + } + return done; + } + + return -1; +} + +int32_t CFile::seek(long int offset, int32_t origin) { + int32_t ret = 0; + int64_t newPos = pos; + + if (origin == SEEK_SET) { + newPos = offset; + } else if (origin == SEEK_CUR) { + newPos += offset; + } else if (origin == SEEK_END) { + newPos = filesize + offset; + } + + if (newPos < 0) { + pos = 0; + } else { + pos = newPos; + } + + if (iFd >= 0) + ret = ::lseek(iFd, pos, SEEK_SET); + + if (mem_file != NULL) { + if (pos > filesize) { + pos = filesize; + } + } + + return ret; +} + +int32_t CFile::fwrite(const char *format, ...) { + char tmp[512]; + tmp[0] = 0; + int32_t result = -1; + + va_list va; + va_start(va, format); + if ((vsprintf(tmp, format, va) >= 0)) { + result = this->write((uint8_t *) tmp, strlen(tmp)); + } + va_end(va); + + + return result; +} + + diff --git a/source/fs/CFile.hpp b/source/fs/CFile.hpp new file mode 100644 index 0000000..6c0421b --- /dev/null +++ b/source/fs/CFile.hpp @@ -0,0 +1,71 @@ +#ifndef CFILE_HPP_ +#define CFILE_HPP_ + +#include +#include +#include +#include +#include +#include + +class CFile { +public: + enum eOpenTypes { + ReadOnly, + WriteOnly, + ReadWrite, + Append + }; + + CFile(); + + CFile(const std::string &filepath, eOpenTypes mode); + + CFile(const uint8_t *memory, int32_t memsize); + + virtual ~CFile(); + + int32_t open(const std::string &filepath, eOpenTypes mode); + + int32_t open(const uint8_t *memory, int32_t memsize); + + BOOL isOpen() const { + if (iFd >= 0) + return true; + + if (mem_file) + return true; + + return false; + } + + void close(); + + int32_t read(uint8_t *ptr, size_t size); + + int32_t write(const uint8_t *ptr, size_t size); + + int32_t fwrite(const char *format, ...); + + int32_t seek(long int offset, int32_t origin); + + uint64_t tell() { + return pos; + }; + + uint64_t size() { + return filesize; + }; + + void rewind() { + this->seek(0, SEEK_SET); + }; + +protected: + int32_t iFd; + const uint8_t *mem_file; + uint64_t filesize; + uint64_t pos; +}; + +#endif diff --git a/source/fs/FSUtils.cpp b/source/fs/FSUtils.cpp new file mode 100644 index 0000000..fbb201f --- /dev/null +++ b/source/fs/FSUtils.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include "FSUtils.h" +#include "CFile.hpp" +#include "utils/logger.h" + +int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) { + //! always initialze input + *inbuffer = nullptr; + if (size) + *size = 0; + + int32_t iFd = open(filepath, O_RDONLY); + if (iFd < 0) + return -1; + + uint32_t filesize = lseek(iFd, 0, SEEK_END); + lseek(iFd, 0, SEEK_SET); + + auto *buffer = (uint8_t *) malloc(filesize); + if (buffer == nullptr) { + close(iFd); + return -2; + } + + uint32_t blocksize = 0x4000; + uint32_t done = 0; + int32_t readBytes = 0; + + while (done < filesize) { + if (done + blocksize > filesize) { + blocksize = filesize - done; + } + readBytes = read(iFd, buffer + done, blocksize); + if (readBytes <= 0) + break; + done += readBytes; + } + + close(iFd); + + if (done != filesize) { + free(buffer); + buffer = nullptr; + return -3; + } + + *inbuffer = buffer; + + //! sign is optional input + if (size) { + *size = filesize; + } + + return filesize; +} + +int32_t FSUtils::CheckFile(const char *filepath) { + if (!filepath) + return 0; + + struct stat filestat; + + char dirnoslash[strlen(filepath) + 2]; + snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath); + + while (dirnoslash[strlen(dirnoslash) - 1] == '/') + dirnoslash[strlen(dirnoslash) - 1] = '\0'; + + char *notRoot = strrchr(dirnoslash, '/'); + if (!notRoot) { + strcat(dirnoslash, "/"); + } + + if (stat(dirnoslash, &filestat) == 0) + return 1; + + return 0; +} + +int32_t FSUtils::CreateSubfolder(const char *fullpath) { + if (!fullpath) + return 0; + + int32_t result = 0; + + char dirnoslash[strlen(fullpath) + 1]; + strcpy(dirnoslash, fullpath); + + int32_t pos = strlen(dirnoslash) - 1; + while (dirnoslash[pos] == '/') { + dirnoslash[pos] = '\0'; + pos--; + } + + if (CheckFile(dirnoslash)) { + return 1; + } else { + char parentpath[strlen(dirnoslash) + 2]; + strcpy(parentpath, dirnoslash); + char *ptr = strrchr(parentpath, '/'); + + if (!ptr) { + //!Device root directory (must be with '/') + strcat(parentpath, "/"); + struct stat filestat; + if (stat(parentpath, &filestat) == 0) + return 1; + + return 0; + } + + ptr++; + ptr[0] = '\0'; + + result = CreateSubfolder(parentpath); + } + + if (!result) + return 0; + + if (mkdir(dirnoslash, 0777) == -1) { + return 0; + } + + return 1; +} + + +bool FSUtils::copyFile(const std::string &in, const std::string &out) { + // Using C++ buffers is **really** slow. Copying in 1023 byte chunks. + // Let's do it the old way. + size_t size; + + int source = open(in.c_str(), O_RDONLY, 0); + int dest = open(out.c_str(), 0x602, 0644); + if (source < 0) { + return false; + } + if (dest < 0) { + close(source); + return false; + } + + auto bufferSize = 1024 * 1024; + char *buf = (char *) malloc(bufferSize); + if (buf == NULL) { + return false; + } + + while ((size = read(source, buf, bufferSize)) > 0) { + write(dest, buf, size); + } + + free(buf); + + close(source); + close(dest); + return true; +} + +int32_t FSUtils::saveBufferToFile(const char *path, const void *buffer, uint32_t size) { + CFile file(path, CFile::WriteOnly); + if (!file.isOpen()) { + return -1; + } + int32_t written = file.write((const uint8_t *) buffer, size); + file.close(); + return written; +} + diff --git a/source/fs/FSUtils.h b/source/fs/FSUtils.h new file mode 100644 index 0000000..5aa7860 --- /dev/null +++ b/source/fs/FSUtils.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +class FSUtils { +public: + static int32_t LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size); + + static int32_t CreateSubfolder(const char *fullpath); + + static int32_t CheckFile(const char *filepath); + + static bool copyFile(const std::string &in, const std::string &out); + + static int32_t saveBufferToFile(const char *path, const void *buffer, uint32_t size); +}; diff --git a/source/network/net.c b/source/network/net.c new file mode 100644 index 0000000..398a7df --- /dev/null +++ b/source/network/net.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +static uint32_t hostIpAddress __attribute__((section(".data"))) = 0; +static volatile int socket_lock __attribute__((section(".data"))) = 0; + +void initNetwork(){ + +} + +int32_t recvwait(int32_t sock, void *buffer, int32_t len) { + while(socket_lock) { + OSSleepTicks(OSMicrosecondsToTicks(1000)); + } + int32_t ret; + while (len > 0) { + ret = recv(sock, buffer, len, 0); + if(ret < 0) { + socket_lock = 0; + return ret; + } + len -= ret; + buffer = (void *)(((char *) buffer) + ret); + } + socket_lock = 0; + return 0; +} + +uint8_t recvbyte(int32_t sock) { + unsigned char buffer[1]; + int32_t ret; + + ret = recvwait(sock, buffer, 1); + if (ret < 0) return ret; + return buffer[0]; +} + +uint32_t recvword(int32_t sock) { + uint32_t result; + int32_t ret; + + ret = recvwait(sock, &result, 4); + if (ret < 0) return ret; + return result; +} + +int32_t checkbyte(int32_t sock) { + while(socket_lock) { + OSSleepTicks(OSMicrosecondsToTicks(1000)); + } + unsigned char buffer[1]; + int32_t ret; + + ret = recv(sock, buffer, 1, MSG_DONTWAIT); + socket_lock = 0; + if (ret < 0) return ret; + if (ret == 0) return -1; + return buffer[0]; +} + +int32_t sendwait(int32_t sock, const void *buffer, int32_t len) { + while(socket_lock) { + OSSleepTicks(OSMicrosecondsToTicks(1000)); + } + int32_t ret; + while (len > 0) { + // For some reason the send blocks/crashes if the buffer is too big.. + int cur_length = len <= 0x30 ? len : 0x30; + ret = send(sock, buffer, cur_length, 0); + if(ret < 0) { + socket_lock = 0; + return ret; + } + len -= ret; + buffer = (void *)(((char *) buffer) + ret); + } + socket_lock = 0; + return 0; +} + +int32_t sendbyte(int32_t sock, unsigned char byte) { + unsigned char buffer[1]; + buffer[0] = byte; + return sendwait(sock, buffer, 1); +} \ No newline at end of file diff --git a/source/network/net.h b/source/network/net.h new file mode 100644 index 0000000..e49752e --- /dev/null +++ b/source/network/net.h @@ -0,0 +1,21 @@ +#ifndef _UTILS_NET_H_ +#define _UTILS_NET_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +int32_t recvwait(int32_t sock, void *buffer, int32_t len); +uint8_t recvbyte(int32_t sock); +uint32_t recvword(int32_t sock); +int32_t checkbyte(int32_t sock); +int32_t sendwait(int32_t sock, const void *buffer, int32_t len); +int32_t sendbyte(int32_t sock, unsigned char byte); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/source/system/CThread.h b/source/system/CThread.h new file mode 100644 index 0000000..b91b249 --- /dev/null +++ b/source/system/CThread.h @@ -0,0 +1,133 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef CTHREAD_H_ +#define CTHREAD_H_ + + +#include +#include +#include +#include + +class CThread { +public: + typedef void (* Callback)(CThread *thread, void *arg); + + //! constructor + CThread(int32_t iAttr, int32_t iPriority = 16, int32_t iStackSize = 0x8000, CThread::Callback callback = NULL, void *callbackArg = NULL) + : pThread(NULL) + , pThreadStack(NULL) + , pCallback(callback) + , pCallbackArg(callbackArg) { + //! save attribute assignment + iAttributes = iAttr; + //! allocate the thread + pThread = (OSThread*)memalign(8, sizeof(OSThread)); + //! allocate the stack + pThreadStack = (uint8_t *) memalign(0x20, iStackSize); + //! create the thread + if(pThread && pThreadStack) + OSCreateThread(pThread, &CThread::threadCallback, 1, (char*)this, pThreadStack+iStackSize, iStackSize, iPriority, iAttributes); + } + + //! destructor + virtual ~CThread() { + shutdownThread(); + } + + static CThread *create(CThread::Callback callback, void *callbackArg, int32_t iAttr = eAttributeNone, int32_t iPriority = 16, int32_t iStackSize = 0x8000) { + return ( new CThread(iAttr, iPriority, iStackSize, callback, callbackArg) ); + } + + //! Get thread ID + virtual void* getThread() const { + return pThread; + } + //! Thread entry function + virtual void executeThread(void) { + if(pCallback) + pCallback(this, pCallbackArg); + } + //! Suspend thread + virtual void suspendThread(void) { + if(isThreadSuspended()) return; + if(pThread) OSSuspendThread(pThread); + } + //! Resume thread + virtual void resumeThread(void) { + if(!isThreadSuspended()) return; + if(pThread) OSResumeThread(pThread); + } + //! Set thread priority + virtual void setThreadPriority(int prio) { + if(pThread) OSSetThreadPriority(pThread, prio); + } + //! Check if thread is suspended + virtual BOOL isThreadSuspended(void) const { + if(pThread) return OSIsThreadSuspended(pThread); + return false; + } + //! Check if thread is terminated + virtual BOOL isThreadTerminated(void) const { + if(pThread) return OSIsThreadTerminated(pThread); + return false; + } + //! Check if thread is running + virtual BOOL isThreadRunning(void) const { + return !isThreadSuspended() && !isThreadRunning(); + } + //! Shutdown thread + virtual void shutdownThread(void) { + //! wait for thread to finish + if(pThread && !(iAttributes & eAttributeDetach)) { + if(isThreadSuspended()) + resumeThread(); + + OSJoinThread(pThread, NULL); + } + //! free the thread stack buffer + if(pThreadStack) + free(pThreadStack); + if(pThread) + free(pThread); + + pThread = NULL; + pThreadStack = NULL; + } + //! Thread attributes + enum eCThreadAttributes { + eAttributeNone = 0x07, + eAttributeAffCore0 = 0x01, + eAttributeAffCore1 = 0x02, + eAttributeAffCore2 = 0x04, + eAttributeDetach = 0x08, + eAttributePinnedAff = 0x10 + }; +private: + static int threadCallback(int argc, const char **argv) { + //! After call to start() continue with the internal function + ((CThread *) argv)->executeThread(); + return 0; + } + int iAttributes; + OSThread *pThread; + uint8_t *pThreadStack; + Callback pCallback; + void *pCallbackArg; +}; + +#endif \ No newline at end of file diff --git a/source/utils/StringTools.cpp b/source/utils/StringTools.cpp new file mode 100644 index 0000000..23b87fa --- /dev/null +++ b/source/utils/StringTools.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +BOOL StringTools::EndsWith(const std::string& a, const std::string& b) { + if (b.size() > a.size()) return false; + return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin()); +} + +const char * StringTools::byte_to_binary(int32_t x) { + static char b[9]; + b[0] = '\0'; + + int32_t z; + for (z = 128; z > 0; z >>= 1) { + strcat(b, ((x & z) == z) ? "1" : "0"); + } + + return b; +} + +std::string StringTools::removeCharFromString(std::string& input,char toBeRemoved) { + std::string output = input; + size_t position; + while(1) { + position = output.find(toBeRemoved); + if(position == std::string::npos) + break; + output.erase(position, 1); + } + return output; +} + +const char * StringTools::fmt(const char * format, ...) { + static char strChar[512]; + strChar[0] = 0; + + va_list va; + va_start(va, format); + if((vsprintf(strChar, format, va) >= 0)) { + va_end(va); + return (const char *) strChar; + } + va_end(va); + + return NULL; +} + +const wchar_t * StringTools::wfmt(const char * format, ...) { + static char tmp[512]; + static wchar_t strWChar[512]; + strWChar[0] = 0; + tmp[0] = 0; + + if(!format) + return (const wchar_t *) strWChar; + + if(strcmp(format, "") == 0) + return (const wchar_t *) strWChar; + + va_list va; + va_start(va, format); + if((vsprintf(tmp, format, va) >= 0)) { + int bt; + int32_t strlength = strlen(tmp); + bt = mbstowcs(strWChar, tmp, (strlength < 512) ? strlength : 512 ); + + if(bt > 0) { + strWChar[bt] = 0; + return (const wchar_t *) strWChar; + } + } + va_end(va); + + return NULL; +} + +int32_t StringTools::strprintf(std::string &str, const char * format, ...) { + static char tmp[512]; + tmp[0] = 0; + int32_t result = 0; + + va_list va; + va_start(va, format); + if((vsprintf(tmp, format, va) >= 0)) { + str = tmp; + result = str.size(); + } + va_end(va); + + return result; +} + +std::string StringTools::strfmt(const char * format, ...) { + std::string str; + static char tmp[512]; + tmp[0] = 0; + + va_list va; + va_start(va, format); + if((vsprintf(tmp, format, va) >= 0)) { + str = tmp; + } + va_end(va); + + return str; +} + +BOOL StringTools::char2wchar_t(const char * strChar, wchar_t * dest) { + if(!strChar || !dest) + return false; + + int bt; + bt = mbstowcs(dest, strChar, strlen(strChar)); + if (bt > 0) { + dest[bt] = 0; + return true; + } + + return false; +} + +int32_t StringTools::strtokcmp(const char * string, const char * compare, const char * separator) { + if(!string || !compare) + return -1; + + char TokCopy[512]; + strncpy(TokCopy, compare, sizeof(TokCopy)); + TokCopy[511] = '\0'; + + char * strTok = strtok(TokCopy, separator); + + while (strTok != NULL) { + if (strcasecmp(string, strTok) == 0) { + return 0; + } + strTok = strtok(NULL,separator); + } + + return -1; +} + +int32_t StringTools::strextcmp(const char * string, const char * extension, char seperator) { + if(!string || !extension) + return -1; + + char *ptr = strrchr(string, seperator); + if(!ptr) + return -1; + + return strcasecmp(ptr + 1, extension); +} + + +std::vector StringTools::stringSplit(const std::string & inValue, const std::string & splitter) { + std::string value = inValue; + std::vector result; + while (true) { + uint32_t index = value.find(splitter); + if (index == std::string::npos) { + result.push_back(value); + break; + } + std::string first = value.substr(0, index); + result.push_back(first); + if (index + splitter.size() == value.length()) { + result.push_back(""); + break; + } + if(index + splitter.size() > value.length()) { + break; + } + value = value.substr(index + splitter.size(), value.length()); + } + return result; +} \ No newline at end of file diff --git a/source/utils/StringTools.h b/source/utils/StringTools.h new file mode 100644 index 0000000..e7c8527 --- /dev/null +++ b/source/utils/StringTools.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef __STRING_TOOLS_H +#define __STRING_TOOLS_H + +#include +#include +#include + +class StringTools{ + public: + static BOOL EndsWith(const std::string& a, const std::string& b); + static const char * byte_to_binary(int32_t x); + static std::string removeCharFromString(std::string& input,char toBeRemoved); + static const char * fmt(const char * format, ...); + static const wchar_t * wfmt(const char * format, ...); + static int32_t strprintf(std::string &str, const char * format, ...); + static std::string strfmt(const char * format, ...); + static BOOL char2wchar_t(const char * src, wchar_t * dest); + static int32_t strtokcmp(const char * string, const char * compare, const char * separator); + static int32_t strextcmp(const char * string, const char * extension, char seperator); + + static const char * FullpathToFilename(const char *path){ + if(!path) return path; + + const char * ptr = path; + const char * Filename = ptr; + + while(*ptr != '\0') + { + if(ptr[0] == '/' && ptr[1] != '\0') + Filename = ptr+1; + + ++ptr; + } + + return Filename; + } + + static void RemoveDoubleSlashs(std::string &str){ + uint32_t length = str.size(); + + //! clear path of double slashes + for(uint32_t i = 1; i < length; ++i) + { + if(str[i-1] == '/' && str[i] == '/') + { + str.erase(i, 1); + i--; + length--; + } + } + } + + static std::vector stringSplit(const std::string & value, const std::string & splitter); +}; + +#endif /* __STRING_TOOLS_H */ diff --git a/source/utils/TCPServer.cpp b/source/utils/TCPServer.cpp new file mode 100644 index 0000000..95f043c --- /dev/null +++ b/source/utils/TCPServer.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include +#include + +#define wiiu_errno (*__gh_errno_ptr()) + +TCPServer::TCPServer(int32_t port,int32_t priority) { + this->port = port; + this->sockfd = -1; + this->clientfd = -1; + memset(&(this->sock_addr),0,sizeof(this->sock_addr)); + + pThread = CThread::create(TCPServer::DoTCPThread, (void*)this, CThread::eAttributeAffCore2,priority); + pThread->resumeThread(); +} + +TCPServer::~TCPServer() { + CloseSockets(); + //DEBUG_FUNCTION_LINE("Thread will be closed\n"); + exitThread = 1; + + ICInvalidateRange((void*)&exitThread, 4); + DCFlushRange((void*)&exitThread, 4); + + if(pThread != NULL) { + //DEBUG_FUNCTION_LINE("Deleting it!\n"); + delete pThread; + } + //DEBUG_FUNCTION_LINE("Thread done\n"); + pThread = NULL; +} + +void TCPServer::CloseSockets() { + if (this->sockfd != -1) { + socketclose(this->sockfd); + } + if (this->clientfd != -1) { + socketclose(this->clientfd); + } + this->sockfd = -1; + this->clientfd = -1; +} + +void TCPServer::ErrorHandling() { + CloseSockets(); + OSSleepTicks(OSMicrosecondsToTicks(1000*1000*2)); +} + +void TCPServer::DoTCPThreadInternal() { + int32_t ret; + socklen_t len; + connected = false; + while (1) { + if(exitThread) { + break; + } + memset(&(this->sock_addr),0,sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = this->port; + sock_addr.sin_addr.s_addr = 0; + + this->sockfd = ret = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(ret == -1) { + ErrorHandling(); + continue; + } + int32_t enable = 1; + + setsockopt(this->sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); + + ret = bind(this->sockfd, (sockaddr *)&sock_addr, 16); + if(ret < 0) { + ErrorHandling(); + continue; + } + ret = listen(this->sockfd, 1); + if(ret < 0) { + ErrorHandling(); + continue; + } + + do { + DEBUG_FUNCTION_LINE("Waiting for a connection\n"); + if(exitThread) { + break; + } + len = 16; + clientfd = ret = accept(sockfd, (sockaddr *)&(sock_addr), &len); + + if(ret == -1) { + ErrorHandling(); + break; + } + + if(!acceptConnection()) { + ErrorHandling(); + break; + } + + connected = true; + + DEBUG_FUNCTION_LINE("Connection accepted\n"); + + whileLoop(); + + DEBUG_FUNCTION_LINE("Client disconnected\n"); + + if(clientfd != -1) { + socketclose(clientfd); + } + clientfd = -1; + } while(0); + DEBUG_FUNCTION_LINE("Closing TCPServer\n"); + connected = false; + onConnectionClosed(); + CloseSockets(); + continue; + } + DEBUG_FUNCTION_LINE("Ending DoTCPThreadInternal\n"); +} + +void TCPServer::DoTCPThread(CThread *thread, void *arg) { + TCPServer * args = (TCPServer * )arg; + return args->DoTCPThreadInternal(); +} \ No newline at end of file diff --git a/source/utils/TCPServer.hpp b/source/utils/TCPServer.hpp new file mode 100644 index 0000000..f135d98 --- /dev/null +++ b/source/utils/TCPServer.hpp @@ -0,0 +1,72 @@ +#ifndef _TCPSERVER_H_ +#define _TCPSERVER_H_ + +#include + +#include +#include +#include + +#include +#include "utils/logger.h" + +class TCPServer { +public: + TCPServer(int32_t port, int32_t priority); + virtual ~TCPServer(); + + BOOL isConnected() { + return connected; + } +protected: + BOOL shouldExit() { + return (exitThread == 1); + } + + int32_t getClientFD() { + return clientfd; + } + + int32_t getSocketFD() { + return sockfd; + } + + void setThreadPriority(int32_t priority) { + if(pThread != NULL){ + pThread->setThreadPriority(priority); + } + } + + struct sockaddr_in getSockAddr() { + return sock_addr; + } +private: + virtual void CloseSockets(); + virtual void ErrorHandling(); + + static void DoTCPThread(CThread *thread, void *arg); + virtual void DoTCPThreadInternal(); + + virtual BOOL acceptConnection() = 0; + + virtual void onConnectionClosed(){ + DEBUG_FUNCTION_LINE("Default onConnectionClosed \n"); + } + + /** + Called when a connection has be accepted. + **/ + virtual BOOL whileLoop() = 0; + + struct sockaddr_in sock_addr; + volatile int32_t sockfd = -1; + volatile int32_t clientfd = -1; + + int32_t port = 0; + volatile BOOL connected = false; + + volatile int32_t exitThread = 0; + CThread *pThread = NULL; +}; + +#endif //_TCPSERVER_H_ \ No newline at end of file diff --git a/source/utils/logger.h b/source/utils/logger.h new file mode 100644 index 0000000..ff7c896 --- /dev/null +++ b/source/utils/logger.h @@ -0,0 +1,37 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#ifdef __LOGGING__ + +#define log_print(str) WHBLogPrint(str) +#define log_printf(FMT, ARGS...) WHBLogPrintf(FMT, ## ARGS); + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0); + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \ + WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0); + +#else + +#define log_print(str) +#define log_printf(FMT, ARGS...) +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) + +#endif + +#ifdef __cplusplus +} +#endif \ No newline at end of file