Example #1
0
def find_steam_root(opts, acf_filename = None):
	if acf_filename is not None:
		# If this library has a depotcache, assume it is also the steam root
		# XXX: This could be tricked if someone has created or copied a
		# depotcache folder into the library, or if several steam
		# installations are sharing libraries. In these cases the user
		# will just have to specify --steam-root= to override it.
		library_root = find_library_root(acf_filename)
		if os.path.isdir(os.path.join(library_root, 'depotcache')):
			return library_root
	path = None
	if sys.platform.startswith('linux'):
		path = os.path.expanduser('~/.steam/root')
	elif sys.platform == 'cygwin':
		try:
			import cygwinreg
		except ImportError:
			if opts.verbose:
				ui._print('python-cygwinreg not installed, searching common Steam paths...')
		else:
			if not hasattr(cygwinreg, 'KEY_WOW64_32KEY'):
				cygwinreg.KEY_WOW64_32KEY = 512
			path = cygwin_path(find_steam_path_from_registry(opts, cygwinreg))
		path = path or guess_steam_path_win(opts, cygwin_path)
	elif sys.platform == 'win32':
		import _winreg
		path = find_steam_path_from_registry(opts, _winreg)
		path = path or guess_steam_path_win(opts)
	if path:
		return path
	ui._cprint('red', 'Unable to find Steam root - rerun with --steam-root=')
	sys.exit(1)
Example #2
0
def find_steam_root(opts, acf_filename=None):
    if acf_filename is not None:
        # If this library has a depotcache, assume it is also the steam root
        # XXX: This could be tricked if someone has created or copied a
        # depotcache folder into the library, or if several steam
        # installations are sharing libraries. In these cases the user
        # will just have to specify --steam-root= to override it.
        library_root = find_library_root(acf_filename)
        if os.path.isdir(os.path.join(library_root, 'depotcache')):
            return library_root
    path = None
    if sys.platform.startswith('linux'):
        path = os.path.expanduser('~/.steam/root')
    elif sys.platform == 'cygwin':
        try:
            import cygwinreg
        except ImportError:
            if opts.verbose:
                ui._print(
                    'python-cygwinreg not installed, searching common Steam paths...'
                )
        else:
            if not hasattr(cygwinreg, 'KEY_WOW64_32KEY'):
                cygwinreg.KEY_WOW64_32KEY = 512
            path = cygwin_path(find_steam_path_from_registry(opts, cygwinreg))
        path = path or guess_steam_path_win(opts, cygwin_path)
    elif sys.platform == 'win32':
        import _winreg
        path = find_steam_path_from_registry(opts, _winreg)
        path = path or guess_steam_path_win(opts)
    if path:
        return path
    ui._cprint('red', 'Unable to find Steam root - rerun with --steam-root=')
    sys.exit(1)
Example #3
0
def verify_manifest_files_exist(manifest_path, game_path, indent, opts):
	def verify_hash():
		if (opts.verify or opts.delete_bad) and not verify_file_hash(filename, depot_hash, indent+g_indent, opts):
			ui._cprint('red', ' (BAD CHECKSUM)', end='')
			return True
	def check_filesize():
		if depot_hash.filetype == 'directory':
			return True
		return filesize == depot_hash.filesize
	def warn_filesize():
		if not check_filesize():
			ui._cprint('red', ' (Filesize != %i, %+i)' % \
					(depot_hash.filesize, filesize - depot_hash.filesize))
			return True

	ok = True
	filenames = FilenameSet()
	for (orig_filename, depot_hash) in depotcache.decode_depotcache(manifest_path):
		filename = os.path.join(game_path, orig_filename.replace('\\', os.path.sep))
		(found, correct, filename, pretty) = insensitive_path(filename, opts)
		filenames.add(filename)

		if opts.file_filter is not None and orig_filename not in opts.file_filter:
			continue

		if found:
			filesize = os.stat(filename).st_size

		corrupt = False

		if not correct:
			ui._print(indent, end='')
			ui._print(pretty, end='')
			if found:
				corrupt = warn_filesize()
				sys.stdout.flush()
				corrupt = corrupt or verify_hash()
				if corrupt and opts.delete_bad:
					ui._cprint('red', ' (DELETED)')
					os.remove(filename)
				else:
					ui._print(' (CASE MISMATCH, ', end='')
					if not opts.rename:
						ui._print('rerun with -r to fix)')
					else:
						ui._print('renamed)')
			else:
				ok = False
				ui._print(' (FILE MISSING)')
		elif opts.verbose > 2 or opts.verify or opts.delete_bad or not check_filesize():
			ui._print(indent + filename, end='')
			corrupt = warn_filesize()
			sys.stdout.flush()
			corrupt = corrupt or verify_hash()
			if corrupt and opts.delete_bad:
				ui._cprint('red', ' (DELETED)', end='')
				os.remove(filename)
			ui._print()
	return (ok, filenames)
