Skip to content

Commit 07587c1

Browse files
committed
Implement FSWrapperReplaceSingleFile
1 parent 1ac6f02 commit 07587c1

7 files changed

Lines changed: 363 additions & 11 deletions

File tree

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
FROM ghcr.io/wiiu-env/devkitppc:20240505
1+
FROM ghcr.io/wiiu-env/devkitppc:20241128
22

33
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
44
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20240424 /artifacts $DEVKITPRO
5-
COPY --from=ghcr.io/wiiu-env/libcontentredirection:20240428 /artifacts $DEVKITPRO
5+
COPY --from=ghcr.io/wiiu-env/libcontentredirection:1.2-dev-20250207-b11c3c8 /artifacts $DEVKITPRO
66

77
WORKDIR project

src/DirInfoEx.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@
33
#include <coreinit/filesystem_fsa.h>
44

55
typedef struct FSDirectoryEntryEx {
6-
FSADirectoryEntry realEntry{};
7-
bool isMarkedAsDeleted = false;
6+
FSADirectoryEntry realEntry = {};
7+
bool isMarkedAsDeleted = false;
88
} FSDirectoryEntryEx;
99

1010
struct DirInfoEx final : DirInfo {
1111
FSDirectoryEntryEx *readResult = nullptr;
1212
int readResultCapacity = 0;
1313
int readResultNumberOfEntries = 0;
1414
FSDirectoryHandle realDirHandle = 0;
15-
};
15+
};
16+
17+
struct DirInfoExSingleFile final : DirInfoBase {
18+
FSADirectoryEntry directoryEntry = {};
19+
bool entryRead = false;
20+
bool entryReadSuccess = false;
21+
FSDirectoryHandle realDirHandle = 0;
22+
};

src/FSWrapper.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@ FSError FSWrapper::FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle)
4242

