def __init__(self, path: Path):
		self.steamdir = path
		try:
			with self.app_info_path.open('rb') as app_info_file:
				try:
					self.app_info = appinfo.load(app_info_file)
					self.app_info_available = True
				except ValueError:
					#This will be thrown by steamfiles.appinfo if the appinfo.vdf structure is different than expected, which apparently has happened in earlier versions of it, so I should probably be prepared for that
					self.app_info = None
					self.app_info_available = False
		except FileNotFoundError:
			self.app_info = None
			self.app_info_available = False
		try:
			with self.config_path.open('rt', encoding='utf-8') as config_file:
				self.config = acf.load(config_file)
				self.config_available = True
		except FileNotFoundError:
			self.config = None
			self.config_available = False
		try:
			with self.localization_path.open('rt', encoding='utf8') as localization_file:
				self.localization = acf.load(localization_file)
				self.localization_available = True
		except FileNotFoundError:
			self.localization = None
			self.localization_available = False
Exemple #2
0
def get_csgo_path(steamapps_folder):
    # Get every SteamLibrary folder
    with open(steamapps_folder + '\\libraryfolders.vdf') as infile:
        libraryfolders = acf.load(infile)
    folders = [steamapps_folder]
    i = 1
    while True:
        try:
            print('Found steamapps folder %s' %
                  (libraryfolders['LibraryFolders'][str(i)] + '\\steamapps'))
            folders.append(libraryfolders['LibraryFolders'][str(i)] +
                           '\\steamapps')
        except KeyError:
            break
        i = i + 1

    # Find the one CS:GO is in
    for folder in folders:
        try:
            print('Opening appmanifest %s...' %
                  (folder + '\\appmanifest_730.acf'))
            with open(folder + '\\appmanifest_730.acf') as infile:
                appmanifest = acf.load(infile)
            print('Valid installdir found: %s' %
                  (folder + "\\common\\" +
                   appmanifest["AppState"]["installdir"]))
            return folder + "\\common\\" + appmanifest["AppState"]["installdir"]
        except FileNotFoundError:
            continue

    print('CS:GO not found :/')
    def _check_steam_for_update(self, app_id: str, branch: str):
        manifest_file = get_server_path(
            ["steamapps", f"appmanifest_{app_id}.acf"])

        if not os.path.isfile(manifest_file):
            self.logger.debug("No local manifet")
            return True

        manifest = None
        with open(manifest_file, "r") as f:
            manifest = acf.load(f)

        stdout = self.run_command(
            (f"{self.config.steamcmd_path} +app_info_update 1 "
             f"+app_info_print {app_id} +quit"),
            redirect_output=True,
        )
        index = stdout.find(f'"{app_id}"')
        app_info = acf.loads(stdout[index:])

        try:
            current_buildid = app_info[app_id]["depots"]["branches"][branch][
                "buildid"]
        except KeyError:
            self.logger.debug("Failed to parse remote manifest")
            return True

        self.logger.debug(f"current: {manifest['AppState']['buildid']}")
        self.logger.debug(f"latest: {current_buildid}")
        return manifest["AppState"]["buildid"] != current_buildid
Exemple #4
0
def find_arma_workshop_dir():
    # Check if Arma is installed in the default library first
    # If not, we'll need to scan the other library folders for it
    if path.exists(path.join(steam_install_path, arma_manifest_suffix)):
        return path.join(steam_install_path, arma_workshop_suffix)

    # Make sure the libraryfolders.vdf manifest is in the usual Steam install location
    if not path.exists(path.join(steam_install_path, steam_library_manifest_suffix)):
        raise FileNotFoundError('Unable to find Steam library folder metadata')

    # Load libraryfolders.vdf and pull out each of the library folder paths from it
    # Library paths are indexed under numeric keys that are parsed as strings, so we'll just drop the two other keys
    # and iterate over the remaining ones which will be the library paths
    with open(path.join(steam_install_path, steam_library_manifest_suffix), 'r') as file:
        lib_path_data = acf.load(file)
    lib_folder_paths = []
    for key in lib_path_data['LibraryFolders'].keys() - ['TimeNextStatsReport', 'ContentStatsID']:
        lib_folder_paths.append(path.join(lib_path_data['LibraryFolders'][key]))

    # Look in each of the library paths for the Arma 3 app manifest file
    for lib_folder_path in lib_folder_paths:
        if path.exists(path.join(lib_folder_path, arma_manifest_suffix)):
            return path.join(lib_folder_path, arma_workshop_suffix)

    return None
