Skip to content

Instantly share code, notes, and snippets.

@NadnerbD
Last active August 9, 2025 06:25
Show Gist options
  • Select an option

  • Save NadnerbD/193484b9ac1a0b9e622feed5c332da48 to your computer and use it in GitHub Desktop.

Select an option

Save NadnerbD/193484b9ac1a0b9e622feed5c332da48 to your computer and use it in GitHub Desktop.
from collections import OrderedDict
import re, json, sys, os
def parseObj(tokens):
if(type(tokens) == str):
tokens = re.split(r'[\t\r\n]+', tokens)
tokens.reverse()
obj = OrderedDict()
while len(tokens):
nt = tokens.pop()
if nt in ["}", ""]:
return obj
elif nt.startswith('"'):
nt = json.loads(nt)
val = tokens.pop()
if val.startswith('"'):
obj[nt] = json.loads(val)
elif val == "{":
obj[nt] = parseObj(tokens)
else:
raise Exception("nt = %r" % nt)
def dumpObj(obj, indent=0):
s = ""
for key in obj:
s += "\t" * indent + json.dumps(key)
if type(obj[key]) == OrderedDict:
s += "\n" + "\t" * indent + "{\n" + dumpObj(obj[key], indent + 1) + "\t" * indent + "}\n"
else:
s += "\t\t%s\n" % json.dumps(obj[key])
return s
with open(sys.argv[1], "r") as f:
manifestpath = os.path.dirname(f.name)
data = f.read()
manifest = parseObj(data)
print(dumpObj(manifest))
if "-username" in sys.argv:
username = sys.argv[sys.argv.index("-username") + 1]
appstate = manifest['AppState']
for depotkey in appstate['StagedDepots']:
cmd = \
"DepotDownloader.exe -app " + appstate['appid'] + \
" -depot " + depotkey + " -manifest " + appstate['StagedDepots'][depotkey]['manifest'] + \
" -username " + username + " -remember-password -dir \"" + \
manifestpath + "\\common\\" + appstate['installdir'] + "\" -validate"
print(cmd)
os.system(cmd)
installed_depot = appstate['StagedDepots'][depotkey]
del installed_depot['dlcappid']
appstate['InstalledDepots'][depotkey] = installed_depot
del appstate['StagedDepots'][depotkey]
appstate['StateFlags'] = "4"
appstate['UpdateResult'] = "0"
appstate['StagingSize'] = "0"
appstate['buildid'] = appstate['TargetBuildID']
appstate['BytesStaged'] = appstate['BytesToStage']
appstate['BytesDownloaded'] = appstate['BytesToDownload']
print(dumpObj(manifest))
with open(sys.argv[1], "w") as f:
f.write(dumpObj(manifest))
@NadnerbD
Copy link
Author

NadnerbD commented Jul 30, 2025

This program expects argument 1 to be the filepath of an .acf file. For example:
python Update_Manifest.py "H:\SteamLibrary\SteamDirectory\steamapps\appmanifest_782330.acf" -username <username>

@metapea
Copy link

metapea commented Jul 31, 2025

This program expects argument 1 to be the filepath of an .acf file. For example: python Update_Manifest.py "H:\SteamLibrary\SteamDirectory\steamapps\appmanifest_782330.acf" -username <username>

A other quick fix was to just drop the Update_Manifest.py and maybe the DepotDownloader.exe on the steamapps folder.
then i got:

  File "Update_Manifest.py", line 44, in <module>
    for depotkey in appstate['StagedDepots']:
KeyError: 'StagedDepots'

@outseeker-
Copy link

I have an add-on suggestion if you're interested? I might give it a burl myself if not, just I would have to learn some python first XD

Rather than the user providing the path and .acf filename at runtime, we could loop through *.acf in a set (default steamapps folder on c:) or configured folder and open each .acf, check if the StateFlags <> 4 and perform required updates rather automatically for any game that needs it? :D
or we could be specific and check if StateFlags == 1030 or 1542 instead of each file, if there might somehow be a problem otherwise like an additional state other than 4 where we don't want to update anything for whatever reason?

Could even go super-classy, and prompt the user with the name field of the .acf to ask This game isn't in 4 (Installed) state, wanna update it Y/N? Could even say what state it Is in, and advise.

Not that it's necessarily a huge trouble to read each of the files to determine which is the right game, then pass that to the updater, and rinse and repeat per game/update, but we could save people who don't know "how to open an .acf file" the trouble entirely I think, and essentially provide a complete drop-in bandaid for this new zstd drama? I mean you've basically done it already, you legend!

@outseeker-
Copy link

outseeker- commented Aug 6, 2025

@metapea Found the reason for that error XD This testing and adding onto things with almost no knowledge has been painfully fun lol
The .acf file you feed to this script is intended to have been written by Steam, with details inside of what update it's trying (and failing) to install.

If your game is already updated, or if for any reason Steam has not filled out the StagedDepots details inside the game's .acf file while trying to update it, this script can't help you right now I think. You might wanna have Steam try to update the game, so it fills out the details in the game's .acf file with the details of the update first. Pretty sure that should have happened before it started trying to download or update anything tho idk whats up with that.

You can just open .acf files with your preferred text editor too, to make sure it's the right file/game and verify what's in there at what point.

@metapea
Copy link

metapea commented Aug 8, 2025

@metapea Found the reason for that error XD This testing and adding onto things with almost no knowledge has been painfully fun lol The .acf file you feed to this script is intended to have been written by Steam, with details inside of what update it's trying (and failing) to install.

If your game is already updated, or if for any reason Steam has not filled out the StagedDepots details inside the game's .acf file while trying to update it, this script can't help you right now I think. You might wanna have Steam try to update the game, so it fills out the details in the game's .acf file with the details of the update first. Pretty sure that should have happened before it started trying to download or update anything tho idk whats up with that.

You can just open .acf files with your preferred text editor too, to make sure it's the right file/game and verify what's in there at what point.

It doesn't change at all before, in the middle of, or after a corrupt download update.
the .acf should say what it's downloading with "TargetBuildID", "InstalledDepots" or maybe "SharedDepots"

@outseeker-
Copy link

Oh, so you didn't need an answer for your question what is KeyError: 'StagedDepots'? cool :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment