#include "FileUtils.h" #include "FSWrapper.h" #include "IFSWrapper.h" #include "utils/StringTools.h" #include "utils/logger.h" #include "utils/utils.h" #include #include #include #include std::mutex workingDirMutexFS; std::map workingDirsFS; std::mutex fsLayerMutex; std::vector> fsLayers; std::string getFullPathForFSClient(FSClient *pClient, const char *path) { std::lock_guard workingDirLock(workingDirMutexFS); std::string res; if (path[0] != '/' && path[0] != '\\') { if (workingDirsFS.count(pClient) == 0) { DEBUG_FUNCTION_LINE_WARN("No working dir found for FS client %08X, fallback to \"/\"", pClient); workingDirsFS[pClient] = "/"; } res = string_format("%s%s", workingDirsFS.at(pClient).c_str(), path); } else { res = path; } std::replace(res.begin(), res.end(), '\\', '/'); return res; } void setWorkingDirForFSClient(FSClient *client, const char *path) { std::lock_guard workingDirLock(workingDirMutexFS); workingDirsFS[client] = path; OSMemoryBarrier(); } void clearFSLayer() { { std::lock_guard workingDirLock(workingDirMutexFS); workingDirsFS.clear(); } { std::lock_guard layerLock(fsLayerMutex); fsLayers.clear(); } } // FUN_0204cc20 #define fsClientHandleFatalErrorAndBlock ((void (*)(FSClientBody *, uint32_t))(0x101C400 + 0x4cc20)) #define fsaDecodeFsaStatusToFsStatus ((FSStatus(*)(FSError))(0x101C400 + 0x4b148)) FSStatus doForLayer(FSClient *client, FSErrorFlag errorMask, const std::function &real_function, const std::function &layer)> &layer_callback, const std::function &layer, FSStatus)> &result_handler) { FSErrorFlag realErrorMask = errorMask; std::lock_guard lock(fsLayerMutex); if (!fsLayers.empty()) { uint32_t startIndex = fsLayers.size(); for (uint32_t i = fsLayers.size(); i > 0; i--) { if ((uint32_t) fsLayers[i - 1]->getHandle() == errorMask) { startIndex = i - 1; realErrorMask = FS_ERROR_FLAG_ALL; break; } } if (startIndex > 0) { for (uint32_t i = startIndex; i > 0; i--) { auto &layer = fsLayers[i - 1]; if (!layer->isActive()) { continue; } FSError layerResult = layer_callback(layer); if (layerResult != FS_ERROR_FORCE_PARENT_LAYER) { auto maskedResult = (FSError) ((layerResult & FS_ERROR_REAL_MASK) | FS_ERROR_EXTRA_MASK); auto result = layerResult >= 0 ? static_cast(layerResult) : fsaDecodeFsaStatusToFsStatus(maskedResult); if (result < FS_STATUS_OK && result != FS_STATUS_END && result != FS_STATUS_CANCELLED) { if (layer->fallbackOnError()) { // Only fallback if FS_ERROR_FORCE_NO_FALLBACK flag is not set. if (static_cast(layerResult & FS_ERROR_EXTRA_MASK) != FS_ERROR_FORCE_NO_FALLBACK) { continue; } } } if (result >= FS_STATUS_OK || result == FS_STATUS_END || result == FS_STATUS_CANCELLED) { DEBUG_FUNCTION_LINE_VERBOSE("Returned %08X by %s", result, layer->getName().c_str()); return result_handler(layer, result); } FSErrorFlag errorFlags = FS_ERROR_FLAG_NONE; bool forceError = false; switch ((int32_t) result) { case FS_STATUS_MAX: errorFlags = FS_ERROR_FLAG_MAX; break; case FS_STATUS_ALREADY_OPEN: errorFlags = FS_ERROR_FLAG_ALREADY_OPEN; break; case FS_STATUS_EXISTS: errorFlags = FS_ERROR_FLAG_EXISTS; break; case FS_STATUS_NOT_FOUND: errorFlags = FS_ERROR_FLAG_NOT_FOUND; break; case FS_STATUS_NOT_FILE: errorFlags = FS_ERROR_FLAG_NOT_FILE; break; case FS_STATUS_NOT_DIR: errorFlags = FS_ERROR_FLAG_NOT_DIR; break; case FS_STATUS_ACCESS_ERROR: errorFlags = FS_ERROR_FLAG_ACCESS_ERROR; break; case FS_STATUS_PERMISSION_ERROR: errorFlags = FS_ERROR_FLAG_PERMISSION_ERROR; break; case FS_STATUS_FILE_TOO_BIG: errorFlags = FS_ERROR_FLAG_FILE_TOO_BIG; break; case FS_STATUS_STORAGE_FULL: errorFlags = FS_ERROR_FLAG_STORAGE_FULL; break; case FS_STATUS_JOURNAL_FULL: errorFlags = FS_ERROR_FLAG_JOURNAL_FULL; break; case FS_STATUS_UNSUPPORTED_CMD: errorFlags = FS_ERROR_FLAG_UNSUPPORTED_CMD; break; case FS_STATUS_MEDIA_NOT_READY: case FS_STATUS_MEDIA_ERROR: case FS_STATUS_CORRUPTED: case FS_STATUS_FATAL_ERROR: forceError = true; break; case FS_STATUS_OK: break; } if (forceError || (realErrorMask != FS_ERROR_FLAG_NONE && (errorFlags & realErrorMask) == 0)) { DEBUG_FUNCTION_LINE_ERR("Transit to Fatal Error"); auto clientBody = fsClientGetBody(client); fsClientHandleFatalErrorAndBlock(clientBody, clientBody->lastError); return FS_STATUS_FATAL_ERROR; } DEBUG_FUNCTION_LINE_VERBOSE("%08X Returned %08X by %s ", errorMask, result, layer->getName().c_str()); return result_handler(layer, result); } } } } auto mask = static_cast((realErrorMask & FS_ERROR_FLAG_REAL_MASK) | FS_ERROR_FLAG_FORCE_REAL); return real_function(mask); } FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock) { if (!cmdBlock) { return nullptr; } auto body = (FSCmdBlockBody *) (ROUNDUP((uint32_t) cmdBlock, 0x40)); return body; } FSClientBody *fsClientGetBody(FSClient *client) { if (!client) { return nullptr; } auto body = (FSClientBody *) (ROUNDUP((uint32_t) client, 0x40)); body->client = client; return body; } FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus status) { if (asyncData->callback != nullptr) { if (asyncData->ioMsgQueue != nullptr) { DEBUG_FUNCTION_LINE_ERR("callback and ioMsgQueue both set."); return FS_STATUS_FATAL_ERROR; } // userCallbacks are called in the DefaultAppIOQueue. asyncData->ioMsgQueue = OSGetDefaultAppIOQueue(); //DEBUG_FUNCTION_LINE("Force to OSGetDefaultAppIOQueue (%08X)", asyncData->ioMsgQueue); } if (asyncData->ioMsgQueue != nullptr) { #pragma GCC diagnostic ignored "-Waddress-of-packed-member" FSAsyncResult *result = &(fsCmdBlockGetBody(block)->asyncResult); //DEBUG_FUNCTION_LINE("Send result %d to ioMsgQueue (%08X)", status, asyncData->ioMsgQueue); result->asyncData.callback = asyncData->callback; result->asyncData.param = asyncData->param; result->asyncData.ioMsgQueue = asyncData->ioMsgQueue; memset(&result->ioMsg, 0, sizeof(result->ioMsg)); result->ioMsg.data = result; result->ioMsg.type = OS_FUNCTION_TYPE_FS_CMD_ASYNC; result->client = client; result->block = block; result->status = status; while (!OSSendMessage(asyncData->ioMsgQueue, (OSMessage *) &(result->ioMsg), OS_MESSAGE_FLAGS_NONE)) { DEBUG_FUNCTION_LINE_ERR("Failed to send message"); } } return FS_STATUS_OK; } int64_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count) { auto sizeToRead = size * count; /* // https://github.com/decaf-emu/decaf-emu/blob/131aeb14fccff8461a5fd9f2aa5c040ba3880ef5/src/libdecaf/src/cafe/libraries/coreinit/coreinit_fs_cmd.cpp#L2346 if (sizeToRead > 0x100000) { sizeToRead = 0x100000; }*/ void *newBuffer = buffer; int32_t curResult; int64_t totalSize = 0; while (sizeToRead > 0) { curResult = read(handle, newBuffer, sizeToRead); if (curResult < 0) { DEBUG_FUNCTION_LINE_ERR("Reading %08X bytes from handle %08X failed. result %08X errno: %d ", size * count, handle, curResult, errno); return -1; } if (curResult == 0) { break; } newBuffer = (void *) (((uint32_t) newBuffer) + curResult); totalSize += curResult; sizeToRead -= curResult; } return totalSize; } int64_t writeFromBuffer(int32_t handle, void *buffer, size_t size, size_t count) { auto sizeToWrite = size * count; void *ptr = buffer; int32_t curResult; int64_t totalSize = 0; while (sizeToWrite > 0) { curResult = write(handle, ptr, sizeToWrite); if (curResult < 0) { DEBUG_FUNCTION_LINE_ERR("Writing %08X bytes from handle %08X failed. result %08X errno: %d ", size * count, handle, curResult, errno); return -1; } if (curResult == 0) { break; } ptr = (void *) (((uint32_t) ptr) + curResult); totalSize += curResult; sizeToWrite -= curResult; } return totalSize; }