4343
dirHandle->path[0] = '\0';
4444
strncat(dirHandle->path, newPath.c_str(), sizeof(dirHandle->path) - 1);
45-
{
46-
std::lock_guard<std::mutex> lock(openDirsMutex);
47-
openDirs.push_back(dirHandle);
48-
OSMemoryBarrier();
49-
}
45+
addDirHandle(dirHandle);
5046
} else {
5147
auto err = errno;
5248
if (err == ENOENT) {
@@ -743,6 +739,12 @@ std::shared_ptr<DirInfoBase> FSWrapper::getDirFromHandle(const FSDirectoryHandle
743739
return nullptr;
744740
}
745741

742+
void FSWrapper::addDirHandle(const std::shared_ptr<DirInfoBase> &dirHandle) {
743+
std::lock_guard lock(openDirsMutex);
744+
openDirs.push_back(dirHandle);
745+
OSMemoryBarrier();
746+
}
747+
746748
void FSWrapper::deleteDirHandle(FSDirectoryHandle handle) {
747749
if (!remove_locked_first_if(openDirsMutex, openDirs, [handle](auto &cur) { return static_cast<FSDirectoryHandle>(cur->handle) == handle; })) {
748750
DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle);

src/FSWrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class FSWrapper : public IFSWrapper {
114114
bool isValidDirHandle(FSDirectoryHandle handle) override;
115115
bool isValidFileHandle(FSFileHandle handle) override;
116116

117+
void addDirHandle(const std::shared_ptr<DirInfoBase> &dirHandle);
117118
void deleteDirHandle(FSDirectoryHandle handle) override;
118119
void deleteFileHandle(FSFileHandle handle) override;
119120

src/FSWrapperReplaceSingleFile.cpp

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
#include "FSWrapperReplaceSingleFile.h"
2+
#include "utils/StringTools.h"
3+
#include "utils/logger.h"
4+
#include "utils/utils.h"
5+
6+
#include <coreinit/cache.h>
7+
#include <coreinit/debug.h>
8+
#include <coreinit/filesystem.h>
9+
10+
#include <filesystem>
11+
12+
FSWrapperReplaceSingleFile::FSWrapperReplaceSingleFile(const std::string &name,
13+
const std::string &fileToReplace,
14+
const std::string &replaceWithPath,
15+
const bool fallbackOnError) : FSWrapper(name,
16+
fileToReplace,
17+
replaceWithPath,
18+
fallbackOnError,
19+
false) {
20+
auto strCpy = fileToReplace;
21+
std::ranges::replace(strCpy, '\\', '/');
22+
auto asPath = std::filesystem::path(strCpy);
23+
mPathToReplace = asPath.parent_path();
24+
mFileNameToReplace = asPath.filename();
25+
mFullPathToReplace = fileToReplace;
26+
27+
strCpy = replaceWithPath;
28+
std::ranges::replace(strCpy, '\\', '/');
29+
asPath = std::filesystem::path(strCpy);
30+
mReplacedWithPath = asPath.parent_path();
31+
mReplacedWithFileName = asPath.filename();
32+
33+
FSAInit();
34+
this->mClientHandle = FSAAddClient(nullptr);
35+
if (mClientHandle < 0) {
36+
DEBUG_FUNCTION_LINE_ERR("[%s] FSAClientHandle failed: %s (%d)", name.c_str(), FSAGetStatusStr(static_cast<FSError>(mClientHandle)), mClientHandle);
37+
mClientHandle = 0;
38+
}
39+
}
40+
41+
FSWrapperReplaceSingleFile::~FSWrapperReplaceSingleFile() {
42+
if (mClientHandle) {
43+
if (const FSError res = FSADelClient(mClientHandle); res != FS_ERROR_OK) {
44+
DEBUG_FUNCTION_LINE_ERR("[%s] FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res);
45+
}
46+
mClientHandle = 0;
47+
}
48+
}
49+
50+
FSError FSWrapperReplaceSingleFile::FSOpenDirWrapper(const char *path,
51+
FSADirectoryHandle *handle) {
52+
if (!IsDirPathToReplace(path)) {
53+
return FS_ERROR_FORCE_PARENT_LAYER;
54+
}
55+
if (handle == nullptr) {
56+
DEBUG_FUNCTION_LINE_ERR("[%s] handle was NULL", getName().c_str());
57+
return FS_ERROR_INVALID_PARAM;
58+
}
59+
if (const auto dirInfo = make_shared_nothrow<DirInfoExSingleFile>()) {
60+
dirInfo->handle = (reinterpret_cast<uint32_t>(dirInfo.get()) & 0x0FFFFFFF) | 0x30000000;
61+
*handle = dirInfo->handle;
62+
addDirHandle(dirInfo);
63+
if (!isValidDirHandle(*handle)) {
64+
FSWrapper::FSCloseDirWrapper(*handle);
65+
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), *handle);
66+
return FS_ERROR_INVALID_DIRHANDLE;
67+
}
68+
if (const auto dirHandle = getDirExFromHandle(*handle); dirHandle != nullptr) {
69+
dirHandle->entryRead = false;
70+
dirHandle->entryReadSuccess = false;
71+
dirHandle->directoryEntry = {};
72+
dirHandle->realDirHandle = 0;
73+
74+
if (mClientHandle) {
75+
FSADirectoryHandle realHandle = 0;
76+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSAOpenDir with %s for parent layer", getName().c_str(), path);
77+
if (const FSError err = FSAOpenDir(mClientHandle, path, &realHandle); err == FS_ERROR_OK) {
78+
dirHandle->realDirHandle = realHandle;
79+
} else {
80+
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to open real dir %s. %s (%d)", getName().c_str(), path, FSAGetStatusStr(err), err);
81+
}
82+
} else {
83+
DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str());
84+
}
85+
OSMemoryBarrier();
86+
}
87+
} else {
88+
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc dir handle", getName().c_str());
89+
return FS_ERROR_MAX_DIRS;
90+
}
91+
return FS_ERROR_OK;
92+
}
93+
94+
FSError FSWrapperReplaceSingleFile::FSReadDirWrapper(const FSADirectoryHandle handle, FSADirectoryEntry *entry) {
95+
if (!isValidDirHandle(handle)) {
96+
return FS_ERROR_FORCE_PARENT_LAYER;
97+
}
98+
const auto dirHandle = getDirExFromHandle(handle);
99+
if (!dirHandle) {
100+
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle);
101+
return FS_ERROR_INVALID_DIRHANDLE;
102+
}
103+
FSError res = FS_ERROR_OK;
104+
do {
105+
if (!dirHandle->entryRead) {
106+
dirHandle->entryRead = true;
107+
const auto newPath = GetNewPath(mFullPathToReplace);
108+
109+
struct stat path_stat {};
110+
111+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] dir read of %s (%s)", getName().c_str(), mFullPathToReplace.c_str(), newPath.c_str());
112+
if (stat(newPath.c_str(), &path_stat) < 0) {
113+
DEBUG_FUNCTION_LINE_WARN("[%s] Path %s (%s) for dir read not found ", getName().c_str(), mFullPathToReplace.c_str(), newPath.c_str());
114+
dirHandle->entryReadSuccess = false;
115+
continue;
116+
}
117+
translate_stat(&path_stat, &dirHandle->directoryEntry.info);
118+
strncpy(dirHandle->directoryEntry.name, mFileNameToReplace.c_str(), sizeof(dirHandle->directoryEntry.name));
119+
memcpy(entry, &dirHandle->directoryEntry, sizeof(FSADirectoryEntry));
120+
121+
dirHandle->entryReadSuccess = true;
122+
OSMemoryBarrier();
123+
} else {
124+
// Read the real directory.
125+
if (dirHandle->realDirHandle != 0) {
126+
if (mClientHandle) {
127+
FSADirectoryEntry realDirEntry;
128+
while (true) {
129+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSReadDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
130+
if (const FSError readDirResult = FSAReadDir(mClientHandle, dirHandle->realDirHandle, &realDirEntry); readDirResult == FS_ERROR_OK) {
131+
// Skip already read new files
132+
if (dirHandle->entryRead && dirHandle->entryReadSuccess && strcmp(dirHandle->directoryEntry.name, realDirEntry.name) == 0) {
133+
continue;
134+
}
135+
136+
// But use new entries!
137+
memcpy(entry, &realDirEntry, sizeof(FSADirectoryEntry));
138+
res = FS_ERROR_OK;
139+
break;
140+
} else if (readDirResult == FS_ERROR_END_OF_DIR) {
141+
res = FS_ERROR_END_OF_DIR;
142+
break;
143+
} else {
144+
DEBUG_FUNCTION_LINE_ERR("[%s] real_FSReadDir returned an unexpected error: %s (%d)", getName().c_str(), FSAGetStatusStr(readDirResult), readDirResult);
145+
res = FS_ERROR_END_OF_DIR;
146+
break;
147+
}
148+
}
149+
} else {
150+
DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str());
151+
}
152+
}
153+
}
154+
return res;
155+
} while (true);
156+
}
157+
158+
FSError FSWrapperReplaceSingleFile::FSCloseDirWrapper(const FSADirectoryHandle handle) {
159+
if (!isValidDirHandle(handle)) {
160+
return FS_ERROR_FORCE_PARENT_LAYER;
161+
}
162+
const auto dirHandle = getDirExFromHandle(handle);
163+
if (dirHandle->realDirHandle != 0) {
164+
if (mClientHandle) {
165+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSCloseDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
166+
auto realResult = FSACloseDir(mClientHandle, dirHandle->realDirHandle);
167+
if (realResult == FS_ERROR_OK) {
168+
dirHandle->realDirHandle = 0;
169+
} else {
170+
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close realDirHandle %d: res %s (%d)", getName().c_str(), dirHandle->realDirHandle, FSAGetStatusStr(realResult), realResult);
171+
return realResult == FS_ERROR_CANCELLED ? FS_ERROR_CANCELLED : FS_ERROR_MEDIA_ERROR;
172+
}
173+
} else {
174+
DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str());
175+
}
176+
} else {
177+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] dirHandle->realDirHandle was 0", getName().c_str());
178+
}
179+
dirHandle->entryRead = false;
180+
dirHandle->entryReadSuccess = false;
181+
182+
OSMemoryBarrier();
183+
return FS_ERROR_OK;
184+
}
185+
186+
FSError FSWrapperReplaceSingleFile::FSRewindDirWrapper(const FSADirectoryHandle handle) {
187+
if (!isValidDirHandle(handle)) {
188+
return FS_ERROR_FORCE_PARENT_LAYER;
189+
}
190+
const auto dirHandle = getDirExFromHandle(handle);
191+
dirHandle->entryRead = false;
192+
dirHandle->entryReadSuccess = false;
193+
if (dirHandle->realDirHandle != 0) {
194+
if (mClientHandle) {
195+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSARewindDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
196+
if (const FSError err = FSARewindDir(mClientHandle, dirHandle->realDirHandle); err != FS_ERROR_OK) {
197+
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to rewind dir for realDirHandle %08X. %s (%d)", getName().c_str(), dirHandle->realDirHandle, FSAGetStatusStr(err), err);
198+
}
199+
} else {
200+
DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str());
201+
}
202+
} else {
203+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] dirHandle->realDirHandle was 0", getName().c_str());
204+
}
205+
OSMemoryBarrier();
206+
207+
return FS_ERROR_OK;
208+
}
209+
210+
bool FSWrapperReplaceSingleFile::SkipDeletedFilesInReadDir() {
211+
return false;
212+
}
213+
214+
bool FSWrapperReplaceSingleFile::IsDirPathToReplace(const std::string_view &path) const {
215+
return starts_with_case_insensitive(path, mPathToReplace);
216+
}
217+
218+
std::string FSWrapperReplaceSingleFile::GetNewPath(const std::string_view &path) const {
219+
auto pathCpy = std::string(path);
220+
SafeReplaceInString(pathCpy, this->mPathToReplace, this->mReplacedWithPath);
221+
SafeReplaceInString(pathCpy, this->mFileNameToReplace, this->mReplacedWithFileName);
222+
std::ranges::replace(pathCpy, '\\', '/');
223+
224+
uint32_t length = pathCpy.size();
225+
226+
//! clear path of double slashes
227+
for (uint32_t i = 1; i < length; ++i) {
228+
if (pathCpy[i - 1] == '/' && pathCpy[i] == '/') {
229+
pathCpy.erase(i, 1);
230+
i--;
231+
length--;
232+
}
233+
}
234+
235+
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Redirect %.*s -> %s", getName().c_str(), int(path.length()), path.data(), res.c_str());
236+
return pathCpy;
237+
}
238+
239+
std::shared_ptr<DirInfoExSingleFile> FSWrapperReplaceSingleFile::getDirExFromHandle(FSADirectoryHandle handle) {
240+
auto dir = std::dynamic_pointer_cast<DirInfoExSingleFile>(getDirFromHandle(handle));
241+
242+
if (!dir) {
243+
DEBUG_FUNCTION_LINE_ERR("[%s] dynamic_pointer_cast<DirInfoExSingleFile *>(%08X) failed", getName().c_str(), handle);
244+
OSFatal("ContentRedirectionModule: dynamic_pointer_cast<DirInfoExSingleFile *> failed");
245+
}
246+
return dir;
247+
}

