Skip to content

Commit

Permalink
#395: Allow reversing the redirect sequence when restoring
Browse files Browse the repository at this point in the history
  • Loading branch information
mtkennerly committed Oct 18, 2024
1 parent 293a559 commit 992abc5
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 51 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ to better preserve the files' executable permissions.
Previously, it would always override the manifest entry completely.
* GUI: Custom games can now be expanded/collapsed, sorted, and filtered.
* GUI: Custom games now have an icon to indicate when they override/extend a manifest entry.
* You can now configure redirects to be processed in reverse sequence when restoring.
* Fixed:
* Files on Windows network shares were not backed up correctly.
For example, a file identified as `\\localhost\share\test.txt`
Expand Down
20 changes: 20 additions & 0 deletions docs/help/redirects.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,23 @@ For example:
Tip: As you're editing your redirects, try running a preview and expanding some
games' file lists. This will show you what effect your redirects
will have when you perform the restore for real.

## Sequence
Redirects are processed top to bottom,
and the output from one redirect can affect the redirects after it.

Let's say you have a save file at `C:/Title/save.dat`,
and you set up two redirects:

* Bidirectional: `C:/Title` -> `C:/Games/Title`
* Bidirectional: `C:/Games` -> `D:/Games`

When backing up, the transformation will be:
`C:/Title/save.dat` -> `C:/Games/Title/save.dat` -> `D:/Games/Title/save.dat`

By default, the same order is used when restoring.
When you have chained bidirectional redirects,
that may lead to an undesired result:
`D:/Games/Title/save.dat` won't trigger the first redirect,
so it would restore to `C:/Games/Title/save.dat`.
You can enable the "reverse sequence of redirects when restoring" option to change this behavior.
1 change: 1 addition & 0 deletions lang/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ theme-light = Light
theme-dark = Dark
redirect-bidirectional = Bidirectional
reverse-redirects-when-restoring = Reverse sequence of redirects when restoring
show-deselected-games = Show deselected games
show-unchanged-games = Show unchanged games
Expand Down
10 changes: 9 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,13 @@ pub fn run(sub: Subcommand, no_manifest_update: bool, try_manifest_update: bool)
log::trace!("step {i} / {}: {name}", games.len());
let game = &manifest.0[name];

let previous = layout.latest_backup(name, false, &config.redirects, &config.restore.toggled_paths);
let previous = layout.latest_backup(
name,
false,
&config.redirects,
config.restore.reverse_redirects,
&config.restore.toggled_paths,
);

if filter.excludes(games_specified, previous.is_some(), &game.cloud) {
log::trace!("[{name}] excluded by backup filter");
Expand All @@ -258,6 +264,7 @@ pub fn run(sub: Subcommand, no_manifest_update: bool, try_manifest_update: bool)
&toggled_registry,
previous,
&config.redirects,
config.restore.reverse_redirects,
&steam_shortcuts,
);
let ignored = !&config.is_game_enabled_for_backup(name) && !games_specified;
Expand Down Expand Up @@ -442,6 +449,7 @@ pub fn run(sub: Subcommand, no_manifest_update: bool, try_manifest_update: bool)
name,
backup_id.as_ref().unwrap_or(&BackupId::Latest),
&config.redirects,
config.restore.reverse_redirects,
&config.restore.toggled_paths,
&config.restore.toggled_registry,
);
Expand Down
17 changes: 14 additions & 3 deletions src/gui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,13 @@ impl App {
return (None, None);
}

let previous =
layout.latest_backup(&key, false, &config.redirects, &config.restore.toggled_paths);
let previous = layout.latest_backup(
&key,
false,
&config.redirects,
config.restore.reverse_redirects,
&config.restore.toggled_paths,
);

