Add libiosuhax features into libmocha

This commit is contained in:
Maschell 2022-06-11 17:58:39 +02:00
parent f8703950aa
commit 41226ab3ca
36 changed files with 1915 additions and 161 deletions

View File

@ -1,6 +1,6 @@
FROM wiiuenv/devkitppc:20220605 FROM wiiuenv/devkitppc:20220605
RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard a060c981e23cdd7e622624692df2d789693432ce && make install && cd .. && rm -rf wut RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard f73f75225b62fd546fedc7b366cee1d87b4b72a5 && make install && cd .. && rm -rf wut
WORKDIR tmp_build WORKDIR tmp_build
COPY . . COPY . .

View File

@ -1,5 +1,5 @@
FROM wiiuenv/devkitppc:20220605 FROM wiiuenv/devkitppc:20220605
RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard a060c981e23cdd7e622624692df2d789693432ce && make install && cd .. && rm -rf wut RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard f73f75225b62fd546fedc7b366cee1d87b4b72a5 && make install && cd .. && rm -rf wut
WORKDIR project WORKDIR project

View File

@ -25,7 +25,8 @@ VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH)
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR)) TARGET := $(notdir $(CURDIR))
BUILD := build BUILD := build
SOURCES := source SOURCES := source \
source/devoptab
DATA := data DATA := data
INCLUDES := source \ INCLUDES := source \
include \ include \
@ -38,7 +39,7 @@ CFLAGS := -Wall -Werror -save-temps \
$(MACHDEP) \ $(MACHDEP) \
$(BUILD_CFLAGS) $(BUILD_CFLAGS)
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -fno-exceptions
CXXFLAGS := $(CFLAGS) -std=gnu++20 CXXFLAGS := $(CFLAGS) -std=gnu++20

View File