def launch():
    if not request.args.get("launcher") or not request.args.get("game_identifier"):
        return "404"

    launcher = request.args.get("launcher")
    game_identifier = request.args.get("game_identifier")

    if request.args.get("launcher") == "STEAM":
        _acf = acf.load(open(settings.STEAM_LIBRARY_PATH + f"appmanifest_{request.args.get('game_identifier')}.acf"))
        _path = settings.STEAM_LIBRARY_GAMES_PATH + _acf["AppState"]["installdir"] + "/"
        
        for _file in os.listdir(_path):
            if _file[0] != "." and _file.split(".")[-1]: 
                path = _path + _file

        subprocess.Popen(["open", settings.STEAM_APPLICATION_PATH], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        time.sleep(30)
    elif request.args.get("launcher") == "ORIGIN":
        print(settings.ORIGIN_LIBRARY_PATH)
        print(game_identifier)
        path = settings.ORIGIN_LIBRARY_PATH + game_identifier + ".app"

    subprocess.Popen(["open", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()

    return "200"
Exemple #6
0
def test_load_dump(acf_data):
    with open(test_file_name, 'rt') as in_file:
        out_file = io.StringIO()
        loaded = acf.load(in_file)
        acf.dump(sort_dict(loaded), out_file)

    # Rewind to the beginning
    out_file.seek(0)
    assert out_file.read() == acf_data
Exemple #7
0
def get_steam_games():
    _steam_games = []

    for _file in os.listdir(settings.STEAM_LIBRARY_PATH):
        if _file[0] != "." and _file.split(".")[-1] == "acf":
            _acf = acf.load(open(settings.STEAM_LIBRARY_PATH + _file))
            _steam_games.append(
                (_acf["AppState"]["name"], _acf["AppState"]["appid"]))

    return _steam_games
	def iter_steam_library_folders(self) -> Iterator[Path]:
		with self.steam_library_list_path.open('rt', encoding='utf-8') as steam_library_list_file:
			steam_library_list = acf.load(steam_library_list_file)
			library_folders = steam_library_list.get('libraryfolders')
			if library_folders:
				#Should always happen unless the format of this file changes
				for k, v in library_folders.items():
					if k.isnumeric():
						yield Path(v['path'])
		yield self.steamdir
    def autolocateSpacehaven(self):
        self.gamePath = None
        self.jarPath = None
        self.modPath = None

        # Open previous location if known
        try:
            with open("previous_spacehaven_path.txt", 'r') as f:
                location = f.read()
                if os.path.exists(location):
                    self.locateSpacehaven(location)
                    return
        except FileNotFoundError:
            ui.log.log(
                "Unable to get last space haven location. Autolocating again.")

        # Steam based locator (Windows)
        try:
            registry_path = "SOFTWARE\\WOW6432Node\\Valve\\Steam" if (
                platform.architecture()[0]
                == "64bit") else "SOFTWARE\\Valve\\Steam"
            steam_path = winreg.QueryValueEx(
                winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path),
                "InstallPath")[0]
            library_folders = acf.load(open(steam_path +
                                            "\\steamapps\\libraryfolders.vdf"),
                                       wrapper=OrderedDict)
            locations = [
                steam_path + "\\steamapps\\common\\SpaceHaven\\spacehaven.exe"
            ]
            for key, value in library_folders["LibraryFolders"].items():
                if str.isnumeric(key):
                    locations.append(
                        value +
                        "\\steamapps\\common\\SpaceHaven\\spacehaven.exe")
            for location in locations:
                if os.path.exists(location):
                    self.locateSpacehaven(location)
                    return
        except FileNotFoundError:
            ui.log.log(
                "Unable to locate Steam registry keys, aborting Steam autolocator"
            )

        for location in POSSIBLE_SPACEHAVEN_LOCATIONS:
            try:
                location = os.path.abspath(location)
                if os.path.exists(location):
                    self.locateSpacehaven(location)
                    return
            except:
                pass
        ui.log.log(
            "Unable to autolocate installation. User will need to pick manually."
        )
Exemple #10
0
def test_load_dump_with_wrapper(acf_data):
    with open(test_file_name, 'rt') as in_file:
        out_file = io.StringIO()
        loaded = acf.load(in_file, wrapper=OrderedDict)
        acf.dump(loaded, out_file)

    # Rewind to the beginning
    out_file.seek(0)

    assert isinstance(loaded, OrderedDict)
    assert out_file.read() == acf_data
Exemple #11
0
def getSteamIDs(filePath=GAME_DIR_STEAM):  #'D:\\SteamLibrary\\steamapps\\'
    listOfFiles = os.listdir(filePath)
    pattern = "*.acf"
    for entry in listOfFiles:
        if fnmatch.fnmatch(entry, pattern):
            with open(filePath + entry) as acfFile:
                STEAM_DATA = acf.load(acfFile, wrapper=OrderedDict)
                gameName = nested_lookup('name', STEAM_DATA)
                gameAppID = nested_lookup('appid', STEAM_DATA)
                gameAdd(
                    gameName[0], gameAppID[0], GameStore.STEAM
                )  #gameName and gameAppID are lists with 1 element only
Exemple #12
0
    def get_installed_build_id(self):
        appmanifest_path = os.path.join(
            self.thrall.config.get_server_root(),
            'steamapps/appmanifest_%s.acf' % settings.CONAN_SERVER_APP_ID)

        if not os.path.exists(appmanifest_path):
            return self.NO_INSTALLED_VERSION

        with open(appmanifest_path, 'r') as f:
            data = acf.load(f)

        return data['AppState']['buildid']
Exemple #13
0
    def get_manifest_updated_times(self):
        if not os.path.exists(self.steamcmd_mod_manifest_path):
            self.logger.debug('Doesnt exist: %s' % self.steamcmd_mod_manifest_path)
            return {}

        with open(self.steamcmd_mod_manifest_path, 'r') as f:
            data = acf.load(f)

        mods_updated = {}
        for workshop_id in data['AppWorkshop']['WorkshopItemsInstalled']:
            mod = data['AppWorkshop']['WorkshopItemsInstalled'][workshop_id]
            mods_updated[workshop_id] = int(mod['timeupdated'])
        return mods_updated
Exemple #14
0
def iter_steam_installed_appids() -> Iterator[tuple[Path, int, Mapping[str, Any]]]:
	for library_folder in steam_installation.iter_steam_library_folders():
		for acf_file_path in library_folder.joinpath('steamapps').glob('*.acf'):
			#Technically I could try and parse it without steamfiles, but that would be irresponsible, so I shouldn't do that
			with acf_file_path.open('rt', encoding='utf-8') as acf_file:
				app_manifest = acf.load(acf_file)
			app_state = app_manifest.get('AppState')
			if not app_state:
				#Should only happen if .acf is junk (or format changes dramatically), there's no other keys than AppState
				if main_config.debug:
					print('This should not happen', acf_file_path, 'is invalid or format is weird and new and spooky, has no AppState')
				continue

			appid_str = app_state.get('appid')
			if appid_str is None:
				#Yeah we need that
				if main_config.debug:
					print(acf_file_path, app_state.get('name'), 'has no appid which is weird')
				continue

			try:
				appid = int(appid_str)
			except ValueError:
				if main_config.debug:
					print('Skipping', acf_file_path, app_state.get('name'), appid_str, 'as appid is not numeric which is weird')
				continue

			try:
				state_flags = StateFlags(int(app_state.get('StateFlags')))
				if not state_flags:
					continue
			except ValueError:
				if main_config.debug:
					print('Skipping', app_state.get('name'), appid, 'as StateFlags are invalid', app_state.get('StateFlags'))
				continue

			#Is StageFlags.AppRunning actually not what it means? Seems that an app that is running doesn't have its StateFlags changed and 64 is instead used for full versions installed where demos are the only version owned, etc
			#Anyway, we're going to check for it this way
			last_owner = app_state.get('LastOwner')
			if last_owner == '0':
				if main_config.debug:
					print('Skipping', app_state.get('name'), appid, 'as nobody actually owns it')
				continue

			#Only yield fully installed games
			if (state_flags & StateFlags.FullyInstalled) == 0:
				if main_config.debug:
					print('Skipping', app_state.get('name'), appid, 'as it is not actually installed (StateFlags =', state_flags, ')')
				continue

			yield library_folder, appid, app_state
Exemple #15
0
 def __init__(self, steam_path=""):
     if os.name != "posix":
         try:
             key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
                                  "SOFTWARE\\Valve\\Steams")
         except FileNotFoundError:
             try:
                 key = winreg.OpenKey(
                     winreg.HKEY_LOCAL_MACHINE,
                     "SOFTWARE\\Wow6432Node\\Valve\\Steam")
             except FileNotFoundError as e:
                 print("gSynch can't find your Steam installation")
                 raise e
         self.steam_path = winreg.QueryValueEx(key, "InstallPath")[0]
     else:
         whereis = subprocess.run(['whereis', 'steam'],
                                  stdout=subprocess.PIPE)
         self.steam_path = whereis.stdout.decode('utf-8')[
             7:]  # we get rid of "steam: " (which is 7 char long)
     self.games_path = [self.steam_path + "\\steamapps"]
     # Check if there is other library directories (see "libraryfolders.vdf analysis.txt")
     try:
         with open(self.steam_path + "\\libraryfolders.vdf") as foldersfile:
             data = acf.load(foldersfile)
     except OSError as e:
         print(
             "We can't figure out where the f**k is your libraryfolders.vdf file !"
             f"Full error :\n {e}")
         sys.exit(-1)  # this is a fatal error
     except TypeError or ValueError as e:
         print(
             f"An error occurred while trying to parse {self.steam_path}\\libraryfolders.vdf. Please open an Issue "
             f"at https://github.com/Gabyfle/gSynch with your libraryfolders.vdf file !"
         )
         sys.exit(-1)  # this is a fatal error
     finally:
         if foldersfile is not None:
             foldersfile.close()
     # Now check if the libraryfolders.vdf file is correct, and if he follow "libraryfolders.vdf analysis.txt" (if
     # not, abort)
     if not data["LibraryFolders"]:
         print(
             f"An error occurred while trying to use data from your libraryfolders.vdf file. Please report this "
             f"to https://github.com/Gabyfle/gSynch in the Issue section, attaching your libraryfolders.vdf file."
         )
         sys.exit(-1)  # this is a fatal error
     else:
         for key in data["LibraryFolders"]:
             if key.isdigit(
             ):  # if the key is a digit, it's a path to a library folder
                 self.games_path.append(data["LibraryFolders"][key])
    def _load_games(self):
        acf_path = "{}/steamapps".format(self.config["path"])
        files = os.listdir(acf_path)

        acf_files = list(filter(lambda f: f.split(".")[-1] == "acf", files))

        for f in acf_files:
            with open("{}/{}".format(acf_path, f), "r") as file:
                fcontent = acf.load(file)
                self.games_dict.update({fcontent["AppState"]["name"]: Game(name=fcontent["AppState"]["name"],
                                                                           appid=fcontent["AppState"]["appid"],
                                                                           img_src=self.config["img_src"]
                                                                           .format(fcontent["AppState"]["appid"]),
                                                                           extra_info=fcontent["AppState"])
                                        })
Exemple #17
0
    def get_install_path(self, app_id):
        """
        Get the installation path of the application "app_id"
        Args:
            app_id: Game's steam id

        Returns:
            str: path to the game
        """
        data = dict  # contains parsed data from appmanifest_id.acf files

        for path in self.games_path:
            for file in os.listdir(path):
                if os.path.isfile(
                    (os.path.join(path, file)
                     )):  # make sure this is a file and not a directory
                    if app_id in file:  # this file contains the application id of the requested app
                        if os.path.splitext(
                                file
                        )[1] == ".acf":  # make sure this is a manifest file
                            try:
                                with open(os.path.join(path,
                                                       file)) as acf_file:
                                    data = acf.load(acf_file)
                            except OSError as e:
                                print(
                                    f"A problem occurred while trying to open {os.path.join(path, file)}. Make sure "
                                    f"gSynch has the permission to read this path!"
                                )
                                sys.exit(-1)  # this is a fatal error
                            except TypeError or ValueError as e:
                                print(
                                    f"An error occurred while trying to parse {os.path.join(path, file)}. Please open "
                                    f"an Issue "
                                    f"at https://github.com/Gabyfle/gSynch with {os.path.join(path, file)} attached!"
                                )
                                sys.exit(-1)  # this is a fatal error
                            finally:
                                if acf_file is not None:
                                    acf_file.close()
        if data is not None and data["installDir"] is not None:
            directory = data["installDir"]
        else:
            raise NotFound(
                f"Sorry, but gSynch can't find the app : {app_id}... Are you sure it's installed ?"
            )
        return directory
def getData(f, m):
    arquivo = open(f, m)
    return acf.load(arquivo)
    def workshop_download(
        self,
        allow_run: bool,
        force: bool,
        stop: bool,
        restart: bool,
        *args,
        **kwargs,
    ) -> int:
        """ downloads Steam workshop items """

        was_running = False
        if not force:
            needs_update = self._check_steam_for_update(
                str(self.config.workshop_id), "public")
            if not needs_update:
                self.logger.success(
                    f"{self.config.workshop_id} is already on latest version")
                self._start_servers(restart, was_running)
                return STATUS_SUCCESS

        if not allow_run:
            was_running = self.is_running(check_all=True)
            if was_running:
                if not (restart or stop):
                    self.logger.warning(
                        f"at least once instance of {self.config.app_id} "
                        "is still running")
                    return STATUS_PARTIAL_FAIL
                self._stop_servers(was_running,
                                   reason="Updates found for workshop app")

        status = self.invoke(
            self.install,
            app_id=self.config.workshop_id,
            allow_run=True,
            force=force,
        )

        if not status == STATUS_SUCCESS:
            return status

        if len(self.config.workshop_items) == 0:
            self.logger.warning("\nno workshop items selected for install")
            return STATUS_PARTIAL_FAIL

        mods_to_update = []
        manifest_file = get_server_path([
            "steamapps",
            "workshop",
            f"appworkshop_{self.config.workshop_id}.acf",
        ], )

        if not force and os.path.isfile(manifest_file):
            manifest = None
            with open(manifest_file, "r") as f:
                manifest = acf.load(f)

            self.logger.info("checking for updates for workshop items...")
            with click.progressbar(self.config.workshop_items) as bar:
                for workshop_item in bar:
                    workshop_item = str(workshop_item)
                    if (workshop_item not in manifest["AppWorkshop"]
                        ["WorkshopItemsInstalled"]):
                        mods_to_update.append(workshop_item)
                        continue

                    last_update_time = int(
                        manifest["AppWorkshop"]["WorkshopItemsInstalled"]
                        [workshop_item]["timeupdated"])

                    try:
                        latest_metadata = self._get_published_file(
                            workshop_item)
                    except requests.HTTPError:
                        self.logger.error(
                            "\ncould not query Steam for updates")
                        return STATUS_FAILED

                    newest_update_time = int(
                        latest_metadata["response"]["publishedfiledetails"][0]
                        ["time_updated"])

                    if last_update_time < newest_update_time:
                        mods_to_update.append(workshop_item)
        else:
            mods_to_update = self.config.workshop_items

        if len(mods_to_update) == 0:
            self.logger.success("all workshop items already up to date")
            self._start_servers(restart, was_running)
            return STATUS_SUCCESS

        self.logger.info("downloading workshop items...")
        with click.progressbar(mods_to_update) as bar:
            for workshop_item in bar:
                try:
                    self.run_command(
                        (f"{self.config.steamcmd_path} "
                         f"{self._steam_login()} +force_install_dir "
                         f"{self.config.server_path} "
                         "+workshop_download_item "
                         f"{self.config.workshop_id} {workshop_item} +quit"))
                except CalledProcessError:
                    self.logger.error("\nfailed to validate workshop items")
                    return STATUS_FAILED

        self.logger.success("\nvalidated workshop items")
        self._start_servers(restart, was_running)
        return STATUS_SUCCESS
    def workshop_download(
        self,
        allow_run: bool,
        force: bool,
        stop: bool,
        restart: bool,
        *args,
        **kwargs,
    ) -> int:
        """ downloads and installs ARK mods """

        status = self.invoke(
            super().workshop_download,
            allow_run=True,
            force=force,
            stop=stop,
            restart=False,
        )

        self.logger.debug("super status: {}".format(status))

        if status == STATUS_SUCCESS:
            mod_path = get_server_path(["ShooterGame", "Content", "Mods"])
            base_src_dir = get_server_path([
                "steamapps",
                "workshop",
                "content",
                str(self.config.workshop_id),
            ])

            mods_to_update = []
            manifest_file = get_server_path([
                "steamapps",
                "workshop",
                f"appworkshop_{self.config.workshop_id}.acf",
            ])

            if not force and os.path.isfile(manifest_file):
                manifest = None
                with open(manifest_file, "r") as f:
                    manifest = acf.load(f)

                    for workshop_item in self.config.workshop_items:
                        workshop_item = str(workshop_item)
                        mod_dir = os.path.join(mod_path, str(workshop_item))
                        mod_file = os.path.join(mod_path,
                                                "{}.mod".format(workshop_item))

                        if not os.path.isfile(mod_file) or (
                                workshop_item not in manifest["AppWorkshop"]
                            ["WorkshopItemsInstalled"]):
                            mods_to_update.append(workshop_item)
                            continue

                        last_update_time = int(
                            manifest["AppWorkshop"]["WorkshopItemsInstalled"]
                            [workshop_item]["timeupdated"])
                        last_extract_time = os.path.getctime(mod_file)

                        if last_update_time > last_extract_time:
                            mods_to_update.append(workshop_item)
            else:
                mods_to_update = self.config.workshop_items

            mods_to_update = self.str_mods(mods_to_update)
            if len(mods_to_update) == 0:
                was_running = self.is_running("@any")
                # automatically check for any servers shutdown by install
                if not was_running:
                    self._start_servers(restart, was_running)
                return STATUS_SUCCESS

            self.logger.info(
                f"{len(mods_to_update)} mod(s) need to be extracted: "
                f"{','.join(mods_to_update)}")

            was_running = self.is_running("@any")
            if was_running:
                if not (restart or stop):
                    self.logger.warning(
                        (f"at least once instance of {self.config.app_id}"
                         " is still running"))
                    return STATUS_PARTIAL_FAIL
                self._stop_servers(
                    was_running,
                    reason=(f"Updates found for {len(mods_to_update)} "
                            f"mod(s): {','.join(mods_to_update)}"),
                )

            self.logger.info("extracting mods...")
            with click.progressbar(mods_to_update) as bar:
                for workshop_item in bar:
                    src_dir = os.path.join(base_src_dir, str(workshop_item))
                    branch_dir = os.path.join(
                        src_dir,
                        "{}NoEditor".format(self.config.workshop_branch),
                    )
                    mod_dir = os.path.join(mod_path, str(workshop_item))
                    mod_file = os.path.join(mod_path,
                                            "{}.mod".format(workshop_item))

                    if not os.path.isdir(src_dir):
                        self.logger.error(
                            "could not find workshop item: {}".format(
                                self.config.workshop_id))
                        return STATUS_FAILED
                    elif os.path.isdir(branch_dir):
                        src_dir = branch_dir

                    if os.path.isdir(mod_dir):
                        self.logger.debug(
                            "removing old mod_dir of {}...".format(
                                workshop_item))
                        shutil.rmtree(mod_dir)
                    if os.path.isfile(mod_file):
                        self.logger.debug(
                            "removing old mod_file of {}...".format(
                                workshop_item))
                        os.remove(mod_file)

                    self.logger.debug("copying {}...".format(workshop_item))
                    shutil.copytree(src_dir, mod_dir)

                    if not self._create_mod_file(mod_dir, mod_file,
                                                 workshop_item):
                        self.logger.error(
                            "could not create .mod file for {}".format(
                                workshop_item))
                        return STATUS_FAILED

                    if not self._extract_files(mod_dir):
                        return STATUS_FAILED
            self.logger.success("workshop items successfully installed")

        if status == STATUS_SUCCESS:
            self._start_servers(restart, was_running)
        return status