if filter.excludes(games_specified, previous.is_some(), &game.cloud) {
log::trace!("[{key}] excluded by backup filter");
Expand All @@ -463,11 +468,11 @@ impl App {
&launchers,
&filter,
&None,
// &ranking,
&config.backup.toggled_paths,
&config.backup.toggled_registry,
previous,
&config.redirects,
config.restore.reverse_redirects,
&steam_shortcuts,
);
if !config.is_game_enabled_for_backup(&key) && full {
Expand Down Expand Up @@ -781,6 +786,7 @@ impl App {
&name,
&backup_id,
&config.redirects,
config.restore.reverse_redirects,
&config.restore.toggled_paths,
&config.restore.toggled_registry,
);
Expand Down Expand Up @@ -1601,6 +1607,11 @@ impl App {
self.save_config();
Task::none()
}
Message::EditedReverseRedirectsOnRestore(enabled) => {
self.config.restore.reverse_redirects = enabled;
self.config.save();
Task::none()
}
Message::EditedCustomGame(action) => {
let mut snap = false;
match action {
Expand Down
1 change: 1 addition & 0 deletions src/gui/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub enum Message {
SelectedCustomGameKind(usize, CustomGameKind),
SelectedCustomGameIntegration(usize, Integration),
EditedRedirect(EditAction, Option<RedirectEditActionField>),
EditedReverseRedirectsOnRestore(bool),
EditedCustomGame(EditAction),
EditedCustomGameAlias(usize, String),
EditedCustomGaleAliasDisplay(usize, bool),
Expand Down
63 changes: 33 additions & 30 deletions src/gui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,39 +205,42 @@ pub fn manifest<'a>(
pub fn redirect<'a>(config: &Config, histories: &TextHistories, modifiers: &keyboard::Modifiers) -> Container<'a> {
let redirects = config.get_redirects();

let inner = Container::new({
config
.redirects
.iter()
.enumerate()
.fold(Column::new().padding(5).spacing(4), |parent, (i, _)| {
parent.push(
Row::new()
.spacing(20)
.push(button::move_up(|x| Message::EditedRedirect(x, None), i))
.push(button::move_down(
|x| Message::EditedRedirect(x, None),
i,
config.redirects.len(),
))
.push(
pick_list(RedirectKind::ALL, Some(redirects[i].kind), move |v| {
Message::SelectedRedirectKind(i, v)
})
.class(style::PickList::Primary),
)
.push(histories.input(UndoSubject::RedirectSource(i)))
.push(button::choose_folder(BrowseSubject::RedirectSource(i), modifiers))
.push(histories.input(UndoSubject::RedirectTarget(i)))
.push(button::choose_folder(BrowseSubject::RedirectTarget(i), modifiers))
.push(button::remove(|x| Message::EditedRedirect(x, None), i)),
)
})
.push(button::add(|x| Message::EditedRedirect(x, None)))
let wrapper = Container::new({
let mut content = Column::new().padding(5).spacing(4).push(checkbox(
TRANSLATOR.reverse_redirects_when_restoring(),
config.restore.reverse_redirects,
Message::EditedReverseRedirectsOnRestore,
));

content = config.redirects.iter().enumerate().fold(content, |parent, (i, _)| {
parent.push(
Row::new()
.spacing(20)
.push(button::move_up(|x| Message::EditedRedirect(x, None), i))
.push(button::move_down(
|x| Message::EditedRedirect(x, None),
i,
config.redirects.len(),
))
.push(
pick_list(RedirectKind::ALL, Some(redirects[i].kind), move |v| {
Message::SelectedRedirectKind(i, v)
})
.class(style::PickList::Primary),
)
.push(histories.input(UndoSubject::RedirectSource(i)))
.push(button::choose_folder(BrowseSubject::RedirectSource(i), modifiers))
.push(histories.input(UndoSubject::RedirectTarget(i)))
.push(button::choose_folder(BrowseSubject::RedirectTarget(i), modifiers))
.push(button::remove(|x| Message::EditedRedirect(x, None), i)),
)
});

content.push(button::add(|x| Message::EditedRedirect(x, None)))
})
.class(style::Container::GameListEntry);

Container::new(inner)
Container::new(wrapper)
}

#[derive(Default)]
Expand Down
4 changes: 4 additions & 0 deletions src/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,10 @@ impl Translator {
}
}

pub fn reverse_redirects_when_restoring(&self) -> String {
translate("reverse-redirects-when-restoring")
}

pub fn game_label(&self) -> String {
translate("label-game")
}
Expand Down
6 changes: 6 additions & 0 deletions src/resource/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,7 @@ pub struct RestoreConfig {
pub toggled_paths: ToggledPaths,
pub toggled_registry: ToggledRegistry,
pub sort: Sort,
pub reverse_redirects: bool,
}

#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
Expand Down Expand Up @@ -1227,6 +1228,7 @@ impl Default for RestoreConfig {
toggled_paths: Default::default(),
toggled_registry: Default::default(),
sort: Default::default(),
reverse_redirects: false,
}
}
}
Expand Down Expand Up @@ -1964,6 +1966,7 @@ mod tests {
toggled_paths: Default::default(),
toggled_registry: Default::default(),
sort: Default::default(),
reverse_redirects: false,
},
scan: Default::default(),
apps: Apps {
Expand Down Expand Up @@ -2087,6 +2090,7 @@ mod tests {
toggled_paths: Default::default(),
toggled_registry: Default::default(),
sort: Default::default(),
reverse_redirects: false,
},
scan: Scan {
show_deselected_games: false,
Expand Down Expand Up @@ -2203,6 +2207,7 @@ restore:
sort:
key: status
reversed: false
reverseRedirects: false
scan:
showDeselectedGames: false
showUnchangedGames: false
Expand Down Expand Up @@ -2282,6 +2287,7 @@ customGames:
toggled_paths: Default::default(),
toggled_registry: Default::default(),
sort: Default::default(),
reverse_redirects: false,
},
scan: Scan {
show_deselected_games: false,
Expand Down
Loading

0 comments on commit 992abc5

Please sign in to comment.