diff --git a/.gitignore b/.gitignore index cd2946a..6c81748 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,3 @@ -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# ========================= -# Operating System Files -# ========================= - -# OSX -# ========================= - -.DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk +release/* +libutils.cbp +libutils.depend \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4389d5d --- /dev/null +++ b/Makefile @@ -0,0 +1,134 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC) +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# 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 +# DATA is a list of directories containing binary files +# LIBDIR is where the built library will be placed +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD ?= release +SOURCES := source \ + source/fs \ + source/utils \ + source/language \ + source/system \ + source/kernel +INCLUDES := include \ + source +DATA := +LIB := lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS = -g -Os -Wall -D__wiiu__ -D_GNU_SOURCE $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +ASFLAGS := -mregnames + +export WIIUBIN := $(LIB)/libutils.a + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -ldynamiclibs + +#--------------------------------------------------------------------------------- +# 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 TOPDIR ?= $(CURDIR)/.. +export DEPSDIR := $(CURDIR)/$(BUILD) + +export INCLUDEDIR := $(PORTLIBS)/include/libutils + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) $(sFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) -I$(LIBOGC_INC) \ + -I$(CURDIR)/$(BUILD) -I$(PORTLIBS)/include + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(PORTLIBS)/lib + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr debug release $(LIB) include + +all: $(WIIUBIN) + +install: + @cp $(BUILD)/lib/libutils.a $(PORTLIBS)/lib + @mkdir -p $(INCLUDEDIR)/utils/ + @mkdir -p $(INCLUDEDIR)/kernel/ + @mkdir -p $(INCLUDEDIR)/system/ + @mkdir -p $(INCLUDEDIR)/language/ + @mkdir -p $(INCLUDEDIR)/fs/ + @cp source/utils/*.h $(INCLUDEDIR)/utils/ + @cp source/system/*.h $(INCLUDEDIR)/system/ + @cp source/language/*.h $(INCLUDEDIR)/language/ + @cp source/fs/*.h $(INCLUDEDIR)/fs/ + @cp source/fs/*.hpp $(INCLUDEDIR)/fs/ + @cp source/kernel/kernel_functions.h $(INCLUDEDIR)/kernel/ + @cp source/kernel/kernel_defs.h $(INCLUDEDIR)/kernel/ + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(WIIUBIN) : $(OFILES) $(LIB) + @rm -f "$(WIIUBIN)" + @$(AR) rcs "$(WIIUBIN)" $(OFILES) + @echo built ... $(notdir $@) + + +$(LIB): + mkdir $(LIB) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/README.md b/README.md new file mode 100644 index 0000000..20a57e2 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# libutils for WiiU homebrew. + +This is a library for common functions. + +## Features + +- FS utility functions +- Wrapper for easy kernel read/write (KernelCopyData to copy data to any place) +- Multiple language support via gettext +- Common system functions (Exception handler, memory functions, Thread wrapper etc.) +- Built in function patcher engine +- String tools + +(For some features the application need to be loaded from the homebrew_launcher to have kernel access) + +## Logging usage +To able to use the logging, you need to compile the target application with follow C/C++ flag: +``` + -D__LOGGING__ +``` + +Usage in the application: +``` +log_init(); // Enables broadcast logging. +printf("Just prints this text\n"); +DEBUG_FUNCTION_LINE("This prints my current function, file and line\n"); +``` + +Read the logs via the [udp_debug_reader](https://github.com/dimok789/loadiine_gx2/tree/master/udp_debug_reader). + +## Usage (TODO) + +Check out the header for more information. + +## Compiling + +For compiling and using this lib, you need the [dynamic_libs](https://github.com/Maschell/dynamic_libs/tree/lib) installed to your portlibs folder. + +Install this static library into your portlibs folder via: + +``` +make && make install +``` + +## Dependencies + +- Application needs to be loaded from the [homebrew_launcher](https://github.com/dimok789/homebrew_launcher) +- [dynamic_libs](https://github.com/Maschell/dynamic_libs/tree/lib) for access to the functions. + +# Credits (TODO) + +- Serveral users diff --git a/source/fs/CFile.cpp b/source/fs/CFile.cpp new file mode 100644 index 0000000..439532b --- /dev/null +++ b/source/fs/CFile.cpp @@ -0,0 +1,200 @@ +#include +#include +#include +#include "CFile.hpp" + +CFile::CFile() +{ + iFd = -1; + mem_file = NULL; + filesize = 0; + pos = 0; +} + +CFile::CFile(const std::string & filepath, eOpenTypes mode) +{ + iFd = -1; + this->open(filepath, mode); +} + +CFile::CFile(const u8 * mem, s32 size) +{ + iFd = -1; + this->open(mem, size); +} + +CFile::~CFile() +{ + this->close(); +} + +s32 CFile::open(const std::string & filepath, eOpenTypes mode) +{ + this->close(); + + s32 openMode = 0; + + switch(mode) + { + default: + case ReadOnly: + openMode = O_RDONLY; + break; + case WriteOnly: + openMode = O_WRONLY; + break; + case ReadWrite: + openMode = O_RDWR; + break; + case Append: + openMode = 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; +} + +s32 CFile::open(const u8 * mem, s32 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; +} + +s32 CFile::read(u8 * ptr, size_t size) +{ + if(iFd >= 0) + { + s32 ret = ::read(iFd, ptr,size); + if(ret > 0) + pos += ret; + return ret; + } + + s32 readsize = size; + + if(readsize > (s64) (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; +} + +s32 CFile::write(const u8 * ptr, size_t size) +{ + if(iFd >= 0) + { + size_t done = 0; + while(done < size) + { + s32 ret = ::write(iFd, ptr, size - done); + if(ret <= 0) + return ret; + + ptr += ret; + done += ret; + pos += ret; + } + return done; + } + + return -1; +} + +s32 CFile::seek(long int offset, s32 origin) +{ + s32 ret = 0; + s64 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; +} + +s32 CFile::fwrite(const char *format, ...) +{ + s32 result = -1; + char * tmp = NULL; + + va_list va; + va_start(va, format); + if((vasprintf(&tmp, format, va) >= 0) && tmp) + { + result = this->write((u8 *)tmp, strlen(tmp)); + } + va_end(va); + + if(tmp){ + free(tmp); + tmp = NULL; + } + + return result; +} + + diff --git a/source/fs/CFile.hpp b/source/fs/CFile.hpp new file mode 100644 index 0000000..13b5b3d --- /dev/null +++ b/source/fs/CFile.hpp @@ -0,0 +1,57 @@ +#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 u8 * memory, s32 memsize); + virtual ~CFile(); + + s32 open(const std::string & filepath, eOpenTypes mode); + s32 open(const u8 * memory, s32 memsize); + + bool isOpen() const { + if(iFd >= 0) + return true; + + if(mem_file) + return true; + + return false; + } + + void close(); + + s32 read(u8 * ptr, size_t size); + s32 write(const u8 * ptr, size_t size); + s32 fwrite(const char *format, ...); + s32 seek(long int offset, s32 origin); + u64 tell() { return pos; }; + u64 size() { return filesize; }; + void rewind() { this->seek(0, SEEK_SET); }; + + protected: + s32 iFd; + const u8 * mem_file; + u64 filesize; + u64 pos; +}; + +#endif diff --git a/source/fs/DirList.cpp b/source/fs/DirList.cpp new file mode 100644 index 0000000..932d9fe --- /dev/null +++ b/source/fs/DirList.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** + * 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 "DirList.h" +#include "utils/StringTools.h" + +DirList::DirList() +{ + Flags = 0; + Filter = 0; +} + +DirList::DirList(const std::string & path, const char *filter, u32 flags) +{ + this->LoadPath(path, filter, flags); + this->SortList(); +} + +DirList::~DirList() +{ + ClearList(); +} + +bool DirList::LoadPath(const std::string & folder, const char *filter, u32 flags) +{ + if(folder.empty()) return false; + + Flags = flags; + Filter = filter; + + std::string folderpath(folder); + u32 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); + + 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) + { + s32 length = folderpath.size(); + if(length > 2 && folderpath[length-1] != '/') + folderpath += '/'; + folderpath += filename; + InternalLoadPath(folderpath); + folderpath.erase(length); + } + + 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; + + s32 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(u32 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(s32 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); +} + +u64 DirList::GetFilesize(s32 index) const +{ + struct stat st; + const char *path = GetFilepath(index); + + if(!path || stat(path, &st) != 0) + return 0; + + return st.st_size; +} + +s32 DirList::GetFileIndex(const char *filename) const +{ + if(!filename) + return -1; + + for (u32 i = 0; i < FileInfo.size(); ++i) + { + if (strcasecmp(GetFilename(i), filename) == 0) + return i; + } + + return -1; +} diff --git a/source/fs/DirList.h b/source/fs/DirList.h new file mode 100644 index 0000000..331abc9 --- /dev/null +++ b/source/fs/DirList.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * 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, u32 flags = Files | Dirs); + //!Destructor + virtual ~DirList(); + //! Load all the files from a directory + bool LoadPath(const std::string & path, const char *filter = NULL, u32 flags = Files | Dirs); + //! Get a filename of the list + //!\param list index + const char * GetFilename(s32 index) const; + //! Get the a filepath of the list + //!\param list index + const char *GetFilepath(s32 index) const { if (!valid(index)) return ""; else return FileInfo[index].FilePath; } + //! Get the a filesize of the list + //!\param list index + u64 GetFilesize(s32 index) const; + //! Is index a dir or a file + //!\param list index + bool IsDir(s32 index) const { if(!valid(index)) return false; return FileInfo[index].isDir; }; + //! Get the filecount of the whole list + s32 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 + s32 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(u32 pos) const { return (pos < FileInfo.size()); }; + + u32 Flags; + const char *Filter; + std::vector FileInfo; +}; + +#endif diff --git a/source/fs/FSOSUtils.cpp b/source/fs/FSOSUtils.cpp new file mode 100644 index 0000000..2ed684e --- /dev/null +++ b/source/fs/FSOSUtils.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include "FSOSUtils.h" +#include +#include +#include + +s32 FSOSUtils::MountFS(void *pClient, void *pCmd, char **mount_path){ + InitOSFunctionPointers(); + InitFSFunctionPointers(); + s32 result = -1; + + void *mountSrc = malloc(FS_MOUNT_SOURCE_SIZE); + if(!mountSrc) + return -3; + + char* mountPath = (char*) malloc(FS_MAX_MOUNTPATH_SIZE); + if(!mountPath) { + free(mountSrc); + mountSrc = NULL; + return -4; + } + + memset(mountSrc, 0, FS_MOUNT_SOURCE_SIZE); + memset(mountPath, 0, FS_MAX_MOUNTPATH_SIZE); + + // Mount sdcard + if (FSGetMountSource(pClient, pCmd, FS_SOURCETYPE_EXTERNAL, mountSrc, -1) == 0) + { + result = FSMount(pClient, pCmd, mountSrc, mountPath, FS_MAX_MOUNTPATH_SIZE, -1); + if((result == 0) && mount_path) { + *mount_path = (char*)malloc(strlen(mountPath) + 1); + if(*mount_path) + strcpy(*mount_path, mountPath); + } + } + + free(mountPath); + free(mountSrc); + + mountPath = NULL; + mountSrc = NULL; + + return result; +} + +s32 FSOSUtils::UmountFS(void *pClient, void *pCmd, const char *mountPath){ + s32 result = -1; + result = FSUnmount(pClient, pCmd, mountPath, -1); + + return result; +} diff --git a/source/fs/FSOSUtils.h b/source/fs/FSOSUtils.h new file mode 100644 index 0000000..7c26877 --- /dev/null +++ b/source/fs/FSOSUtils.h @@ -0,0 +1,13 @@ +#ifndef __FS_OS_UTILS_H_ +#define __FS_OS_UTILS_H_ + +#include + +class FSOSUtils{ + public: + + static s32 MountFS(void *pClient, void *pCmd, char **mount_path); + static s32 UmountFS(void *pClient, void *pCmd, const char *mountPath); +}; + +#endif // __FS_OS_UTILS_H_ diff --git a/source/fs/FSUtils.cpp b/source/fs/FSUtils.cpp new file mode 100644 index 0000000..df34f3c --- /dev/null +++ b/source/fs/FSUtils.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include "FSUtils.h" + +s32 FSUtils::LoadFileToMem(const char *filepath, u8 **inbuffer, u32 *size){ + //! always initialze input + *inbuffer = NULL; + if(size) + *size = 0; + + s32 iFd = open(filepath, O_RDONLY); + if (iFd < 0) + return -1; + + u32 filesize = lseek(iFd, 0, SEEK_END); + lseek(iFd, 0, SEEK_SET); + + u8 *buffer = (u8 *) malloc(filesize); + if (buffer == NULL) + { + close(iFd); + return -2; + } + + u32 blocksize = 0x4000; + u32 done = 0; + s32 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; +} + +s32 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; +} + +s32 FSUtils::CreateSubfolder(const char * fullpath){ + if(!fullpath) + return 0; + + s32 result = 0; + + char dirnoslash[strlen(fullpath)+1]; + strcpy(dirnoslash, fullpath); + + s32 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; +} diff --git a/source/fs/FSUtils.h b/source/fs/FSUtils.h new file mode 100644 index 0000000..f642ad7 --- /dev/null +++ b/source/fs/FSUtils.h @@ -0,0 +1,14 @@ +#ifndef __FS_UTILS_H_ +#define __FS_UTILS_H_ +#include + +class FSUtils{ + public: + static s32 LoadFileToMem(const char *filepath, u8 **inbuffer, u32 *size); + + //! todo: C++ class + static s32 CreateSubfolder(const char * fullpath); + static s32 CheckFile(const char * filepath); +}; + +#endif // __FS_UTILS_H_ diff --git a/source/fs/sd_fat_devoptab.cpp b/source/fs/sd_fat_devoptab.cpp new file mode 100644 index 0000000..c3b1d93 --- /dev/null +++ b/source/fs/sd_fat_devoptab.cpp @@ -0,0 +1,1061 @@ +/*************************************************************************** + * Copyright (C) 2015 + * 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. + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "FSOSUtils.h" +#include "sd_fat_devoptab.h" +#include "utils/StringTools.h" +#include "utils/logger.h" + +#define FS_ALIGNMENT 0x40 +#define FS_ALIGN(x) (((x) + FS_ALIGNMENT - 1) & ~(FS_ALIGNMENT - 1)) + +typedef struct _sd_fat_private_t { + char *mount_path; + void *pClient; + void *pCmd; + void *pMutex; +} sd_fat_private_t; + +typedef struct _sd_fat_file_state_t { + sd_fat_private_t *dev; + int fd; /* File descriptor */ + int flags; /* Opening flags */ + bool read; /* True if allowed to read from file */ + bool write; /* True if allowed to write to file */ + bool append; /* True if allowed to append to file */ + u64 pos; /* Current position within the file (in bytes) */ + u64 len; /* Total length of the file (in bytes) */ + struct _sd_fat_file_state_t *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ + struct _sd_fat_file_state_t *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ +} sd_fat_file_state_t; + +typedef struct _sd_fat_dir_entry_t { + sd_fat_private_t *dev; + int dirHandle; +} sd_fat_dir_entry_t; + +static sd_fat_private_t *sd_fat_get_device_data(const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Search the devoptab table for the specified device name + // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 3; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + return (sd_fat_private_t *)devoptab->deviceData; + } + } + } + + return NULL; +} + +static char *sd_fat_real_path (const char *path, sd_fat_private_t *dev) +{ + // Sanity check + if (!path) + return NULL; + + // Move the path pointer to the start of the actual path + if (strchr(path, ':') != NULL) { + path = strchr(path, ':') + 1; + } + + int mount_len = strlen(dev->mount_path); + + char *new_name = (char*)malloc(mount_len + strlen(path) + 1); + if(new_name) { + strcpy(new_name, dev->mount_path); + strcpy(new_name + mount_len, path); + return new_name; + } + return new_name; +} + +static int sd_fat_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fileStruct; + + file->dev = dev; + // Determine which mode the file is opened for + file->flags = flags; + + const char *mode_str; + + if ((flags & 0x03) == O_RDONLY) { + file->read = true; + file->write = false; + file->append = false; + mode_str = "r"; + } else if ((flags & 0x03) == O_WRONLY) { + file->read = false; + file->write = true; + file->append = (flags & O_APPEND); + mode_str = file->append ? "a" : "w"; + } else if ((flags & 0x03) == O_RDWR) { + file->read = true; + file->write = true; + file->append = (flags & O_APPEND); + mode_str = file->append ? "a+" : "r+"; + } else { + r->_errno = EACCES; + return -1; + } + + s32 fd = -1; + + OSLockMutex(dev->pMutex); + + char *real_path = sd_fat_real_path(path, dev); + if(!path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = FSOpenFile(dev->pClient, dev->pCmd, real_path, mode_str, &fd, -1); + + free(real_path); + + if(result == 0) + { + FSStat stats; + result = FSGetStatFile(dev->pClient, dev->pCmd, fd, &stats, -1); + if(result != 0) { + FSCloseFile(dev->pClient, dev->pCmd, fd, -1); + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } + file->fd = fd; + file->pos = 0; + file->len = stats.size; + OSUnlockMutex(dev->pMutex); + return (int)file; + } + + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; +} + + +static int sd_fat_close_r (struct _reent *r, void *fd) +{ + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(file->dev->pMutex); + + int result = FSCloseFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); + + OSUnlockMutex(file->dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return -1; + } + return 0; +} + +static off_t sd_fat_seek_r (struct _reent *r, void* fd, off_t pos, int dir) +{ + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return 0; + } + + OSLockMutex(file->dev->pMutex); + + switch(dir) + { + case SEEK_SET: + file->pos = pos; + break; + case SEEK_CUR: + file->pos += pos; + break; + case SEEK_END: + file->pos = file->len + pos; + break; + default: + r->_errno = EINVAL; + return -1; + } + + int result = FSSetPosFile(file->dev->pClient, file->dev->pCmd, file->fd, file->pos, -1); + + OSUnlockMutex(file->dev->pMutex); + + if(result == 0) + { + return file->pos; + } + + return result; +} + +static ssize_t sd_fat_write_r (struct _reent *r, void *fd, const char *ptr, size_t len) +{ + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return 0; + } + + if(!file->write) + { + r->_errno = EACCES; + return 0; + } + + OSLockMutex(file->dev->pMutex); + + size_t len_aligned = FS_ALIGN(len); + if(len_aligned > 0x4000) + len_aligned = 0x4000; + + unsigned char *tmpBuf = (unsigned char *)memalign(FS_ALIGNMENT, len_aligned); + if(!tmpBuf) { + r->_errno = ENOMEM; + OSUnlockMutex(file->dev->pMutex); + return 0; + } + + size_t done = 0; + + while(done < len) + { + size_t write_size = (len_aligned < (len - done)) ? len_aligned : (len - done); + memcpy(tmpBuf, ptr + done, write_size); + + int result = FSWriteFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, write_size, file->fd, 0, -1); + if(result < 0) + { + r->_errno = result; + break; + } + else if(result == 0) + { + if(write_size > 0) + done = 0; + break; + } + else + { + done += result; + file->pos += result; + } + } + + free(tmpBuf); + OSUnlockMutex(file->dev->pMutex); + return done; +} + +static ssize_t sd_fat_read_r (struct _reent *r, void* fd, char *ptr, size_t len) +{ + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return 0; + } + + if(!file->read) + { + r->_errno = EACCES; + return 0; + } + + OSLockMutex(file->dev->pMutex); + + size_t len_aligned = FS_ALIGN(len); + if(len_aligned > 0x4000) + len_aligned = 0x4000; + + unsigned char *tmpBuf = (unsigned char *)memalign(FS_ALIGNMENT, len_aligned); + if(!tmpBuf) { + r->_errno = ENOMEM; + OSUnlockMutex(file->dev->pMutex); + return 0; + } + + size_t done = 0; + + while(done < len) + { + size_t read_size = (len_aligned < (len - done)) ? len_aligned : (len - done); + + int result = FSReadFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, read_size, file->fd, 0, -1); + if(result < 0) + { + r->_errno = result; + done = 0; + break; + } + else if(result == 0) + { + //! TODO: error on read_size > 0 + break; + } + else + { + memcpy(ptr + done, tmpBuf, read_size); + done += result; + file->pos += result; + } + } + + free(tmpBuf); + OSUnlockMutex(file->dev->pMutex); + return done; +} + + +static int sd_fat_fstat_r (struct _reent *r, void* fd, struct stat *st) +{ + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(file->dev->pMutex); + + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); + + FSStat stats; + int result = FSGetStatFile(file->dev->pClient, file->dev->pCmd, file->fd, &stats, -1); + if(result != 0) { + r->_errno = result; + OSUnlockMutex(file->dev->pMutex); + return -1; + } + + st->st_mode = S_IFREG; + st->st_size = stats.size; + st->st_blocks = (stats.size + 511) >> 9; + st->st_nlink = 1; + + // Fill in the generic entry stats + st->st_dev = stats.ent_id; + st->st_uid = stats.owner_id; + st->st_gid = stats.group_id; + st->st_ino = stats.ent_id; + st->st_atime = stats.mtime; + st->st_ctime = stats.ctime; + st->st_mtime = stats.mtime; + OSUnlockMutex(file->dev->pMutex); + return 0; +} + +static int sd_fat_ftruncate_r (struct _reent *r, void* fd, off_t len) +{ + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(file->dev->pMutex); + + int result = FSTruncateFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); + + OSUnlockMutex(file->dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int sd_fat_fsync_r (struct _reent *r, void* fd) +{ + sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(file->dev->pMutex); + + int result = FSFlushFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); + + OSUnlockMutex(file->dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int sd_fat_stat_r (struct _reent *r, const char *path, struct stat *st) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); + + char *real_path = sd_fat_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + FSStat stats; + + int result = FSGetStat(dev->pClient, dev->pCmd, real_path, &stats, -1); + + free(real_path); + + if(result < 0) { + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } + + // mark root also as directory + st->st_mode = ((stats.flag & 0x80000000) || (strlen(dev->mount_path) + 1 == strlen(real_path)))? S_IFDIR : S_IFREG; + st->st_nlink = 1; + st->st_size = stats.size; + st->st_blocks = (stats.size + 511) >> 9; + // Fill in the generic entry stats + st->st_dev = stats.ent_id; + st->st_uid = stats.owner_id; + st->st_gid = stats.group_id; + st->st_ino = stats.ent_id; + st->st_atime = stats.mtime; + st->st_ctime = stats.ctime; + st->st_mtime = stats.mtime; + + OSUnlockMutex(dev->pMutex); + + return 0; +} + +static int sd_fat_link_r (struct _reent *r, const char *existing, const char *newLink) +{ + r->_errno = ENOTSUP; + return -1; +} + +static int sd_fat_unlink_r (struct _reent *r, const char *name) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(name); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_path = sd_fat_real_path(name, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + + int result = FSRemove(dev->pClient, dev->pCmd, real_path, -1); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int sd_fat_chdir_r (struct _reent *r, const char *name) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(name); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_path = sd_fat_real_path(name, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = FSChangeDir(dev->pClient, dev->pCmd, real_path, -1); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int sd_fat_rename_r (struct _reent *r, const char *oldName, const char *newName) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(oldName); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_oldpath = sd_fat_real_path(oldName, dev); + if(!real_oldpath) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + char *real_newpath = sd_fat_real_path(newName, dev); + if(!real_newpath) { + r->_errno = ENOMEM; + free(real_oldpath); + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = FSRename(dev->pClient, dev->pCmd, real_oldpath, real_newpath, -1); + + free(real_oldpath); + free(real_newpath); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; + +} + +static int sd_fat_mkdir_r (struct _reent *r, const char *path, int mode) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_path = sd_fat_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = FSMakeDir(dev->pClient, dev->pCmd, real_path, -1); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int sd_fat_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct statvfs)); + + char *real_path = sd_fat_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + u64 size; + + int result = FSGetFreeSpaceSize(dev->pClient, dev->pCmd, real_path, &size, -1); + + free(real_path); + + if(result < 0) { + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } + + // File system block size + buf->f_bsize = 512; + + // Fundamental file system block size + buf->f_frsize = 512; + + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = size >> 9; // this is unknown + + // Free blocks available for all and for non-privileged processes + buf->f_bfree = buf->f_bavail = size >> 9; + + // Number of inodes at this point in time + buf->f_files = 0xffffffff; + + // Free inodes available for all and for non-privileged processes + buf->f_ffree = 0xffffffff; + + // File system id + buf->f_fsid = (int)dev; + + // Bit mask of f_flag values. + buf->f_flag = 0; + + // Maximum length of filenames + buf->f_namemax = 255; + + OSUnlockMutex(dev->pMutex); + + return 0; +} + +static DIR_ITER *sd_fat_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) +{ + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return NULL; + } + + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; + + OSLockMutex(dev->pMutex); + + char *real_path = sd_fat_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return NULL; + } + + s32 dirHandle; + + int result = FSOpenDir(dev->pClient, dev->pCmd, real_path, &dirHandle, -1); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return NULL; + } + + dirIter->dev = dev; + dirIter->dirHandle = dirHandle; + + return dirState; +} + +static int sd_fat_dirclose_r (struct _reent *r, DIR_ITER *dirState) +{ + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; + if(!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dirIter->dev->pMutex); + + int result = FSCloseDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1); + + OSUnlockMutex(dirIter->dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return -1; + } + return 0; +} + +static int sd_fat_dirreset_r (struct _reent *r, DIR_ITER *dirState) +{ + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; + if(!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dirIter->dev->pMutex); + + int result = FSRewindDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1); + + OSUnlockMutex(dirIter->dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return -1; + } + return 0; +} + +static int sd_fat_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) +{ + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; + if(!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dirIter->dev->pMutex); + + FSDirEntry * dir_entry = (FSDirEntry *)malloc(sizeof(FSDirEntry)); + + int result = FSReadDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, dir_entry, -1); + if(result < 0) + { + free(dir_entry); + r->_errno = result; + OSUnlockMutex(dirIter->dev->pMutex); + return -1; + } + + // Fetch the current entry + strcpy(filename, dir_entry->name); + + if(st) + { + memset(st, 0, sizeof(struct stat)); + st->st_mode = (dir_entry->stat.flag & 0x80000000) ? S_IFDIR : S_IFREG; + st->st_nlink = 1; + st->st_size = dir_entry->stat.size; + st->st_blocks = (dir_entry->stat.size + 511) >> 9; + st->st_dev = dir_entry->stat.ent_id; + st->st_uid = dir_entry->stat.owner_id; + st->st_gid = dir_entry->stat.group_id; + st->st_ino = dir_entry->stat.ent_id; + st->st_atime = dir_entry->stat.mtime; + st->st_ctime = dir_entry->stat.ctime; + st->st_mtime = dir_entry->stat.mtime; + } + + free(dir_entry); + OSUnlockMutex(dirIter->dev->pMutex); + return 0; +} + +// SD device driver devoptab +static const devoptab_t devops_sd_fat = { + NULL, /* Device name */ + sizeof (sd_fat_file_state_t), + sd_fat_open_r, + sd_fat_close_r, + sd_fat_write_r, + sd_fat_read_r, + sd_fat_seek_r, + sd_fat_fstat_r, + sd_fat_stat_r, + sd_fat_link_r, + sd_fat_unlink_r, + sd_fat_chdir_r, + sd_fat_rename_r, + sd_fat_mkdir_r, + sizeof (sd_fat_dir_entry_t), + sd_fat_diropen_r, + sd_fat_dirreset_r, + sd_fat_dirnext_r, + sd_fat_dirclose_r, + sd_fat_statvfs_r, + sd_fat_ftruncate_r, + sd_fat_fsync_r, + NULL, /* Device data */ + NULL, /* sd_fat_chmod_r */ + NULL, /* sd_fat_fchmod_r */ + NULL /* sd_fat_rmdir_r */ +}; + + +static int sd_fat_add_device (const char *name, const char *mount_path, void *pClient, void *pCmd) +{ + devoptab_t *dev = NULL; + char *devname = NULL; + char *devpath = NULL; + int i; + + // Sanity check + if (!name) { + errno = EINVAL; + return -1; + } + + // Allocate a devoptab for this device + dev = (devoptab_t *) malloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { + errno = ENOMEM; + return -1; + } + + // Use the space allocated at the end of the devoptab for storing the device name + devname = (char*)(dev + 1); + strcpy(devname, name); + + // create private data + int mount_path_len = 0; + if(mount_path != NULL){ + mount_path_len = strlen(mount_path); + } + sd_fat_private_t *priv = (sd_fat_private_t *)malloc(sizeof(sd_fat_private_t) + mount_path_len + 1); + if(!priv) { + free(dev); + errno = ENOMEM; + return -1; + } + + devpath = (char*)(priv+1); + if(mount_path != NULL){ + strcpy(devpath, mount_path); + } + + // setup private data + priv->mount_path = devpath; + priv->pClient = pClient; + priv->pCmd = pCmd; + priv->pMutex = malloc(OS_MUTEX_SIZE); + + if(!priv->pMutex) { + free(dev); + free(priv); + errno = ENOMEM; + return -1; + } + + OSInitMutex(priv->pMutex); + + // Setup the devoptab + memcpy(dev, &devops_sd_fat, sizeof(devoptab_t)); + dev->name = devname; + dev->deviceData = priv; + + // Add the device to the devoptab table (if there is a free slot) + for (i = 3; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0]) { + devoptab_list[i] = dev; + return 0; + } + } + + // failure, free all memory + free(priv); + free(dev); + + // If we reach here then there are no free slots in the devoptab table for this device + errno = EADDRNOTAVAIL; + return -1; +} + +/* +Because of some weird reason unmounting doesn't work properly. +This fix if mainly when a usb drive is connected. +It resets the devoptab_list, otherwise mounting again would throw an exception (in strlen). +No memory is free'd here. Maybe a problem?!?!? +*/ + +void deleteDevTabsNames(){ + const devoptab_t * devoptab = NULL; + u32 last_entry = (u32) devoptab_list[STD_MAX-1]; + for (int i = 3; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + + if (devoptab) { + //log_printf("check devotab %d %08X\n",i,devoptab); + if((u32) devoptab != last_entry){ + devoptab_list[i] = (const devoptab_t *)last_entry; + //log_printf("Removed devotab %d %08X %08X %08X\n",i,devoptab,devoptab->name,devoptab->deviceData); + } + } + } +} + +static int sd_fat_remove_device (const char *path, void **pClient, void **pCmd, char **mountPath) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Find and remove the specified device from the devoptab table + // NOTE: We do this manually due to a 'bug' in RemoveDevice + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 3; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + devoptab_list[i] = devoptab_list[0]; + + if(devoptab->deviceData) + { + sd_fat_private_t *priv = (sd_fat_private_t *)devoptab->deviceData; + if(pClient != NULL) *pClient = priv->pClient; + if(pCmd != NULL) *pCmd = priv->pCmd; + if(mountPath != NULL){ + *mountPath = (char*) malloc(strlen(priv->mount_path)+1); + if(*mountPath) + strcpy(*mountPath, priv->mount_path); + } + if(priv->pMutex) + free(priv->pMutex); + free(devoptab->deviceData); + } + + free((devoptab_t*)devoptab); + return 0; + } + } + } + + return -1; +} + +s32 mount_sd_fat(const char *path){ + int result = -1; + + // get command and client + void* pClient = malloc(FS_CLIENT_SIZE); + void* pCmd = malloc(FS_CMD_BLOCK_SIZE); + + if(!pClient || !pCmd) { + // just in case free if not 0 + if(pClient) + free(pClient); + if(pCmd) + free(pCmd); + return -2; + } + + FSInit(); + FSInitCmdBlock(pCmd); + FSAddClientEx(pClient, 0, -1); + + char *mountPath = NULL; + + if(FSOSUtils::MountFS(pClient, pCmd, &mountPath) == 0) { + result = sd_fat_add_device(path, mountPath, pClient, pCmd); + free(mountPath); + } + + return result; +} + +s32 unmount_sd_fat(const char *path){ + void *pClient = 0; + void *pCmd = 0; + char *mountPath = 0; + + int result = sd_fat_remove_device(path, &pClient, &pCmd, &mountPath); + if(result == 0) + { + FSOSUtils::UmountFS(pClient, pCmd, mountPath); + FSDelClient(pClient); + free(pClient); + free(pCmd); + free(mountPath); + //FSShutdown(); + } + return result; +} + +int mount_fake(){ + return sd_fat_add_device("fake", NULL, NULL, NULL); +} + +int unmount_fake(){ + return sd_fat_remove_device ("fake", NULL,NULL,NULL); +} diff --git a/source/fs/sd_fat_devoptab.h b/source/fs/sd_fat_devoptab.h new file mode 100644 index 0000000..ae406a2 --- /dev/null +++ b/source/fs/sd_fat_devoptab.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2015 + * 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. + ***************************************************************************/ +#ifndef __SD_FAT_DEVOPTAB_H_ +#define __SD_FAT_DEVOPTAB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +s32 mount_sd_fat(const char *path); +s32 unmount_sd_fat(const char *path); + +#ifdef __cplusplus +} +#endif + +#endif // __SD_FAT_DEVOPTAB_H_ diff --git a/source/kernel/kernel_defs.h b/source/kernel/kernel_defs.h new file mode 100644 index 0000000..e69264e --- /dev/null +++ b/source/kernel/kernel_defs.h @@ -0,0 +1,26 @@ +#ifndef __KERNEL_DEFS_H_ +#define __KERNEL_DEFS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _bat_t +{ + u32 h; + u32 l; +} bat_t; + +typedef struct _bat_table_t +{ + bat_t bat[8]; +} bat_table_t; + +#ifdef __cplusplus +} +#endif + +#endif // __KERNEL_DEFS_H_ diff --git a/source/kernel/kernel_functions.c b/source/kernel/kernel_functions.c new file mode 100644 index 0000000..084d1b0 --- /dev/null +++ b/source/kernel/kernel_functions.c @@ -0,0 +1,8 @@ +#include +#include "kernel_defs.h" +#include "kernel_functions.h" +#include "syscalls.h" + +void SetupKernelCallback(void){ + KernelSetupSyscalls(); +} diff --git a/source/kernel/kernel_functions.h b/source/kernel/kernel_functions.h new file mode 100644 index 0000000..240e15a --- /dev/null +++ b/source/kernel/kernel_functions.h @@ -0,0 +1,19 @@ +#ifndef __KERNEL_FUNCTIONS_H_ +#define __KERNEL_FUNCTIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "kernel_defs.h" + +void SetupKernelCallback(void); + +void SC0x25_KernelCopyData(u32 addr, u32 src, u32 len); +void SC0x36_KernelReadDBATs(bat_table_t * table); +void SC0x37_KernelWriteDBATs(bat_table_t * table); +#ifdef __cplusplus +} +#endif + +#endif // __KERNEL_FUNCTIONS_H_ diff --git a/source/kernel/syscalls.c b/source/kernel/syscalls.c new file mode 100644 index 0000000..57bc2b8 --- /dev/null +++ b/source/kernel/syscalls.c @@ -0,0 +1,238 @@ +#include "kernel_defs.h" +#include +#include +#include "utils/utils.h" +#include "syscalls.h" + +extern void my_PrepareTitle_hook(void); + +static void KernelCopyData(u32 addr, u32 src, u32 len) +{ + /* + * Setup a DBAT access with cache inhibited to write through and read directly from memory + */ + u32 dbatu0, dbatl0, dbatu1, dbatl1; + // save the original DBAT value + asm volatile("mfdbatu %0, 0" : "=r" (dbatu0)); + asm volatile("mfdbatl %0, 0" : "=r" (dbatl0)); + asm volatile("mfdbatu %0, 1" : "=r" (dbatu1)); + asm volatile("mfdbatl %0, 1" : "=r" (dbatl1)); + + u32 target_dbatu0 = 0; + u32 target_dbatl0 = 0; + u32 target_dbatu1 = 0; + u32 target_dbatl1 = 0; + + unsigned char *dst_p = (unsigned char*)addr; + unsigned char *src_p = (unsigned char*)src; + + // we only need DBAT modification for addresses out of our own DBAT range + // as our own DBAT is available everywhere for user and supervisor + // since our own DBAT is on DBAT5 position we don't collide here + if(addr < 0x00800000 || addr >= 0x01000000) + { + target_dbatu0 = (addr & 0x00F00000) | 0xC0000000 | 0x1F; + target_dbatl0 = (addr & 0xFFF00000) | 0x32; + asm volatile("mtdbatu 0, %0" : : "r" (target_dbatu0)); + asm volatile("mtdbatl 0, %0" : : "r" (target_dbatl0)); + dst_p = (unsigned char*)((addr & 0xFFFFFF) | 0xC0000000); + } + if(src < 0x00800000 || src >= 0x01000000) + { + target_dbatu1 = (src & 0x00F00000) | 0xB0000000 | 0x1F; + target_dbatl1 = (src & 0xFFF00000) | 0x32; + + asm volatile("mtdbatu 1, %0" : : "r" (target_dbatu1)); + asm volatile("mtdbatl 1, %0" : : "r" (target_dbatl1)); + src_p = (unsigned char*)((src & 0xFFFFFF) | 0xB0000000); + } + + asm volatile("eieio; isync"); + + u32 i; + for(i = 0; i < len; i++) + { + // if we are on the edge to next chunk + if((target_dbatu0 != 0) && (((u32)dst_p & 0x00F00000) != (target_dbatu0 & 0x00F00000))) + { + target_dbatu0 = ((addr + i) & 0x00F00000) | 0xC0000000 | 0x1F; + target_dbatl0 = ((addr + i) & 0xFFF00000) | 0x32; + dst_p = (unsigned char*)(((addr + i) & 0xFFFFFF) | 0xC0000000); + + asm volatile("eieio; isync"); + asm volatile("mtdbatu 0, %0" : : "r" (target_dbatu0)); + asm volatile("mtdbatl 0, %0" : : "r" (target_dbatl0)); + asm volatile("eieio; isync"); + } + if((target_dbatu1 != 0) && (((u32)src_p & 0x00F00000) != (target_dbatu1 & 0x00F00000))) + { + target_dbatu1 = ((src + i) & 0x00F00000) | 0xB0000000 | 0x1F; + target_dbatl1 = ((src + i) & 0xFFF00000) | 0x32; + src_p = (unsigned char*)(((src + i) & 0xFFFFFF) | 0xB0000000); + + asm volatile("eieio; isync"); + asm volatile("mtdbatu 1, %0" : : "r" (target_dbatu1)); + asm volatile("mtdbatl 1, %0" : : "r" (target_dbatl1)); + asm volatile("eieio; isync"); + } + + *dst_p = *src_p; + + ++dst_p; + ++src_p; + } + + /* + * Restore original DBAT value + */ + asm volatile("eieio; isync"); + asm volatile("mtdbatu 0, %0" : : "r" (dbatu0)); + asm volatile("mtdbatl 0, %0" : : "r" (dbatl0)); + asm volatile("mtdbatu 1, %0" : : "r" (dbatu1)); + asm volatile("mtdbatl 1, %0" : : "r" (dbatl1)); + asm volatile("eieio; isync"); +} + +static void KernelReadDBATs(bat_table_t * table) +{ + u32 i = 0; + + asm volatile("eieio; isync"); + + asm volatile("mfspr %0, 536" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 537" : "=r" (table->bat[i].l)); + i++; + asm volatile("mfspr %0, 538" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 539" : "=r" (table->bat[i].l)); + i++; + asm volatile("mfspr %0, 540" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 541" : "=r" (table->bat[i].l)); + i++; + asm volatile("mfspr %0, 542" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 543" : "=r" (table->bat[i].l)); + i++; + + asm volatile("mfspr %0, 568" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 569" : "=r" (table->bat[i].l)); + i++; + asm volatile("mfspr %0, 570" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 571" : "=r" (table->bat[i].l)); + i++; + asm volatile("mfspr %0, 572" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 573" : "=r" (table->bat[i].l)); + i++; + asm volatile("mfspr %0, 574" : "=r" (table->bat[i].h)); + asm volatile("mfspr %0, 575" : "=r" (table->bat[i].l)); +} + +static void KernelWriteDBATs(bat_table_t * table) +{ + u32 i = 0; + + asm volatile("eieio; isync"); + + asm volatile("mtspr 536, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 537, %0" : : "r" (table->bat[i].l)); + i++; + asm volatile("mtspr 538, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 539, %0" : : "r" (table->bat[i].l)); + i++; + asm volatile("mtspr 540, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 541, %0" : : "r" (table->bat[i].l)); + i++; + asm volatile("mtspr 542, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 543, %0" : : "r" (table->bat[i].l)); + i++; + + asm volatile("mtspr 568, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 569, %0" : : "r" (table->bat[i].l)); + i++; + asm volatile("mtspr 570, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 571, %0" : : "r" (table->bat[i].l)); + i++; + asm volatile("mtspr 572, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 573, %0" : : "r" (table->bat[i].l)); + i++; + asm volatile("mtspr 574, %0" : : "r" (table->bat[i].h)); + asm volatile("mtspr 575, %0" : : "r" (table->bat[i].l)); + + asm volatile("eieio; isync"); +} + +/* Read a 32-bit word with kernel permissions */ +uint32_t __attribute__ ((noinline)) kern_read(const void *addr) +{ + uint32_t result; + asm volatile ( + "li 3,1\n" + "li 4,0\n" + "li 5,0\n" + "li 6,0\n" + "li 7,0\n" + "lis 8,1\n" + "mr 9,%1\n" + "li 0,0x3400\n" + "mr %0,1\n" + "sc\n" + "nop\n" + "mr 1,%0\n" + "mr %0,3\n" + : "=r"(result) + : "b"(addr) + : "memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12" + ); + + return result; +} + +/* Write a 32-bit word with kernel permissions */ +void __attribute__ ((noinline)) kern_write(void *addr, uint32_t value) +{ + asm volatile ( + "li 3,1\n" + "li 4,0\n" + "mr 5,%1\n" + "li 6,0\n" + "li 7,0\n" + "lis 8,1\n" + "mr 9,%0\n" + "mr %1,1\n" + "li 0,0x3500\n" + "sc\n" + "nop\n" + "mr 1,%1\n" + : + : "r"(addr), "r"(value) + : "memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12" + ); +} + +void KernelSetupSyscalls(void) +{ + //! assign 1 so that this variable gets into the retained .data section + static uint8_t ucSyscallsSetupRequired = 1; + if(!ucSyscallsSetupRequired) + return; + + ucSyscallsSetupRequired = 0; + + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl1 + (0x36 * 4)), (u32)KernelReadDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl2 + (0x36 * 4)), (u32)KernelReadDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl3 + (0x36 * 4)), (u32)KernelReadDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl4 + (0x36 * 4)), (u32)KernelReadDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl5 + (0x36 * 4)), (u32)KernelReadDBATs); + + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl1 + (0x37 * 4)), (u32)KernelWriteDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl2 + (0x37 * 4)), (u32)KernelWriteDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl3 + (0x37 * 4)), (u32)KernelWriteDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl4 + (0x37 * 4)), (u32)KernelWriteDBATs); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl5 + (0x37 * 4)), (u32)KernelWriteDBATs); + + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl1 + (0x25 * 4)), (u32)KernelCopyData); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl2 + (0x25 * 4)), (u32)KernelCopyData); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl3 + (0x25 * 4)), (u32)KernelCopyData); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl4 + (0x25 * 4)), (u32)KernelCopyData); + kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl5 + (0x25 * 4)), (u32)KernelCopyData); +} diff --git a/source/kernel/syscalls.h b/source/kernel/syscalls.h new file mode 100644 index 0000000..9a51d72 --- /dev/null +++ b/source/kernel/syscalls.h @@ -0,0 +1,23 @@ +#ifndef __SYSCALLS_H_ +#define __SYSCALLS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "kernel_defs.h" + +void KernelSetupSyscalls(void); + +void SC0x25_KernelCopyData(u32 addr, u32 src, u32 len); +void SC0x36_KernelReadDBATs(bat_table_t * table); +void SC0x37_KernelWriteDBATs(bat_table_t * table); + +uint32_t __attribute__ ((noinline)) kern_read(const void *addr); +void __attribute__ ((noinline)) kern_write(void *addr, uint32_t value); + +#ifdef __cplusplus +} +#endif + +#endif // __KERNEL_FUNCTIONS_H_ diff --git a/source/kernel/syscalls_asm.s b/source/kernel/syscalls_asm.s new file mode 100644 index 0000000..60bf1fa --- /dev/null +++ b/source/kernel/syscalls_asm.s @@ -0,0 +1,20 @@ +# Created by dimok +# Syscalls for kernel that we use + + .globl SC0x36_KernelReadDBATs +SC0x36_KernelReadDBATs: + li r0, 0x3600 + sc + blr + + .globl SC0x37_KernelWriteDBATs +SC0x37_KernelWriteDBATs: + li r0, 0x3700 + sc + blr + + .globl SC0x25_KernelCopyData +SC0x25_KernelCopyData: + li r0, 0x2500 + sc + blr diff --git a/source/language/gettext.cpp b/source/language/gettext.cpp new file mode 100644 index 0000000..c2088bb --- /dev/null +++ b/source/language/gettext.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** + * 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 . + ****************************************************************************/ +#include +#include +#include + +#include +#include +#include "gettext.h" +#include "fs/CFile.hpp" +#include "utils/StringTools.h" + +typedef struct _MSG +{ + u32 id; + char* msgstr; + struct _MSG *next; +} MSG; +static MSG *baseMSG = 0; + +#define HASHWORDBITS 32 + +/* Defines the so called `hashpjw' function by P.J. Weinberger + [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, + 1986, 1987 Bell Telephone Laboratories, Inc.] */ +static inline u32 hash_string(const char *str_param) +{ + u32 hval, g; + const char *str = str_param; + + /* Compute the hash value for the given string. */ + hval = 0; + while (*str != '\0') + { + hval <<= 4; + hval += (u8) *str++; + g = hval & ((u32) 0xf << (HASHWORDBITS - 4)); + if (g != 0) + { + hval ^= g >> (HASHWORDBITS - 8); + hval ^= g; + } + } + return hval; +} + +/* Expand some escape sequences found in the argument string. */ +static char * +expand_escape(const char *str) +{ + char *retval, *rp; + const char *cp = str; + + retval = (char *) malloc(strlen(str) + 1); + if (retval == NULL) return NULL; + rp = retval; + + while (cp[0] != '\0' && cp[0] != '\\') + *rp++ = *cp++; + if (cp[0] == '\0') goto terminate; + do + { + + /* Here cp[0] == '\\'. */ + switch (*++cp) + { + case '\"': /* " */ + *rp++ = '\"'; + ++cp; + break; + case 'a': /* alert */ + *rp++ = '\a'; + ++cp; + break; + case 'b': /* backspace */ + *rp++ = '\b'; + ++cp; + break; + case 'f': /* form feed */ + *rp++ = '\f'; + ++cp; + break; + case 'n': /* new line */ + *rp++ = '\n'; + ++cp; + break; + case 'r': /* carriage return */ + *rp++ = '\r'; + ++cp; + break; + case 't': /* horizontal tab */ + *rp++ = '\t'; + ++cp; + break; + case 'v': /* vertical tab */ + *rp++ = '\v'; + ++cp; + break; + case '\\': + *rp = '\\'; + ++cp; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int ch = *cp++ - '0'; + + if (*cp >= '0' && *cp <= '7') + { + ch *= 8; + ch += *cp++ - '0'; + + if (*cp >= '0' && *cp <= '7') + { + ch *= 8; + ch += *cp++ - '0'; + } + } + *rp = ch; + } + break; + default: + *rp = '\\'; + break; + } + + while (cp[0] != '\0' && cp[0] != '\\') + *rp++ = *cp++; + } while (cp[0] != '\0'); + + /* Terminate string. */ + terminate: *rp = '\0'; + return retval; +} + +static MSG *findMSG(u32 id) +{ + MSG *msg; + for (msg = baseMSG; msg; msg = msg->next) + { + if (msg->id == id) return msg; + } + return NULL; +} + +static MSG *setMSG(const char *msgid, const char *msgstr) +{ + u32 id = hash_string(msgid); + MSG *msg = findMSG(id); + if (!msg) + { + msg = (MSG *) malloc(sizeof(MSG)); + msg->id = id; + msg->msgstr = NULL; + msg->next = baseMSG; + baseMSG = msg; + } + if (msg) + { + if (msgstr) + { + if (msg->msgstr) free(msg->msgstr); + //msg->msgstr = strdup(msgstr); + msg->msgstr = expand_escape(msgstr); + } + return msg; + } + return NULL; +} + +extern "C" void gettextCleanUp(void) +{ + while (baseMSG) + { + MSG *nextMsg = baseMSG->next; + free(baseMSG->msgstr); + free(baseMSG); + baseMSG = nextMsg; + } +} + +extern "C" bool gettextLoadLanguage(const char* langFile) +{ + char *lastID = NULL; + gettextCleanUp(); + + CFile file(langFile, CFile::ReadOnly); + if (!file.isOpen()) + return false; + + std::string strBuffer; + strBuffer.resize(file.size()); + file.read((u8 *) &strBuffer[0], strBuffer.size()); + file.close(); + + //! remove all windows crap signs + size_t position; + while(1) + { + position = strBuffer.find('\r'); + if(position == std::string::npos) + break; + + strBuffer.erase(position, 1); + } + + std::vector lines = StringTools::stringSplit(strBuffer, "\n"); + + + if(lines.empty()) + return false; + + for(unsigned int i = 0; i < lines.size(); i++) + { + std::string & line = lines[i]; + // lines starting with # are comments + if (line[0] == '#') + continue; + else if (strncmp(line.c_str(), "msgid \"", 7) == 0) + { + char *msgid, *end; + if (lastID) + { + free(lastID); + lastID = NULL; + } + msgid = &line[7]; + end = strrchr(msgid, '"'); + if (end && end - msgid > 1) + { + *end = 0; + lastID = strdup(msgid); + } + } + else if (strncmp(line.c_str(), "msgstr \"", 8) == 0) + { + char *msgstr, *end; + + if (lastID == NULL) continue; + + msgstr = &line[8]; + end = strrchr(msgstr, '"'); + if (end && end - msgstr > 1) + { + *end = 0; + setMSG(lastID, msgstr); + } + free(lastID); + lastID = NULL; + } + + } + return true; +} + +extern "C" const char *gettext(const char *msgid) +{ + if(!msgid) return NULL; + MSG *msg = findMSG(hash_string(msgid)); + if (msg && msg->msgstr) return msg->msgstr; + return msgid; +} + diff --git a/source/language/gettext.h b/source/language/gettext.h new file mode 100644 index 0000000..e1da228 --- /dev/null +++ b/source/language/gettext.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * 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 _GETTEXT_H_ +#define _GETTEXT_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + bool gettextLoadLanguage(const char* langFile); + void gettextCleanUp(void); + /* + * input msg = a text in ASCII + * output = the translated msg in utf-8 + */ + const char *gettext(const char *msg); +#define tr(s) gettext(s) +#define trNOOP(s) s + +#ifdef __cplusplus +} +#endif + +#endif /* _GETTEXT_H_ */ diff --git a/source/system/AsyncDeleter.cpp b/source/system/AsyncDeleter.cpp new file mode 100644 index 0000000..fbc5126 --- /dev/null +++ b/source/system/AsyncDeleter.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** + * 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 . + ****************************************************************************/ +#include "AsyncDeleter.h" + +AsyncDeleter * AsyncDeleter::deleterInstance = NULL; + +AsyncDeleter::AsyncDeleter() + : CThread(CThread::eAttributeAffCore1 | CThread::eAttributePinnedAff) + , exitApplication(false) +{ +} + +AsyncDeleter::~AsyncDeleter() +{ + exitApplication = true; +} + +void AsyncDeleter::triggerDeleteProcess(void) +{ + if(!deleterInstance) + deleterInstance = new AsyncDeleter; + + //! to trigger the event after GUI process is finished execution + //! this function is used to swap elements from one to next array + if(!deleterInstance->deleteElements.empty()) + { + deleterInstance->deleteMutex.lock(); + while(!deleterInstance->deleteElements.empty()) + { + deleterInstance->realDeleteElements.push(deleterInstance->deleteElements.front()); + deleterInstance->deleteElements.pop(); + } + deleterInstance->deleteMutex.unlock(); + deleterInstance->resumeThread(); + } +} + +void AsyncDeleter::executeThread(void) +{ + while(!exitApplication || !realDeleteElements.empty()) + { + if(realDeleteElements.empty()) suspendThread(); + //! delete elements that require post process deleting + //! because otherwise they would block or do invalid access on GUI thread + while(!realDeleteElements.empty()) + { + deleteMutex.lock(); + AsyncDeleter::Element *element = realDeleteElements.front(); + realDeleteElements.pop(); + deleteMutex.unlock(); + + delete element; + } + } + +} diff --git a/source/system/AsyncDeleter.h b/source/system/AsyncDeleter.h new file mode 100644 index 0000000..2e06061 --- /dev/null +++ b/source/system/AsyncDeleter.h @@ -0,0 +1,81 @@ +/**************************************************************************** + * 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 _ASYNC_DELETER_H +#define _ASYNC_DELETER_H + +#include +#include "CThread.h" +#include "CMutex.h" + +class AsyncDeleter : public CThread +{ +public: + static void destroyInstance() + { + if(deleterInstance != NULL){ + delete deleterInstance; + deleterInstance = NULL; + } + } + + class Element + { + public: + Element() {} + virtual ~Element() {} + }; + + static void pushForDelete(AsyncDeleter::Element *e) + { + if(!deleterInstance) + deleterInstance = new AsyncDeleter; + deleterInstance->deleteElements.push(e); + } + + static bool deleteListEmpty() + { + if(!deleterInstance) + return true; + return deleterInstance->deleteElements.empty(); + } + + static bool realListEmpty() + { + if(!deleterInstance) + return true; + return deleterInstance->realDeleteElements.empty(); + } + + + + static void triggerDeleteProcess(void); + +private: + AsyncDeleter(); + virtual ~AsyncDeleter(); + + static AsyncDeleter *deleterInstance; + + void executeThread(void); + + bool exitApplication; + std::queue deleteElements; + std::queue realDeleteElements; + CMutex deleteMutex; +}; + +#endif // _ASYNC_DELETER_H diff --git a/source/system/CMutex.h b/source/system/CMutex.h new file mode 100644 index 0000000..99d5b32 --- /dev/null +++ b/source/system/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 = malloc(OS_MUTEX_SIZE); + 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: + void *pMutex; +}; + +class CMutexLock +{ +public: + CMutexLock() { + mutex.lock(); + } + virtual ~CMutexLock() { + mutex.unlock(); + } +private: + CMutex mutex; +}; + +#endif // _CMUTEX_H_ diff --git a/source/system/CThread.h b/source/system/CThread.h new file mode 100644 index 0000000..0f8c7dd --- /dev/null +++ b/source/system/CThread.h @@ -0,0 +1,121 @@ +/**************************************************************************** + * 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 "utils/logger.h" + +class CThread +{ +public: + typedef void (* Callback)(CThread *thread, void *arg); + + //! constructor + CThread(s32 iAttr, s32 iPriority = 16, s32 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, 0x1000); + //! allocate the stack + pThreadStack = (u8 *) memalign(0x20, iStackSize); + //! create the thread + if(pThread && pThreadStack) + OSCreateThread(pThread, &CThread::threadCallback, 1, this, (u32)pThreadStack+iStackSize, iStackSize, iPriority, iAttributes); + } + + //! destructor + virtual ~CThread() { shutdownThread(); } + + static CThread *create(CThread::Callback callback, void *callbackArg, s32 iAttr = eAttributeNone, s32 iPriority = 16, s32 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(s32 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)) + { + while(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 s32 threadCallback(s32 argc, void *arg) + { + //! After call to start() continue with the internal function + ((CThread *) arg)->executeThread(); + return 0; + } + s32 iAttributes; + OSThread *pThread; + u8 *pThreadStack; + Callback pCallback; + void *pCallbackArg; +}; + +#endif diff --git a/source/system/exception_handler.c b/source/system/exception_handler.c new file mode 100644 index 0000000..a5ea1e6 --- /dev/null +++ b/source/system/exception_handler.c @@ -0,0 +1,143 @@ +#include +#include +#include "utils/logger.h" +#include "exception_handler.h" + +#define OS_EXCEPTION_MODE_GLOBAL_ALL_CORES 4 + +#define OS_EXCEPTION_DSI 2 +#define OS_EXCEPTION_ISI 3 +#define OS_EXCEPTION_PROGRAM 6 + + +#define CPU_STACK_TRACE_DEPTH 10 +#define __stringify(rn) #rn + +#define mfspr(_rn) \ +({ register uint32_t _rval = 0; \ + asm volatile("mfspr %0," __stringify(_rn) \ + : "=r" (_rval));\ + _rval; \ +}) + +typedef struct _framerec { + struct _framerec *up; + void *lr; +} frame_rec, *frame_rec_t; + +static const char *exception_names[] = { + "DSI", + "ISI", + "PROGRAM" +}; + +static const char exception_print_formats[18][45] = { + "Exception type %s occurred!\n", // 0 + "GPR00 %08X GPR08 %08X GPR16 %08X GPR24 %08X\n", // 1 + "GPR01 %08X GPR09 %08X GPR17 %08X GPR25 %08X\n", // 2 + "GPR02 %08X GPR10 %08X GPR18 %08X GPR26 %08X\n", // 3 + "GPR03 %08X GPR11 %08X GPR19 %08X GPR27 %08X\n", // 4 + "GPR04 %08X GPR12 %08X GPR20 %08X GPR28 %08X\n", // 5 + "GPR05 %08X GPR13 %08X GPR21 %08X GPR29 %08X\n", // 6 + "GPR06 %08X GPR14 %08X GPR22 %08X GPR30 %08X\n", // 7 + "GPR07 %08X GPR15 %08X GPR23 %08X GPR31 %08X\n", // 8 + "LR %08X SRR0 %08x SRR1 %08x\n", // 9 + "DAR %08X DSISR %08X\n", // 10 + "\nSTACK DUMP:", // 11 + " --> ", // 12 + " -->\n", // 13 + "\n", // 14 + "%p", // 15 + "\nCODE DUMP:\n", // 16 + "%p: %08X %08X %08X %08X\n", // 17 +}; + +static unsigned char exception_cb(void * c, unsigned char exception_type) { + char buf[850]; + int pos = 0; + + OSContext *context = (OSContext *) c; + /* + * This part is mostly from libogc. Thanks to the devs over there. + */ + pos += sprintf(buf + pos, exception_print_formats[0], exception_names[exception_type]); + pos += sprintf(buf + pos, exception_print_formats[1], context->gpr[0], context->gpr[8], context->gpr[16], context->gpr[24]); + pos += sprintf(buf + pos, exception_print_formats[2], context->gpr[1], context->gpr[9], context->gpr[17], context->gpr[25]); + pos += sprintf(buf + pos, exception_print_formats[3], context->gpr[2], context->gpr[10], context->gpr[18], context->gpr[26]); + pos += sprintf(buf + pos, exception_print_formats[4], context->gpr[3], context->gpr[11], context->gpr[19], context->gpr[27]); + pos += sprintf(buf + pos, exception_print_formats[5], context->gpr[4], context->gpr[12], context->gpr[20], context->gpr[28]); + pos += sprintf(buf + pos, exception_print_formats[6], context->gpr[5], context->gpr[13], context->gpr[21], context->gpr[29]); + pos += sprintf(buf + pos, exception_print_formats[7], context->gpr[6], context->gpr[14], context->gpr[22], context->gpr[30]); + pos += sprintf(buf + pos, exception_print_formats[8], context->gpr[7], context->gpr[15], context->gpr[23], context->gpr[31]); + pos += sprintf(buf + pos, exception_print_formats[9], context->lr, context->srr0, context->srr1); + + //if(exception_type == OS_EXCEPTION_DSI) { + pos += sprintf(buf + pos, exception_print_formats[10], context->ex1, context->ex0); // this freezes + //} + + void *pc = (void*)context->srr0; + void *lr = (void*)context->lr; + void *r1 = (void*)context->gpr[1]; + register uint32_t i = 0; + register frame_rec_t l,p = (frame_rec_t)lr; + + l = p; + p = r1; + if(!p) + asm volatile("mr %0,%%r1" : "=r"(p)); + + pos += sprintf(buf + pos, exception_print_formats[11]); + + for(i = 0; i < CPU_STACK_TRACE_DEPTH-1 && p->up; p = p->up, i++) { + if(i % 4) + pos += sprintf(buf + pos, exception_print_formats[12]); + else { + if(i > 0) + pos += sprintf(buf + pos, exception_print_formats[13]); + else + pos += sprintf(buf + pos, exception_print_formats[14]); + } + + switch(i) { + case 0: + if(pc) + pos += sprintf(buf + pos, exception_print_formats[15],pc); + break; + case 1: + if(!l) + l = (frame_rec_t)mfspr(8); + pos += sprintf(buf + pos, exception_print_formats[15],(void*)l); + break; + default: + pos += sprintf(buf + pos, exception_print_formats[15],(void*)(p->up->lr)); + break; + } + } + + //if(exception_type == OS_EXCEPTION_DSI) { + uint32_t *pAdd = (uint32_t*)context->srr0; + pos += sprintf(buf + pos, exception_print_formats[16]); + // TODO by Dimok: this was actually be 3 instead of 2 lines in libogc .... but there is just no more space anymore on the screen + for (i = 0; i < 8; i += 4) + pos += sprintf(buf + pos, exception_print_formats[17], &(pAdd[i]),pAdd[i], pAdd[i+1], pAdd[i+2], pAdd[i+3]); + //} + log_print(buf); + OSFatal(buf); + return 1; +} + +static unsigned char dsi_exception_cb(OSContext * context) { + return exception_cb(context, 0); +} +static unsigned char isi_exception_cb(OSContext * context) { + return exception_cb(context, 1); +} +static unsigned char program_exception_cb(OSContext * context) { + return exception_cb(context, 2); +} + +void setup_os_exceptions(void) { + OSSetExceptionCallback(OS_EXCEPTION_DSI, &dsi_exception_cb); + OSSetExceptionCallback(OS_EXCEPTION_ISI, &isi_exception_cb); + OSSetExceptionCallback(OS_EXCEPTION_PROGRAM, &program_exception_cb); +} diff --git a/source/system/exception_handler.h b/source/system/exception_handler.h new file mode 100644 index 0000000..7626f92 --- /dev/null +++ b/source/system/exception_handler.h @@ -0,0 +1,14 @@ +#ifndef __EXCEPTION_HANDLER_H_ +#define __EXCEPTION_HANDLER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void setup_os_exceptions(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/system/memory.c b/source/system/memory.c new file mode 100644 index 0000000..5058871 --- /dev/null +++ b/source/system/memory.c @@ -0,0 +1,216 @@ +/**************************************************************************** + * 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 . + ****************************************************************************/ +#include +#include +#include +#include "memory.h" + +#define MEMORY_ARENA_1 0 +#define MEMORY_ARENA_2 1 +#define MEMORY_ARENA_3 2 +#define MEMORY_ARENA_4 3 +#define MEMORY_ARENA_5 4 +#define MEMORY_ARENA_6 5 +#define MEMORY_ARENA_7 6 +#define MEMORY_ARENA_8 7 +#define MEMORY_ARENA_FG_BUCKET 8 + +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +//! Memory functions +//! This is the only place where those are needed so lets keep them more or less private +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +extern u32 * pMEMAllocFromDefaultHeapEx; +extern u32 * pMEMAllocFromDefaultHeap; +extern u32 * pMEMFreeToDefaultHeap; + +extern s32 (* MEMGetBaseHeapHandle)(s32 mem_arena); +extern u32 (* MEMGetAllocatableSizeForFrmHeapEx)(s32 heap, s32 align); +extern void *(* MEMAllocFromFrmHeapEx)(s32 heap, u32 size, s32 align); +extern void (* MEMFreeToFrmHeap)(s32 heap, s32 mode); +extern void *(* MEMAllocFromExpHeapEx)(s32 heap, u32 size, s32 align); +extern s32 (* MEMCreateExpHeapEx)(void* address, u32 size, unsigned short flags); +extern void *(* MEMDestroyExpHeap)(s32 heap); +extern void (* MEMFreeToExpHeap)(s32 heap, void* ptr); + +static s32 mem1_heap = -1; +static s32 bucket_heap = -1; + +void memoryInitialize(void) +{ + s32 mem1_heap_handle = MEMGetBaseHeapHandle(MEMORY_ARENA_1); + u32 mem1_allocatable_size = MEMGetAllocatableSizeForFrmHeapEx(mem1_heap_handle, 4); + void *mem1_memory = MEMAllocFromFrmHeapEx(mem1_heap_handle, mem1_allocatable_size, 4); + if(mem1_memory) + mem1_heap = MEMCreateExpHeapEx(mem1_memory, mem1_allocatable_size, 0); + + s32 bucket_heap_handle = MEMGetBaseHeapHandle(MEMORY_ARENA_FG_BUCKET); + u32 bucket_allocatable_size = MEMGetAllocatableSizeForFrmHeapEx(bucket_heap_handle, 4); + void *bucket_memory = MEMAllocFromFrmHeapEx(bucket_heap_handle, bucket_allocatable_size, 4); + if(bucket_memory) + bucket_heap = MEMCreateExpHeapEx(bucket_memory, bucket_allocatable_size, 0); +} + +void memoryRelease(void) +{ + MEMDestroyExpHeap(mem1_heap); + MEMFreeToFrmHeap(MEMGetBaseHeapHandle(MEMORY_ARENA_1), 3); + mem1_heap = -1; + + MEMDestroyExpHeap(bucket_heap); + MEMFreeToFrmHeap(MEMGetBaseHeapHandle(MEMORY_ARENA_FG_BUCKET), 3); + bucket_heap = -1; +} + +//!------------------------------------------------------------------------------------------- +//! wraps +//!------------------------------------------------------------------------------------------- +void *__wrap_malloc(size_t size) +{ + // pointer to a function resolve + return ((void * (*)(size_t))(*pMEMAllocFromDefaultHeap))(size); +} + +void *__wrap_memalign(size_t align, size_t size) +{ + if (align < 4) + align = 4; + + // pointer to a function resolve + return ((void * (*)(size_t, size_t))(*pMEMAllocFromDefaultHeapEx))(size, align); +} + +void __wrap_free(void *p) +{ + // pointer to a function resolve + if(p != 0) + ((void (*)(void *))(*pMEMFreeToDefaultHeap))(p); +} + +void *__wrap_calloc(size_t n, size_t size) +{ + void *p = __wrap_malloc(n * size); + if (p != 0) { + memset(p, 0, n * size); + } + return p; +} + +size_t __wrap_malloc_usable_size(void *p) +{ + //! TODO: this is totally wrong and needs to be addressed + return 0x7FFFFFFF; +} + +void *__wrap_realloc(void *ptr, size_t size) +{ + void *newPtr; + + if (!ptr) { + newPtr = __wrap_malloc(size); + if (!newPtr) { goto error; } + } else { + newPtr = __wrap_malloc(size); + if (!newPtr) { goto error; } + + memcpy(newPtr, ptr, size); + + __wrap_free(ptr); + } + + return newPtr; +error: + return NULL; +} + /* + void *new_ptr = __wrap_malloc(size); + if (new_ptr != 0) + { + memcpy(new_ptr, p, __wrap_malloc_usable_size(p) < size ? __wrap_malloc_usable_size(p) : size); + __wrap_free(p); + } + return new_ptr; +}*/ + +//!------------------------------------------------------------------------------------------- +//! reent versions +//!------------------------------------------------------------------------------------------- +void *__wrap__malloc_r(struct _reent *r, size_t size) +{ + return __wrap_malloc(size); +} + +void *__wrap__calloc_r(struct _reent *r, size_t n, size_t size) +{ + return __wrap_calloc(n, size); +} + +void *__wrap__memalign_r(struct _reent *r, size_t align, size_t size) +{ + return __wrap_memalign(align, size); +} + +void __wrap__free_r(struct _reent *r, void *p) +{ + __wrap_free(p); +} + +size_t __wrap__malloc_usable_size_r(struct _reent *r, void *p) +{ + return __wrap_malloc_usable_size(p); +} + +void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) +{ + return __wrap_realloc(p, size); +} + +//!------------------------------------------------------------------------------------------- +//! some wrappers +//!------------------------------------------------------------------------------------------- +void * MEM2_alloc(u32 size, u32 align) +{ + return __wrap_memalign(align, size); +} + +void MEM2_free(void *ptr) +{ + __wrap_free(ptr); +} + +void * MEM1_alloc(u32 size, u32 align) +{ + if (align < 4) + align = 4; + return MEMAllocFromExpHeapEx(mem1_heap, size, align); +} + +void MEM1_free(void *ptr) +{ + MEMFreeToExpHeap(mem1_heap, ptr); +} + +void * MEMBucket_alloc(u32 size, u32 align) +{ + if (align < 4) + align = 4; + return MEMAllocFromExpHeapEx(bucket_heap, size, align); +} + +void MEMBucket_free(void *ptr) +{ + MEMFreeToExpHeap(bucket_heap, ptr); +} diff --git a/source/system/memory.h b/source/system/memory.h new file mode 100644 index 0000000..db67a05 --- /dev/null +++ b/source/system/memory.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * 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 __MEMORY_H_ +#define __MEMORY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void memoryInitialize(void); +void memoryRelease(void); + +void * MEM2_alloc(u32 size, u32 align); +void MEM2_free(void *ptr); + +void * MEM1_alloc(u32 size, u32 align); +void MEM1_free(void *ptr); + +void * MEMBucket_alloc(u32 size, u32 align); +void MEMBucket_free(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif // __MEMORY_H_ diff --git a/source/utils/StringTools.cpp b/source/utils/StringTools.cpp new file mode 100644 index 0000000..879544b --- /dev/null +++ b/source/utils/StringTools.cpp @@ -0,0 +1,230 @@ +/*************************************************************************** + * 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 "StringTools.h" + +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(s32 x){ + static char b[9]; + b[0] = '\0'; + + s32 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; + char * tmp = NULL; + + va_list va; + va_start(va, format); + if((vasprintf(&tmp, format, va) >= 0) && tmp) + { + snprintf(strChar, sizeof(strChar), tmp); + free(tmp); + va_end(va); + return (const char *) strChar; + } + va_end(va); + + if(tmp) + free(tmp); + + return NULL; +} + +const wchar_t * StringTools::wfmt(const char * format, ...){ + static wchar_t strWChar[512]; + strWChar[0] = 0; + + if(!format) + return (const wchar_t *) strWChar; + + if(strcmp(format, "") == 0) + return (const wchar_t *) strWChar; + + char * tmp = NULL; + + va_list va; + va_start(va, format); + if((vasprintf(&tmp, format, va) >= 0) && tmp) + { + int bt; + s32 strlength = strlen(tmp); + bt = mbstowcs(strWChar, tmp, (strlength < 512) ? strlength : 512 ); + free(tmp); + tmp = 0; + + if(bt > 0) + { + strWChar[bt] = 0; + return (const wchar_t *) strWChar; + } + } + va_end(va); + + if(tmp) + free(tmp); + + return NULL; +} + +s32 StringTools::strprintf(std::string &str, const char * format, ...){ + s32 result = 0; + char * tmp = NULL; + + va_list va; + va_start(va, format); + if((vasprintf(&tmp, format, va) >= 0) && tmp) + { + str = tmp; + result = str.size(); + } + va_end(va); + + if(tmp) + free(tmp); + + return result; +} + +std::string StringTools::strfmt(const char * format, ...){ + std::string str; + char * tmp = NULL; + + va_list va; + va_start(va, format); + if((vasprintf(&tmp, format, va) >= 0) && tmp) + { + str = tmp; + } + va_end(va); + + if(tmp) + free(tmp); + + 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; +} + +s32 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; +} + +s32 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) { + u32 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/source/utils/StringTools.h b/source/utils/StringTools.h new file mode 100644 index 0000000..a66aeca --- /dev/null +++ b/source/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(s32 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 s32 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 s32 strtokcmp(const char * string, const char * compare, const char * separator); + static s32 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){ + u32 length = str.size(); + + //! clear path of double slashes + for(u32 i = 1; i < length; ++i) + { + if(str[i-1] == '/' && str[i] == '/') + { + str.erase(i, 1); + i--; + length--; + } + } + } + + static std::vector stringSplit(const std::string & value, const std::string & splitter); +}; + +#endif /* __STRING_TOOLS_H */ + diff --git a/source/utils/function_patcher.cpp b/source/utils/function_patcher.cpp new file mode 100644 index 0000000..979e64e --- /dev/null +++ b/source/utils/function_patcher.cpp @@ -0,0 +1,377 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * With code from chadderz and 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 . + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "function_patcher.h" +#include "logger.h" +#include "kernel/kernel_defs.h" +#include "kernel/kernel_functions.h" + +#define LIB_CODE_RW_BASE_OFFSET 0xC1000000 +#define CODE_RW_BASE_OFFSET 0x00000000 +#define DEBUG_LOG_DYN 0 + + +/* +* Patches a function that is loaded at the start of each application. Its not required to restore, at least when they are really dynamic. +* "normal" functions should be patch with the normal patcher. Current Code by Maschell with the help of dimok. Orignal code by Chadderz. +*/ +void PatchInvidualMethodHooks(hooks_magic_t method_hooks[],s32 hook_information_size, volatile u32 dynamic_method_calls[]){ + SetupKernelCallback(); //Patch Kernel. Just to be sure. + + DEBUG_FUNCTION_LINE("Patching %d given functions\n",hook_information_size); + /* Patch branches to it. */ + volatile u32 *space = &dynamic_method_calls[0]; + + s32 method_hooks_count = hook_information_size; + + u32 skip_instr = 1; + u32 my_instr_len = 6; + u32 instr_len = my_instr_len + skip_instr; + u32 flush_len = 4*instr_len; + for(s32 i = 0; i < method_hooks_count; i++) + { + DEBUG_FUNCTION_LINE("Patching %s ...",method_hooks[i].functionName); + if(method_hooks[i].functionType == STATIC_FUNCTION && method_hooks[i].alreadyPatched == 1){ + if(isDynamicFunction((u32)OSEffectiveToPhysical((void*)method_hooks[i].realAddr))){ + log_printf("The function %s is a dynamic function. Please fix that <3\n", method_hooks[i].functionName); + method_hooks[i].functionType = DYNAMIC_FUNCTION; + }else{ + log_printf("Skipping %s, its already patched\n", method_hooks[i].functionName); + space += instr_len; + continue; + } + } + + u32 physical = 0; + u32 repl_addr = (u32)method_hooks[i].replaceAddr; + u32 call_addr = (u32)method_hooks[i].replaceCall; + + u32 real_addr = GetAddressOfFunction(method_hooks[i].functionName,method_hooks[i].library); + + if(!real_addr){ + log_printf("\n"); + DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s\n", method_hooks[i].functionName); + space += instr_len; + continue; + } + + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("%s is located at %08X!\n", method_hooks[i].functionName,real_addr);} + + physical = (u32)OSEffectiveToPhysical((void*)real_addr); + if(!physical){ + log_printf("Error. Something is wrong with the physical address\n"); + space += instr_len; + continue; + } + + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("%s physical is located at %08X!\n", method_hooks[i].functionName,physical);} + + *(volatile u32 *)(call_addr) = (u32)(space) - CODE_RW_BASE_OFFSET; + + + SC0x25_KernelCopyData((u32)space, physical, 4); + space++; + + //Only works if skip_instr == 1 + if(skip_instr == 1){ + // fill the restore instruction section + method_hooks[i].realAddr = real_addr; + method_hooks[i].restoreInstruction = *(space-1); + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("method_hooks[i].realAddr = %08X!\n", method_hooks[i].realAddr);} + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("method_hooks[i].restoreInstruction = %08X!\n",method_hooks[i].restoreInstruction) ;} + } + else{ + log_printf("Error. Can't save %s for restoring!\n", method_hooks[i].functionName); + } + + //adding jump to real function thx @ dimok for the assembler code + /* + 90 61 ff e0 stw r3,-32(r1) + 3c 60 12 34 lis r3,4660 + 60 63 56 78 ori r3,r3,22136 + 7c 69 03 a6 mtctr r3 + 80 61 ff e0 lwz r3,-32(r1) + 4e 80 04 20 bctr*/ + *space = 0x9061FFE0; + space++; + *space = 0x3C600000 | (((real_addr + (skip_instr * 4)) >> 16) & 0x0000FFFF); // lis r3, real_addr@h + space++; + *space = 0x60630000 | ((real_addr + (skip_instr * 4)) & 0x0000ffff); // ori r3, r3, real_addr@l + space++; + *space = 0x7C6903A6; // mtctr r3 + space++; + *space = 0x8061FFE0; // lwz r3,-32(r1) + space++; + *space = 0x4E800420; // bctr + space++; + DCFlushRange((void*)(space - instr_len), flush_len); + ICInvalidateRange((unsigned char*)(space - instr_len), flush_len); + + //setting jump back + u32 replace_instr = 0x48000002 | (repl_addr & 0x03fffffc); + DCFlushRange(&replace_instr, 4); + + SC0x25_KernelCopyData(physical, (u32)OSEffectiveToPhysical(&replace_instr), 4); + ICInvalidateRange((void*)(real_addr), 4); + + method_hooks[i].alreadyPatched = 1; + log_printf("done!\n"); + + } + DEBUG_FUNCTION_LINE("Done with patching given functions!\n"); +} + +/* ****************************************************************** */ +/* RESTORE ORIGINAL INSTRUCTIONS */ +/* ****************************************************************** */ +void RestoreInvidualInstructions(hooks_magic_t method_hooks[],s32 hook_information_size) +{ + DEBUG_FUNCTION_LINE("Restoring given functions!\n"); + s32 method_hooks_count = hook_information_size; + for(s32 i = 0; i < method_hooks_count; i++) + { + DEBUG_FUNCTION_LINE("Restoring %s... ",method_hooks[i].functionName); + if(method_hooks[i].restoreInstruction == 0 || method_hooks[i].realAddr == 0){ + log_printf("I dont have the information for the restore =( skip\n"); + continue; + } + + u32 real_addr = GetAddressOfFunction(method_hooks[i].functionName,method_hooks[i].library); + + if(!real_addr){ + log_printf("OSDynLoad_FindExport failed for %s\n", method_hooks[i].functionName); + continue; + } + + u32 physical = (u32)OSEffectiveToPhysical((void*)real_addr); + if(!physical){ + log_printf("Something is wrong with the physical address\n"); + continue; + } + + if(isDynamicFunction(physical)) + { + log_printf("Its a dynamic function. We don't need to restore it!\n",method_hooks[i].functionName); + } + else + { + physical = (u32)OSEffectiveToPhysical((void*)method_hooks[i].realAddr); //When its an static function, we need to use the old location + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("Restoring %08X to %08X\n",(u32)method_hooks[i].restoreInstruction,physical);} + SC0x25_KernelCopyData(physical,(u32)&method_hooks[i].restoreInstruction , 4); + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("ICInvalidateRange %08X\n",(void*)method_hooks[i].realAddr);} + ICInvalidateRange((void*)method_hooks[i].realAddr, 4); + log_printf("done\n"); + } + method_hooks[i].alreadyPatched = 0; // In case a + } + + DEBUG_FUNCTION_LINE("Done with restoring given functions!\n"); +} + +s32 isDynamicFunction(u32 physicalAddress){ + if((physicalAddress & 0x80000000) == 0x80000000){ + return 1; + } + return 0; +} + +u32 GetAddressOfFunction(const char * functionName,u32 library){ + u32 real_addr = 0; + + if(strcmp(functionName, "OSDynLoad_Acquire") == 0) + { + memcpy(&real_addr, &OSDynLoad_Acquire, 4); + return real_addr; + } + else if(strcmp(functionName, "LiWaitOneChunk") == 0) + { + real_addr = (u32)addr_LiWaitOneChunk; + return real_addr; + } + else if(strcmp(functionName, "LiBounceOneChunk") == 0) + { + //! not required on firmwares above 3.1.0 + if(OS_FIRMWARE >= 400) + return 0; + + u32 addr_LiBounceOneChunk = 0x010003A0; + real_addr = (u32)addr_LiBounceOneChunk; + return real_addr; + } + + u32 rpl_handle = 0; + if(library == LIB_CORE_INIT){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_CORE_INIT\n", functionName);} + if(coreinit_handle == 0){InitAcquireOS();} + if(coreinit_handle == 0){DEBUG_FUNCTION_LINE("LIB_CORE_INIT failed to acquire\n"); return 0;} + + rpl_handle = coreinit_handle; + } + else if(library == LIB_NSYSNET){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_NSYSNET\n", functionName);} + if(nsysnet_handle == 0){InitAcquireSocket();} + if(nsysnet_handle == 0){DEBUG_FUNCTION_LINE("LIB_NSYSNET failed to acquire\n"); return 0;} + rpl_handle = nsysnet_handle; + } + else if(library == LIB_GX2){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_GX2\n", functionName);} + if(gx2_handle == 0){InitAcquireGX2();} + if(gx2_handle == 0){DEBUG_FUNCTION_LINE("LIB_GX2 failed to acquire\n"); return 0;} + rpl_handle = gx2_handle; + } + else if(library == LIB_AOC){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_AOC\n", functionName);} + if(aoc_handle == 0){InitAcquireAoc();} + if(aoc_handle == 0){DEBUG_FUNCTION_LINE("LIB_AOC failed to acquire\n"); return 0;} + rpl_handle = aoc_handle; + } + else if(library == LIB_AX){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_AX\n", functionName);} + if(sound_handle == 0){InitAcquireAX();} + if(sound_handle == 0){DEBUG_FUNCTION_LINE("LIB_AX failed to acquire\n"); return 0;} + rpl_handle = sound_handle; + } + else if(library == LIB_AX_OLD){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_AX_OLD\n", functionName);} + if(sound_handle_old == 0){InitAcquireAX();} + if(sound_handle_old == 0){DEBUG_FUNCTION_LINE("LIB_AX_OLD failed to acquire\n"); return 0;} + rpl_handle = sound_handle_old; + } + else if(library == LIB_FS){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_FS\n", functionName);} + if(coreinit_handle == 0){InitAcquireOS();} + if(coreinit_handle == 0){DEBUG_FUNCTION_LINE("LIB_FS failed to acquire\n"); return 0;} + rpl_handle = coreinit_handle; + } + else if(library == LIB_OS){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_OS\n", functionName);} + if(coreinit_handle == 0){InitAcquireOS();} + if(coreinit_handle == 0){DEBUG_FUNCTION_LINE("LIB_OS failed to acquire\n"); return 0;} + rpl_handle = coreinit_handle; + } + else if(library == LIB_PADSCORE){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_PADSCORE\n", functionName);} + if(padscore_handle == 0){InitAcquirePadScore();} + if(padscore_handle == 0){DEBUG_FUNCTION_LINE("LIB_PADSCORE failed to acquire\n"); return 0;} + rpl_handle = padscore_handle; + } + else if(library == LIB_SOCKET){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_SOCKET\n", functionName);} + if(nsysnet_handle == 0){InitAcquireSocket();} + if(nsysnet_handle == 0){DEBUG_FUNCTION_LINE("LIB_SOCKET failed to acquire\n"); return 0;} + rpl_handle = nsysnet_handle; + } + else if(library == LIB_SYS){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_SYS\n", functionName);} + if(sysapp_handle == 0){InitAcquireSys();} + if(sysapp_handle == 0){DEBUG_FUNCTION_LINE("LIB_SYS failed to acquire\n"); return 0;} + rpl_handle = sysapp_handle; + } + else if(library == LIB_VPAD){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_VPAD\n", functionName);} + if(vpad_handle == 0){InitAcquireVPad();} + if(vpad_handle == 0){DEBUG_FUNCTION_LINE("LIB_VPAD failed to acquire\n"); return 0;} + rpl_handle = vpad_handle; + } + else if(library == LIB_NN_ACP){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_NN_ACP\n", functionName);} + if(acp_handle == 0){InitAcquireACP();} + if(acp_handle == 0){DEBUG_FUNCTION_LINE("LIB_NN_ACP failed to acquire\n"); return 0;} + rpl_handle = acp_handle; + } + else if(library == LIB_SYSHID){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_SYSHID\n", functionName);} + if(syshid_handle == 0){InitAcquireSysHID();} + if(syshid_handle == 0){DEBUG_FUNCTION_LINE("LIB_SYSHID failed to acquire\n"); return 0;} + rpl_handle = syshid_handle; + } + else if(library == LIB_VPADBASE){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_VPADBASE\n", functionName);} + if(vpadbase_handle == 0){InitAcquireVPad();} + if(vpadbase_handle == 0){DEBUG_FUNCTION_LINE("LIB_VPADBASE failed to acquire\n"); return 0;} + rpl_handle = vpadbase_handle; + } + else if(library == LIB_PROC_UI){ + if(DEBUG_LOG_DYN){DEBUG_FUNCTION_LINE("FindExport of %s! From LIB_PROC_UI\n", functionName);} + if(proc_ui_handle == 0){InitAcquireProcUI();} + if(proc_ui_handle == 0){DEBUG_FUNCTION_LINE("LIB_PROC_UI failed to acquire\n"); return 0;} + rpl_handle = proc_ui_handle; + } + else if(library == LIB_NTAG){ + if(DEBUG_LOG_DYN){log_printf("FindExport of %s! From LIB_NTAG\n", functionName);} + if(ntag_handle == 0){InitAcquireNTAG();} + if(ntag_handle == 0){log_print("LIB_NTAG failed to acquire\n"); return 0;} + rpl_handle = ntag_handle; + } + else if(library == LIB_NFP){ + if(DEBUG_LOG_DYN){log_printf("FindExport of %s! From LIB_NFP\n", functionName);} + if(nfp_handle == 0){InitAcquireNFP();} + if(nfp_handle == 0){log_print("LIB_NFP failed to acquire\n"); return 0;} + rpl_handle = nfp_handle; + } + else if(library == LIB_SAVE){ + if(DEBUG_LOG_DYN){log_printf("FindExport of %s! From LIB_SAVE\n", functionName);} + if(nn_save_handle == 0){InitAcquireSave();} + if(nn_save_handle == 0){log_print("LIB_SAVE failed to acquire\n"); return 0;} + rpl_handle = nn_save_handle; + } + else if(library == LIB_ACT){ + if(DEBUG_LOG_DYN){log_printf("FindExport of %s! From LIB_ACT\n", functionName);} + if(nn_act_handle == 0){InitAcquireACT();} + if(nn_act_handle == 0){log_print("LIB_ACT failed to acquire\n"); return 0;} + rpl_handle = nn_act_handle; + } + else if(library == LIB_NIM){ + if(DEBUG_LOG_DYN){log_printf("FindExport of %s! From LIB_NIM\n", functionName);} + if(nn_nim_handle == 0){InitAcquireNim();} + if(nn_nim_handle == 0){log_print("LIB_NIM failed to acquire\n"); return 0;} + rpl_handle = nn_nim_handle; + } + + if(!rpl_handle){ + DEBUG_FUNCTION_LINE("Failed to find the RPL handle for %s\n", functionName); + return 0; + } + + OSDynLoad_FindExport(rpl_handle, 0, functionName, &real_addr); + + if(!real_addr){ + DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s\n", functionName); + return 0; + } + + if((library == LIB_NN_ACP) && (u32)(*(volatile u32*)(real_addr) & 0x48000002) == 0x48000000) + { + u32 address_diff = (u32)(*(volatile u32*)(real_addr) & 0x03FFFFFC); + if((address_diff & 0x03000000) == 0x03000000) { + address_diff |= 0xFC000000; + } + real_addr += (s32)address_diff; + if((u32)(*(volatile u32*)(real_addr) & 0x48000002) == 0x48000000){ + return 0; + } + } + + return real_addr; +} diff --git a/source/utils/function_patcher.h b/source/utils/function_patcher.h new file mode 100644 index 0000000..9f6ebde --- /dev/null +++ b/source/utils/function_patcher.h @@ -0,0 +1,102 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * With code from chadderz and 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 _FUNCTION_HOOKS_H_ +#define _FUNCTION_HOOKS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Macros for libs */ +#define LIB_CORE_INIT 0 +#define LIB_NSYSNET 1 +#define LIB_GX2 2 +#define LIB_AOC 3 +#define LIB_AX 4 +#define LIB_FS 5 +#define LIB_OS 6 +#define LIB_PADSCORE 7 +#define LIB_SOCKET 8 +#define LIB_SYS 9 +#define LIB_VPAD 10 +#define LIB_NN_ACP 11 +#define LIB_SYSHID 12 +#define LIB_VPADBASE 13 +#define LIB_AX_OLD 14 +#define LIB_PROC_UI 15 +#define LIB_NTAG 16 +#define LIB_NFP 17 +#define LIB_SAVE 18 +#define LIB_ACT 19 +#define LIB_NIM 20 + +// functions types +#define STATIC_FUNCTION 0 +#define DYNAMIC_FUNCTION 1 + +//Orignal code by Chadderz. +#define DECL(res, name, ...) \ + res (* real_ ## name)(__VA_ARGS__) __attribute__((section(".data"))); \ + res my_ ## name(__VA_ARGS__) + +#define FUNCTION_PATCHER_METHOD_STORE_SIZE 7 + +typedef struct { + const u32 replaceAddr; + const u32 replaceCall; + const u32 library; + const char functionName[50]; + u32 realAddr; + u32 restoreInstruction; + u8 functionType; + u8 alreadyPatched; +} hooks_magic_t; + +void PatchInvidualMethodHooks(hooks_magic_t hook_information[],s32 hook_information_size, volatile u32 dynamic_method_calls[]); +void RestoreInvidualInstructions(hooks_magic_t hook_information[],s32 hook_information_size); +u32 GetAddressOfFunction(const char * functionName,u32 library); +s32 isDynamicFunction(u32 physicalAddress); + +//Orignal code by Chadderz. +#define MAKE_MAGIC(x, lib,functionType) { (u32) my_ ## x, (u32) &real_ ## x, lib, # x,0,0,functionType,0} +#define MAKE_MAGIC_NAME(x,y, lib,functionType) { (u32) my_ ## x, (u32) &real_ ## x, lib, # y,0,0,functionType,0} + +#ifdef __cplusplus +} +#endif + +#endif /* _FS_H */ diff --git a/source/utils/logger.c b/source/utils/logger.c new file mode 100644 index 0000000..c813752 --- /dev/null +++ b/source/utils/logger.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include "logger.h" +#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_(){ + InitOSFunctionPointers(); + InitSocketFunctionPointers(); + + 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) + os_usleep(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 log_printf_(const char *format, ...) +{ + if(log_socket < 0) { + return; + } + + + char * tmp = NULL; + + va_list va; + va_start(va, format); + if((vasprintf(&tmp, format, va) >= 0) && tmp) + { + log_print_(tmp); + } + va_end(va); + + if(tmp) + free(tmp); +} + diff --git a/source/utils/logger.h b/source/utils/logger.h new file mode 100644 index 0000000..7842a2f --- /dev/null +++ b/source/utils/logger.h @@ -0,0 +1,43 @@ +#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, ...); + +#ifdef __LOGGING__ + +#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 __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + log_printf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#else + +#define log_init() +#define log_deinit() +#define log_print(x) +#define log_printf(x, ...) +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) + +#endif //__LOGGING__ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/utils/utils.h b/source/utils/utils.h new file mode 100644 index 0000000..2b33e34 --- /dev/null +++ b/source/utils/utils.h @@ -0,0 +1,46 @@ +#ifndef __UTILS_H_ +#define __UTILS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FlushBlock(addr) asm volatile("dcbf %0, %1\n" \ + "icbi %0, %1\n" \ + "sync\n" \ + "eieio\n" \ + "isync\n" \ + : \ + :"r"(0), "r"(((addr) & ~31)) \ + :"memory", "ctr", "lr", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" \ + ); + +#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) + +// those work only in powers of 2 +#define ROUNDDOWN(val, align) ((val) & ~(align-1)) +#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align-1)), align) + +#define le16(i) ((((u16) ((i) & 0xFF)) << 8) | ((u16) (((i) & 0xFF00) >> 8))) +#define le32(i) ((((u32)le16((i) & 0xFFFF)) << 16) | ((u32)le16(((i) & 0xFFFF0000) >> 16))) +#define le64(i) ((((u64)le32((i) & 0xFFFFFFFFLL)) << 32) | ((u64)le32(((i) & 0xFFFFFFFF00000000LL) >> 32))) + +#ifdef __cplusplus +} +#endif + +#endif // __UTILS_H_