@ -1,5 +1,7 @@
[![Publish Docker Image](https://github.com/wiiu-env/libmocha/actions/workflows/push_image.yml/badge.svg)](https://github.com/wiiu-env/libmocha/actions/workflows/push_image.yml) [![Publish Docker Image](https://github.com/wiiu-env/libmocha/actions/workflows/push_image.yml/badge.svg)](https://github.com/wiiu-env/libmocha/actions/workflows/push_image.yml)
**This library is still WIP, may not work as expected or have breaking changes in the near future**
# libmocha # libmocha
Requires the [MochaPayload](https://github.com/wiiu-env/MochaPayload) to be running via [EnvironmentLoader](https://github.com/wiiu-env/EnvironmentLoader). Requires the [MochaPayload](https://github.com/wiiu-env/MochaPayload) to be running via [EnvironmentLoader](https://github.com/wiiu-env/EnvironmentLoader).
Requires [wut](https://github.com/devkitPro/wut) for building. Requires [wut](https://github.com/devkitPro/wut) for building.

View File

@ -1,66 +1,13 @@
#pragma once #pragma once
#include <coreinit/filesystem.h> #include <coreinit/filesystem.h>
#include <stdint.h> #include <coreinit/filesystem_fsa.h>
#include <cstdint>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef enum FSAMountFlags {
FSA_MOUNT_FLAG_LOCAL_MOUNT = 0,
FSA_MOUNT_FLAG_BIND_MOUNT = 1,
FSA_MOUNT_FLAG_GLOBAL_MOUNT = 2,
} FSAMountFlags;
typedef enum FSAUnmountFlags {
FSA_UNMOUNT_FLAG_BIND_MOUNT = 0x80000000,
} FSAUnmountFlags;
/**
* Mounts a source to a given path for a given FSClient (or globally if FSA_MOUNT_FLAG_GLOBAL_MOUNT is set)
*
* @param client valid FSClient pointer with unlocked permissions
* @param source Mount source e.g. /dev/sdcard01
* @param target Must not start with /vol/storage_ if FSA_MOUNT_FLAG_GLOBAL_MOUNT is **not** set. Requires to start with "/vol/storage_" if FSA_MOUNT_FLAG_GLOBAL_MOUNT or FSA_UNMOUNT_FLAG_BIND_MOUNT is set
* @param flags Determines the mount type.
* @param arg_buf unknown
* @param arg_len unknown
* @return
*/
FSError FSAEx_Mount(FSClient *client, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len);
/**
* Mounts a source to a given path for a given FSClient (or globally if FSA_MOUNT_FLAG_GLOBAL_MOUNT is set)
*
* @param clientHandle valid /dev/fsa handle with unlocked permissions
* @param source Mount source e.g. /dev/sdcard01
* @param target Must not start with /vol/storage_ if FSA_MOUNT_FLAG_GLOBAL_MOUNT is **not** set. Requires to start with "/vol/storage_" if FSA_MOUNT_FLAG_GLOBAL_MOUNT is set
* @param flags Determines the mount type.
* @param arg_buf unknown
* @param arg_len unknown
* @return
*/
FSError FSAEx_MountEx(int clientHandle, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len);
/**
* Unmounts a given path
* @param client valid FSClient pointer with unlocked permissions
* @param mountedTarget path where the mount is mounted to.
* @param flags FSA_UNMOUNT_FLAG_BIND_MOUNT is expected for a BindMount
* @return
*/
FSError FSAEx_Unmount(FSClient *client, const char *mountedTarget, FSAUnmountFlags flags);
/**
* Unmounts a given path
* @param clientHandle valid /dev/fsa handle with unlocked permissions
* @param mountedTarget path where the mount is mounted to.
* @param flags FSA_UNMOUNT_FLAG_BIND_MOUNT is expected for a BindMount
* @return
*/
FSError FSAEx_UnmountEx(int clientHandle, const char *mountedTarget, FSAUnmountFlags flags);
/** /**
* Opens a device for raw read/write * Opens a device for raw read/write
* @param client valid FSClient pointer with unlocked permissions * @param client valid FSClient pointer with unlocked permissions
@ -77,7 +24,7 @@ FSError FSAEx_RawOpen(FSClient *client, char *device_path, int32_t *outHandle);
* @param outHandle pointer where the handle of the raw device will be stored * @param outHandle pointer where the handle of the raw device will be stored
* @return * @return
*/ */
FSError FSAEx_RawOpenEx(int clientHandle, char *device_path, int32_t *outHandle); FSError FSAEx_RawOpenEx(FSAClientHandle clientHandle, char *device_path, int32_t *outHandle);
/** /**
* Closes a devices that was previously opened via FSAEx_RawOpen * Closes a devices that was previously opened via FSAEx_RawOpen
@ -93,7 +40,7 @@ FSError FSAEx_RawClose(FSClient *client, int32_t device_handle);
* @param device_handle device handle * @param device_handle device handle
* @return * @return
*/ */
FSError FSAEx_RawCloseEx(int clientHandle, int32_t device_handle); FSError FSAEx_RawCloseEx(FSAClientHandle clientHandle, int32_t device_handle);
/** /**
* Read data from a device handle. * Read data from a device handle.
@ -119,7 +66,7 @@ FSError FSAEx_RawRead(FSClient *client, void *data, uint32_t size_bytes, uint32_
* @param device_handle valid device handle. * @param device_handle valid device handle.
* @return * @return
*/ */
FSError FSAEx_RawReadEx(int clientHandle, void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle); FSError FSAEx_RawReadEx(FSAClientHandle clientHandle, void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle);
/** /**
@ -146,7 +93,7 @@ FSError FSAEx_RawWrite(FSClient *client, const void *data, uint32_t size_bytes,
* @param device_handle valid device handle. * @param device_handle valid device handle.
* @return * @return
*/ */
FSError FSAEx_RawWriteEx(int clientHandle, const void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle); FSError FSAEx_RawWriteEx(FSAClientHandle clientHandle, const void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <coreinit/filesystem.h> #include <coreinit/filesystem.h>
#include <coreinit/filesystem_fsa.h>
#include <mocha/commands.h> #include <mocha/commands.h>
#include <stdint.h> #include <stdint.h>
@ -12,6 +13,10 @@ enum MochaUtilsStatus {
MOCHA_RESULT_SUCCESS = 0, MOCHA_RESULT_SUCCESS = 0,
MOCHA_RESULT_INVALID_ARGUMENT = -0x01, MOCHA_RESULT_INVALID_ARGUMENT = -0x01,
MOCHA_RESULT_MAX_CLIENT = -0x02, MOCHA_RESULT_MAX_CLIENT = -0x02,
MOCHA_RESULT_OUT_OF_MEMORY = -0x03,
MOCHA_RESULT_ALREADY_EXISTS = -0x04,
MOCHA_RESULT_ADD_DEVOPTAB_FAILED = -0x05,
MOCHA_RESULT_NOT_FOUND = -0x06,
MOCHA_RESULT_UNSUPPORTED_API_VERSION = -0x10, MOCHA_RESULT_UNSUPPORTED_API_VERSION = -0x10,
MOCHA_RESULT_UNSUPPORTED_COMMAND = -0x11, MOCHA_RESULT_UNSUPPORTED_COMMAND = -0x11,
MOCHA_RESULT_LIB_UNINITIALIZED = -0x20, MOCHA_RESULT_LIB_UNINITIALIZED = -0x20,
@ -25,7 +30,11 @@ enum MochaUtilsStatus {
*/ */
MochaUtilsStatus Mocha_InitLibrary(); MochaUtilsStatus Mocha_InitLibrary();
MochaUtilsStatus Mocha_DeInitLibrary(); /**
* Deinitializes the mocha lib
* @return
*/
MochaUtilsStatus Mocha_DeinitLibrary();
/** /**
* Retrieves the API Version of the running mocha. * Retrieves the API Version of the running mocha.
@ -38,42 +47,129 @@ MochaUtilsStatus Mocha_DeInitLibrary();
*/ */
MochaUtilsStatus Mocha_CheckAPIVersion(uint32_t *outVersion); MochaUtilsStatus Mocha_CheckAPIVersion(uint32_t *outVersion);
/*** /**
* Copies data within IOSU with kernel permission.
* @param dst - Destination address
* @param src - Source address
* @param size - Bytes to copy.
* @return MOCHA_RESULT_SUCCESS: The data has been copied successfully<br>
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
*/
MochaUtilsStatus Mocha_IOSUKernelMemcpy(uint32_t dst, uint32_t src, uint32_t size);
/**
* Writes data to a given address with kernel permission.
* @param address - Address where the data will be written to.
* @param buffer - Pointer to the data which should be written.
* @param size - Bytes to write.
* @return MOCHA_RESULT_SUCCESS: The data has been written successfully<br>
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
*/
MochaUtilsStatus Mocha_IOSUKernelWrite(uint32_t address, const uint8_t *buffer, uint32_t size);
/**
* Reads data from a given address with kernel permission.
* @param address - Address where the data will be read from.
* @param buffer - Pointer to the buffer where the read will be stored
* @param size - Bytes to read.
* @return MOCHA_RESULT_SUCCESS: The data has been read successfully<br>
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
*/
MochaUtilsStatus Mocha_IOSUKernelRead(uint32_t address, uint8_t *out_buffer, uint32_t size);
/**
* Write 4 bytes with IOSU kernel permission
* @param address Address where the value will be written.
* @param value Value that will be written to address.
* @return MOCHA_RESULT_SUCCESS: The data has been written successfully<br>
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
*/
MochaUtilsStatus Mocha_IOSUKernelWrite32(uint32_t address, uint32_t value);
/**
* Reads 4 bytes with IOSU kernel permission
* @param address Address from which the data will be read.
* @param out_buffer Pointer where the result will be stored
* @return MOCHA_RESULT_SUCCESS: The data has been read successfully<br>
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
*/
MochaUtilsStatus Mocha_IOSUKernelRead32(uint32_t address, uint32_t *out_buffer);
typedef struct WiiUConsoleOTP {
uint8_t data[0x400];
} WiiUConsoleOTP;
/**
* Read the consoles OTP into the given buffer.
*
* @param out_buffer Buffer where the result will be stored.
* @return MOCHA_RESULT_SUCCESS: The OTP has been read into the buffer<br>
* MOCHA_RESULT_INVALID_ARGUMENT: invalid environmentPathBuffer pointer or bufferLen \< 0x100<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path.
*/
MochaUtilsStatus Mocha_ReadOTP(WiiUConsoleOTP *out_buffer);
/**
* Calls an iosu SVC.
* @param svc_id
* @param args array of argument with the length arg_cnt
* @param arg_cnt number of arguments
* @param outResult
* @return MOCHA_RESULT_SUCCESS: The SVC has been called successfully, the result has been stored in outResult.<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNKNOWN_ERROR
*/
int Mocha_IOSUCallSVC(uint32_t svc_id, uint32_t *args, uint32_t arg_cnt, int32_t *outResult);
/**
* Returns the path of the currently loaded environment * Returns the path of the currently loaded environment
* @param environmentPathBuffer: buffer where the result will be stored * @param environmentPathBuffer: buffer where the result will be stored
* @param bufferLen: length of the buffer. Required to be >= 0x100 * @param bufferLen: length of the buffer. Required to be >= 0x100
* @return MOCHA_RESULT_SUCCESS: The environment path has been stored in environmentPathBuffer<br> * @return MOCHA_RESULT_SUCCESS: The environment path has been stored in environmentPathBuffer<br>
* MOCHA_RESULT_INVALID_ARGUMENT: invalid environmentPathBuffer pointer or bufferLen \< 0x100<br> * MOCHA_RESULT_INVALID_ARGUMENT: invalid environmentPathBuffer pointer or bufferLen \< 0x100<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br> * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br> * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path. * MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path.
*/ */
MochaUtilsStatus Mocha_GetEnvironmentPath(char *environmentPathBuffer, uint32_t bufferLen); MochaUtilsStatus Mocha_GetEnvironmentPath(char *environmentPathBuffer, uint32_t bufferLen);
/*** /**
* Signals Mocha to not redirect the men.rpx to root.rpx anymore * Signals Mocha to not redirect the men.rpx to root.rpx anymore
* @return MOCHA_RESULT_SUCCESS * @return MOCHA_RESULT_SUCCESS: <br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br> * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br> * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path. * MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path.
*/ */
MochaUtilsStatus Mocha_RPXHookCompleted(); MochaUtilsStatus Mocha_RPXHookCompleted();
/*** /**
* Starts the MCP Thread in mocha to allows usage of /dev/iosuhax and wupclient * Starts the MCP thread of mocha to allows usage of /dev/iosuhax and wupclient
* @return MOCHA_RESULT_SUCCESS * @return MOCHA_RESULT_SUCCESS: Thread has been started<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br> * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br> * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path. * MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path.
*/ */
MochaUtilsStatus Mocha_StartMCPThread(); MochaUtilsStatus Mocha_StartMCPThread();
/*** /**
* Starts the MCP Thread in mocha to allows usage of /dev/iosuhax and wupclient * Enables logging via USB (FTDI FT232 chipset only) via OSReport and friends.
* @return MOCHA_RESULT_SUCCESS * @param avoidLogCatchup If set to true, the log start at the moment this function is called (for the first time)
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br> * @return MOCHA_RESULT_SUCCESS: Logging via USB starts or has already been started<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br> * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path. * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path.
*/ */
MochaUtilsStatus Mocha_StartUSBLogging(bool avoidLogCatchup); MochaUtilsStatus Mocha_StartUSBLogging(bool avoidLogCatchup);
@ -81,11 +177,12 @@ MochaUtilsStatus Mocha_StartUSBLogging(bool avoidLogCatchup);
* Gives a FSClient full permissions. <br> * Gives a FSClient full permissions. <br>
* Requires Mocha API Version: 1 * Requires Mocha API Version: 1
* @param client The FSClient that should have full permission * @param client The FSClient that should have full permission
* @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully. * @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully. <br>
* MOCHA_RESULT_INVALID_ARGUMENT: client was NULL <br>
* MOCHA_RESULT_MAX_CLIENT: The maximum number of FS Clients have been unlocked.<br> * MOCHA_RESULT_MAX_CLIENT: The maximum number of FS Clients have been unlocked.<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br> * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br> * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path. * MOCHA_RESULT_UNKNOWN_ERROR: Failed to unlock a given FSClient
*/ */
MochaUtilsStatus Mocha_UnlockFSClient(FSClient *client); MochaUtilsStatus Mocha_UnlockFSClient(FSClient *client);
@ -93,24 +190,88 @@ MochaUtilsStatus Mocha_UnlockFSClient(FSClient *client);
* Gives a /dev/fsa handle full permissions. <br> * Gives a /dev/fsa handle full permissions. <br>
* Requires Mocha API Version: 1 * Requires Mocha API Version: 1
* @param client The /dev/fsa handle that should have full permission * @param client The /dev/fsa handle that should have full permission
* @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully. * @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully. <br>
* MOCHA_RESULT_MAX_CLIENT: The maximum number of FS Clients have been unlocked.<br> * MOCHA_RESULT_MAX_CLIENT: The maximum number of FS Clients have been unlocked.<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br> * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br> * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path. * MOCHA_RESULT_UNKNOWN_ERROR: Failed to unlock the given client
*/ */
MochaUtilsStatus Mocha_UnlockFSClientEx(int clientHandle); MochaUtilsStatus Mocha_UnlockFSClientEx(int clientHandle);
/**
* After successfully calling this function, the next loaded .rpx will be replaced according to the given loadInfo.<br>
* <br>
* Loading a .rpx from within a file (archive e.g. a WUHB) is supported. <br>
* To achieve this, the fileoffset (offset inside file specified via path) and filesize (size of the .rpx) need to be set. <br>
* If filesize is set to 0, the whole file (starting at fileoffset) will be loaded as .rpx <br>
*
* The path is **relative** to the root of the given target device.
*
* @param loadInfo Information about the .rpx replacement.
* @return MOCHA_RESULT_SUCCESS: Loading the next RPX will be redirected. <br>
* MOCHA_RESULT_INVALID_ARGUMENT: The given loadInfo was NULL <br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to setup a redirect of RPX.
*/
MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo); MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo);
typedef struct WUDDiscKey { typedef struct WUDDiscKey {
uint8_t key[0x10]; uint8_t key[0x10];
} WUDDiscKey; } WUDDiscKey;
/**
* Reads the disc key (used to decrypt the SI partition) of the inserted disc.
*
* @param discKey target buffer where the result will be stored.
* @return MOCHA_RESULT_SUCCESS: The disc key of the inserted disc has been read into the given buffer.<br>
* MOCHA_RESULT_INVALID_ARGUMENT: The given discKey buffer was NULL <br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to read the discKey.
*/
MochaUtilsStatus Mocha_ODMGetDiscKey(WUDDiscKey *discKey); MochaUtilsStatus Mocha_ODMGetDiscKey(WUDDiscKey *discKey);
/**
* Reads *size* bytes from *offset* from the SEEPROM of the console. Total size of SEEPROM is 0x200
* @param out_buffer buffer where the result will be stored
* @param offset offset in bytes. Must be an even number.
* @param size size in bytes
* @return MOCHA_RESULT_SUCCESS: The SEEPROM has been read into the given buffer.<br>
* MOCHA_RESULT_INVALID_ARGUMENT: The given out_buffer was NULL or the offset was < 0 or an odd value<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to read the seeprom.
*/
MochaUtilsStatus Mocha_SEEPROMRead(uint8_t *out_buffer, uint32_t offset, uint32_t size); MochaUtilsStatus Mocha_SEEPROMRead(uint8_t *out_buffer, uint32_t offset, uint32_t size);
/**
* Mounts a device (dev_path) to a given path (mount_path) and make a accessible via the
* newlib devoptab (standard POSIX file I/O)
*
* Requires Mocha API Version: 1
* @param virt_name Name which should be used for the devoptab. When choosing e.g. "storage_usb" the mounted device can be accessed via "storage_usb:/".
* @param dev_path (optional) Cafe OS internal device path (e.g. /dev/slc01). If the given dev_path is NULL, an existing mount will be used (and is expected)
* @param mount_path Path where CafeOS should mount the device to. Must be globally unique and start with "/vol/storage_"
* @return MOCHA_RESULT_SUCCESS: The device has been mounted successfully <br>
* MOCHA_RESULT_MAX_CLIENT: The maximum number of FSAClients have been registered.<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path.
*/
MochaUtilsStatus Mocha_MountFS(const char *virt_name, const char *dev_path, const char *mount_path);
MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, const char *mount_path, FSAMountFlags mountFlags, void *mountArgBuf, int mountArgBufLen);
/**
* Unmounts a mount by it's name.
* @param virt_name Name of the mount.
* @return MOCHA_RESULT_SUCCESS: The unmount was successful <br>
* MOCHA_RESULT_INVALID_ARGUMENT: <br>
* MOCHA_RESULT_NOT_FOUND: No mount with the given name has been found.
*/
MochaUtilsStatus Mocha_UnmountFS(const char *virt_name);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View File

@ -0,0 +1,20 @@
#pragma once
#include <coreinit/fastmutex.h>
class FastLockWrapper {
public:
FastLockWrapper() {
OSFastMutex_Init(&mutex, "generic lock");
}
void lock() {
OSFastMutex_Lock(&mutex);
}
void unlock() {
OSFastMutex_Unlock(&mutex);
}
private:
OSFastMutex mutex{};
};

View File

@ -0,0 +1,203 @@
#include "devoptab_fs.h"
#include "logger.h"
#include "mocha/mocha.h"
#include <coreinit/cache.h>
#include <coreinit/filesystem_fsa.h>
#include <mocha/mocha.h>
#include <mutex>
static const devoptab_t fsa_default_devoptab = {
.structSize = sizeof(__fsa_file_t),
.open_r = __fsa_open,
.close_r = __fsa_close,
.write_r = __fsa_write,
.read_r = __fsa_read,
.seek_r = __fsa_seek,
.fstat_r = __fsa_fstat,
.stat_r = __fsa_stat,
.link_r = __fsa_link,
.unlink_r = __fsa_unlink,
.chdir_r = __fsa_chdir,
.rename_r = __fsa_rename,
.mkdir_r = __fsa_mkdir,
.dirStateSize = sizeof(__fsa_dir_t),
.diropen_r = __fsa_diropen,
.dirreset_r = __fsa_dirreset,
.dirnext_r = __fsa_dirnext,
.dirclose_r = __fsa_dirclose,
.statvfs_r = __fsa_statvfs,
.ftruncate_r = __fsa_ftruncate,
.fsync_r = __fsa_fsync,
.chmod_r = __fsa_chmod,
.fchmod_r = __fsa_fchmod,
.rmdir_r = __fsa_rmdir,
.lstat_r = __fsa_stat,
.utimes_r = __fsa_utimes,
};
static bool fsa_initialised = false;
static FSADeviceData fsa_mounts[8];
static void fsaResetMount(FSADeviceData *mount, uint32_t id) {
*mount = {};
memcpy(&mount->device, &fsa_default_devoptab, sizeof(fsa_default_devoptab));
mount->device.name = mount->name;
mount->device.deviceData = mount;
mount->id = id;
mount->setup = false;
mount->mounted = false;
mount->clientHandle = -1;
mount->deviceSizeInSectors = 0;
mount->deviceSectorSize = 0;
memset(mount->mount_path, 0, sizeof(mount->mount_path));
memset(mount->name, 0, sizeof(mount->name));
DCFlushRange(mount, sizeof(*mount));
}
void fsaInit() {
if (!fsa_initialised) {
uint32_t total = sizeof(fsa_mounts) / sizeof(fsa_mounts[0]);
for (uint32_t i = 0; i < total; i++) {
fsaResetMount(&fsa_mounts[i], i);
}
fsa_initialised = true;
}
}
std::mutex fsaMutex;
FSADeviceData *fsa_alloc() {
uint32_t i;
uint32_t total = sizeof(fsa_mounts) / sizeof(fsa_mounts[0]);
FSADeviceData *mount;
fsaInit();
for (i = 0; i < total; i++) {
mount = &fsa_mounts[i];
if (!mount->setup) {
return mount;
}
}
return nullptr;
}
static void fsa_free(FSADeviceData *mount) {
FSError res;
if (mount->mounted) {
if ((res = FSAUnmount(mount->clientHandle, mount->mount_path, FSA_UNMOUNT_FLAG_FORCE)) < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAUnmount %s for %s failed: %s", mount->mount_path, mount->name, FSAGetStatusStr(res));
}
}
res = FSADelClient(mount->clientHandle);
if (res < 0) {
DEBUG_FUNCTION_LINE_ERR("FSADelClient for %s failed: %d", mount->name, FSAGetStatusStr(res));
}
fsaResetMount(mount, mount->id);
}
MochaUtilsStatus Mocha_UnmountFS(const char *virt_name) {
if (!virt_name) {
return MOCHA_RESULT_INVALID_ARGUMENT;
}
std::lock_guard<std::mutex> lock(fsaMutex);
uint32_t total = sizeof(fsa_mounts) / sizeof(fsa_mounts[0]);
fsaInit();
for (uint32_t i = 0; i < total; i++) {
FSADeviceData *mount = &fsa_mounts[i];
if (!mount->setup) {
continue;
}
if (strcmp(mount->name, virt_name) == 0) {
RemoveDevice(mount->name);
fsa_free(mount);
return MOCHA_RESULT_SUCCESS;
}
}
DEBUG_FUNCTION_LINE_ERR("Failed to find fsa mount data for %s", virt_name);
return MOCHA_RESULT_NOT_FOUND;
}
extern int mochaInitDone;
MochaUtilsStatus Mocha_MountFS(const char *virt_name, const char *dev_path, const char *mount_path) {
return Mocha_MountFSEx(virt_name, dev_path, mount_path, FSA_MOUNT_FLAG_GLOBAL_MOUNT, nullptr, 0);
}
MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, const char *mount_path, FSAMountFlags mountFlags, void *mountArgBuf, int mountArgBufLen) {
if (!mochaInitDone) {
if (Mocha_InitLibrary() != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Mocha_InitLibrary failed");
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
}
}
FSAInit();
std::lock_guard<std::mutex> lock(fsaMutex);
FSADeviceData *mount = fsa_alloc();
if (mount == nullptr) {
DEBUG_FUNCTION_LINE_ERR("fsa_alloc() failed");
OSMemoryBarrier();
return MOCHA_RESULT_OUT_OF_MEMORY;
}
mount->clientHandle = FSAAddClient(nullptr);
if (mount->clientHandle < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAAddClient() failed: %s", FSAGetStatusStr(static_cast<FSError>(mount->clientHandle)));
fsa_free(mount);
return MOCHA_RESULT_MAX_CLIENT;
}
if (Mocha_UnlockFSClientEx(mount->clientHandle) != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Mocha_UnlockFSClientEx failed");
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
}
mount->mounted = false;
strncpy(mount->name, virt_name, sizeof(mount->name) - 1);
strncpy(mount->mount_path, mount_path, sizeof(mount->mount_path) - 1);
FSError res;
if (dev_path) {
res = FSAMount(mount->clientHandle, dev_path, mount_path, mountFlags, mountArgBuf, mountArgBufLen);
if (res < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAMount(0x%08X, %s, %s, FSA_MOUNT_FLAG_GLOBAL_MOUNT, nullptr, 0) failed: %s", mount->clientHandle, dev_path, mount_path, FSAGetStatusStr(res));
fsa_free(mount);
if (res == FS_ERROR_ALREADY_EXISTS) {
return MOCHA_RESULT_ALREADY_EXISTS;
}
return MOCHA_RESULT_UNKNOWN_ERROR;
}
mount->mounted = true;
} else {
mount->mounted = false;
}
if ((res = FSAChangeDir(mount->clientHandle, mount->mount_path)) < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAChangeDir(0x%08X, %s) failed: %s", mount->clientHandle, mount->mount_path, FSAGetStatusStr(res));
}
FSADeviceInfo deviceInfo;
if ((res = FSAGetDeviceInfo(mount->clientHandle, mount_path, &deviceInfo)) >= 0) {
mount->deviceSizeInSectors = deviceInfo.deviceSizeInSectors;
mount->deviceSectorSize = deviceInfo.deviceSectorSize;
} else {
mount->deviceSizeInSectors = 0xFFFFFFFF;
mount->deviceSectorSize = 512;
DEBUG_FUNCTION_LINE_ERR("Failed to get DeviceInfo for %s: %s", mount_path, FSAGetStatusStr(res));
}
if (AddDevice(&mount->device) < 0) {
DEBUG_FUNCTION_LINE_ERR("AddDevice failed for %s.", virt_name);
fsa_free(mount);
return MOCHA_RESULT_ADD_DEVOPTAB_FAILED;
}
mount->setup = true;
return MOCHA_RESULT_SUCCESS;
}

View File

@ -0,0 +1,109 @@
#pragma once
#include <coreinit/filesystem_fsa.h>
#include "FastLockWrapper.h"
#include <cerrno>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <malloc.h>
#include <sys/dirent.h>
#include <sys/iosupport.h>
#include <sys/param.h>
#include <unistd.h>
typedef struct FSADeviceData {
devoptab_t device{};
bool setup{};
bool mounted{};
uint32_t id{};
char name[32]{};
char mount_path[256]{};
uint64_t deviceSizeInSectors{};
uint32_t deviceSectorSize{};
FSAClientHandle clientHandle{};
FastLockWrapper mutex;
} FSADeviceData;
/**
* Open file struct
*/
typedef struct
{
//! FS handle
FSAFileHandle fd;
//! Flags used in open(2)
int flags;
//! Current file offset
uint32_t offset;
//! Path stored for internal path tracking
char path[FS_MAX_PATH + 1];
} __fsa_file_t;
/**
* Open directory struct
*/
typedef struct
{
//! Should be set to FS_DIRITER_MAGIC
uint32_t magic;
//! FS handle
FSADirectoryHandle fd;
//! Temporary storage for reading entries
FSADirectoryEntry entry_data;
} __fsa_dir_t;
#define FS_DIRITER_MAGIC 0x77696975
#ifdef __cplusplus
extern "C" {
#endif
int __fsa_open(struct _reent *r, void *fileStruct, const char *path,
int flags, int mode);
int __fsa_close(struct _reent *r, void *fd);
ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr,
size_t len);
ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len);
off_t __fsa_seek(struct _reent *r, void *fd, off_t pos, int dir);
int __fsa_fstat(struct _reent *r, void *fd, struct stat *st);
int __fsa_stat(struct _reent *r, const char *file, struct stat *st);
int __fsa_link(struct _reent *r, const char *existing,
const char *newLink);
int __fsa_unlink(struct _reent *r, const char *name);
int __fsa_chdir(struct _reent *r, const char *name);
int __fsa_rename(struct _reent *r, const char *oldName,
const char *newName);
int __fsa_mkdir(struct _reent *r, const char *path, int mode);
DIR_ITER *__fsa_diropen(struct _reent *r, DIR_ITER *dirState,
const char *path);
int __fsa_dirreset(struct _reent *r, DIR_ITER *dirState);
int __fsa_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename,
struct stat *filestat);
int __fsa_dirclose(struct _reent *r, DIR_ITER *dirState);
int __fsa_statvfs(struct _reent *r, const char *path,
struct statvfs *buf);
int __fsa_ftruncate(struct _reent *r, void *fd, off_t len);
int __fsa_fsync(struct _reent *r, void *fd);
int __fsa_chmod(struct _reent *r, const char *path, mode_t mode);
int __fsa_fchmod(struct _reent *r, void *fd, mode_t mode);
int __fsa_rmdir(struct _reent *r, const char *name);
int __fsa_utimes(struct _reent *r, const char *filename, const struct timeval times[2]);
// devoptab_fs_utils.c
char *__fsa_fixpath(struct _reent *r, const char *path);
int __fsa_translate_error(FSError error);
time_t __fsa_translate_time(FSTime timeValue);
FSMode __fsa_translate_permission_mode(int mode);
mode_t __fsa_translate_stat_mode(FSStat fileStat);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,32 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_chdir(struct _reent *r,
const char *path) {
FSError status;
if (!path) {
r->_errno = EINVAL;
return -1;
}
char *fixedPath = __fsa_fixpath(r, path);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAChangeDir(deviceData->clientHandle, fixedPath);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAChangeDir(0x%08X, %s) failed: %s", deviceData->clientHandle, fixedPath, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedPath);
return 0;
}

