diff --git a/src/main.py b/src/main.py index e7c7553..bc8f0e6 100644 --- a/src/main.py +++ b/src/main.py @@ -49,6 +49,7 @@ from src.store.managers.online_cover_manager import OnlineCoverManager from src.store.managers.sgdb_manager import SGDBManager from src.store.managers.steam_api_manager import SteamAPIManager from src.store.store import Store +from src.utils.migrate_files_v1_to_v2 import migrate_files_v1_to_v2 from src.window import CartridgesWindow @@ -281,5 +282,6 @@ def main(_version): """App entry point""" setup_logging() log_system_info() + migrate_files_v1_to_v2() app = CartridgesApplication() return app.run(sys.argv) diff --git a/src/utils/migrate_files_v1_to_v2.py b/src/utils/migrate_files_v1_to_v2.py new file mode 100644 index 0000000..ae68741 --- /dev/null +++ b/src/utils/migrate_files_v1_to_v2.py @@ -0,0 +1,128 @@ +# migrate_files_v1_to_v2.py +# +# Copyright 2023 Geoffrey Coulaud +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# SPDX-License-Identifier: GPL-3.0-or-later + +import json +import logging +from pathlib import Path + +from src import shared + +old_data_dir = Path.home() / ".local" / "share" +old_cartridges_data_dir = old_data_dir / "cartridges" +migrated_file_path = old_cartridges_data_dir / ".migrated" +old_games_dir = old_cartridges_data_dir / "games" +old_covers_dir = old_cartridges_data_dir / "covers" + + +def migrate_game_covers(game_path: Path): + """Migrate a game covers from a source game path to the current dir""" + for suffix in (".tiff", ".gif"): + cover_path = old_covers_dir / game_path.with_suffix(suffix).name + if not cover_path.is_file(): + continue + destination_cover_path = shared.covers_dir / cover_path.name + logging.info("Moving %s -> %s", str(cover_path), str(destination_cover_path)) + cover_path.rename(destination_cover_path) + + +def migrate_files_v1_to_v2(): + """ + Migrate user data from the v1.X locations to the latest location. + + Fix for commit 4a204442b5d8ba2e918f8c2605d72e483bf35efd + where the windows directories for data, config and cache changed. + """ + + # Skip if there is no old dir + # Skip if old == current + # Skip if already migrated + if ( + not old_data_dir.is_dir() + or str(old_data_dir) == str(shared.data_dir) + or migrated_file_path.is_file() + ): + return + + logging.info("Migrating data dir %s", str(old_data_dir)) + + # Create the current data dir if needed + if not shared.data_dir.is_dir(): + shared.data_dir.mkdir(parents=True) + + old_game_paths = set(old_games_dir.glob("*.json")) + old_imported_game_paths = set( + filter(lambda path: path.name.startswith("imported_"), old_game_paths) + ) + old_other_game_paths = old_game_paths - old_imported_game_paths + + # Discover current imported games + imported_game_number = 0 + imported_execs = set() + for game_path in shared.games_dir.glob("imported_*.json"): + try: + game_data = json.load(game_path.open("r")) + except (OSError, json.JSONDecodeError): + continue + number = int(game_data["game_id"].replace("imported_", "")) + imported_game_number = max(number, imported_game_number) + imported_execs.add(game_data["executable"]) + + # Migrate imported game files + for game_path in old_imported_game_paths: + try: + game_data = json.load(game_path.open("r")) + except (OSError, json.JSONDecodeError): + continue + + # Don't migrate if there's a game with the same exec + if game_data["executable"] in imported_execs: + continue + + # Migrate with updated index + imported_game_number += 1 + game_id = f"imported_{imported_game_number}" + game_data["game_id"] = game_id + destination_game_path = shared.games_dir / f"{game_id}.json" + logging.info( + "Moving (updated id) %s -> %s", str(game_path), str(destination_game_path) + ) + json.dump( + game_data, + destination_game_path.open("w"), + indent=4, + sort_keys=True, + ) + game_path.unlink() + migrate_game_covers(game_path) + + # Migrate all other games + for game_path in old_other_game_paths: + # Do nothing if already in games dir + destination_game_path = shared.games_dir / game_path.name + if destination_game_path.exists(): + continue + + # Else, migrate the game + logging.info("Moving %s -> %s", str(game_path), str(destination_game_path)) + game_path.rename(destination_game_path) + migrate_game_covers(game_path) + + # Signal that this dir is migrated + migrated_file_path.touch() + logging.info("Migration done")