src/FSWrapperReplaceSingleFile.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#pragma once
2+
#include "DirInfoEx.h"
3+
#include "FSWrapper.h"
4+
5+
#include <coreinit/filesystem.h>
6+
7+
class FSWrapperReplaceSingleFile final : public FSWrapper {
8+
public:
9+
FSWrapperReplaceSingleFile(const std::string &name,
10+
const std::string &fileToReplace,
11+
const std::string &replaceWithPath,
12+
bool fallbackOnError);
13+
14+
~FSWrapperReplaceSingleFile() override;
15+
16+
FSError FSOpenDirWrapper(const char *path,
17+
FSDirectoryHandle *handle) override;
18+
19+
FSError FSReadDirWrapper(FSDirectoryHandle handle,
20+
FSDirectoryEntry *entry) override;
21+
22+
FSError FSCloseDirWrapper(FSDirectoryHandle handle) override;
23+
24+
FSError FSRewindDirWrapper(FSDirectoryHandle handle) override;
25+
26+
bool SkipDeletedFilesInReadDir() override;
27+
28+
uint32_t getLayerId() override {
29+
return static_cast<uint32_t>(mClientHandle);
30+
}
31+
32+
33+
protected:
34+
[[nodiscard]] std::string GetNewPath(const std::string_view &path) const override;
35+
36+
std::shared_ptr<DirInfo> getNewDirInfoHandle() override {
37+
OSFatal("FSWrapperReplaceSingleFile::getNewDirInfoHandle. Not implemented");
38+
return {};
39+
}
40+
41+
private:
42+
bool IsDirPathToReplace(const std::string_view &path) const;
43+
44+
std::shared_ptr<DirInfoExSingleFile> getDirExFromHandle(FSDirectoryHandle handle);
45+
46+
FSAClientHandle mClientHandle;
47+
std::string mPathToReplace;
48+
std::string mFileNameToReplace;
49+
std::string mFullPathToReplace;
50+
std::string mReplacedWithPath;
51+
std::string mReplacedWithFileName;
52+
};

0 commit comments

Comments
 (0)