Example #4
0
def guess_steam_path_win(opts, translate=lambda x: x):
    for path in [
            r'c:\program files (x86)\steam', r'c:\program files\steam',
            r'c:\steam'
    ]:
        if opts.verbose:
            ui._print("Searching '%s'..." % translate(path))
        if os.path.isdir(translate(path)):
            return path
    ui._cprint('red', 'Unable to find Steam root - rerun with --steam-root=')
    sys.exit(1)
Example #5
0
def guess_steam_path_win(opts, translate = lambda x: x):
	for path in [
			r'c:\program files (x86)\steam',
			r'c:\program files\steam',
			r'c:\steam'
			]:
		if opts.verbose:
			ui._print("Searching '%s'..." % translate(path))
		if os.path.isdir(translate(path)):
			return path
	ui._cprint('red', 'Unable to find Steam root - rerun with --steam-root=')
	sys.exit(1)
Example #6
0
def check_depots_exist(mounted_depots, managed_depots, library_root, indent, opts):
	ok = True
	num_mounted = 0
	for depot in managed_depots:
		if depot in mounted_depots:
			num_mounted += 1
			manifest = manifest_filename(depot, mounted_depots[depot])
			path = manifest_path(library_root, manifest)
			if not os.path.exists(path):
				ui._cprint('red', '%s%s NOT FOUND!' % (indent, manifest), end='')
				ui._print(' (Verify the game cache and try again)')
				ok = False
		elif opts.verbose > 1:
			ui._print('%s%s (not mounted)' % (indent, depot))
	assert(num_mounted == len(mounted_depots))

	return ok
Example #7
0
def check_depots_exist(mounted_depots, managed_depots, library_root, indent, opts):
	ok = True
	num_mounted = 0
	for depot in managed_depots:
		if depot in mounted_depots:
			num_mounted += 1
			manifest = manifest_filename(depot, mounted_depots[depot])
			path = manifest_path(library_root, manifest)
			if not os.path.exists(path):
				ui._cprint('red', '%s%s NOT FOUND!' % (indent, manifest), end='')
				ui._print(' (Verify the game cache and try again)')
				ok = False
		elif opts.verbose > 1:
			ui._print('%s%s (not mounted)' % (indent, depot))
	assert(num_mounted == len(mounted_depots))

	return ok
Example #8
0
def find_extra_files(game_path, known_filenames, indent, opts):
    known_filenames_l = set(map(str.lower, known_filenames))
    if opts.move:
        dest_root = os.path.realpath(os.path.join(game_path, '..'))
        dest_root = os.path.join(dest_root,
                                 os.path.basename(game_path) + '~EXTRANEOUS')
    for (root, dirs, files) in os.walk(game_path,
                                       topdown=not (opts.delete or opts.move)):
        for fname in dirs + files:
            path = os.path.join(root, fname)
            if path in known_filenames:
                continue
            ui._print(indent, end='')
            extra = '\n'
            if opts.move:
                if fname in dirs:
                    try:
                        os.rmdir(path)
                        extra = ' (REMOVED)\n'
                    except OSError as e:
                        extra = ' %s\n' % str(e)
                else:
                    dest = os.path.join(dest_root,
                                        os.path.relpath(path, game_path))
                    try:
                        mkdir_recursive(os.path.dirname(dest))
                        os.rename(path, dest)
                        extra = '\n%s  --> %s\n' % (indent,
                                                    os.path.relpath(dest))
                    except OSError as e:
                        extra = ' %s\n' % str(e)
            elif opts.delete:
                extra = ' (DELETED)\n'
                if fname in dirs:
                    os.rmdir(path)
                else:
                    os.remove(path)
            if path.lower() in known_filenames_l:
                ui._cprint('back_blue yellow',
                           path,
                           end=' (DUPLICATE WITH DIFFERING CASE)%s' % extra)
            else:
                ui._cprint('back_blue yellow', path, end=extra)