View File

@ -0,0 +1,38 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
#include <sys/stat.h>
int __fsa_chmod(struct _reent *r,
const char *path,
mode_t mode) {
FSError status;
if (!path) {
r->_errno = EINVAL;
return -1;
}
char *fixedPath = __fsa_fixpath(r, path);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
FSMode translatedMode = __fsa_translate_permission_mode(mode);
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAChangeMode(deviceData->clientHandle, fixedPath, translatedMode);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAChangeMode(0x%08X, %s, 0x%X) failed: %s", deviceData->clientHandle, fixedPath, translatedMode, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedPath);
return 0;
}

View File

@ -0,0 +1,29 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_close(struct _reent *r,
void *fd) {
FSError status;
__fsa_file_t *file;
if (!fd) {
r->_errno = EINVAL;
return -1;
}
file = (__fsa_file_t *) fd;
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSACloseFile(deviceData->clientHandle, file->fd);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSACloseFile(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
return 0;
}

View File

@ -0,0 +1,29 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_dirclose(struct _reent *r,
DIR_ITER *dirState) {
FSError status;
__fsa_dir_t *dir;
if (!dirState) {
r->_errno = EINVAL;
return -1;
}
dir = (__fsa_dir_t *) (dirState->dirStruct);
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSACloseDir(deviceData->clientHandle, dir->fd);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSACloseDir(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, dir->fd, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
return 0;
}

View File

@ -0,0 +1,52 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_dirnext(struct _reent *r,
DIR_ITER *dirState,
char *filename,
struct stat *filestat) {
FSError status;
__fsa_dir_t *dir;
if (!dirState || !filename || !filestat) {
r->_errno = EINVAL;
return -1;
}
dir = (__fsa_dir_t *) (dirState->dirStruct);
memset(&dir->entry_data, 0, sizeof(dir->entry_data));
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAReadDir(deviceData->clientHandle, dir->fd, &dir->entry_data);
if (status < 0) {
if (status != FS_ERROR_END_OF_DIR) {
DEBUG_FUNCTION_LINE_ERR("FSAReadDir(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, dir->fd, &dir->entry_data, FSAGetStatusStr(status));
}
r->_errno = __fsa_translate_error(status);
return -1;
}
// Fill in the stat info
memset(filestat, 0, sizeof(struct stat));
filestat->st_dev = (dev_t) deviceData->clientHandle;
filestat->st_ino = dir->entry_data.info.entryId;
filestat->st_mode = __fsa_translate_stat_mode(dir->entry_data.info);
filestat->st_nlink = 1;
filestat->st_uid = dir->entry_data.info.owner;
filestat->st_gid = dir->entry_data.info.group;
filestat->st_rdev = filestat->st_dev;
filestat->st_size = dir->entry_data.info.size;
filestat->st_blksize = deviceData->deviceSectorSize;
filestat->st_blocks = (filestat->st_size + filestat->st_blksize - 1) / filestat->st_blksize;
filestat->st_atime = __fsa_translate_time(dir->entry_data.info.modified);
filestat->st_ctime = __fsa_translate_time(dir->entry_data.info.created);
filestat->st_mtime = __fsa_translate_time(dir->entry_data.info.modified);
memset(filename, 0, NAME_MAX);
strcpy(filename, dir->entry_data.name);
return 0;
}

View File

@ -0,0 +1,41 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
DIR_ITER *
__fsa_diropen(struct _reent *r,
DIR_ITER *dirState,
const char *path) {
FSADirectoryHandle fd;
FSError status;
__fsa_dir_t *dir;
if (!dirState || !path) {
r->_errno = EINVAL;
return NULL;
}
char *fixedPath = __fsa_fixpath(r, path);
if (!fixedPath) {
return NULL;
}
dir = (__fsa_dir_t *) (dirState->dirStruct);
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAOpenDir(deviceData->clientHandle, fixedPath, &fd);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAOpenDir(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &fd, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return NULL;
}
free(fixedPath);
dir->magic = FS_DIRITER_MAGIC;
dir->fd = fd;
memset(&dir->entry_data, 0, sizeof(dir->entry_data));
return dirState;
}

View File

@ -0,0 +1,28 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_dirreset(struct _reent *r,
DIR_ITER *dirState) {
FSError status;
__fsa_dir_t *dir;
if (!dirState) {
r->_errno = EINVAL;
return -1;
}
dir = (__fsa_dir_t *) (dirState->dirStruct);
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSARewindDir(deviceData->clientHandle, dir->fd);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSARewindDir(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, dir->fd, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
return 0;
}

View File

@ -0,0 +1,27 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_fchmod(struct _reent *r,
void *fd,
mode_t mode) {
FSError status;
__fsa_file_t *file;
file = (__fsa_file_t *) fd;
FSMode translatedMode = __fsa_translate_permission_mode(mode);
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAChangeMode(deviceData->clientHandle, file->path, translatedMode);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAChangeMode(0x%08X, %s, 0x%X) failed: %s", deviceData->clientHandle, file->path, translatedMode, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
return 0;
}

View File

@ -0,0 +1,41 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_fstat(struct _reent *r,
void *fd,
struct stat *st) {
FSError status;
FSAStat fsStat;
__fsa_file_t *file;
if (!fd || !st) {
r->_errno = EINVAL;
return -1;
}
file = (__fsa_file_t *) fd;
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAGetStatFile(deviceData->clientHandle, file->fd, &fsStat);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSARewindDir(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, &fsStat, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
memset(st, 0, sizeof(struct stat));
st->st_size = fsStat.size;
st->st_uid = fsStat.owner;
st->st_gid = fsStat.group;
st->st_nlink = 1;
st->st_mode = __fsa_translate_stat_mode(fsStat);
st->st_atime = __fsa_translate_time(fsStat.modified);
st->st_ctime = __fsa_translate_time(fsStat.created);
st->st_mtime = __fsa_translate_time(fsStat.modified);
return 0;
}

View File

@ -0,0 +1,29 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_fsync(struct _reent *r,
void *fd) {
FSError status;
__fsa_file_t *file;
if (!fd) {
r->_errno = EINVAL;
return -1;
}
file = (__fsa_file_t *) fd;
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAFlushFile(deviceData->clientHandle, file->fd);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAFlushFile(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
return 0;
}

View File

@ -0,0 +1,8 @@
#include "devoptab_fs.h"
int __fsa_link(struct _reent *r,
const char *existing,
const char *newLink) {
r->_errno = ENOSYS;
return -1;
}

View File

@ -0,0 +1,38 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_mkdir(struct _reent *r,
const char *path,
int mode) {
FSError status;
char *fixedPath;
if (!path) {
r->_errno = EINVAL;
return -1;
}
fixedPath = __fsa_fixpath(r, path);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
FSMode translatedMode = __fsa_translate_permission_mode(mode);
status = FSAMakeDir(deviceData->clientHandle, fixedPath, translatedMode);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAMakeDir(0x%08X, %s, 0x%X) failed: %s", deviceData->clientHandle, fixedPath, translatedMode, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedPath);
return 0;
}

View File

@ -0,0 +1,92 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
// Use this extended hidden flag value to provide FS_OPEN_FLAG_ENCRYPTED in underlying FSOpenFileEx() call
#define O_ENCRYPTED 0x4000000
int __fsa_open(struct _reent *r,
void *fileStruct,
const char *path,
int flags,
int mode) {
FSAFileHandle fd;
FSError status;
const char *fsMode;
__fsa_file_t *file;
if (!fileStruct || !path) {
r->_errno = EINVAL;
return -1;
}
// Map flags to open modes
if ((flags & O_ACCMODE) == O_RDONLY) {
fsMode = "r";
if (flags & O_APPEND) {
r->_errno = EINVAL;
return -1;
}
} else if ((flags & O_ACCMODE) == O_WRONLY) {
if (flags & O_APPEND) {
fsMode = "a";
} else if (flags & O_TRUNC) {
fsMode = "w";
} else {
r->_errno = EINVAL;
return -1;
}
} else if ((flags & O_ACCMODE) == O_RDWR) {
if (flags & O_APPEND) {
fsMode = "a+";
} else if (flags & O_TRUNC) {
fsMode = "w+";
} else {
fsMode = "r+";
}
} else {
r->_errno = EINVAL;
return -1;
}
char *fixedPath = __fsa_fixpath(r, path);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
// Open the file
FSOpenFileFlags openFlags = (mode & O_ENCRYPTED) ? FS_OPEN_FLAG_ENCRYPTED : FS_OPEN_FLAG_NONE;
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
FSMode translatedMode = __fsa_translate_permission_mode(mode);
uint32_t preAllocSize = 0;
status = FSAOpenFileEx(deviceData->clientHandle, fixedPath, fsMode, translatedMode, openFlags, preAllocSize, &fd);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAOpenFileEx(0x%08X, %s, %s, 0x%X, 0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, fsMode, translatedMode, openFlags, preAllocSize, &fd, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
free(fixedPath);
return -1;
}
file = (__fsa_file_t *) fileStruct;
file->fd = fd;
file->flags = (flags & (O_ACCMODE | O_APPEND | O_SYNC));
strncpy(file->path, fixedPath, FS_MAX_PATH);
free(fixedPath);
if (flags & O_APPEND) {
status = FSAGetPosFile(deviceData->clientHandle, fd, &file->offset);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAGetPosFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, fd, &file->offset, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
}
return 0;
}

View File

@ -0,0 +1,77 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
#include <sys/param.h>
ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len) {
if (!fd || !ptr) {
r->_errno = EINVAL;
return -1;
}
// Check that the file was opened with read access
__fsa_file_t *file = (__fsa_file_t *) fd;
if ((file->flags & O_ACCMODE) == O_WRONLY) {
r->_errno = EBADF;
return -1;
}
// cache-aligned, cache-line-sized
__attribute__((aligned(0x40))) uint8_t alignedBuffer[0x40];
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
size_t bytesRead = 0;
while (bytesRead < len) {
// only use input buffer if cache-aligned and read size is a multiple of cache line size
// otherwise read into alignedBuffer
uint8_t *tmp = (uint8_t *) ptr;
size_t size = len - bytesRead;
if ((uintptr_t) ptr & 0x3F) {
// read partial cache-line front-end
tmp = alignedBuffer;
size = MIN(size, 0x40 - ((uintptr_t) ptr & 0x3F));
} else if (size < 0x40) {
// read partial cache-line back-end
tmp = alignedBuffer;
} else {
// read whole cache lines
size &= ~0x3F;
}
// Limit each request to 1 MiB
if (size > 0x100000) {
size = 0x100000;
}
FSError status = FSAReadFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAReadFile(0x%08X, 0x%08X, 1, 0x%08X, 0x%08X, 0) failed: %s", deviceData->clientHandle, tmp, size, file->fd, FSAGetStatusStr(status));
if (bytesRead != 0) {
return bytesRead; // error after partial read
}
r->_errno = __fsa_translate_error(status);
return -1;
}
if (tmp == alignedBuffer) {
memcpy(ptr, alignedBuffer, status);
}
bytesRead += status;
ptr += status;
if ((size_t) status != size) {
return bytesRead; // partial read
}
}
return bytesRead;
}

View File

@ -0,0 +1,45 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_rename(struct _reent *r,
const char *oldName,
const char *newName) {
FSError status;
char *fixedOldPath, *fixedNewPath;
if (!oldName || !newName) {
r->_errno = EINVAL;
return -1;
}
fixedOldPath = __fsa_fixpath(r, oldName);
if (!fixedOldPath) {
r->_errno = ENOMEM;
return -1;
}
fixedNewPath = __fsa_fixpath(r, newName);
if (!fixedNewPath) {
free(fixedOldPath);
r->_errno = ENOMEM;
return -1;
}
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSARename(deviceData->clientHandle, fixedOldPath, fixedNewPath);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSARename(0x%08X, %s, %s) failed: %s", deviceData->clientHandle, fixedOldPath, fixedNewPath, FSAGetStatusStr(status));
free(fixedOldPath);
free(fixedNewPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedOldPath);
free(fixedNewPath);
return 0;
}

View File

@ -0,0 +1,71 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_rmdir(struct _reent *r,
const char *name) {
FSError status;
if (!name) {
r->_errno = EINVAL;
return -1;
}
char *fixedPath = __fsa_fixpath(r, name);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
// Check if directory still has files in which case return error
FSDirectoryHandle dirHandle = 0;
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAOpenDir(deviceData->clientHandle, fixedPath, &dirHandle);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAOpenDir(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &dirHandle, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
auto *dir_entry = (FSADirectoryEntry *) malloc(sizeof(FSADirectoryEntry));
if (dir_entry == nullptr) {
DEBUG_FUNCTION_LINE_ERR("malloc(sizeof(FSADirectoryEntry)) failed");
free(fixedPath);
FSACloseDir(deviceData->clientHandle, dirHandle);
r->_errno = ENOMEM;
return -1;
}
status = FSAReadDir(deviceData->clientHandle, dirHandle, dir_entry);
FSACloseDir(deviceData->clientHandle, dirHandle);
if (status == FS_ERROR_OK) {
free(fixedPath);
free(dir_entry);
r->_errno = ENOTEMPTY;
return -1;
} else if (status != FS_ERROR_END_OF_DIR) {
DEBUG_FUNCTION_LINE_ERR("FSAReadDir(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, dirHandle, dir_entry, FSAGetStatusStr(status));
free(fixedPath);
free(dir_entry);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(dir_entry);
// Remove folder since folder is empty
status = FSARemove(deviceData->clientHandle, fixedPath);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSARemove(0x%08X, %s) failed: %s", deviceData->clientHandle, fixedPath, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedPath);
return 0;
}

View File

@ -0,0 +1,72 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
off_t __fsa_seek(struct _reent *r,
void *fd,
off_t pos,
int whence) {
FSError status;
FSAStat fsStat;
uint64_t offset;
__fsa_file_t *file;
if (!fd) {
r->_errno = EINVAL;
return -1;
}
file = (__fsa_file_t *) fd;
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAGetStatFile(deviceData->clientHandle, file->fd, &fsStat);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAGetStatFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, &fsStat, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
// Find the offset to see from
switch (whence) {
// Set absolute position; start offset is 0
case SEEK_SET:
offset = 0;
break;
// Set position relative to the current position
case SEEK_CUR:
offset = file->offset;
break;
// Set position relative to the end of the file
case SEEK_END:
offset = fsStat.size;
break;
// An invalid option was provided
default:
r->_errno = EINVAL;
return -1;
}
// TODO: A better check that prevents overflow.
if (pos < 0 && (off_t) offset < -pos) {
// Don't allow seek to before the beginning of the file
r->_errno = EINVAL;
return -1;
}
status = FSASetPosFile(deviceData->clientHandle, file->fd, file->offset);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSASetPosFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, file->offset, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
// Update the current offset
file->offset = offset + pos;
return file->offset;
}

View File

@ -0,0 +1,50 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_stat(struct _reent *r,
const char *path,
struct stat *st) {
FSError status;
FSAStat fsStat;
if (!path || !st) {
r->_errno = EINVAL;
return -1;
}
char *fixedPath = __fsa_fixpath(r, path);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAGetStat(deviceData->clientHandle, fixedPath, &fsStat);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAGetStat(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &fsStat, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedPath);
memset(st, 0, sizeof(struct stat));
st->st_dev = (dev_t) deviceData->clientHandle;
st->st_ino = fsStat.entryId;
st->st_mode = __fsa_translate_stat_mode(fsStat);
st->st_nlink = 1;
st->st_uid = fsStat.owner;
st->st_gid = fsStat.group;
st->st_rdev = st->st_dev;
st->st_size = fsStat.size;
st->st_blksize = deviceData->deviceSectorSize;
st->st_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize;
st->st_atime = __fsa_translate_time(fsStat.modified);
st->st_ctime = __fsa_translate_time(fsStat.created);
st->st_mtime = __fsa_translate_time(fsStat.modified);
return 0;
}

View File

@ -0,0 +1,50 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_statvfs(struct _reent *r,
const char *path,
struct statvfs *buf) {
FSError status;
uint64_t freeSpace;
memset(buf, 0, sizeof(struct statvfs));
char *fixedPath = __fsa_fixpath(r, path);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSAGetFreeSpaceSize(deviceData->clientHandle, fixedPath, &freeSpace);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAGetFreeSpaceSize(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &freeSpace, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedPath);
// File system block size
buf->f_bsize = deviceData->deviceSectorSize;
// Fundamental file system block size
buf->f_frsize = deviceData->deviceSectorSize;
// Total number of blocks on file system in units of f_frsize
buf->f_blocks = deviceData->deviceSizeInSectors;
// Free blocks available for all and for non-privileged processes
buf->f_bfree = buf->f_bavail = (uint32_t) (freeSpace / buf->f_frsize);
// 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 = (unsigned long) deviceData->clientHandle;
// Bit mask of f_flag values.
buf->f_flag = 0;
// Maximum length of filenames
buf->f_namemax = 255;
return 0;
}

View File

@ -0,0 +1,38 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_ftruncate(struct _reent *r,
void *fd,
off_t len) {
FSError status;
__fsa_file_t *file;
// Make sure length is non-negative
if (!fd || len < 0) {
r->_errno = EINVAL;
return -1;
}
// Set the new file size
file = (__fsa_file_t *) fd;
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSASetPosFile(deviceData->clientHandle, file->fd, len);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSASetPosFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, len, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
status = FSATruncateFile(deviceData->clientHandle, file->fd);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSATruncateFile(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, FSAGetStatusStr(status));
r->_errno = __fsa_translate_error(status);
return -1;
}
return 0;
}

View File

@ -0,0 +1,35 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
int __fsa_unlink(struct _reent *r,
const char *name) {
FSError status;
char *fixedPath;
if (!name) {
r->_errno = EINVAL;
return -1;
}
fixedPath = __fsa_fixpath(r, name);
if (!fixedPath) {
r->_errno = ENOMEM;
return -1;
}
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
status = FSARemove(deviceData->clientHandle, fixedPath);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSARemove(0x%08X, %s) failed: %s", deviceData->clientHandle, fixedPath, FSAGetStatusStr(status));
free(fixedPath);
r->_errno = __fsa_translate_error(status);
return -1;
}
free(fixedPath);
return 0;
}

View File

@ -0,0 +1,169 @@
#include "devoptab_fs.h"
char *
__fsa_fixpath(struct _reent *r,
const char *path) {
char *p;
char *fixedPath;
if (!path) {
r->_errno = EINVAL;
return nullptr;
}
p = strchr(path, ':') + 1;
if (!strchr(path, ':')) {
p = (char *) path;
}
if (strlen(p) > FS_MAX_PATH) {
r->_errno = ENAMETOOLONG;
return nullptr;
}
fixedPath = (char *) memalign(0x40, FS_MAX_PATH + 1);
if (!fixedPath) {
r->_errno = ENOMEM;
return nullptr;
}
if (p[0] == '/') {
auto *deviceData = (FSADeviceData *) r->deviceData;
strcpy(fixedPath, deviceData->mount_path);
strcat(fixedPath, p);
} else {
strcpy(fixedPath, p);
}
return fixedPath;
}
mode_t __fsa_translate_stat_mode(FSStat fileStat) {
mode_t retMode = 0;
if ((fileStat.flags & FS_STAT_LINK) == FS_STAT_LINK) {
retMode |= S_IFLNK;
} else if ((fileStat.flags & FS_STAT_DIRECTORY) == FS_STAT_DIRECTORY) {
retMode |= S_IFDIR;
} else if ((fileStat.flags & FS_STAT_FILE) == FS_STAT_FILE) {
retMode |= S_IFREG;
} else if (fileStat.size > 0) {
// Some regular Wii U files have no type info but will have a size
retMode |= S_IFREG;
}
// Convert normal CafeOS hexadecimal permission bits into Unix octal permission bits
mode_t permissionMode = (((fileStat.mode >> 2) & S_IRWXU) | ((fileStat.mode >> 1) & S_IRWXG) | (fileStat.mode & S_IRWXO));
return retMode | permissionMode;
}
// The Wii U FSTime epoch is at 1980, so we must map it to 1970 for gettime
#define WIIU_FSTIME_EPOCH_YEAR (1980)
#define EPOCH_YEAR (1970)
#define EPOCH_YEARS_SINCE_LEAP 2
#define EPOCH_YEARS_SINCE_CENTURY 70
#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370
#define EPOCH_DIFF_YEARS(year) (year - EPOCH_YEAR)
#define EPOCH_DIFF_DAYS(year) \
((EPOCH_DIFF_YEARS(year) * 365) + \
(EPOCH_DIFF_YEARS(year) - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - \
(EPOCH_DIFF_YEARS(year) - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 + \
(EPOCH_DIFF_YEARS(year) - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400)
#define EPOCH_DIFF_SECS(year) (60ull * 60ull * 24ull * (uint64_t) EPOCH_DIFF_DAYS(year))
time_t __fsa_translate_time(FSTime timeValue) {
return (timeValue / 1000000) + EPOCH_DIFF_SECS(WIIU_FSTIME_EPOCH_YEAR);
}
FSMode
__fsa_translate_permission_mode(int mode) {
// Convert normal Unix octal permission bits into CafeOS hexadecimal permission bits
return (FSMode) (((mode & S_IRWXU) << 2) | ((mode & S_IRWXG) << 1) | (mode & S_IRWXO));
}
int __fsa_translate_error(FSError error) {
switch (error) {
case FS_ERROR_END_OF_DIR:
case FS_ERROR_END_OF_FILE:
return ENOENT;
case FS_ERROR_ALREADY_EXISTS:
return EEXIST;
case FS_ERROR_MEDIA_ERROR:
return EIO;
case FS_ERROR_NOT_FOUND:
return ENOENT;
case FS_ERROR_PERMISSION_ERROR:
return EPERM;
case FS_ERROR_STORAGE_FULL:
return ENOSPC;
case FS_ERROR_BUSY:
return EBUSY;
case FS_ERROR_CANCELLED:
return ECANCELED;
case FS_ERROR_FILE_TOO_BIG:
return EFBIG;
case FS_ERROR_INVALID_PATH:
return ENAMETOOLONG;
case FS_ERROR_NOT_DIR:
return ENOTDIR;
case FS_ERROR_NOT_FILE:
return EISDIR;
case FS_ERROR_OUT_OF_RANGE:
return ESPIPE;
case FS_ERROR_UNSUPPORTED_COMMAND:
return ENOTSUP;
case FS_ERROR_WRITE_PROTECTED:
return EROFS;
case FS_ERROR_NOT_INIT:
return ENODEV;
// TODO
case FS_ERROR_MAX_MOUNT_POINTS:
break;
case FS_ERROR_MAX_VOLUMES:
break;
case FS_ERROR_MAX_CLIENTS:
break;
case FS_ERROR_MAX_FILES:
break;
case FS_ERROR_MAX_DIRS:
break;
case FS_ERROR_ALREADY_OPEN:
break;
case FS_ERROR_NOT_EMPTY:
break;
case FS_ERROR_ACCESS_ERROR:
break;
case FS_ERROR_DATA_CORRUPTED:
break;
case FS_ERROR_JOURNAL_FULL:
break;
case FS_ERROR_UNAVAILABLE_COMMAND:
break;
case FS_ERROR_INVALID_PARAM:
break;
case FS_ERROR_INVALID_BUFFER:
break;
case FS_ERROR_INVALID_ALIGNMENT:
break;
case FS_ERROR_INVALID_CLIENTHANDLE:
break;
case FS_ERROR_INVALID_FILEHANDLE:
break;
case FS_ERROR_INVALID_DIRHANDLE:
break;
case FS_ERROR_OUT_OF_RESOURCES:
break;
case FS_ERROR_MEDIA_NOT_READY:
break;
case FS_ERROR_INVALID_MEDIA:
break;
default:
break;
}
return (int) EIO;
}

View File

@ -0,0 +1,9 @@
#include "devoptab_fs.h"
int __fsa_utimes(struct _reent *r,
const char *filename,
const struct timeval times[2]) {
// TODO: FSChangeMode and FSStatFile?
r->_errno = ENOSYS;
return -1;
}

View File

@ -0,0 +1,73 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr, size_t len) {
if (!fd || !ptr) {
r->_errno = EINVAL;
return -1;
}
// Check that the file was opened with write access
__fsa_file_t *file = (__fsa_file_t *) fd;
if ((file->flags & O_ACCMODE) == O_RDONLY) {
r->_errno = EBADF;
return -1;
}
// cache-aligned, cache-line-sized
__attribute__((aligned(0x40))) uint8_t alignedBuffer[0x40];
auto *deviceData = (FSADeviceData *) r->deviceData;
std::lock_guard<FastLockWrapper> lock(deviceData->mutex);
size_t bytesWritten = 0;
while (bytesWritten < len) {
// only use input buffer if cache-aligned and write size is a multiple of cache line size
// otherwise write from alignedBuffer
uint8_t *tmp = (uint8_t *) ptr;
size_t size = len - bytesWritten;
if ((uintptr_t) ptr & 0x3F) {
// write partial cache-line front-end
tmp = alignedBuffer;
size = MIN(size, 0x40 - ((uintptr_t) ptr & 0x3F));
} else if (size < 0x40) {
// write partial cache-line back-end
tmp = alignedBuffer;
} else {
// write whole cache lines
size &= ~0x3F;
}
// Limit each request to 256 KiB
if (size > 0x40000) {
size = 0x40000;
}
if (tmp == alignedBuffer) {
memcpy(tmp, ptr, size);
}
FSError status = FSAWriteFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0);
if (status < 0) {
DEBUG_FUNCTION_LINE_ERR("FSAWriteFile(0x%08X, 0x%08X, 1, 0x%08X, 0x%08X, 0) failed: %s", deviceData->clientHandle, tmp, size, file->fd, FSAGetStatusStr(status));
if (bytesWritten != 0) {
return bytesWritten; // error after partial write
}
r->_errno = __fsa_translate_error(status);
return -1;
}
bytesWritten += status;
ptr += status;
if ((size_t) status != size) {
return bytesWritten; // partial write
}
}
return bytesWritten;
}

View File

@ -1,75 +1,11 @@
#include "mocha/fsa.h" #include "mocha/fsa.h"
#include "logger.h"
#include "utils.h" #include "utils.h"
#include <coreinit/debug.h> #include <coreinit/debug.h>
#include <coreinit/filesystem.h> #include <coreinit/filesystem.h>
#include <coreinit/filesystem_fsa.h> #include <coreinit/filesystem_fsa.h>
#include <cstring> #include <cstring>
#include <malloc.h> #include <malloc.h>
#include <mocha/fsa.h>
#include <string_view>
FSError FSAEx_Mount(FSClient *client, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len) {
if (!client) {
return FS_ERROR_INVALID_CLIENTHANDLE;
}
return FSAEx_MountEx(FSGetClientBody(client)->clientHandle, source, target, flags, arg_buf, arg_len);
}
FSError FSAEx_MountEx(int clientHandle, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len) {
auto *buffer = (FSAShimBuffer *) memalign(0x40, sizeof(FSAShimBuffer));
if (!buffer) {
return FS_ERROR_INVALID_BUFFER;
}
// Check if source and target path is valid.
if (!std::string_view(target).starts_with("/vol/") || !std::string_view(source).starts_with("/dev/")) {
return FS_ERROR_INVALID_PATH;
}
if (flags == FSA_MOUNT_FLAG_BIND_MOUNT || flags == FSA_MOUNT_FLAG_GLOBAL_MOUNT) {
// Return error if target path DOES NOT start with "/vol/storage_"
if (!std::string_view(target).starts_with("/vol/storage_")) {
return FS_ERROR_INVALID_PATH;
}
} else {
// Return error if target path starts with "/vol/storage_"
if (std::string_view(target).starts_with("/vol/storage_")) {
return FS_ERROR_INVALID_PATH;
}
}
auto res = __FSAShimSetupRequestMount(buffer, clientHandle, source, target, 2, arg_buf, arg_len);
if (res != 0) {
free(buffer);
return res;
}
res = __FSAShimSend(buffer, 0);
free(buffer);
return res;
}
FSError FSAEx_Unmount(FSClient *client, const char *mountedTarget, FSAUnmountFlags flags) {
if (!client) {
return FS_ERROR_INVALID_CLIENTHANDLE;
}
return FSAEx_UnmountEx(FSGetClientBody(client)->clientHandle, mountedTarget, flags);
}
FSError FSAEx_UnmountEx(int clientHandle, const char *mountedTarget, FSAUnmountFlags flags) {
auto *buffer = (FSAShimBuffer *) memalign(0x40, sizeof(FSAShimBuffer));
if (!buffer) {
return FS_ERROR_INVALID_BUFFER;
}
auto res = __FSAShimSetupRequestUnmount(buffer, clientHandle, mountedTarget, flags);
if (res != 0) {
free(buffer);
return res;
}
res = __FSAShimSend(buffer, 0);
free(buffer);
return res;
}
FSError FSAEx_RawOpen(FSClient *client, char *device_path, int32_t *outHandle) { FSError FSAEx_RawOpen(FSClient *client, char *device_path, int32_t *outHandle) {
if (!client) { if (!client) {
@ -168,10 +104,10 @@ FSError FSAEx_RawReadEx(int clientHandle, void *data, uint32_t size_bytes, uint3
if ((uint32_t) data & 0x3F) { if ((uint32_t) data & 0x3F) {
auto *alignedBuffer = memalign(0x40, ROUNDUP(size_bytes * cnt, 0x40)); auto *alignedBuffer = memalign(0x40, ROUNDUP(size_bytes * cnt, 0x40));
if (!alignedBuffer) { if (!alignedBuffer) {
OSReport("## ERROR: FSAEx_RawReadEx buffer not aligned (%08X).\n", data); DEBUG_FUNCTION_LINE_ERR("FSAEx_RawReadEx buffer not aligned (%08X).\n", data);
return FS_ERROR_INVALID_ALIGNMENT; return FS_ERROR_INVALID_ALIGNMENT;
} }
OSReport("## WARNING: FSAEx_RawReadEx buffer not aligned (%08X). Align to 0x40 for best performance\n", data); DEBUG_FUNCTION_LINE_WARN("FSAEx_RawReadEx buffer not aligned (%08X). Align to 0x40 for best performance\n", data);
tmp = alignedBuffer; tmp = alignedBuffer;
} }

24
source/logger.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <coreinit/debug.h>
#include <cstring>
#define __FILENAME__ ({ \
const char *__filename = __FILE__; \
const char *__pos = strrchr(__filename, '/'); \
if (!__pos) __pos = strrchr(__filename, '\\'); \
__pos ? __pos + 1 : __filename; \
})
#define LOG_APP_TYPE "L"
#define LOG_APP_NAME "libmocha"
#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \
do { \
LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \
} while (0)
#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARNING## ", "\n", FMT, ##ARGS)

View File

@ -1,19 +1,58 @@
#include "utils.h" #include "utils.h"
#include "logger.h"
#include "mocha/commands.h" #include "mocha/commands.h"
#include "mocha/mocha.h" #include "mocha/mocha.h"
#include <coreinit/ios.h> #include <coreinit/ios.h>
#include <cstring> #include <cstring>
#include <malloc.h>
#include <stdint.h> #include <stdint.h>
int iosuhaxHandle = -1;
int mochaInitDone = 0; int mochaInitDone = 0;
uint32_t mochaApiVersion = 0; uint32_t mochaApiVersion = 0;
#define IOCTL_MEM_WRITE 0x00
#define IOCTL_MEM_READ 0x01
#define IOCTL_SVC 0x02
#define IOCTL_MEMCPY 0x04
#define IOCTL_REPEATED_WRITE 0x05
#define IOCTL_KERN_READ32 0x06
#define IOCTL_KERN_WRITE32 0x07
#define IOCTL_READ_OTP 0x08
#define IOCTL_CHECK_IF_IOSUHAX 0x5B
#define IOSUHAX_MAGIC_WORD 0x4E696365
MochaUtilsStatus Mocha_InitLibrary() { MochaUtilsStatus Mocha_InitLibrary() {
if (mochaInitDone) {
return MOCHA_RESULT_SUCCESS;
}
if (iosuhaxHandle < 0) {
int haxHandle = IOS_Open((char *) ("/dev/iosuhax"), static_cast<IOSOpenMode>(0));
if (haxHandle >= 0) {
ALIGN_0x40 int res[0x40 >> 2];
*res = 0;
IOS_Ioctl(haxHandle, IOCTL_CHECK_IF_IOSUHAX, (void *) nullptr, 0, res, 4);
if (*res != IOSUHAX_MAGIC_WORD) {
IOS_Close(haxHandle);
DEBUG_FUNCTION_LINE_ERR("Unexpected /dev/iosuhax magic");
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
}
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to open /dev/iosuhax");
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
}
iosuhaxHandle = haxHandle;
}
mochaInitDone = 1; mochaInitDone = 1;
mochaApiVersion = 0; mochaApiVersion = 0;
uint32_t version = 0; uint32_t version = 0;
if (Mocha_CheckAPIVersion(&version) != MOCHA_RESULT_SUCCESS) { if (Mocha_CheckAPIVersion(&version) != MOCHA_RESULT_SUCCESS) {
return MOCHA_RESULT_SUCCESS; IOS_Close(iosuhaxHandle);
iosuhaxHandle = -1;
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
} }
mochaApiVersion = version; mochaApiVersion = version;
@ -21,10 +60,14 @@ MochaUtilsStatus Mocha_InitLibrary() {
return MOCHA_RESULT_SUCCESS; return MOCHA_RESULT_SUCCESS;
} }
MochaUtilsStatus Mocha_DeInitLibrary() { MochaUtilsStatus Mocha_DeinitLibrary() {
mochaInitDone = 0; mochaInitDone = 0;
mochaApiVersion = 0; mochaApiVersion = 0;
if (iosuhaxHandle >= 0) {
IOS_Close(iosuhaxHandle);
}
return MOCHA_RESULT_SUCCESS; return MOCHA_RESULT_SUCCESS;
} }
@ -53,6 +96,138 @@ MochaUtilsStatus Mocha_CheckAPIVersion(uint32_t *version) {
return res; return res;
} }
MochaUtilsStatus Mocha_IOSUKernelMemcpy(uint32_t dst, uint32_t src, uint32_t size) {
if (size == 0) {
return MOCHA_RESULT_SUCCESS;
}
if (dst == 0 || src == 0) {
return MOCHA_RESULT_INVALID_ARGUMENT;
}
if (!mochaInitDone || iosuhaxHandle < 0) {
return MOCHA_RESULT_LIB_UNINITIALIZED;
}
ALIGN_0x40 uint32_t io_buf[0x40 >> 2];
io_buf[0] = dst;
io_buf[1] = src;
io_buf[2] = size;
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_MEMCPY, io_buf, 3 * sizeof(uint32_t), nullptr, 0);
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
}
MochaUtilsStatus Mocha_IOSUKernelWrite(uint32_t address, const uint8_t *buffer, uint32_t size) {
if (size == 0) {
return MOCHA_RESULT_SUCCESS;
}
if (address == 0 || buffer == nullptr) {
return MOCHA_RESULT_INVALID_ARGUMENT;
}
if (!mochaInitDone || iosuhaxHandle < 0) {
return MOCHA_RESULT_LIB_UNINITIALIZED;
}
auto *io_buf = (uint32_t *) memalign(0x40, ROUNDUP(size + 4, 0x40));
if (!io_buf) {
return MOCHA_RESULT_OUT_OF_MEMORY;
}
io_buf[0] = address;
memcpy(io_buf + 1, buffer, size);
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_MEM_WRITE, io_buf, size + 4, nullptr, 0);
free(io_buf);
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
}
MochaUtilsStatus Mocha_IOSUKernelRead(uint32_t address, uint8_t *out_buffer, uint32_t size) {
if (size == 0) {
return MOCHA_RESULT_SUCCESS;
}
if (address == 0 || out_buffer == nullptr) {
return MOCHA_RESULT_INVALID_ARGUMENT;
}
if (!mochaInitDone || iosuhaxHandle < 0) {
return MOCHA_RESULT_LIB_UNINITIALIZED;
}
ALIGN_0x40 uint32_t io_buf[0x40 >> 2];
io_buf[0] = address;
void *tmp_buf = nullptr;
if (((uintptr_t) out_buffer & 0x3F) || (size & 0x3F)) {
tmp_buf = (uint32_t *) memalign(0x40, ROUNDUP(size, 0x40));
if (!tmp_buf) {
return MOCHA_RESULT_OUT_OF_MEMORY;
}
}
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_MEM_READ, io_buf, sizeof(address), tmp_buf ? tmp_buf : out_buffer, size);
if (res >= 0 && tmp_buf) {
memcpy(out_buffer, tmp_buf, size);
}
free(tmp_buf);
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
}
MochaUtilsStatus Mocha_IOSUKernelWrite32(uint32_t address, uint32_t value) {
return Mocha_IOSUKernelWrite(address, reinterpret_cast<const uint8_t *>(&value), 4);
}
MochaUtilsStatus Mocha_IOSUKernelRead32(uint32_t address, uint32_t *out_buffer) {
return Mocha_IOSUKernelRead(address, reinterpret_cast<uint8_t *>(out_buffer), 4);
}
MochaUtilsStatus Mocha_ReadOTP(WiiUConsoleOTP *out_buffer) {
if (out_buffer == nullptr) {
return MOCHA_RESULT_INVALID_ARGUMENT;
}
if (!mochaInitDone || iosuhaxHandle < 0) {
return MOCHA_RESULT_LIB_UNINITIALIZED;
}
ALIGN_0x40 uint32_t io_buf[0x400 >> 2];
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_READ_OTP, nullptr, 0, io_buf, 0x400);
if (res < 0) {
return MOCHA_RESULT_UNKNOWN_ERROR;
}
memcpy(out_buffer, io_buf, 0x400);
return MOCHA_RESULT_SUCCESS;
}
int Mocha_IOSUCallSVC(uint32_t svc_id, uint32_t *args, uint32_t arg_cnt, int32_t *outResult) {
if (!mochaInitDone || iosuhaxHandle < 0) {
return MOCHA_RESULT_LIB_UNINITIALIZED;
}
ALIGN_0x40 uint32_t arguments[0x40 >> 2];
arguments[0] = svc_id;
if (args && arg_cnt) {
if (arg_cnt > 8) {
arg_cnt = 8;
}
memcpy(arguments + 1, args, arg_cnt * 4);
}
ALIGN_0x40 int result[0x40 >> 2];
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_SVC, arguments, (1 + arg_cnt) * 4, result, 4);
if (res >= 0) {
*outResult = *result;
}
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
}
MochaUtilsStatus Mocha_GetEnvironmentPath(char *environmentPathBuffer, uint32_t bufferLen) { MochaUtilsStatus Mocha_GetEnvironmentPath(char *environmentPathBuffer, uint32_t bufferLen) {
if (!mochaInitDone) { if (!mochaInitDone) {
return MOCHA_RESULT_LIB_UNINITIALIZED; return MOCHA_RESULT_LIB_UNINITIALIZED;
@ -169,6 +344,9 @@ MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo) {
if (mochaApiVersion < 1) { if (mochaApiVersion < 1) {
return MOCHA_RESULT_UNSUPPORTED_COMMAND; return MOCHA_RESULT_UNSUPPORTED_COMMAND;
} }
if (!loadInfo) {
return MOCHA_RESULT_INVALID_ARGUMENT;
}
MochaUtilsStatus res = MOCHA_RESULT_UNKNOWN_ERROR; MochaUtilsStatus res = MOCHA_RESULT_UNKNOWN_ERROR;
int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0); int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0);
if (mcpFd >= 0) { if (mcpFd >= 0) {
@ -212,7 +390,7 @@ MochaUtilsStatus Mocha_ODMGetDiscKey(WUDDiscKey *discKey) {
return res; return res;
} }
extern int bspRead(const char *, uint32_t, const char *, uint32_t, uint16_t *); extern "C" int bspRead(const char *, uint32_t, const char *, uint32_t, uint16_t *);
MochaUtilsStatus Mocha_SEEPROMRead(uint8_t *out_buffer, uint32_t offset, uint32_t size) { MochaUtilsStatus Mocha_SEEPROMRead(uint8_t *out_buffer, uint32_t offset, uint32_t size) {
if (!mochaInitDone) { if (!mochaInitDone) {
return MOCHA_RESULT_LIB_UNINITIALIZED; return MOCHA_RESULT_LIB_UNINITIALIZED;