commit 9ba9e5ca1b408ffa70fdbedc3c59f10417b85525 Author: Maschell Date: Sun Nov 24 14:35:38 2019 +0100 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c258747 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/* +*.mod +sysapp.layout +sysapp.cbp +sysapp.cscope_file_list diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6f3a551 --- /dev/null +++ b/Makefile @@ -0,0 +1,272 @@ +# You probably never need to adjust this Makefile. +# All changes can be done in the makefile.mk + +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPRO") +endif + +export PATH := $(DEVKITPPC)/bin:$(PORTLIBS)/bin:$(PATH) +export PORTLIBS := $(DEVKITPRO)/portlibs/ppc +export WUPSDIR := $(DEVKITPRO)/wups +export GCC_VER := $(shell $(DEVKITPPC)/bin/powerpc-eabi-gcc -dumpversion) + +PREFIX := powerpc-eabi- + +export AS := $(PREFIX)as +export CC := $(PREFIX)gcc +export CXX := $(PREFIX)g++ +export LD := $(PREFIX)ld +export AR := $(PREFIX)ar +export OBJCOPY := $(PREFIX)objcopy + +#--------------------------------------------------------------------------------- +# 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 +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build + +ifeq ($(notdir $(CURDIR)),$(BUILD)) + include ../makefile.mk +else + include makefile.mk +endif + +include $(WUPSDIR)/plugin_makefile.mk + + +#MAP ?= $(TARGET:.mod=.map) + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +MACHDEP = -DESPRESSO -mcpu=750 -meabi -mhard-float + +# -Os: optimise size +# -Wall: generate lots of warnings +# -D__wiiu__: define the symbol __wiiu__ (used in some headers) +# -mcpu=750: enable processor specific compilation +# -meabi: enable eabi specific compilation +# -mhard-float: enable hardware floating point instructions +# -nostartfiles: Do not use the standard system startup files when linking +# -ffunction-sections: split up functions so linker can garbage collect +# -fdata-sections: split up data so linker can garbage collect +COMMON_CFLAGS := -O0 -Wall $(MACHDEP) -meabi -ffunction-sections -fdata-sections -Wl,-q $(COMMON_CFLAGS) + +CFLAGS += -D__WIIU__ -D__WUT__ + +# -x c: compile as c code +# -std=c11: use the c11 standard +CFLAGS := $(COMMON_CFLAGS) -x c -std=gnu11 $(CFLAGS) + +# -x c++: compile as c++ code +# -std=gnu++11: use the c++11 standard +CXXFLAGS := $(COMMON_CFLAGS) -x c++ -std=gnu++11 $(CXXFLAGS) + +#--------------------------------------------------------------------------------- +# any extra ld flags +#-------------------------------------------------------------------------------- +# --gc-sections: remove unneeded symbols +# -Map: generate a map file +LDFLAGS += $(ARCH) -Wl,-Map,$(notdir $@).map,--gc-sections,-wrap,__gxx_personality_v0 + + +#--------------------------------------------------------------------------------- +Q := @ +MAKEFLAGS += --no-print-directory +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS += +# +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS += + +#--------------------------------------------------------------------------------- +# 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 PROJECTDIR := $(CURDIR) +export OUTPUT := $(CURDIR)/$(TARGETDIR)/$(TARGET) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +TTFFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.ttf))) +PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export REAL_LD := $(CC) +else + export REAL_LD := $(CXX) +endif + +export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) \ + $(PNGFILES:.png=.png.o) $(addsuffix .o,$(BINFILES)) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE_FULL += $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(EXTERNAL_INCLUDE) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS_FULL += $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(EXTERNAL_LIBPATHS) + + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean install + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).mod $(OUTPUT) + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +############################################################################### +# Rule to make everything. +PHONY += all + +all : $(OUTPUT) +############################################################################### +# Special build rules + + +# Rule to make the module file. +$(OUTPUT) : $(OFILES) + @echo "linking ... " $@ + @$(REAL_LD) $(OFILES) $(LDFLAGS) $(LIBS) $(LIBPATHS_FULL) -o $@ + +############################################################################### +# Standard build rules +#--------------------------------------------------------------------------------- +%.a: +#--------------------------------------------------------------------------------- + @echo $(notdir $@) + @rm -f $@ + @$(AR) -rc $@ $^ + +#--------------------------------------------------------------------------------- +%.o: %.cpp + @echo $(notdir $<) + @$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(INCLUDE_FULL) -c $< -o $@ $(ERROR_FILTER) + +#--------------------------------------------------------------------------------- +%.o: %.c + @echo $(notdir $<) + @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(INCLUDE_FULL) -c $< -o $@ $(ERROR_FILTER) + +#--------------------------------------------------------------------------------- +%.o: %.S + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(INCLUDE_FULL) -c $< -o $@ $(ERROR_FILTER) + +#--------------------------------------------------------------------------------- +%.png.o : %.png + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +#--------------------------------------------------------------------------------- +%.jpg.o : %.jpg + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +#--------------------------------------------------------------------------------- +%.ttf.o : %.ttf + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +#--------------------------------------------------------------------------------- +%.bin.o : %.bin + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +#--------------------------------------------------------------------------------- +%.wav.o : %.wav + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +#--------------------------------------------------------------------------------- +%.mp3.o : %.mp3 + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +#--------------------------------------------------------------------------------- +%.ogg.o : %.ogg + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +############################################################################### +# Assembly listing rules + +# Rule to make assembly listing. +PHONY += list +list : $(LIST) + +# Rule to make the listing file. +%.list : $(TARGET) + $(LOG) + -$Qmkdir -p $(dir $@) + $Q$(OBJDUMP) -d $< > $@ + +############################################################################### +# Clean rule + +# Rule to clean files. +PHONY += clean +clean : + $Qrm -rf $(wildcard $(BUILD) $(BIN)) + +############################################################################### +# Phony targets + +.PHONY : $(PHONY) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/makefile.mk b/makefile.mk new file mode 100644 index 0000000..6b3a8f1 --- /dev/null +++ b/makefile.mk @@ -0,0 +1,50 @@ +# Compiling the projects with libutils logging code? +DO_LOGGING := 1 + +# Target filename +TARGET := $(notdir $(CURDIR)).mod + +# Source directories +SOURCES := src src/utils src/fs src/plugin + +# Data directories +DATA := + +# Include directories +INCLUDES := src + +#--------------------------------------------------------------------------------- +# options for code generation and linking +#--------------------------------------------------------------------------------- +# Extra C AND C++ compiler flags +COMMON_CFLAGS := +# Extra C compiler flags +CFLAGS := +# Extra C++ compiler flags +CXXFLAGS := +# Extra linking flags for all linking steps +LDFLAGS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(WUPSDIR) $(WUT_ROOT) $(PORTLIBS) + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lwups -lwut + +#--------------------------------------------------------------------------------- +# Will be added to the final lib paths +# example: +# -L$C:/library1/lib +#--------------------------------------------------------------------------------- +EXTERNAL_LIBPATHS := + +#--------------------------------------------------------------------------------- +# Will be added to the final include paths +# -IC:/library1/include +#--------------------------------------------------------------------------------- +EXTERNAL_INCLUDE := diff --git a/src/fs/CFile.cpp b/src/fs/CFile.cpp new file mode 100644 index 0000000..f6bef9d --- /dev/null +++ b/src/fs/CFile.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include + +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/src/fs/CFile.hpp b/src/fs/CFile.hpp new file mode 100644 index 0000000..8816a49 --- /dev/null +++ b/src/fs/CFile.hpp @@ -0,0 +1,61 @@ +#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/src/fs/DirList.cpp b/src/fs/DirList.cpp new file mode 100644 index 0000000..6b98c49 --- /dev/null +++ b/src/fs/DirList.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** + * 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. + * + * DirList Class + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +DirList::DirList() { + Flags = 0; + Filter = 0; + Depth = 0; +} + +DirList::DirList(const std::string & path, const char *filter, uint32_t flags, uint32_t maxDepth) { + this->LoadPath(path, filter, flags, maxDepth); + this->SortList(); +} + +DirList::~DirList() { + ClearList(); +} + +BOOL DirList::LoadPath(const std::string & folder, const char *filter, uint32_t flags, uint32_t maxDepth) { + if(folder.empty()) return false; + + Flags = flags; + Filter = filter; + Depth = maxDepth; + + std::string folderpath(folder); + uint32_t length = folderpath.size(); + + //! clear path of double slashes + StringTools::RemoveDoubleSlashs(folderpath); + + //! remove last slash if exists + if(length > 0 && folderpath[length-1] == '/') + folderpath.erase(length-1); + + //! add root slash if missing + if(folderpath.find('/') == std::string::npos) { + folderpath += '/'; + } + + return InternalLoadPath(folderpath); +} + +BOOL DirList::InternalLoadPath(std::string &folderpath) { + if(folderpath.size() < 3) + return false; + + struct dirent *dirent = NULL; + DIR *dir = NULL; + + dir = opendir(folderpath.c_str()); + if (dir == NULL) + return false; + + while ((dirent = readdir(dir)) != 0) { + BOOL isDir = dirent->d_type & DT_DIR; + const char *filename = dirent->d_name; + + if(isDir) { + if(strcmp(filename,".") == 0 || strcmp(filename,"..") == 0) + continue; + + if((Flags & CheckSubfolders) && (Depth > 0)) { + int32_t length = folderpath.size(); + if(length > 2 && folderpath[length-1] != '/') { + folderpath += '/'; + } + folderpath += filename; + + Depth--; + InternalLoadPath(folderpath); + folderpath.erase(length); + Depth++; + } + + if(!(Flags & Dirs)) + continue; + } else if(!(Flags & Files)) { + continue; + } + + if(Filter) { + char * fileext = strrchr(filename, '.'); + if(!fileext) + continue; + + if(StringTools::strtokcmp(fileext, Filter, ",") == 0) + AddEntrie(folderpath, filename, isDir); + } else { + AddEntrie(folderpath, filename, isDir); + } + } + closedir(dir); + + return true; +} + +void DirList::AddEntrie(const std::string &filepath, const char * filename, BOOL isDir) { + if(!filename) + return; + + int32_t pos = FileInfo.size(); + + FileInfo.resize(pos+1); + + FileInfo[pos].FilePath = (char *) malloc(filepath.size()+strlen(filename)+2); + if(!FileInfo[pos].FilePath) { + FileInfo.resize(pos); + return; + } + + sprintf(FileInfo[pos].FilePath, "%s/%s", filepath.c_str(), filename); + FileInfo[pos].isDir = isDir; +} + +void DirList::ClearList() { + for(uint32_t i = 0; i < FileInfo.size(); ++i) { + if(FileInfo[i].FilePath) { + free(FileInfo[i].FilePath); + FileInfo[i].FilePath = NULL; + } + } + + FileInfo.clear(); + std::vector().swap(FileInfo); +} + +const char * DirList::GetFilename(int32_t ind) const { + if (!valid(ind)) + return ""; + + return StringTools::FullpathToFilename(FileInfo[ind].FilePath); +} + +static BOOL SortCallback(const DirEntry & f1, const DirEntry & f2) { + if(f1.isDir && !(f2.isDir)) return true; + if(!(f1.isDir) && f2.isDir) return false; + + if(f1.FilePath && !f2.FilePath) return true; + if(!f1.FilePath) return false; + + if(strcasecmp(f1.FilePath, f2.FilePath) > 0) + return false; + + return true; +} + +void DirList::SortList() { + if(FileInfo.size() > 1) + std::sort(FileInfo.begin(), FileInfo.end(), SortCallback); +} + +void DirList::SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b)) { + if(FileInfo.size() > 1) + std::sort(FileInfo.begin(), FileInfo.end(), SortFunc); +} + +uint64_t DirList::GetFilesize(int32_t index) const { + struct stat st; + const char *path = GetFilepath(index); + + if(!path || stat(path, &st) != 0) + return 0; + + return st.st_size; +} + +int32_t DirList::GetFileIndex(const char *filename) const { + if(!filename) + return -1; + + for (uint32_t i = 0; i < FileInfo.size(); ++i) { + if (strcasecmp(GetFilename(i), filename) == 0) + return i; + } + + return -1; +} diff --git a/src/fs/DirList.h b/src/fs/DirList.h new file mode 100644 index 0000000..64072df --- /dev/null +++ b/src/fs/DirList.h @@ -0,0 +1,103 @@ +/**************************************************************************** + * 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. + * + * DirList Class + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef ___DIRLIST_H_ +#define ___DIRLIST_H_ + +#include +#include +#include + +typedef struct { + char * FilePath; + BOOL isDir; +} DirEntry; + +class DirList { +public: + //!Constructor + DirList(void); + //!\param path Path from where to load the filelist of all files + //!\param filter A fileext that needs to be filtered + //!\param flags search/filter flags from the enum + DirList(const std::string & path, const char *filter = NULL, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff); + //!Destructor + virtual ~DirList(); + //! Load all the files from a directory + BOOL LoadPath(const std::string & path, const char *filter = NULL, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff); + //! Get a filename of the list + //!\param list index + const char * GetFilename(int32_t index) const; + //! Get the a filepath of the list + //!\param list index + const char *GetFilepath(int32_t index) const { + if (!valid(index)) return ""; + else return FileInfo[index].FilePath; + } + //! Get the a filesize of the list + //!\param list index + uint64_t GetFilesize(int32_t index) const; + //! Is index a dir or a file + //!\param list index + BOOL IsDir(int32_t index) const { + if(!valid(index)) return false; + return FileInfo[index].isDir; + }; + //! Get the filecount of the whole list + int32_t GetFilecount() const { + return FileInfo.size(); + }; + //! Sort list by filepath + void SortList(); + //! Custom sort command for custom sort functions definitions + void SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b)); + //! Get the index of the specified filename + int32_t GetFileIndex(const char *filename) const; + //! Enum for search/filter flags + enum { + Files = 0x01, + Dirs = 0x02, + CheckSubfolders = 0x08, + }; +protected: + // Internal parser + BOOL InternalLoadPath(std::string &path); + //!Add a list entrie + void AddEntrie(const std::string &filepath, const char * filename, BOOL isDir); + //! Clear the list + void ClearList(); + //! Check if valid pos is requested + inline BOOL valid(uint32_t pos) const { + return (pos < FileInfo.size()); + }; + + uint32_t Flags; + uint32_t Depth; + const char *Filter; + std::vector FileInfo; +}; + +#endif diff --git a/src/fs/FSUtils.cpp b/src/fs/FSUtils.cpp new file mode 100644 index 0000000..e9dadb5 --- /dev/null +++ b/src/fs/FSUtils.cpp @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) { + //! always initialze input + *inbuffer = NULL; + 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); + + uint8_t *buffer = (uint8_t *) malloc(filesize); + if (buffer == NULL) { + 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 = NULL; + 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::saveBufferToFile(const char * path, void * buffer, uint32_t size) { + CFile file(path, CFile::WriteOnly); + if (!file.isOpen()) { + DEBUG_FUNCTION_LINE("Failed to open %s\n",path); + return false; + } + file.write((const uint8_t*) buffer,size); + file.close(); + return true; +} + diff --git a/src/fs/FSUtils.h b/src/fs/FSUtils.h new file mode 100644 index 0000000..b589a44 --- /dev/null +++ b/src/fs/FSUtils.h @@ -0,0 +1,16 @@ +#ifndef __FS_UTILS_H_ +#define __FS_UTILS_H_ + +#include + +class FSUtils { +public: + static int32_t LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size); + + //! todo: C++ class + static int32_t CreateSubfolder(const char * fullpath); + static int32_t CheckFile(const char * filepath); + static BOOL saveBufferToFile(const char * path, void * buffer, uint32_t size); +}; + +#endif // __FS_UTILS_H_ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6b19b9f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,39 @@ +#include +#include "utils/TcpReceiver.h" +#include + +WUPS_PLUGIN_NAME("Wiiload"); +WUPS_PLUGIN_DESCRIPTION("Wiiload Server"); +WUPS_PLUGIN_VERSION("0.1"); +WUPS_PLUGIN_AUTHOR("Maschell"); +WUPS_PLUGIN_LICENSE("GPL"); + +WUPS_USE_WUT_CRT() + +TcpReceiver * thread = NULL; + + +/* Entry point */ +ON_APPLICATION_START(args) { + WHBInitializeSocketLibrary(); + + log_init(); + DEBUG_FUNCTION_LINE("Started wiiload thread\n"); + thread = new TcpReceiver(4299); +} + +void stopThread(){ + if(thread != NULL){ + delete thread; + thread = NULL; + } +} + + +ON_APPLICATION_END(){ + DEBUG_FUNCTION_LINE("Kill thread\n"); + stopThread(); + + DEBUG_FUNCTION_LINE("Unmount SD\n"); +} + diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..dfc7038 --- /dev/null +++ b/src/main.h @@ -0,0 +1,32 @@ +#ifndef _MAIN_H_ +#define _MAIN_H_ + +/* Main */ +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +#define MAXPATHLEN 256 + +#define WIIU_EAGAIN EWOULDBLOCK +#define ENODATA 1 +#define EISCONN 3 +#define EWOULDBLOCK 6 +#define EALREADY 10 +#define EAGAIN EWOULDBLOCK +#define EINVAL 11 +#define ENOMEM 18 +#define EINPROGRESS 22 + +#define wiiu_geterrno() (socketlasterr()) + +//! C wrapper for our C++ functions +int Menu_Main(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/plugin/PluginInformation.h b/src/plugin/PluginInformation.h new file mode 100644 index 0000000..7463edd --- /dev/null +++ b/src/plugin/PluginInformation.h @@ -0,0 +1,117 @@ +/* based on module.c + * by Alex Chadwick + * + * Copyright (C) 2014, Alex Chadwick + * Modified 2018, Maschell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _PLUGIN_INFORMATION_H_ +#define _PLUGIN_INFORMATION_H_ + +#include +#include +#include "utils/logger.h" +#include "utils/ipcclient.h" + +class PluginInformation { +public: + PluginInformation(plugin_information_handle handle, const char * path, const char * name, const char * author) { + this->handle = handle; + this->path = path; + this->name = name; + this->author = author; + } + + ~PluginInformation() { + IPC_Delete_Plugin_Information(this->handle); + } + + std::string getName() { + return this->name; + } + + std::string getAuthor() { + return this->author; + } + + std::string getVersion() { + return this->version; + } + + std::string getLicense() { + return this->license; + } + + std::string getBuildTimestamp() { + return this->buildtimestamp; + } + + std::string getDescription() { + return this->description; + } + + std::string getPath() { + return path; + } + + size_t getSize() { + return this->size; + } + + plugin_information_handle getHandle() { + return this->handle; + } +private: + void setVersion(const char * version) { + this->version = version; + } + + void setLicense(const char * license) { + this->license = license; + } + + void setBuildTimestamp(const char * buildtimestamp) { + this->buildtimestamp = buildtimestamp; + } + + void setDescription(const char * description) { + this->description = description; + } + + void setSize(size_t size) { + this->size = size; + } + + plugin_information_handle handle; + + std::string path; + std::string name; + std::string author; + std::string version; + std::string license; + std::string buildtimestamp; + std::string description; + + size_t size = 0; +}; + + +#endif diff --git a/src/plugin/PluginInformationUtils.cpp b/src/plugin/PluginInformationUtils.cpp new file mode 100644 index 0000000..6467478 --- /dev/null +++ b/src/plugin/PluginInformationUtils.cpp @@ -0,0 +1,121 @@ +#include +#include "PluginInformationUtils.h" +#include "utils/ipcclient.h" + +std::vector PluginInformationUtils::getPluginInformationByStruct(plugin_information_handle * handleList, uint32_t handleListSize) { + std::vector result; + + if(handleListSize > 0) { + DEBUG_FUNCTION_LINE("Getting details for handles\n"); + plugin_information * informationList = NULL; + uint32_t informationListSize = 0; + uint32_t res = IPC_Get_Plugin_Information_Details(handleList, handleListSize, &informationList, &informationListSize); + if(res == 0) { + for(uint32_t i = 0; i pluginInformation) { + for(size_t i = 0; i < pluginInformation.size(); i++) { + PluginInformation * curPluginInformation = pluginInformation[i]; + if(curPluginInformation != NULL) { + delete curPluginInformation; + } + } +} + +std::vector PluginInformationUtils::getPluginsByPath(std::string path) { + std::vector result; + plugin_information_handle * handleList = NULL; + uint32_t handleListSize = 0; + + uint32_t res = IPC_Get_Plugin_Information(path.c_str(), &handleList, &handleListSize); + if(res == 0) { + DEBUG_FUNCTION_LINE("SUCCESS reading plugins from %s. handleListSize %d, handlelist %08X \n",path, handleListSize, handleList); + result = getPluginInformationByStruct(handleList, handleListSize); + } + + if(handleList != NULL) { + free(handleList); + } + return result; +} + +PluginInformation * PluginInformationUtils::loadPluginInformation(std::string path) { + std::vector result; + plugin_information_handle handle = NULL; + + uint32_t res = IPC_Get_Plugin_Information_For_Filepath(path.c_str(), &handle); + if(res == 0 && handle != NULL) { + DEBUG_FUNCTION_LINE("SUCCESS reading plugins from %s. handle %08X \n",path.c_str(), &handle); + result = getPluginInformationByStruct(&handle, 1); + } + + if(result.size() > 0){ + return result.at(0); + } + return NULL; +} + + +bool PluginInformationUtils::loadAndLinkPluginsOnRestart(std::vector pluginInformation) { + uint32_t handleListSize = pluginInformation.size(); + + DEBUG_FUNCTION_LINE("Convert PluginInformation* to plugin_information_handle *\n"); + + plugin_information_handle * handleList = (plugin_information_handle *) malloc(handleListSize * sizeof(plugin_information_handle)); + if(handleList == NULL) { + return false; + } + + DEBUG_FUNCTION_LINE("Allocation was okay %08X\n", handleList); + + + uint32_t cur = 0; + for (std::vector::iterator it = pluginInformation.begin() ; it != pluginInformation.end(); ++it) { + PluginInformation * curPlugin = *it; + handleList[cur] = curPlugin->getHandle(); + DEBUG_FUNCTION_LINE("Adding to List %08X\n", handleList[cur]); + cur++; + } + bool result = false; + int32_t res = IPC_Link_Plugin_Information_On_Restart(handleList, handleListSize); + + if(res >= 0) { + DEBUG_FUNCTION_LINE("result was %d\n", res); + result = true; + } + + free(handleList); + return result; +} + +std::vector PluginInformationUtils::getPluginsLoadedInMemory() { + std::vector result; + plugin_information_handle * handleList = NULL; + uint32_t handleListSize = 0; + + uint32_t res = IPC_Get_Plugin_Information_Loaded(&handleList, &handleListSize); + if(res == 0) { + result = getPluginInformationByStruct(handleList, handleListSize); + } + + if(handleList != NULL) { + free(handleList); + } + return result; +} diff --git a/src/plugin/PluginInformationUtils.h b/src/plugin/PluginInformationUtils.h new file mode 100644 index 0000000..0d4ce4a --- /dev/null +++ b/src/plugin/PluginInformationUtils.h @@ -0,0 +1,70 @@ +/* based on module.c + * by Alex Chadwick + * + * Copyright (C) 2014, Alex Chadwick + * Modified 2018,2019 Maschell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _PLUGIN_INFORMATION_UTILS_H_ +#define _PLUGIN_INFORMATION_UTILS_H_ + +#include +#include +#include "PluginInformation.h" +#include "utils/logger.h" +#include "utils/ipcclient.h" + +class PluginInformationUtils { +public: + + + static PluginInformation * loadPluginInformation(std::string path); + + + /** + \brief Parses the meta data of all plugins in the given directory. + + \param path the path of the directory which should be scanned. + + \return a list of PluginInformation objects, one for each valid plugin. + **/ + static std::vector getPluginsByPath(std::string path); + + + /** + \brief Gets plugin information from the global struct. + + \return a list of MetaInformation objects for all plugins currently loaded and linked (relocated). Will only contain + plugin which are still on the sd card. + **/ + static std::vector getPluginsLoadedInMemory(); + + + static void clearPluginInformation(std::vector pluginInformation) ; + + + static bool loadAndLinkPluginsOnRestart(std::vector pluginInformation); + + static std::vector getPluginInformationByStruct(plugin_information_handle * handleList, uint32_t handleListSize); +}; + + +#endif diff --git a/src/plugin/PluginLoader.cpp b/src/plugin/PluginLoader.cpp new file mode 100644 index 0000000..b5bd2a1 --- /dev/null +++ b/src/plugin/PluginLoader.cpp @@ -0,0 +1,60 @@ +#include +#include "PluginLoader.h" +#include "utils/ipcclient.h" + +PluginLoader * PluginLoader::createInstance(uint32_t startAddress, uint32_t endAddress) { + plugin_loader_handle handle = IPC_Open_Plugin_Loader(startAddress, endAddress); + + if(handle != 0) { + return new PluginLoader(handle, startAddress,endAddress); + } + return NULL; +} + +void PluginLoader::destroyInstance(PluginLoader * loader) { + if(loader != NULL) { + delete loader; + } +} + +PluginLoader::PluginLoader(plugin_information_handle handle,uint32_t startAddress, uint32_t endAddress) { + this->handle = handle; + this->startAddress = startAddress; + this->endAddress = endAddress; +} + +PluginLoader::~PluginLoader() { + IPC_Close_Plugin_Loader(this->handle); +} + +bool PluginLoader::loadAndLinkPlugins(std::vector pluginInformation) { + uint32_t handleListSize = pluginInformation.size(); + + DEBUG_FUNCTION_LINE("Convert PluginInformation* to plugin_information_handle *\n"); + + plugin_information_handle * handleList = (plugin_information_handle *) malloc(handleListSize * sizeof(plugin_information_handle)); + if(handleList == NULL) { + return false; + } + + DEBUG_FUNCTION_LINE("Allocation was okay %08X\n", handleList); + + + uint32_t cur = 0; + for (std::vector::iterator it = pluginInformation.begin() ; it != pluginInformation.end(); ++it) { + PluginInformation * curPlugin = *it; + handleList[cur] = curPlugin->getHandle(); + DEBUG_FUNCTION_LINE("Adding to List %08X\n", handleList[cur]); + cur++; + } + bool result = false; + int32_t res = IPC_Link_Plugin_Information(this->handle, handleList, handleListSize); + + if(res >= 0) { + DEBUG_FUNCTION_LINE("result was %d\n", res); + result = true; + } + + free(handleList); + return result; +} diff --git a/src/plugin/PluginLoader.h b/src/plugin/PluginLoader.h new file mode 100644 index 0000000..de25831 --- /dev/null +++ b/src/plugin/PluginLoader.h @@ -0,0 +1,78 @@ +#ifndef _PLUGIN_LOADER_H_ +#define _PLUGIN_LOADER_H_ + +#include +#include "PluginInformation.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + + +class PluginLoader { + +public: + static PluginLoader * createInstance(uint32_t startAddress, uint32_t endAddress); + + static void destroyInstance(PluginLoader * loader); + + ~PluginLoader(); + + /** + \brief Takes a list of plugins that should be linked (relocated) loaded into the memory. + The function that should be replaced will be replaced in the order of the given plugin list. + So two plugin will override the same function, the plugin first in this list will override the function first. + Also the hooks of the plugins will be called in the order their plugin where passed to this method. + + \param A list of plugin that should be linked (relocated) an loaded into memory + + \return Returns true if all plugins were linked successfully. Returns false if at least one plugin failed while linking. + **/ + bool loadAndLinkPlugins(std::vector pluginInformation); + + + + /** + \brief Load + + \param pluginInformation a PluginInformation object of the plugin that should be linked (relocated) and loaded. + **/ + bool loadAndLinkPlugin(PluginInformation * pluginInformation); + + + /* + size_t getTotalSpace() { + return ((uint32_t) this->endAddress - (uint32_t) this->startAddress); + } + + size_t getAvailableSpace() { + return ((uint32_t) this->endAddress - (uint32_t) this->currentStoreAddress); + } + + size_t getUsedSpace() { + return getTotalSpace() - getAvailableSpace(); + } + + void resetPluginLoader() { + this->currentStoreAddress = ROUNDUP((uint32_t)startAddress, 0x10000); + }*/ +private: + PluginLoader(plugin_loader_handle handle, uint32_t startAddress, uint32_t endAddress); + + static std::vector getPluginInformationByStruct(plugin_information_handle * handleList, uint32_t handleListSize); + + plugin_loader_handle handle = 0; + + uint32_t startAddress = 0; + uint32_t endAddress = 0; + uint32_t currentStoreAddress = 0; +}; + + +#endif diff --git a/src/utils/CMutex.h b/src/utils/CMutex.h new file mode 100644 index 0000000..76f23ac --- /dev/null +++ b/src/utils/CMutex.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * 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 _CMUTEX_H_ +#define _CMUTEX_H_ + +#include +#include + +class CMutex +{ +public: + CMutex() { + pMutex = (OSMutex*) malloc(sizeof(OSMutex)); + if(!pMutex) + return; + + OSInitMutex(pMutex); + } + virtual ~CMutex() { + if(pMutex) + free(pMutex); + } + + void lock(void) { + if(pMutex) + OSLockMutex(pMutex); + } + void unlock(void) { + if(pMutex) + OSUnlockMutex(pMutex); + } + BOOL tryLock(void) { + if(!pMutex) + return false; + + return (OSTryLockMutex(pMutex) != 0); + } +private: + OSMutex *pMutex; +}; + +class CMutexLock +{ +public: + CMutexLock() { + mutex.lock(); + } + virtual ~CMutexLock() { + mutex.unlock(); + } +private: + CMutex mutex; +}; + +#endif // _CMUTEX_H_ diff --git a/src/utils/CThread.h b/src/utils/CThread.h new file mode 100644 index 0000000..be96e90 --- /dev/null +++ b/src/utils/CThread.h @@ -0,0 +1,134 @@ +/**************************************************************************** + * 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 +#include "utils/logger.h" + +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(); + DEBUG_FUNCTION_LINE("END\n"); + } + + 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 diff --git a/src/utils/StringTools.cpp b/src/utils/StringTools.cpp new file mode 100644 index 0000000..c1d672a --- /dev/null +++ b/src/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; +} diff --git a/src/utils/StringTools.h b/src/utils/StringTools.h new file mode 100644 index 0000000..c648f4c --- /dev/null +++ b/src/utils/StringTools.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * 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/src/utils/TcpReceiver.cpp b/src/utils/TcpReceiver.cpp new file mode 100644 index 0000000..bcd3ce7 --- /dev/null +++ b/src/utils/TcpReceiver.cpp @@ -0,0 +1,336 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "TcpReceiver.h" +#include "fs/CFile.hpp" +#include "fs/FSUtils.h" +#include "utils/logger.h" +#include "utils/StringTools.h" +#include "utils/net.h" +#include "utils/utils.h" +#include "plugin/PluginInformationUtils.h" + +#define WUPS_TEMP_PLUGIN_PATH "fs:/vol/external01/wiiu/plugins/temp/" +#define WUPS_TEMP_PLUGIN_FILE "fs:/vol/external01/wiiu/plugins/temp/temp.mod" + +#define RPX_TEMP_PATH "fs:/vol/external01/wiiu/apps/" +#define RPX_TEMP_FILE "fs:/vol/external01/wiiu/apps/temp.rpx" +#define RPX_TEMP_FILE_EX "wiiu/apps/temp.rpx" + +extern "C"{ + uint64_t _SYSGetSystemApplicationTitleId(int32_t); + void _SYSLaunchTitleWithStdArgsInNoSplash(uint64_t, uint32_t); +} +TcpReceiver::TcpReceiver(int32_t port) + : CThread(CThread::eAttributeAffCore0) + , exitRequested(false) + , serverPort(port) + , serverSocket(-1) { + + resumeThread(); +} + +TcpReceiver::~TcpReceiver() { + exitRequested = true; + + if(serverSocket >= 0) { + shutdown(serverSocket, SHUT_RDWR); + } +} + +#define wiiu_geterrno() (socketlasterr()) + +void TcpReceiver::executeThread() { + serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (serverSocket < 0) + return; + + DEBUG_FUNCTION_LINE("\n"); + + uint32_t enable = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); + + struct sockaddr_in bindAddress; + memset(&bindAddress, 0, sizeof(bindAddress)); + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = serverPort; + bindAddress.sin_addr.s_addr = INADDR_ANY; + + DEBUG_FUNCTION_LINE("\n"); + + socklen_t len; + int32_t ret; + if ((ret = bind(serverSocket, (struct sockaddr *)&bindAddress, 16)) < 0) { + socketclose(serverSocket); + return; + } + + DEBUG_FUNCTION_LINE("\n"); + + if ((ret = listen(serverSocket, 1)) < 0) { + socketclose(serverSocket); + return; + } + + DEBUG_FUNCTION_LINE("\n"); + + struct sockaddr_in clientAddr; + memset(&clientAddr, 0, sizeof(clientAddr)); + int32_t addrlen = sizeof(struct sockaddr); + + while(!exitRequested) { + + DEBUG_FUNCTION_LINE("\n"); + len = 16; + int32_t clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &len); + if(clientSocket >= 0) { + + DEBUG_FUNCTION_LINE("\n"); + uint32_t ipAddress = clientAddr.sin_addr.s_addr; + //serverReceiveStart(this, ipAddress); + int32_t result = loadToMemory(clientSocket, ipAddress); + //serverReceiveFinished(this, ipAddress, result); + socketclose(clientSocket); + + if(result > 0) + break; + } else { + DEBUG_FUNCTION_LINE("Server socket accept failed %i %d\n", clientSocket,wiiu_geterrno()); + OSSleepTicks(OSMicrosecondsToTicks(100000)); + } + } + + socketclose(serverSocket); +} + +typedef struct __attribute((packed)) { + uint32_t command; + uint32_t target; + uint32_t filesize; + uint32_t fileoffset; + char path[256]; +}LOAD_REQUEST; + +int32_t TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) { + DEBUG_FUNCTION_LINE("Loading file from ip %08X\n", ipAddress); + + uint32_t fileSize = 0; + uint32_t fileSizeUnc = 0; + unsigned char haxx[8]; + memset(haxx, 0, sizeof(haxx)); + //skip haxx + recvwait(clientSocket, haxx, sizeof(haxx)); + recvwait(clientSocket, (unsigned char*)&fileSize, sizeof(fileSize)); + + if (haxx[4] > 0 || haxx[5] > 4) { + recvwait(clientSocket, (unsigned char*)&fileSizeUnc, sizeof(fileSizeUnc)); // Compressed protocol, read another 4 bytes + } + + struct in_addr in; + uint32_t bytesRead = 0; + in.s_addr = ipAddress; + + DEBUG_FUNCTION_LINE("transfer start\n"); + + unsigned char* loadAddress = (unsigned char*)memalign(0x40, fileSize); + if(!loadAddress) { + OSSleepTicks(OSSecondsToTicks(1)); + return NOT_ENOUGH_MEMORY; + } + + // Copy rpl in memory + while(bytesRead < fileSize) { + + uint32_t blockSize = 0x1000; + if(blockSize > (fileSize - bytesRead)) + blockSize = fileSize - bytesRead; + + int32_t ret = recv(clientSocket, loadAddress + bytesRead, blockSize, 0); + if(ret <= 0) { + DEBUG_FUNCTION_LINE("Failure on reading file\n"); + break; + } + + bytesRead += ret; + } + + if(bytesRead != fileSize) { + free(loadAddress); + DEBUG_FUNCTION_LINE("File loading not finished, %i of %i bytes received\n", bytesRead, fileSize); + return FILE_READ_ERROR; + } + + bool res = false; + bool loadedRPX = false; + + // Do we need to unzip this thing? + if (haxx[4] > 0 || haxx[5] > 4) { + unsigned char* inflatedData = NULL; + + // We need to unzip... + if (loadAddress[0] == 'P' && loadAddress[1] == 'K' && loadAddress[2] == 0x03 && loadAddress[3] == 0x04) { + //! TODO: + //! mhmm this is incorrect, it has to parse the zip + + // Section is compressed, inflate + inflatedData = (unsigned char*)malloc(fileSizeUnc); + if(!inflatedData) { + free(loadAddress); + + return NOT_ENOUGH_MEMORY; + } + + int32_t ret = 0; + z_stream s; + memset(&s, 0, sizeof(s)); + + s.zalloc = Z_NULL; + s.zfree = Z_NULL; + s.opaque = Z_NULL; + + ret = inflateInit(&s); + if (ret != Z_OK) { + free(loadAddress); + free(inflatedData); + + return FILE_READ_ERROR; + } + + s.avail_in = fileSize; + s.next_in = (Bytef *)(&loadAddress[0]); + + s.avail_out = fileSizeUnc; + s.next_out = (Bytef *)&inflatedData[0]; + + ret = inflate(&s, Z_FINISH); + if (ret != Z_OK && ret != Z_STREAM_END) { + free(loadAddress); + free(inflatedData); + + return FILE_READ_ERROR; + } + + inflateEnd(&s); + fileSize = fileSizeUnc; + } else { + // Section is compressed, inflate + inflatedData = (unsigned char*)malloc(fileSizeUnc); + if(!inflatedData) { + free(loadAddress); + + return NOT_ENOUGH_MEMORY; + } + + uLongf f = fileSizeUnc; + int32_t result = uncompress((Bytef*)&inflatedData[0], &f, (Bytef*)loadAddress, fileSize); + if(result != Z_OK) { + DEBUG_FUNCTION_LINE("uncompress failed %i\n", result); + + return FILE_READ_ERROR; + } + + fileSizeUnc = f; + fileSize = fileSizeUnc; + } + + if(inflatedData[0x7] == 0xCA && inflatedData[0x8] == 0xFE){ + DEBUG_FUNCTION_LINE("Try to load a rpx\n"); + FSUtils::CreateSubfolder(RPX_TEMP_PATH); + res = FSUtils::saveBufferToFile(RPX_TEMP_FILE,inflatedData, fileSize); + free(inflatedData); + loadedRPX = true; + }else{ + FSUtils::CreateSubfolder(WUPS_TEMP_PLUGIN_PATH); + res = FSUtils::saveBufferToFile(WUPS_TEMP_PLUGIN_FILE,inflatedData, fileSize); + free(inflatedData); + } + + } else { + if(loadAddress[0x7] == 0xCA && loadAddress[0x8] == 0xFE){ + DEBUG_FUNCTION_LINE("Try to load a rpx\n"); + FSUtils::CreateSubfolder(RPX_TEMP_PATH); + res = FSUtils::saveBufferToFile(RPX_TEMP_FILE,loadAddress, fileSize); + free(loadAddress); + loadedRPX = true; + }else{ + FSUtils::CreateSubfolder(WUPS_TEMP_PLUGIN_PATH); + res = FSUtils::saveBufferToFile(WUPS_TEMP_PLUGIN_FILE,loadAddress, fileSize); + free(loadAddress); + } + } + + if(!res) { + return NOT_ENOUGH_MEMORY; + } + + if(loadedRPX){ + LOAD_REQUEST request; + memset(&request, 0, sizeof(request)); + + log_printf("Loading file %s\n", RPX_TEMP_FILE_EX); + request.command = 0xFC; // IPC_CUSTOM_LOAD_CUSTOM_RPX; + request.target = 0; // LOAD_FILE_TARGET_SD_CARD + request.filesize = 0; // unknown + request.fileoffset = 0; // + + strncpy(request.path, RPX_TEMP_FILE_EX, 255); + + int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode)0); + if(mcpFd >= 0) { + int out = 0; + IOS_Ioctl(mcpFd, 100, &request, sizeof(request), &out, sizeof(out)); + IOS_Close(mcpFd); + if(out == 2) { + + } + } + + uint64_t titleID = _SYSGetSystemApplicationTitleId(8); + _SYSLaunchTitleWithStdArgsInNoSplash(titleID, 0); + return fileSize; + } + + + PluginInformation * newFile = PluginInformationUtils::loadPluginInformation("sd:/wiiu/plugins/temp/temp.mod"); + if(newFile == NULL){ + return -1; + } + + std::vector alreadyLoaded = PluginInformationUtils::getPluginsLoadedInMemory(); + + std::vector newList; + + newList.push_back(newFile); + + for (std::vector::iterator it = alreadyLoaded.begin() ; it != alreadyLoaded.end(); ++it) { + PluginInformation * curPlugin = *it; + if(curPlugin->getPath().compare(newFile->getPath()) != 0){ + if(curPlugin->getName().compare(newFile->getName()) == 0 && + curPlugin->getAuthor().compare(newFile->getAuthor()) == 0 + ){ + DEBUG_FUNCTION_LINE("Name and Author of the new plugin are identical to an old one. Loading the new one! %s %s\n",newFile->getName().c_str(),newFile->getAuthor().c_str()); + continue; + } + newList.push_back(curPlugin); + }else{ + DEBUG_FUNCTION_LINE("%s was overridden\n",newFile->getPath().c_str()); + } + } + + PluginInformationUtils::loadAndLinkPluginsOnRestart(newList); + + alreadyLoaded.push_back(newFile); + PluginInformationUtils::clearPluginInformation(alreadyLoaded); + + SYSRelaunchTitle(NULL,NULL); + + return fileSize; +} diff --git a/src/utils/TcpReceiver.h b/src/utils/TcpReceiver.h new file mode 100644 index 0000000..6f208df --- /dev/null +++ b/src/utils/TcpReceiver.h @@ -0,0 +1,38 @@ +#ifndef TCP_RECEIVER_H_ +#define TCP_RECEIVER_H_ + +#include +#include + +#include "CThread.h" + +class TcpReceiver : public CThread { +public: + enum eLoadResults { + SUCCESS = 0, + INVALID_INPUT = -1, + FILE_OPEN_FAILURE = -2, + FILE_READ_ERROR = -3, + NOT_ENOUGH_MEMORY = -4, + NOT_A_VALID_PLUGIN = -5, + }; + + TcpReceiver(int32_t port); + ~TcpReceiver(); + + //sigslot::signal2 serverReceiveStart; + //sigslot::signal3 serverReceiveFinished; + +private: + + void executeThread(); + int32_t loadToMemory(int32_t clientSocket, uint32_t ipAddress); + bool saveFileToSDCard(const char * path, void * buffer,uint32_t size); + + bool exitRequested; + int32_t serverPort; + int32_t serverSocket; +}; + + +#endif diff --git a/src/utils/ipcclient.cpp b/src/utils/ipcclient.cpp new file mode 100644 index 0000000..acad836 --- /dev/null +++ b/src/utils/ipcclient.cpp @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include "utils/logger.h" +#include "utils/utils.h" + +#include "ipcclient.h" + +int (*ipc_ioctl)(ipcmessage *message) = (int (*)(ipcmessage*)) *(uint32_t*)0x80800000; + +#define ALIGN(align) __attribute__((aligned(align))) + +int32_t doIOCTL(int32_t command, uint32_t *in_buf, uint32_t in_length, uint32_t *io_buf, uint32_t io_length) { + ALIGN(0x20) ipcmessage message; + + memset(&message,0,sizeof(message)); + + message.command = command; + + message.ioctl.buffer_in = in_buf; + message.ioctl.length_in = in_length; + message.ioctl.buffer_io = io_buf; + message.ioctl.length_io = io_length; + + DEBUG_FUNCTION_LINE("command: %d in_buf %08X size: %d io_buf %08X size: %d \n",command, in_buf,in_length,io_buf,io_length); + + //DCFlushRange(&message, sizeof(ipcmessage)); + //ICInvalidatRange(&message, sizeof(ipcmessage)); + + return ((int (*)(ipcmessage *))((uint32_t*)*((uint32_t*)0x80800000)) )(&message); +} + +plugin_loader_handle IPC_Open_Plugin_Loader(uint32_t startAddress, uint32_t endAddress) { + uint32_t *io_buf = (uint32_t*)memalign(0x20, ROUNDUP(8,0x20)); + if(!io_buf) { + return (plugin_loader_handle) NULL; + } + + io_buf[0] = startAddress; + io_buf[1] = endAddress; + + int32_t ret = doIOCTL(IOCTL_OPEN_PLUGIN_LOADER, io_buf, 8, io_buf, 4); + if(ret < 0) { + free(io_buf); + return (plugin_loader_handle) NULL; + } + + plugin_information_handle result = (plugin_loader_handle) io_buf[0]; + free(io_buf); + return result; +} + +bool IPC_Close_Plugin_Loader(plugin_loader_handle handle) { + uint32_t *io_buf = (uint32_t*)memalign(0x20, ROUNDUP(4,0x20)); + if(!io_buf) { + return false; + } + + io_buf[0] = handle; + + int32_t ret = doIOCTL(IOCTL_CLOSE_PLUGIN_LOADER, io_buf, 4, NULL, 0); + if(ret < 0) { + free(io_buf); + return false; + } + + free(io_buf); + return true; +} + +int32_t IPC_Get_Plugin_Information(const char * path, plugin_information_handle ** handleList, uint32_t * handleListSize) { + uint32_t buffersize = ROUNDUP((128 * sizeof(plugin_information_handle)) + 4,0x20); + uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize); + if(!io_buf) { + return -1; + } + + io_buf[0] = (uint32_t) path; + + int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_GET_INFORMATION_FOR_PATH, io_buf, 4, io_buf, buffersize); + if(ret < 0) { + free(io_buf); + return ret; + } + uint32_t length = io_buf[0]; + if(handleListSize != NULL) { + *handleListSize = length; + } + + uint32_t result = -1; + + if(handleList != NULL) { + // we create a new buffer so the caller can free it properly + uint32_t outbuffersize = ROUNDUP((length * sizeof(plugin_information_handle)),0x20); + *handleList = (uint32_t*)memalign(0x20, outbuffersize); + if(*handleList != NULL) { + result = 0; + memcpy(*handleList, &(io_buf[1]), length * sizeof(plugin_information_handle)); + } + } + + free(io_buf); + return result; +} + +int32_t IPC_Get_Plugin_Information_Loaded(plugin_information_handle ** handleList, uint32_t * handleListSize) { + uint32_t buffersize = ROUNDUP((128 * sizeof(plugin_information_handle)),0x20); + uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize); + if(!io_buf) { + return -1; + } + + int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_GET_INFORMATION_LOADED, io_buf, 0, io_buf, buffersize); + if(ret < 0) { + free(io_buf); + return ret; + } + // DEBUG_FUNCTION_LINE("IPC_Get_Plugin_Information_Loaded was fine\n"); + + uint32_t length = io_buf[0]; + if(handleListSize != NULL) { + // DEBUG_FUNCTION_LINE("length set to %d\n", length); + *handleListSize = length; + } + uint32_t result = -1; + + if(handleList != NULL && length > 0) { + // we create a new buffer so the caller can free it properly + uint32_t outbuffersize = ROUNDUP((length * sizeof(plugin_information_handle)),0x20); + *handleList = (uint32_t*)memalign(0x20, outbuffersize); + if(*handleList != NULL) { + result = 0; + memcpy(*handleList, &(io_buf[1]), length * sizeof(plugin_information_handle)); + } + } + + free(io_buf); + return result; +} + +int32_t IPC_Get_Plugin_Information_Details(plugin_information_handle * handles, uint32_t handlesize, plugin_information ** informationList, uint32_t * informationListSize) { + uint32_t buffersize = ROUNDUP((handlesize * sizeof(plugin_information)),0x20); + if(buffersize < 8){ + buffersize = 8; + } + uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize); + if(!io_buf) { + if(io_buf != NULL) { + free(io_buf); + } + return -1; + } + + io_buf[0] = (uint32_t) handles; + io_buf[1] = handlesize; + + int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_GET_INFORMATION_DETAILS, io_buf, 8, io_buf, buffersize); + if(ret < 0) { + free(io_buf); + return ret; + } + + uint32_t result = -1; + + if(informationListSize != NULL) { + *informationListSize = handlesize; + } + + if(informationList != NULL) { + // we create a new buffer so the caller can free it properly + uint32_t outbuffersize = ROUNDUP((handlesize * sizeof(plugin_information)),0x20); + *informationList = (plugin_information*)memalign(0x20, outbuffersize); + if(*informationList != NULL) { + result = 0; + memcpy(*informationList, &(io_buf[0]), handlesize * sizeof(plugin_information)); + } + } + + free(io_buf); + return result; +} + +int32_t IPC_Delete_Plugin_Information(plugin_information_handle handle) { + uint32_t *io_buf = (uint32_t*)memalign(0x20, ROUNDUP(4,0x20)); + if(!io_buf) { + if(io_buf != NULL) { + free(io_buf); + } + return -1; + } + + io_buf[0] = (uint32_t) handle; + + int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_DELETE_INFORMATION, io_buf, 4, NULL, 0); + if(ret < 0) { + free(io_buf); + return ret; + } + + free(io_buf); + return 0; +} + +int32_t IPC_Link_Plugin_Information(plugin_loader_handle handle, plugin_information_handle * handleList, uint32_t listSize) { + uint32_t buffersize = ROUNDUP((listSize * sizeof(plugin_information_handle)),0x20); + uint32_t io_buffersize = ROUNDUP(12,0x20); + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buffersize); + uint32_t * buf = (uint32_t*)memalign(0x20, buffersize); + if(!io_buf || !buf) { + if(buf != NULL) { + free(buf); + } + if(io_buf != NULL) { + free(io_buf); + } + return -1; + } + + memcpy(buf, handleList, listSize * sizeof(plugin_information_handle*)); + + io_buf[0] = handle; + io_buf[1] = (uint32_t) buf; + io_buf[2] = listSize; + + int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION, io_buf, 12, io_buf, io_buffersize); + if(ret < 0) { + free(io_buf); + free(buf); + return ret; + } + int32_t result = (int32_t) io_buf[0]; + + free(io_buf); + free(buf); + return result; +} + +int32_t IPC_Link_Plugin_Information_On_Restart(plugin_information_handle * handleList, uint32_t listSize) { + uint32_t buffersize = ROUNDUP((listSize * sizeof(plugin_information_handle)),0x20); + uint32_t io_buffersize = ROUNDUP(8,0x20); + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buffersize); + uint32_t * buf = (uint32_t*)memalign(0x20, buffersize); + if(!io_buf || !buf) { + if(buf != NULL) { + free(buf); + } + if(io_buf != NULL) { + free(io_buf); + } + return -1; + } + + memcpy(buf, handleList, listSize * sizeof(plugin_information_handle*)); + + io_buf[0] = (uint32_t) buf; + io_buf[1] = listSize; + + int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION_ON_RESTART, io_buf, 8, io_buf, io_buffersize); + if(ret < 0) { + free(io_buf); + free(buf); + return ret; + } + int32_t result = (int32_t) io_buf[0]; + + free(io_buf); + free(buf); + return result; +} + + + +int32_t IPC_Get_Plugin_Information_For_Filepath(const char * path, plugin_information_handle * handle) { + uint32_t buffersize = ROUNDUP((sizeof(plugin_information_handle)),0x20); + uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize); + if(!io_buf) { + return -1; + } + + io_buf[0] = (uint32_t) path; + + int32_t ret = doIOCTL(IOCTL_PLUGIN_INFORMATION_GET_INFORMATION_FOR_FILEPATH, io_buf, 4, io_buf, 4); + if(ret < 0) { + free(io_buf); + return ret; + } + *handle = io_buf[0]; + + free(io_buf); + return 0; +} + + diff --git a/src/utils/ipcclient.h b/src/utils/ipcclient.h new file mode 100644 index 0000000..8c4625b --- /dev/null +++ b/src/utils/ipcclient.h @@ -0,0 +1,71 @@ +#ifndef __IPC_UTILS_H_ +#define __IPC_UTILS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define IPC_ERROR_INVALID_NONE 0 +#define IPC_ERROR_INVALID_SIZE 0xFFFFFFFF +#define IPC_ERROR_INVALID_ARG 0xFFFFFFFE +#define IPC_ERROR_FAILED_ALLOC 0xFFFFFFFD + +#define IOCTL_OPEN_PLUGIN_LOADER 0x01 +#define IOCTL_CLOSE_PLUGIN_LOADER 0x02 +#define IOCTL_PLUGIN_LOADER_GET_INFORMATION_FOR_PATH 0x03 +#define IOCTL_PLUGIN_LOADER_GET_INFORMATION_LOADED 0x04 +#define IOCTL_PLUGIN_LOADER_GET_INFORMATION_DETAILS 0x05 +#define IOCTL_PLUGIN_LOADER_DELETE_INFORMATION 0x06 +#define IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION 0x07 +#define IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION_ON_RESTART 0x08 +#define IOCTL_PLUGIN_INFORMATION_GET_INFORMATION_FOR_FILEPATH 0x09 + +/* IPC message */ +typedef struct ipcmessage { + uint32_t command; + union { + struct { + uint32_t *buffer_in; + uint32_t length_in; + uint32_t *buffer_io; + uint32_t length_io; + } ioctl; + }; +} __attribute__((packed)) ipcmessage; + +typedef uint32_t plugin_information_handle; +typedef uint32_t plugin_loader_handle; + +/* plugin_information message */ +typedef struct plugin_information { + plugin_information_handle handle; + char path[256]; + char name[256]; + char author[256]; +} plugin_information; + +extern int (*ipc_ioctl)(ipcmessage *message); + +plugin_loader_handle IPC_Open_Plugin_Loader(uint32_t startAddress, uint32_t endAddress); + +bool IPC_Close_Plugin_Loader(plugin_loader_handle handle); + + +int32_t IPC_Get_Plugin_Information(const char * path, plugin_information_handle ** handleList, uint32_t * handleListSize); +int32_t IPC_Get_Plugin_Information_For_Filepath(const char * path, plugin_information_handle * handle); +int32_t IPC_Get_Plugin_Information_Loaded(plugin_information_handle ** handleList, uint32_t * handleListSize); +int32_t IPC_Get_Plugin_Information_Details(plugin_information_handle * handles, uint32_t handlesize, plugin_information ** informationList, uint32_t * informationListSize); + +int32_t IPC_Delete_Plugin_Information(plugin_information_handle handle); + +int32_t IPC_Link_Plugin_Information(plugin_loader_handle handle, plugin_information_handle * handleList, uint32_t listSize); +int32_t IPC_Link_Plugin_Information_On_Restart(plugin_information_handle * handleList, uint32_t listSize); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/utils/logger.c b/src/utils/logger.c new file mode 100644 index 0000000..0922230 --- /dev/null +++ b/src/utils/logger.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int log_socket __attribute__((section(".data")))= -1; +static struct sockaddr_in connect_addr __attribute__((section(".data"))); +static volatile int log_lock __attribute__((section(".data"))) = 0; + +void log_init_() { + int broadcastEnable = 1; + log_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (log_socket < 0) + return; + + setsockopt(log_socket, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)); + + memset(&connect_addr, 0, sizeof(struct sockaddr_in)); + connect_addr.sin_family = AF_INET; + connect_addr.sin_port = 4405; + connect_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); +} + +void log_print_(const char *str) { + // socket is always 0 initially as it is in the BSS + if(log_socket < 0) { + return; + } + + while(log_lock) + OSSleepTicks(OSMicrosecondsToTicks(1000)); + log_lock = 1; + + int len = strlen(str); + int ret; + while (len > 0) { + int block = len < 1400 ? len : 1400; // take max 1400 bytes per UDP packet + ret = sendto(log_socket, str, block, 0, (struct sockaddr *)&connect_addr, sizeof(struct sockaddr_in)); + if(ret < 0) + break; + + len -= ret; + str += ret; + } + + log_lock = 0; +} + +void OSFatal_printf(const char *format, ...) { + char tmp[512]; + tmp[0] = 0; + va_list va; + va_start(va, format); + if((vsprintf(tmp, format, va) >= 0)) { + OSFatal(tmp); + } + va_end(va); +} + +void log_printf_(const char *format, ...) { + if(log_socket < 0) { + return; + } + + char tmp[512]; + tmp[0] = 0; + + va_list va; + va_start(va, format); + if((vsprintf(tmp, format, va) >= 0)) { + log_print_(tmp); + } + va_end(va); +} + diff --git a/src/utils/logger.h b/src/utils/logger.h new file mode 100644 index 0000000..d026b05 --- /dev/null +++ b/src/utils/logger.h @@ -0,0 +1,38 @@ +#ifndef __LOGGER_H_ +#define __LOGGER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void log_init_(); +//void log_deinit_(void); +void log_print_(const char *str); +void log_printf_(const char *format, ...); +void OSFatal_printf(const char *format, ...); + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \ + OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + + + +#define log_init() log_init_() +//#define log_deinit() log_deinit_() +#define log_print(str) log_print_(str) +#define log_printf(FMT, ARGS...) log_printf_(FMT, ## ARGS); + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + log_printf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/utils/net.c b/src/utils/net.c new file mode 100644 index 0000000..c615d92 --- /dev/null +++ b/src/utils/net.c @@ -0,0 +1,84 @@ +#include "net.h" +#include + +static volatile int socket_lock __attribute__((section(".data"))) = 0; + +int32_t recvwait(int32_t sock, void *buffer, int32_t len) { + while(socket_lock) { + usleep(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) { + usleep(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) { + usleep(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); +} diff --git a/src/utils/net.h b/src/utils/net.h new file mode 100644 index 0000000..759fe56 --- /dev/null +++ b/src/utils/net.h @@ -0,0 +1,20 @@ +#ifndef _UTILS_NET_H_ +#define _UTILS_NET_H_ +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +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 diff --git a/src/utils/romfs_dev.c b/src/utils/romfs_dev.c new file mode 100644 index 0000000..34e9986 --- /dev/null +++ b/src/utils/romfs_dev.c @@ -0,0 +1,702 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "romfs_dev.h" + +typedef enum { + RomfsSource_FileDescriptor, +} RomfsSource; + +typedef struct romfs_mount { + devoptab_t device; + bool setup; + RomfsSource fd_type; + int32_t id; + int32_t fd; + time_t mtime; + uint64_t offset; + romfs_header header; + romfs_dir *cwd; + uint32_t *dirHashTable, *fileHashTable; + void *dirTable, *fileTable; + char name[32]; +} romfs_mount; + +extern int __system_argc; +extern char** __system_argv; + +//static char __thread __component[PATH_MAX+1]; +static char __component[PATH_MAX+1]; + +#define romFS_root(m) ((romfs_dir*)(m)->dirTable) +#define romFS_dir(m,x) ((romfs_dir*) ((uint8_t*)(m)->dirTable + (x))) +#define romFS_file(m,x) ((romfs_file*)((uint8_t*)(m)->fileTable + (x))) +#define romFS_none ((uint32_t)~0) +#define romFS_dir_mode (S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH) +#define romFS_file_mode (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH) + +uint64_t swapLong(uint64_t X) { + uint64_t x = (uint64_t) X; + x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32; + x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16; + x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8; + return x; +} + +#define REVERSE_SHORT(n) ((unsigned short) (((n & 0xFF) << 8) | \ + ((n & 0xFF00) >> 8))) +#define REVERSE_INT(n) ((unsigned int) (((n & 0xFF) << 24) | \ + ((n & 0xFF00) << 8) | \ + ((n & 0xFF0000) >> 8) | \ + ((n & 0xFF000000) >> 24))) + +static ssize_t _romfs_read(romfs_mount *mount, uint64_t offset, void* buffer, uint64_t size) { + uint64_t pos = mount->offset + offset; + size_t _read = 0; + if(mount->fd_type == RomfsSource_FileDescriptor) { + off_t seek_offset = lseek(mount->fd, pos, SEEK_SET); + if(pos != seek_offset) { + return -1; + } + _read = read(mount->fd, buffer, size); + } + return _read; +} + +static bool _romfs_read_chk(romfs_mount *mount, uint64_t offset, void* buffer, uint64_t size) { + return _romfs_read(mount, offset, buffer, size) == size; +} + +//----------------------------------------------------------------------------- + +static int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode); +static int romfs_close(struct _reent *r, void *fd); +static ssize_t romfs_read(struct _reent *r, void *fd, char *ptr, size_t len); +static off_t romfs_seek(struct _reent *r, void *fd, off_t pos, int dir); +static int romfs_fstat(struct _reent *r, void *fd, struct stat *st); +static int romfs_stat(struct _reent *r, const char *path, struct stat *st); +static int romfs_chdir(struct _reent *r, const char *path); +static DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path); +static int romfs_dirreset(struct _reent *r, DIR_ITER *dirState); +static int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +static int romfs_dirclose(struct _reent *r, DIR_ITER *dirState); + +typedef struct { + romfs_mount *mount; + romfs_file *file; + uint64_t offset, pos; +} romfs_fileobj; + +typedef struct { + romfs_mount *mount; + romfs_dir* dir; + uint32_t state; + uint32_t childDir; + uint32_t childFile; +} romfs_diriter; + +static devoptab_t romFS_devoptab = { + .structSize = sizeof(romfs_fileobj), + .open_r = romfs_open, + .close_r = romfs_close, + .read_r = romfs_read, + .seek_r = romfs_seek, + .fstat_r = romfs_fstat, + .stat_r = romfs_stat, + .chdir_r = romfs_chdir, + .dirStateSize = sizeof(romfs_diriter), + .diropen_r = romfs_diropen, + .dirreset_r = romfs_dirreset, + .dirnext_r = romfs_dirnext, + .dirclose_r = romfs_dirclose, +}; + +static bool romfs_initialised = false; +static romfs_mount romfs_mounts[32]; + +//----------------------------------------------------------------------------- + +static int32_t romfsMountCommon(const char *name, romfs_mount *mount); +static void romfsInitMtime(romfs_mount *mount); + +static void _romfsResetMount(romfs_mount *mount, int32_t id) { + memset(mount, 0, sizeof(*mount)); + memcpy(&mount->device, &romFS_devoptab, sizeof(romFS_devoptab)); + mount->device.name = mount->name; + mount->device.deviceData = mount; + mount->id = id; +} + +static void _romfsInit(void) { + uint32_t i; + uint32_t total = sizeof(romfs_mounts) / sizeof(romfs_mount); + + if(!romfs_initialised) { + for(i = 0; i < total; i++) { + _romfsResetMount(&romfs_mounts[i], i); + } + + romfs_initialised = true; + } +} + +static romfs_mount *romfsFindMount(const char *name) { + uint32_t i; + uint32_t total = sizeof(romfs_mounts) / sizeof(romfs_mount); + romfs_mount *mount = NULL; + + _romfsInit(); + + for(i=0; isetup) + return mount; + } else if(mount->setup) { //Find the mount with the input name. + if(strncmp(mount->name, name, sizeof(mount->name))==0) + return mount; + } + } + + return NULL; +} + +__attribute__((weak)) const char* __romfs_path = NULL; + +static romfs_mount* romfs_alloc(void) { + return romfsFindMount(NULL); +} + +static void romfs_free(romfs_mount *mount) { + free(mount->fileTable); + free(mount->fileHashTable); + free(mount->dirTable); + free(mount->dirHashTable); + _romfsResetMount(mount, mount->id); +} + +static void romfs_mountclose(romfs_mount *mount) { + if(mount->fd_type == RomfsSource_FileDescriptor) { + close(mount->fd); + } + romfs_free(mount); +} + +int32_t romfsMount(const char *name, const char * filepath) { + romfs_mount *mount = romfs_alloc(); + if(mount == NULL) + return 99; + + // Regular RomFS + mount->fd_type = RomfsSource_FileDescriptor; + + mount->fd = open(filepath, 0); + if (mount->fd == -1) { + romfs_free(mount); + return -1; + } + + romfsInitMtime(mount); + return romfsMountCommon(name, mount); + +_fail0: + romfs_mountclose(mount); + return 10; +} + +int32_t romfsMountCommon(const char *name, romfs_mount *mount) { + memset(mount->name, 0, sizeof(mount->name)); + strncpy(mount->name, name, sizeof(mount->name)-1); + + if (_romfs_read(mount, 0, &mount->header, sizeof(mount->header)) != sizeof(mount->header)) + goto fail; + + mount->dirHashTable = (uint32_t*)malloc(swapLong(mount->header.dirHashTableSize)); + if (!mount->dirHashTable) + goto fail; + if (!_romfs_read_chk(mount, swapLong(mount->header.dirHashTableOff), mount->dirHashTable, swapLong(mount->header.dirHashTableSize))) + goto fail; + + mount->dirTable = malloc(swapLong(mount->header.dirTableSize)); + if (!mount->dirTable) + goto fail; + if (!_romfs_read_chk(mount, swapLong(mount->header.dirTableOff), mount->dirTable, swapLong(mount->header.dirTableSize))) + goto fail; + mount->fileHashTable = (uint32_t*)malloc(swapLong(mount->header.fileHashTableSize)); + if (!mount->fileHashTable) + goto fail; + if (!_romfs_read_chk(mount, swapLong(mount->header.fileHashTableOff), mount->fileHashTable, swapLong(mount->header.fileHashTableSize))) + goto fail; + mount->fileTable = malloc(swapLong(mount->header.fileTableSize)); + if (!mount->fileTable) + goto fail; + + if (!_romfs_read_chk(mount, swapLong(mount->header.fileTableOff), mount->fileTable, swapLong(mount->header.fileTableSize))) + goto fail; + mount->cwd = romFS_root(mount); + + // add device if this is the first one + if(AddDevice(&mount->device) < 0) + goto fail; + mount->setup = true; + return 0; + +fail: + romfs_mountclose(mount); + return 10; +} + +static void romfsInitMtime(romfs_mount *mount) { + mount->mtime = time(NULL); +} + +int32_t romfsUnmount(const char *name) { + romfs_mount *mount; + char tmpname[34]; + + mount = romfsFindMount(name); + if (mount == NULL) + return -1; + + // Remove device + memset(tmpname, 0, sizeof(tmpname)); + strncpy(tmpname, mount->name, sizeof(tmpname)-2); + strncat(tmpname, ":", sizeof(tmpname)-strlen(tmpname)-1); + + RemoveDevice(tmpname); + + romfs_mountclose(mount); + + return 0; +} + +//----------------------------------------------------------------------------- + +static uint32_t calcHash(uint32_t parent, const uint8_t* name, uint32_t namelen, uint32_t total) { + uint32_t hash = parent ^ 123456789; + uint32_t i; + for (i = 0; i < namelen; i ++) { + hash = (hash >> 5) | (hash << 27); + hash ^= name[i]; + } + return hash % total; +} + +static romfs_dir* searchForDir(romfs_mount *mount, romfs_dir* parent, const uint8_t* name, uint32_t namelen) { + uint64_t parentOff = (uintptr_t)parent - (uintptr_t)mount->dirTable; + uint32_t hash = calcHash(parentOff, name, namelen, swapLong(mount->header.dirHashTableSize)/4); + romfs_dir* curDir = NULL; + uint32_t curOff; + for (curOff = REVERSE_INT(mount->dirHashTable[hash]); curOff != romFS_none; curOff = REVERSE_INT(curDir->nextHash)) { + curDir = romFS_dir(mount, curOff); + if (REVERSE_INT(curDir->parent) != parentOff) + continue; + if (REVERSE_INT(curDir->nameLen) != namelen) + continue; + if (memcmp(curDir->name, name, namelen) != 0) + continue; + return curDir; + } + return NULL; +} + +static romfs_file* searchForFile(romfs_mount *mount, romfs_dir* parent, const uint8_t* name, uint32_t namelen) { + uint64_t parentOff = (uintptr_t)parent - (uintptr_t)mount->dirTable; + uint32_t hash = calcHash(parentOff, name, namelen, swapLong(mount->header.fileHashTableSize)/4); + romfs_file* curFile = NULL; + uint32_t curOff; + for (curOff = REVERSE_INT(mount->fileHashTable[hash]); curOff != romFS_none; curOff = REVERSE_INT(curFile->nextHash)) { + curFile = romFS_file(mount, curOff); + if (REVERSE_INT(curFile->parent) != parentOff) + continue; + if (REVERSE_INT(curFile->nameLen) != namelen) + continue; + if (memcmp(curFile->name, name, namelen) != 0) + continue; + return curFile; + } + return NULL; +} + +static int navigateToDir(romfs_mount *mount, romfs_dir** ppDir, const char** pPath, bool isDir) { + char* colonPos = strchr(*pPath, ':'); + if (colonPos) + *pPath = colonPos+1; + if (!**pPath) + return EILSEQ; + + *ppDir = mount->cwd; + if (**pPath == '/') { + *ppDir = romFS_root(mount); + (*pPath)++; + } + + while (**pPath) { + char* slashPos = strchr(*pPath, '/'); + char* component = __component; + + if (slashPos) { + uint32_t len = slashPos - *pPath; + if (!len) + return EILSEQ; + if (len > PATH_MAX) + return ENAMETOOLONG; + + memcpy(component, *pPath, len); + component[len] = 0; + *pPath = slashPos+1; + } else if (isDir) { + component = (char*)*pPath; + *pPath += strlen(component); + } else + return 0; + + if (component[0]=='.') { + if (!component[1]) + continue; + if (component[1]=='.' && !component[2]) { + *ppDir = romFS_dir(mount, REVERSE_INT((*ppDir)->parent)); + continue; + } + } + + *ppDir = searchForDir(mount, *ppDir, (uint8_t*)component, strlen(component)); + if (!*ppDir) + return EEXIST; + } + + if (!isDir && !**pPath) + return EILSEQ; + + return 0; +} + +static ino_t dir_inode(romfs_mount *mount, romfs_dir *dir) { + return (uint32_t*)dir - (uint32_t*)mount->dirTable; +} + +static off_t dir_size(romfs_dir *dir) { + return sizeof(romfs_dir) + (REVERSE_INT(dir->nameLen)+3)/4; +} + +static nlink_t dir_nlink(romfs_mount *mount, romfs_dir *dir) { + nlink_t count = 2; // one for self, one for parent + uint32_t offset = REVERSE_INT(dir->childDir); + + while(offset != romFS_none) { + romfs_dir *tmp = romFS_dir(mount, offset); + ++count; + offset = REVERSE_INT(tmp->sibling); + } + + offset = REVERSE_INT(dir->childFile); + while(offset != romFS_none) { + romfs_file *tmp = romFS_file(mount, offset); + ++count; + offset = REVERSE_INT(tmp->sibling); + } + + return count; +} + +static ino_t file_inode(romfs_mount *mount, romfs_file *file) { + return ((uint32_t*)file - (uint32_t*)mount->fileTable) + swapLong(mount->header.dirTableSize)/4; +} + +int romfs_GetFileInfoPerPath(const char* romfs, const char *path, romfs_fileInfo* out) { + if(out == NULL){ + return -1; + } + romfs_mount* mount = (romfs_mount*)romfsFindMount(romfs); + if(mount == NULL){ + return -2; + } + romfs_dir* curDir = NULL; + int errno2 = navigateToDir(mount, &curDir, &path, false); + if (errno2 != 0){ + return -3; + } + + romfs_file* file = searchForFile(mount, curDir, (uint8_t*)path, strlen(path)); + if (!file) { + return -4; + } + + out->length = swapLong(file->dataSize); + out->offset = swapLong(mount->header.fileDataOff) + swapLong(file->dataOff); + + return 0; +} + + +//----------------------------------------------------------------------------- + +int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { + romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct; + + fileobj->mount = (romfs_mount*)r->deviceData; + + if ((flags & O_ACCMODE) != O_RDONLY) { + r->_errno = EROFS; + return -1; + } + + romfs_dir* curDir = NULL; + r->_errno = navigateToDir(fileobj->mount, &curDir, &path, false); + if (r->_errno != 0) + return -1; + + romfs_file* file = searchForFile(fileobj->mount, curDir, (uint8_t*)path, strlen(path)); + if (!file) { + if(flags & O_CREAT) + r->_errno = EROFS; + else + r->_errno = ENOENT; + return -1; + } else if((flags & O_CREAT) && (flags & O_EXCL)) { + r->_errno = EEXIST; + return -1; + } + + fileobj->file = file; + fileobj->offset = swapLong(fileobj->mount->header.fileDataOff) + swapLong(file->dataOff); + fileobj->pos = 0; + + return 0; +} + +int romfs_close(struct _reent *r, void *fd) { + return 0; +} + +ssize_t romfs_read(struct _reent *r, void *fd, char *ptr, size_t len) { + romfs_fileobj* file = (romfs_fileobj*)fd; + uint64_t endPos = file->pos + len; + + /* check if past end-of-file */ + if(file->pos >= swapLong(file->file->dataSize)) + return 0; + + /* truncate the read to end-of-file */ + if(endPos > swapLong(file->file->dataSize)) + endPos = swapLong(file->file->dataSize); + len = endPos - file->pos; + + ssize_t adv = _romfs_read(file->mount, file->offset + file->pos, ptr, len); + if(adv >= 0) { + file->pos += adv; + return adv; + } + + r->_errno = EIO; + return -1; +} + +off_t romfs_seek(struct _reent *r, void *fd, off_t pos, int dir) { + romfs_fileobj* file = (romfs_fileobj*)fd; + off_t start; + switch (dir) { + case SEEK_SET: + start = 0; + break; + + case SEEK_CUR: + start = file->pos; + break; + + case SEEK_END: + start = swapLong(file->file->dataSize); + break; + + default: + r->_errno = EINVAL; + return -1; + } + + /* don't allow negative position */ + if(pos < 0) { + if(start + pos < 0) { + r->_errno = EINVAL; + return -1; + } + } + /* check for overflow */ + else if(INT64_MAX - pos < start) { + r->_errno = EOVERFLOW; + return -1; + } + + file->pos = start + pos; + return file->pos; +} + +int romfs_fstat(struct _reent *r, void *fd, struct stat *st) { + romfs_fileobj* file = (romfs_fileobj*)fd; + memset(st, 0, sizeof(struct stat)); + st->st_ino = file_inode(file->mount, file->file); + st->st_mode = romFS_file_mode; + st->st_nlink = 1; + st->st_size = (off_t)swapLong(file->file->dataSize); + st->st_blksize = 512; + st->st_blocks = (st->st_blksize + 511) / 512; + st->st_atime = st->st_mtime = st->st_ctime = file->mount->mtime; + + return 0; +} + +int romfs_stat(struct _reent *r, const char *path, struct stat *st) { + romfs_mount* mount = (romfs_mount*)r->deviceData; + romfs_dir* curDir = NULL; + r->_errno = navigateToDir(mount, &curDir, &path, false); + if(r->_errno != 0) + return -1; + + romfs_dir* dir = searchForDir(mount, curDir, (uint8_t*)path, strlen(path)); + if(dir) { + memset(st, 0, sizeof(*st)); + st->st_ino = dir_inode(mount, dir); + st->st_mode = romFS_dir_mode; + st->st_nlink = dir_nlink(mount, dir); + st->st_size = dir_size(dir); + st->st_blksize = 512; + st->st_blocks = (st->st_blksize + 511) / 512; + st->st_atime = st->st_mtime = st->st_ctime = mount->mtime; + + return 0; + } + + romfs_file* file = searchForFile(mount, curDir, (uint8_t*)path, strlen(path)); + if(file) { + memset(st, 0, sizeof(*st)); + st->st_ino = file_inode(mount, file); + st->st_mode = romFS_file_mode; + st->st_nlink = 1; + st->st_size = swapLong(file->dataSize); + st->st_blksize = 512; + st->st_blocks = (st->st_blksize + 511) / 512; + st->st_atime = st->st_mtime = st->st_ctime = mount->mtime; + + return 0; + } + + r->_errno = ENOENT; + return 1; +} + +int romfs_chdir(struct _reent *r, const char *path) { + romfs_mount* mount = (romfs_mount*)r->deviceData; + romfs_dir* curDir = NULL; + r->_errno = navigateToDir(mount, &curDir, &path, false); + if (r->_errno != 0) + return -1; + + mount->cwd = curDir; + return 0; +} + +DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) { + romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct); + romfs_dir* curDir = NULL; + iter->mount = (romfs_mount*)r->deviceData; + + r->_errno = navigateToDir(iter->mount, &curDir, &path, true); + if(r->_errno != 0) + return NULL; + + iter->dir = curDir; + iter->state = 0; + iter->childDir = REVERSE_INT(curDir->childDir); + iter->childFile = REVERSE_INT(curDir->childFile); + + return dirState; +} + +int romfs_dirreset(struct _reent *r, DIR_ITER *dirState) { + romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct); + + iter->state = 0; + iter->childDir = REVERSE_INT(iter->dir->childDir); + iter->childFile = REVERSE_INT(iter->dir->childFile); + + return 0; +} + +int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { + romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct); + + if(iter->state == 0) { + /* '.' entry */ + memset(filestat, 0, sizeof(*filestat)); + filestat->st_ino = dir_inode(iter->mount, iter->dir); + filestat->st_mode = romFS_dir_mode; + + strcpy(filename, "."); + iter->state = 1; + return 0; + } else if(iter->state == 1) { + /* '..' entry */ + romfs_dir* dir = romFS_dir(iter->mount, REVERSE_INT(iter->dir->parent)); + + memset(filestat, 0, sizeof(*filestat)); + filestat->st_ino = dir_inode(iter->mount, dir); + filestat->st_mode = romFS_dir_mode; + + strcpy(filename, ".."); + iter->state = 2; + return 0; + } + + if(iter->childDir != romFS_none) { + romfs_dir* dir = romFS_dir(iter->mount, iter->childDir); + iter->childDir = REVERSE_INT(dir->sibling); + + memset(filestat, 0, sizeof(*filestat)); + filestat->st_ino = dir_inode(iter->mount, dir); + filestat->st_mode = romFS_dir_mode; + + memset(filename, 0, NAME_MAX); + + if(REVERSE_INT(dir->nameLen) >= NAME_MAX) { + r->_errno = ENAMETOOLONG; + return -1; + } + + strncpy(filename, (char*)dir->name, REVERSE_INT(dir->nameLen)); + + return 0; + } else if(iter->childFile != romFS_none) { + romfs_file* file = romFS_file(iter->mount, iter->childFile); + iter->childFile = REVERSE_INT(file->sibling); + + memset(filestat, 0, sizeof(*filestat)); + filestat->st_ino = file_inode(iter->mount, file); + filestat->st_mode = romFS_file_mode; + + memset(filename, 0, NAME_MAX); + + if(REVERSE_INT(file->nameLen) >= NAME_MAX) { + r->_errno = ENAMETOOLONG; + return -1; + } + + strncpy(filename, (char*)file->name, REVERSE_INT(file->nameLen)); + + return 0; + } + + r->_errno = ENOENT; + return -1; +} + +int romfs_dirclose(struct _reent *r, DIR_ITER *dirState) { + return 0; +} diff --git a/src/utils/romfs_dev.h b/src/utils/romfs_dev.h new file mode 100644 index 0000000..367f202 --- /dev/null +++ b/src/utils/romfs_dev.h @@ -0,0 +1,90 @@ +/** + * @file romfs_dev.h + * @brief RomFS driver. + * @author yellows8 + * @author mtheall + * @author fincs + * @copyright libnx Authors + */ +#pragma once + +#include + +/// RomFS header. +typedef struct { + uint64_t headerSize; ///< Size of the header. + uint64_t dirHashTableOff; ///< Offset of the directory hash table. + uint64_t dirHashTableSize; ///< Size of the directory hash table. + uint64_t dirTableOff; ///< Offset of the directory table. + uint64_t dirTableSize; ///< Size of the directory table. + uint64_t fileHashTableOff; ///< Offset of the file hash table. + uint64_t fileHashTableSize; ///< Size of the file hash table. + uint64_t fileTableOff; ///< Offset of the file table. + uint64_t fileTableSize; ///< Size of the file table. + uint64_t fileDataOff; ///< Offset of the file data. +} romfs_header; + +/// RomFS directory. +typedef struct { + uint32_t parent; ///< Offset of the parent directory. + uint32_t sibling; ///< Offset of the next sibling directory. + uint32_t childDir; ///< Offset of the first child directory. + uint32_t childFile; ///< Offset of the first file. + uint32_t nextHash; ///< Directory hash table pointer. + uint32_t nameLen; ///< Name length. + uint8_t name[]; ///< Name. (UTF-8) +} romfs_dir; + +/// RomFS file. +typedef struct { + uint32_t parent; ///< Offset of the parent directory. + uint32_t sibling; ///< Offset of the next sibling file. + uint64_t dataOff; ///< Offset of the file's data. + uint64_t dataSize; ///< Length of the file's data. + uint32_t nextHash; ///< File hash table pointer. + uint32_t nameLen; ///< Name length. + uint8_t name[]; ///< Name. (UTF-8) +} romfs_file; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Mounts the Application's RomFS. + * @param name Device mount name. + */ +int32_t romfsMount(const char *name, const char * path); + + +/** + * @brief Mounts RomFS from an open file. + * @param file FsFile of the RomFS image. + * @param offset Offset of the RomFS within the file. + * @param name Device mount name. +bool romfsMountFromFile(FsFile file, uint64_t offset, const char *name); +*/ +/* +static inline bool romfsInitFromFile(int32_t fd, uint64_t offset) { + return romfsMountFromFile(fd, offset, "romfs"); +}*/ + +/// Unmounts the RomFS device. +int32_t romfsUnmount(const char *name); +/* +static inline bool romfsExit(void) { + return romfsUnmount("romfs"); +}*/ + +/// RomFS file. +typedef struct { + uint64_t length; ///< Offset of the file's data. + uint64_t offset; ///< Length of the file's data. +} romfs_fileInfo; + +int romfs_GetFileInfoPerPath(const char* romfs, const char *path, romfs_fileInfo* out); + +#ifdef __cplusplus +} +#endif + diff --git a/src/utils/utils.c b/src/utils/utils.c new file mode 100644 index 0000000..cd3486b --- /dev/null +++ b/src/utils/utils.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include + +// https://gist.github.com/ccbrown/9722406 +void dumpHex(const void* data, size_t size) { + char ascii[17]; + size_t i, j; + ascii[16] = '\0'; + DEBUG_FUNCTION_LINE("0x%08X (0x0000): ", data); + for (i = 0; i < size; ++i) { + log_printf("%02X ", ((unsigned char*)data)[i]); + if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { + ascii[i % 16] = ((unsigned char*)data)[i]; + } else { + ascii[i % 16] = '.'; + } + if ((i+1) % 8 == 0 || i+1 == size) { + log_printf(" "); + if ((i+1) % 16 == 0) { + log_printf("| %s \n", ascii); + if(i + 1 < size) { + DEBUG_FUNCTION_LINE("0x%08X (0x%04X); ", data + i + 1,i+1); + } + } else if (i+1 == size) { + ascii[(i+1) % 16] = '\0'; + if ((i+1) % 16 <= 8) { + log_printf(" "); + } + for (j = (i+1) % 16; j < 16; ++j) { + log_printf(" "); + } + log_printf("| %s \n", ascii); + } + } + } +} + +// You must free the result if result is non-NULL. +char *str_replace(char *orig, char *rep, char *with) { + char *result; // the return string + char *ins; // the next insert point + char *tmp; // varies + int len_rep; // length of rep (the string to remove) + int len_with; // length of with (the string to replace rep with) + int len_front; // distance between rep and end of last rep + int count; // number of replacements + + // sanity checks and initialization + if (!orig || !rep) + return NULL; + len_rep = strlen(rep); + if (len_rep == 0) + return NULL; // empty rep causes infinite loop during count + if (!with) + with = ""; + len_with = strlen(with); + + // count the number of replacements needed + ins = orig; + for (count = 0; tmp = strstr(ins, rep); ++count) { + ins = tmp + len_rep; + } + + tmp = result = (char*)malloc(strlen(orig) + (len_with - len_rep) * count + 1); + + if (!result) + return NULL; + + // first time through the loop, all the variable are set correctly + // from here on, + // tmp points to the end of the result string + // ins points to the next occurrence of rep in orig + // orig points to the remainder of orig after "end of rep" + while (count--) { + ins = strstr(orig, rep); + len_front = ins - orig; + tmp = strncpy(tmp, orig, len_front) + len_front; + tmp = strcpy(tmp, with) + len_with; + orig += len_front + len_rep; // move to next "end of rep" + } + strcpy(tmp, orig); + return result; +} diff --git a/src/utils/utils.h b/src/utils/utils.h new file mode 100644 index 0000000..5673a41 --- /dev/null +++ b/src/utils/utils.h @@ -0,0 +1,39 @@ +#ifndef __UTILS_H_ +#define __UTILS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIMIT(x, min, max) \ + ({ \ + typeof( x ) _x = x; \ + typeof( min ) _min = min; \ + typeof( max ) _max = max; \ + ( ( ( _x ) < ( _min ) ) ? ( _min ) : ( ( _x ) > ( _max ) ) ? ( _max) : ( _x ) ); \ +}) + +#define DegToRad(a) ( (a) * 0.01745329252f ) +#define RadToDeg(a) ( (a) * 57.29577951f ) + +#define ALIGN4(x) (((x) + 3) & ~3) +#define ALIGN32(x) (((x) + 31) & ~31) + + +#define ROUNDUP(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) + +#define le16(i) ((((uint16_t) ((i) & 0xFF)) << 8) | ((uint16_t) (((i) & 0xFF00) >> 8))) +#define le32(i) ((((uint32_t)le16((i) & 0xFFFF)) << 16) | ((uint32_t)le16(((i) & 0xFFFF0000) >> 16))) +#define le64(i) ((((uint64_t)le32((i) & 0xFFFFFFFFLL)) << 32) | ((uint64_t)le32(((i) & 0xFFFFFFFF00000000LL) >> 32))) + +//Needs to have log_init() called beforehand. +void dumpHex(const void* data, size_t size); +char *str_replace(char *orig, char *rep, char *with); + +#ifdef __cplusplus +} +#endif + +#endif // __UTILS_H_