Example #9
0
def find_game_path(app_state, library_root, acf_filename, opts):
	# XXX TODO: acf games can be installed in other libraries, I need to
	# try it to find if that would change this logic.
	#
	# NOTE: There is also a UserConfig.appinstalldir, however it may be
	# unreliable if the acf has been copied from another location and the
	# game has not yet been launched.
	install_dir = app_state['installdir']
	if install_dir == '':
		ui._cprint('yellow', g_indent + 'WARNING: Blank installdir in %s, trying UserConfig.appinstalldir...' % acf_filename)
		# FIXME: This may be in the Windows format which will probably break this!
		install_dir = os.path.basename(app_state['UserConfig']['appinstalldir'])

	(found, game_path, pretty) = insensitive_path(os.path.join(library_root, 'SteamApps/common/%s' %
		install_dir), opts)
	if found:
		# TODO: Warn if a second directory exists with the same name
		# but differing case, since that may confuse Steam or the game
		pass
	else:
		ui._print(g_indent, end='')
		ui._cprint(colours[False], 'Missing game directory', end=': ')
		ui._print(pretty)
		return None
	if pretty is not None:
		ui._print(g_indent, end='')
		ui._cprint('back_yellow black', 'WARNING: Case Mismatch', end='')
		if not opts.rename:
			ui._print(' (rerun with -r to fix)', end='')
		ui._print(': ', end='')
		ui._print(pretty)
	return game_path
Example #10
0
def find_extra_files(game_path, known_filenames, indent, opts):
	known_filenames_l = set(map(str.lower, known_filenames))
	if opts.move:
		dest_root = os.path.realpath(os.path.join(game_path, '..'))
		dest_root = os.path.join(dest_root, os.path.basename(game_path) + '~EXTRANEOUS')
	for (root, dirs, files) in os.walk(game_path, topdown = not (opts.delete or opts.move)):
		for fname in dirs + files:
			path = os.path.join(root, fname)
			if path in known_filenames:
				continue
			ui._print(indent, end='')
			extra='\n'
			if opts.move:
				if fname in dirs:
					try:
						os.rmdir(path)
						extra = ' (REMOVED)\n'
					except OSError as e:
						extra = ' %s\n' % str(e)
				else:
					dest = os.path.join(dest_root, os.path.relpath(path, game_path))
					try:
						mkdir_recursive(os.path.dirname(dest))
						os.rename(path, dest)
						extra = '\n%s  --> %s\n' % (indent, os.path.relpath(dest))
					except OSError as e:
						extra = ' %s\n' % str(e)
			elif opts.delete:
				extra = ' (DELETED)\n'
				if fname in dirs:
					os.rmdir(path)
				else:
					os.remove(path)
			if path.lower() in known_filenames_l:
				ui._cprint('back_blue yellow', path, end=' (DUPLICATE WITH DIFFERING CASE)%s' % extra)
			else:
				ui._cprint('back_blue yellow', path, end=extra)
Example #11
0
def check_acf(acf_filename, opts):
    app_state = acf.parse_acf(acf_filename)['AppState']

    if 'appID' in app_state:
        app_id = app_state['appID']
    else:
        app_id = app_state['appid']
    name = app_state['UserConfig']['name']
    ui._print('%s (%s):' % (name, app_id))

    library_root = find_library_root(acf_filename)

    game_path = find_game_path(app_state, library_root, acf_filename, opts)
    if game_path is None: return

    mounted_depots = get_mounted_depots(app_state)
    try:
        managed_depots = app_state['ManagedDepots'].split(',')
    except KeyError:
        #ui._cprint('back_yellow black', 'WARNING: No ManagedDepots, using MountedDepots instead!')
        managed_depots = UnknownLen(mounted_depots.keys())

    ok = depot_summary_ok(mounted_depots)
    colour = colours[ok]
    if opts.verbose or not ok:
        ui._print(g_indent, end='')
        ui._cprint(colour, str_depot_summary(mounted_depots, managed_depots))
    if not ok:
        if opts.uninstall:
            ui._print(g_indent, end='')
            path = os.path.join(os.path.curdir, acf_filename)
            os.rename(path, path + '~')
            ui._cprint('back_yellow black', 'UNINSTALLED!')
        return

    ok = check_depots_exist(mounted_depots, managed_depots, opts.steam_root,
                            g_indent * 2, opts)
    if not ok: return

    (ok, filenames) = check_all_depot_files_exist(mounted_depots,
                                                  opts.steam_root, game_path,
                                                  g_indent * 2, opts)
    if opts.extra or opts.delete or opts.move:
        if opts.verbose:  # So they don't appear to be under a manifest heading
            ui._print(g_indent * 2 + 'Untracked files:')
        find_extra_files(game_path, filenames, g_indent * 3, opts)
    if not ok: return

    ui._cprint('green', 'OK')
