2022-04-14 22:41:41 +02:00
|
|
|
#include "FSWrapper.h"
|
|
|
|
#include "FSWrapperMergeDirsWithParent.h"
|
|
|
|
#include "FileUtils.h"
|
|
|
|
#include "IFSWrapper.h"
|
2022-10-10 19:38:51 +02:00
|
|
|
#include "malloc.h"
|
2022-04-14 22:41:41 +02:00
|
|
|
#include "utils/logger.h"
|
2022-04-29 13:34:04 +02:00
|
|
|
#include "utils/utils.h"
|
2022-04-14 22:41:41 +02:00
|
|
|
#include <content_redirection/redirection.h>
|
2022-10-10 19:38:51 +02:00
|
|
|
#include <coreinit/dynload.h>
|
2022-04-14 22:41:41 +02:00
|
|
|
#include <mutex>
|
|
|
|
#include <wums/exports.h>
|
|
|
|
|
2022-10-10 19:38:51 +02:00
|
|
|
struct AOCTitle {
|
|
|
|
WUT_UNKNOWN_BYTES(0x68);
|
|
|
|
};
|
|
|
|
WUT_CHECK_SIZE(AOCTitle, 0x68);
|
|
|
|
|
|
|
|
bool getAOCPath(std::string &outStr) {
|
|
|
|
int32_t (*AOC_Initialize)() = nullptr;
|
|
|
|
int32_t (*AOC_Finalize)() = nullptr;
|
|
|
|
int32_t (*AOC_ListTitle)(uint32_t * titleCountOut, AOCTitle * titleList, uint32_t maxCount, void *workBuffer, uint32_t workBufferSize) = nullptr;
|
|
|
|
int32_t (*AOC_OpenTitle)(char *pathOut, AOCTitle *aocTitleInfo, void *workBuffer, uint32_t workBufferSize) = nullptr;
|
|
|
|
int32_t (*AOC_CalculateWorkBufferSize)(uint32_t count) = nullptr;
|
|
|
|
int32_t (*AOC_CloseTitle)(AOCTitle * aocTitleInfo) = nullptr;
|
|
|
|
|
|
|
|
AOCTitle title{};
|
|
|
|
char aocPath[256];
|
|
|
|
aocPath[0] = '\0';
|
|
|
|
uint32_t outCount = 0;
|
|
|
|
uint32_t workBufferSize = 0;
|
|
|
|
void *workBuffer = nullptr;
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
OSDynLoad_Module aoc_handle;
|
|
|
|
if (OSDynLoad_Acquire("nn_aoc.rpl", &aoc_handle) != OS_DYNLOAD_OK) {
|
|
|
|
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_Acquire failed");
|
|
|
|
return false;
|
|
|
|
}
|
2023-04-17 14:16:13 +02:00
|
|
|
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_Initialize", reinterpret_cast<void **>(&AOC_Initialize)) != OS_DYNLOAD_OK) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-17 14:16:13 +02:00
|
|
|
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_Finalize", reinterpret_cast<void **>(&AOC_Finalize)) != OS_DYNLOAD_OK) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-17 14:16:13 +02:00
|
|
|
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_OpenTitle", reinterpret_cast<void **>(&AOC_OpenTitle)) != OS_DYNLOAD_OK) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-17 14:16:13 +02:00
|
|
|
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_ListTitle", reinterpret_cast<void **>(&AOC_ListTitle)) != OS_DYNLOAD_OK) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-17 14:16:13 +02:00
|
|
|
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_CalculateWorkBufferSize", reinterpret_cast<void **>(&AOC_CalculateWorkBufferSize)) != OS_DYNLOAD_OK) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
|
|
|
|
goto end;
|
|
|
|
}
|
2023-04-17 14:16:13 +02:00
|
|
|
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_CloseTitle", reinterpret_cast<void **>(&AOC_CloseTitle)) != OS_DYNLOAD_OK) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
AOC_Initialize();
|
|
|
|
|
|
|
|
workBufferSize = AOC_CalculateWorkBufferSize(1);
|
|
|
|
workBuffer = memalign(0x40, workBufferSize);
|
|
|
|
if (!workBuffer) {
|
|
|
|
DEBUG_FUNCTION_LINE_WARN("Failed to alloc workBuffer");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (AOC_ListTitle(&outCount, &title, 1, workBuffer, workBufferSize) < 0) {
|
|
|
|
DEBUG_FUNCTION_LINE_WARN("AOC_ListTitle failed");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (AOC_OpenTitle(aocPath, &title, workBuffer, workBufferSize) < 0) {
|
|
|
|
DEBUG_FUNCTION_LINE_WARN("AOC_OpenTitle failed");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = true;
|
|
|
|
outStr = aocPath;
|
|
|
|
AOC_CloseTitle(&title);
|
|
|
|
end:
|
|
|
|
free(workBuffer);
|
|
|
|
AOC_Finalize();
|
|
|
|
OSDynLoad_Release(aoc_handle);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-04-14 22:41:41 +02:00
|
|
|
ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType) {
|
|
|
|
if (!handle || layerName == nullptr || replacementDir == nullptr) {
|
2022-09-10 21:24:58 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG");
|
2022-04-14 22:41:41 +02:00
|
|
|
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
|
|
|
|
}
|
2022-04-29 13:34:04 +02:00
|
|
|
std::unique_ptr<IFSWrapper> ptr;
|
2022-04-14 22:41:41 +02:00
|
|
|
if (layerType == FS_LAYER_TYPE_CONTENT_REPLACE) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/content\" to \"%s\", mode: \"replace\"", replacementDir);
|
2022-04-29 13:34:04 +02:00
|
|
|
ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/content", replacementDir, false, false);
|
2022-04-14 22:41:41 +02:00
|
|
|
} else if (layerType == FS_LAYER_TYPE_CONTENT_MERGE) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/content\" to \"%s\", mode: \"merge\"", replacementDir);
|
2022-04-29 13:34:04 +02:00
|
|
|
ptr = make_unique_nothrow<FSWrapperMergeDirsWithParent>(layerName, "/vol/content", replacementDir, true);
|
2022-10-10 19:38:51 +02:00
|
|
|
} else if (layerType == FS_LAYER_TYPE_AOC_MERGE || layerType == FS_LAYER_TYPE_AOC_REPLACE) {
|
|
|
|
std::string targetPath;
|
|
|
|
if (!getAOCPath(targetPath)) {
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("(%s) Failed to get the AOC path. Not redirecting /vol/aoc", layerName);
|
|
|
|
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
DEBUG_FUNCTION_LINE_INFO("Redirecting \"%s\" to \"%s\", mode: \"%s\"", targetPath.c_str(), replacementDir, layerType == FS_LAYER_TYPE_AOC_MERGE ? "merge" : "replace");
|
|
|
|
if (layerType == FS_LAYER_TYPE_AOC_MERGE) {
|
|
|
|
ptr = make_unique_nothrow<FSWrapperMergeDirsWithParent>(layerName, targetPath.c_str(), replacementDir, true);
|
|
|
|
} else {
|
|
|
|
ptr = make_unique_nothrow<FSWrapper>(layerName, targetPath.c_str(), replacementDir, false, false);
|
|
|
|
}
|
2022-04-14 22:41:41 +02:00
|
|
|
} else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE) {
|
2022-10-10 19:38:51 +02:00
|
|
|
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/save\" to \"%s\", mode: \"replace\"", replacementDir);
|
2022-04-29 13:34:04 +02:00
|
|
|
ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/save", replacementDir, false, true);
|
2024-04-21 11:37:53 +02:00
|
|
|
} else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE_IGNORE_VOL_SAVE_COMMON) {
|
|
|
|
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/save\" to \"%s\", mode: \"replace\", ignore: (\"/vol/save/common\")", replacementDir);
|
|
|
|
std::vector<std::string> ignorePaths({"/vol/save/common"});
|
|
|
|
ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/save", replacementDir, false, true, ignorePaths);
|
2022-04-14 22:41:41 +02:00
|
|
|
} else {
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("CONTENT_REDIRECTION_API_ERROR_UNKNOWN_LAYER_DIR_TYPE: %s %s %d", layerName, replacementDir, layerType);
|
|
|
|
return CONTENT_REDIRECTION_API_ERROR_UNKNOWN_FS_LAYER_TYPE;
|
|
|
|
}
|
|
|
|
if (ptr) {
|
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Added new layer (%s). Replacement dir: %s Type:%d", layerName, replacementDir, layerType);
|
|
|
|
std::lock_guard<std::mutex> lock(fsLayerMutex);
|
2022-04-29 13:34:04 +02:00
|
|
|
*handle = (CRLayerHandle) ptr->getHandle();
|
|
|
|
fsLayers.push_back(std::move(ptr));
|
2022-04-14 22:41:41 +02:00
|
|
|
return CONTENT_REDIRECTION_API_ERROR_NONE;
|
|
|
|
}
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory");
|
|
|
|
return CONTENT_REDIRECTION_API_ERROR_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) {
|
2022-10-11 13:19:33 +02:00
|
|
|
if (!remove_locked_first_if(fsLayerMutex, fsLayers, [handle](auto &cur) { return (CRLayerHandle) cur->getHandle() == handle; })) {
|
2022-09-10 21:24:58 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
|
2022-04-14 22:41:41 +02:00
|
|
|
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
|
|
|
|
}
|
|
|
|
return CONTENT_REDIRECTION_API_ERROR_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) {
|
|
|
|
std::lock_guard<std::mutex> lock(fsLayerMutex);
|
|
|
|
for (auto &cur : fsLayers) {
|
2022-04-29 13:34:04 +02:00
|
|
|
if ((CRLayerHandle) cur->getHandle() == handle) {
|
|
|
|
cur->setActive(active);
|
|
|
|
return CONTENT_REDIRECTION_API_ERROR_NONE;
|
2022-04-14 22:41:41 +02:00
|
|
|
}
|
|
|
|
}
|
2022-04-29 13:34:04 +02:00
|
|
|
|
2022-09-10 21:24:58 +02:00
|
|
|
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
|
2022-04-29 13:34:04 +02:00
|
|
|
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
|
2022-04-14 22:41:41 +02:00
|
|
|
}
|
|
|
|
|
2022-09-03 16:57:44 +02:00
|
|
|
ContentRedirectionApiErrorType CRGetVersion(ContentRedirectionVersion *outVersion) {
|
|
|
|
if (outVersion == nullptr) {
|
|
|
|
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
*outVersion = 1;
|
|
|
|
return CONTENT_REDIRECTION_API_ERROR_NONE;
|
2022-04-14 22:41:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CRAddDevice(const devoptab_t *device) {
|
|
|
|
return AddDevice(device);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CRRemoveDevice(const char *name) {
|
|
|
|
return RemoveDevice(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
WUMS_EXPORT_FUNCTION(CRGetVersion);
|
|
|
|
WUMS_EXPORT_FUNCTION(CRAddFSLayer);
|
|
|
|
WUMS_EXPORT_FUNCTION(CRRemoveFSLayer);
|
|
|
|
WUMS_EXPORT_FUNCTION(CRSetActive);
|
|
|
|
WUMS_EXPORT_FUNCTION(CRAddDevice);
|
|
|
|
WUMS_EXPORT_FUNCTION(CRRemoveDevice);
|