interposer@v1.0.3 bf1942@v1.61 callofduty@v1.5b counterstrikesource@v1.0.0 darkmessiah@unversioned eliteforce@v1.30 jediacademy@v1.0.0 jedioutcast@v1.4.1 portalknights@unversioned quake3arena@v1.0.0 rtcw@unversioned ut2004@v3339
136 lines
5.3 KiB
Markdown
136 lines
5.3 KiB
Markdown
---
|
||
sidebar_label: File Redirection
|
||
sidebar_position: 4
|
||
---
|
||
|
||
# File Redirection
|
||
|
||
File redirection intercepts calls to `CreateFileW/A`, `GetFileAttributesW/A`, `FindFirstFileW/A`, and the `LoadLibrary` family, replacing path arguments on the fly before the call reaches Windows. The game opens and reads the redirected file without any knowledge that its path was changed.
|
||
|
||
## Why Use It
|
||
|
||
Common use cases:
|
||
|
||
- **Portable save files** — redirect a hard-coded save path (e.g. `C:\Program Files\MyGame\saves\`) to a per-user location (`%USERPROFILE%\Saved Games\My Game\`)
|
||
- **Config file portability** — redirect absolute paths baked into old games to locations inside the game directory
|
||
- **Multi-user coexistence** — different users can be redirected to different profile directories without modifying the game
|
||
- **Registry-adjacent paths** — some games write config to hard-coded paths under `C:\Windows` or `C:\Program Files`; redirect these to writable locations
|
||
|
||
## Configuring Redirects
|
||
|
||
Redirects are defined as a list in `.interposer/Config.yml` under the `FileRedirects` key. Each entry has a `Pattern` (regex) and a `Replacement`:
|
||
|
||
```yaml
|
||
FileRedirects:
|
||
- Pattern: 'C:\\Games\\MyGame\\Saves\\(.+)'
|
||
Replacement: '%USERPROFILE%\Saved Games\My Game\$1'
|
||
```
|
||
|
||
:::note Backwards compatibility
|
||
The legacy key name `Redirects` is still accepted and behaves identically. It is read only when `FileRedirects` is not present, so prefer `FileRedirects` for new configs.
|
||
:::
|
||
|
||
:::tip Use single-quoted strings for patterns
|
||
YAML single-quoted strings pass backslashes through literally. No extra escaping is needed when writing Windows paths as regex patterns. Double-quoted strings interpret YAML escape sequences and should be avoided here.
|
||
:::
|
||
|
||
Rules are evaluated in order. **The first matching rule wins**. Subsequent rules are not checked once a match is found.
|
||
|
||
## Pattern Syntax
|
||
|
||
Patterns are [ECMAScript regular expressions](https://en.cppreference.com/w/cpp/regex/ecmascript), matched case-insensitively against the full file path.
|
||
|
||
In a single-quoted YAML string, each `\\` represents one literal backslash character, which the regex engine then treats as a literal backslash (matching `\` in a Windows path). To match a directory separator, write `\\\\` in a double-quoted string or `\\` in a single-quoted string.
|
||
|
||
| You want to match | Single-quoted YAML | Regex sees |
|
||
|---|---|---|
|
||
| A literal backslash | `'\\'` | `\\` (matches `\`) |
|
||
| `C:\Games\MyGame\` | `'C:\\Games\\MyGame\\'` | `C:\\Games\\MyGame\\` |
|
||
| Any characters | `'(.+)'` | `(.+)` |
|
||
| A literal dot | `'\.'` | `\.` |
|
||
|
||
### Capture Groups
|
||
|
||
Use parentheses to capture parts of the matched path for use in the replacement:
|
||
|
||
```yaml
|
||
- Pattern: 'C:\\Games\\MyGame\\Saves\\(.+)'
|
||
Replacement: '%USERPROFILE%\Saved Games\My Game\$1'
|
||
```
|
||
|
||
For the path `C:\Games\MyGame\Saves\profile.dat`:
|
||
- `$1` captures `profile.dat`
|
||
- The replacement expands to `%USERPROFILE%\Saved Games\My Game\profile.dat` (with `%APPDATA%` further expanded)
|
||
|
||
Up to nine capture groups (`$1` through `$9`) are supported.
|
||
|
||
## Replacement Syntax
|
||
|
||
| Token | Meaning |
|
||
|---|---|
|
||
| `$1` – `$9` | Replaced with the corresponding capture group from the pattern match. |
|
||
| `%VARNAME%` | Expanded to the value of the named Windows environment variable after capture group substitution. |
|
||
|
||
Environment variable expansion happens after capture group substitution, so a capture group can itself contain a variable reference if needed.
|
||
|
||
## Examples
|
||
|
||
### Redirect a save directory
|
||
|
||
```yaml
|
||
FileRedirects:
|
||
- Pattern: 'C:\\Games\\Quake\\id1\\save\\(.+)'
|
||
Replacement: '%APPDATA%\Quake\save\$1'
|
||
```
|
||
|
||
### Redirect an absolute config path to the game directory
|
||
|
||
```yaml
|
||
FileRedirects:
|
||
- Pattern: 'C:\\Program Files.*\\MyGame\\config\.cfg'
|
||
Replacement: '%GAMEDIR%\config.cfg'
|
||
```
|
||
|
||
### Redirect multiple directories with one rule
|
||
|
||
This example redirects any file under `C:\OldGame\data\` or `C:\OldGame\mods\` to corresponding locations under `%APPDATA%\OldGame\`:
|
||
|
||
```yaml
|
||
FileRedirects:
|
||
- Pattern: 'C:\\OldGame\\(data|mods)\\(.+)'
|
||
Replacement: '%APPDATA%\OldGame\$1\$2'
|
||
```
|
||
|
||
### Multiple rules — first match wins
|
||
|
||
```yaml
|
||
FileRedirects:
|
||
- Pattern: 'C:\\Games\\MyGame\\Saves\\current\\(.+)'
|
||
Replacement: '%USERPROFILE%\Saved Games\My Game\slot1\$1'
|
||
- Pattern: 'C:\\Games\\MyGame\\Saves\\(.+)'
|
||
Replacement: '%USERPROFILE%\Saved Games\My Game\$1'
|
||
```
|
||
|
||
The first rule redirects any path under `current\` specifically; the second catches everything else under `Saves\`.
|
||
|
||
## Verifying Redirects
|
||
|
||
Enable file logging and look for `[FILE REDIRECT]` entries:
|
||
|
||
```
|
||
2025-03-14 12:00:01 [FILE REDIRECT] C:\Games\MyGame\Saves\profile.dat -> C:\Users\Pat\AppData\Roaming\MyGame\Saves\profile.dat
|
||
```
|
||
|
||
If a redirect is not firing, check the exact path in `[FILE READ]` or `[FILE ATTR]` entries and compare it against your pattern.
|
||
|
||
## Hooked Functions
|
||
|
||
File redirection applies to the following Windows API functions:
|
||
|
||
| Function | Notes |
|
||
|---|---|
|
||
| `CreateFileW` / `CreateFileA` | Applies redirect before opening; ANSI variant converts to wide first. |
|
||
| `GetFileAttributesW` / `GetFileAttributesA` | Applies redirect before querying attributes. |
|
||
| `FindFirstFileW` / `FindFirstFileA` | Applies redirect before beginning enumeration. |
|
||
| `LoadLibraryW` / `LoadLibraryA` | Applies redirect before loading a DLL. |
|
||
| `LoadLibraryExW` / `LoadLibraryExA` | Applies redirect before loading a DLL with flags. |
|