Example #12
0
def check_acf(acf_filename, opts):
	app_state = acf.parse_acf(acf_filename)['AppState']

	if 'appID' in app_state:
		app_id = app_state['appID']
	else:
		app_id = app_state['appid']
        try:
            name = app_state['UserConfig']['name']
        except:
            name = app_state['name']
	ui._print('%s (%s):' % (name, app_id))

	library_root = find_library_root(acf_filename)

	game_path = find_game_path(app_state, library_root, acf_filename, opts)
	if game_path is None: return

	mounted_depots = get_mounted_depots(app_state)
	try:
		managed_depots = app_state['ManagedDepots'].split(',')
	except KeyError:
		#ui._cprint('back_yellow black', 'WARNING: No ManagedDepots, using MountedDepots instead!')
		managed_depots = UnknownLen(mounted_depots.keys())

	ok = depot_summary_ok(mounted_depots)
	colour = colours[ok]
	if opts.verbose or not ok:
		ui._print(g_indent, end='')
		ui._cprint(colour, str_depot_summary(mounted_depots, managed_depots))
	if not ok:
		if opts.uninstall:
			ui._print(g_indent, end='')
			path = os.path.join(os.path.curdir, acf_filename)
			os.rename(path, path + '~')
			ui._cprint('back_yellow black', 'UNINSTALLED!')
		return

	ok = check_depots_exist(mounted_depots, managed_depots, opts.steam_root, g_indent*2, opts)
	if not ok: return

	(ok, filenames) = check_all_depot_files_exist(mounted_depots, opts.steam_root, game_path, g_indent*2, opts)
	if opts.extra or opts.delete or opts.move:
		if opts.verbose: # So they don't appear to be under a manifest heading
			ui._print(g_indent*2 + 'Untracked files:')
		find_extra_files(game_path, filenames, g_indent*3, opts)
	if not ok: return

	ui._cprint('green', 'OK')
Example #13
0
def verify_file_hash(filename, depot_hash, indent, opts):
	if depot_hash.filetype == 'directory':
		return os.path.isdir(filename)

	s = hashlib.sha1()
	f = open(filename, 'rb')

	bad_found = False

	off = 0
	for chunk in sorted(depot_hash):
		assert(chunk.off == off)
		buf = f.read(chunk.len)
		off += chunk.len
		s.update(buf)

		sha = hashlib.sha1(buf).hexdigest()
		if sha != chunk.sha:
			if opts.verify == 1:
				return False

			if not bad_found:
				ui._cprint('red', ' (BAD CHECKSUM)')
				bad_found = True
			ui._print(indent, end='')
			ui._cprint('red', '%.10i:%.10i found %s expected %s' % \
					(chunk.off, chunk.off+chunk.len, sha, chunk.sha))

	assert(off == depot_hash.filesize)

	if bad_found:
		ui._print(indent, end='')

	eof_garbage = False
	while True:
		buf = f.read(1024*1024)
		if buf == '':
			break
		if not eof_garbage:
			ui._cprint('red', ' (Garbage found at end of file!)', end='')
			eof_garbage = True
		s.update(buf)

	if bad_found:
		return False
	return s.hexdigest() == depot_hash.sha
Example #14
0
def verify_file_hash(filename, depot_hash, indent, opts):
    if depot_hash.filetype == 'directory':
        return os.path.isdir(filename)

    s = hashlib.sha1()
    f = open(filename, 'rb')

    bad_found = False

    off = 0
    for chunk in sorted(depot_hash):
        assert (chunk.off == off)
        buf = f.read(chunk.len)
        off += chunk.len
        s.update(buf)

        sha = hashlib.sha1(buf).hexdigest()
        if sha != chunk.sha:
            if opts.verify == 1:
                return False

            if not bad_found:
                ui._cprint('red', ' (BAD CHECKSUM)')
                bad_found = True
            ui._print(indent, end='')
            ui._cprint('red', '%.10i:%.10i found %s expected %s' % \
              (chunk.off, chunk.off+chunk.len, sha, chunk.sha))

    assert (off == depot_hash.filesize)

    if bad_found:
        ui._print(indent, end='')

    eof_garbage = False
    while True:
        buf = f.read(1024 * 1024)
        if buf == '':
            break
        if not eof_garbage:
            ui._cprint('red', ' (Garbage found at end of file!)', end='')
            eof_garbage = True
        s.update(buf)

    if bad_found:
        return False
    return s.hexdigest() == depot_hash.sha
Example #15
0
def find_game_path(app_state, library_root, acf_filename, opts):
    # XXX TODO: acf games can be installed in other libraries, I need to
    # try it to find if that would change this logic.
    #
    # NOTE: There is also a UserConfig.appinstalldir, however it may be
    # unreliable if the acf has been copied from another location and the
    # game has not yet been launched.
    install_dir = app_state['installdir']
    if install_dir == '':
        ui._cprint(
            'yellow', g_indent +
            'WARNING: Blank installdir in %s, trying UserConfig.appinstalldir...'
            % acf_filename)
        install_dir = os.path.basename(
            app_state['UserConfig']['appinstalldir'])
    # Occasionally the install_dir is the full path in the Windows format.
    # This seems to happen sometimes when moving games from one install to
    # another. AFAICT the full path is never used - the acf file must be in
    # the same steam library as the install regardless, so discard the rest
    # of the path.
    install_dir = install_dir.split('\\')[-1]

    (found, correct, game_path, pretty) = insensitive_path(
        os.path.join(library_root, 'SteamApps/common/%s' % install_dir), opts)
    if found:
        # TODO: Warn if a second directory exists with the same name
        # but differing case, since that may confuse Steam or the game
        pass
    else:
        ui._print(g_indent, end='')
        ui._cprint(colours[False], 'Missing game directory', end=': ')
        ui._print(pretty)
        return None
    if not correct:
        ui._print(g_indent, end='')
        ui._cprint('back_yellow black', 'WARNING: Case Mismatch', end='')
        if not opts.rename:
            ui._print(' (rerun with -r to fix)', end='')
        ui._print(': ', end='')
        ui._print(pretty)
    return game_path
Example #16
0
def find_game_path(app_state, library_root, acf_filename, opts):
	# XXX TODO: acf games can be installed in other libraries, I need to
	# try it to find if that would change this logic.
	#
	# NOTE: There is also a UserConfig.appinstalldir, however it may be
	# unreliable if the acf has been copied from another location and the
	# game has not yet been launched.
	install_dir = app_state['installdir']
	if install_dir == '':
		ui._cprint('yellow', g_indent + 'WARNING: Blank installdir in %s, trying UserConfig.appinstalldir...' % acf_filename)
		install_dir = os.path.basename(app_state['UserConfig']['appinstalldir'])
	# Occasionally the install_dir is the full path in the Windows format.
	# This seems to happen sometimes when moving games from one install to
	# another. AFAICT the full path is never used - the acf file must be in
	# the same steam library as the install regardless, so discard the rest
	# of the path.
	install_dir = install_dir.split('\\')[-1]

	(found, correct, game_path, pretty) = insensitive_path(os.path.join(library_root, 'SteamApps/common/%s' %
		install_dir), opts)
	if found:
		# TODO: Warn if a second directory exists with the same name
		# but differing case, since that may confuse Steam or the game
		pass
	else:
		ui._print(g_indent, end='')
		ui._cprint(colours[False], 'Missing game directory', end=': ')
		ui._print(pretty)
		return None
	if not correct:
		ui._print(g_indent, end='')
		ui._cprint('back_yellow black', 'WARNING: Case Mismatch', end='')
		if not opts.rename:
			ui._print(' (rerun with -r to fix)', end='')
		ui._print(': ', end='')
		ui._print(pretty)
	return game_path
Example #17
0
	def verify_hash():
		if opts.verify and not verify_file_hash(filename, depot_hash, indent+g_indent, opts):
			ui._cprint('red', ' (BAD CHECKSUM)', end='')
Example #18
0
 def warn_filesize():
     if not check_filesize():
         ui._cprint('red', ' (Filesize != %i, %+i)' % \
           (depot_hash.filesize, filesize - depot_hash.filesize))
Example #19
0
	def verify_hash():
		if (opts.verify or opts.delete_bad) and not verify_file_hash(filename, depot_hash, indent+g_indent, opts):
			ui._cprint('red', ' (BAD CHECKSUM)', end='')
			return True
Example #20
0
 def verify_hash():
     if opts.verify and not verify_file_hash(filename, depot_hash,
                                             indent + g_indent, opts):
         ui._cprint('red', ' (BAD CHECKSUM)', end='')
Example #21
0
	def warn_filesize():
		if not check_filesize():
			ui._cprint('red', ' (Filesize != %i, %+i)' % \
					(depot_hash.filesize, filesize - depot_hash.filesize))