none
[分享]用於備份你的那些重要 Windows Apps 數據文件的批處理程序 RRS feed

  • 常规讨论

  • 這篇文章主要是針對遊戲而言,雖然我知道這個板塊是針對企業環境的,但應該還是有人可能會與到類似的問題,所以也在這裡張貼一下。

    此次分享的批處理程序是用來備份產生在當前用戶指定 Windows Apps 數據文件,比如遊戲存檔。其中尤以 XBOX Live 遊戲存檔爲重。

    你可能認爲 XBOX Live 的遊戲存檔會自動放在雲端,很安全。但是依據我自己的經歷看,這個機制存在不完善的地方,當一個 XBOX Live 在啓動過程中(指剛剛顯示完 LOGO 的時候),可能會因爲某種尚未明瞭的機制出發積分重置功能。
    一旦完成重置, 除了成就會保留外,其他記錄統統會歸零。

    這個問題對你的影響取決與,你對遊戲的投入程度。對此,我遇到兩次,第一次是年初玩 "The Gunstringer", 當時剛剛顯示完 LOGO,然後程序崩潰,自動重啓後,本來已經完成的第一大關記錄就沒有了。因爲我不是太喜歡玩這個遊戲,所以還不是大問題。

    第二次是這週,同樣的事情發生在 "Microsoft Mahjong", 要知道這個遊戲花費了相當多的精力,雖然成就在,但是看到所有的一切居然要從頭開始,真的真的是非常沮喪,甚至有一種想摔電腦的衝動。

    當冷靜下來後,開始思考我能否將遊戲存檔恢復。首先想到的方法,是完全不關閉程序,然後重新運行并登錄,但這並不管用。事實上這個問題其實在之前的一兩次運行就已經存在,當時我以為是網絡問題導致數據沒有完成更新。
    考慮其他方案,因爲去年我是通過 Native VHD 方式體驗 Windows 8, 當 180 天體驗期結束後,我沒有刪除 VHD,而是保留下來。
    所以我能夠恢復遊戲存檔的方法,就祇能從中入手。

    先建立了一個測試帳戶,然後用 Processor Monitor 進行跟蹤。鎖定了與遊戲存檔相關的目錄

    %localappdata%\Packages\Microsoft.MicrosoftMahjong_7wekyb4d7baqw\LocalState

    接下來我先在其父目錄備份 LocalState

    robocopy LocalState LocalState.bak -copy:dat -dcopy:t -s -e

    然後挂載原來的 VHD 文件,把原來的 LocalState 複製過來進行替換。

    運行遊戲,但是遊戲一運行就崩潰。找到問題原因沒有費太多時間,權限沒有給足。

    cacls LocalCache /s | clip

    把 LocalCache 的 SDDL 字符串複製出來,然後

    cacls LocalState /s:SDDL
    icacls LocalState /setintegritylevel (oi)(ci)m

    這樣遊戲就可以運行了。不過記錄依然是重置後的。

    接著我又不斷嘗試,最後確認其中的一個文件

    SyncedData.bin.ark

    是記錄了遊戲分數信息。但遊戲開發商似乎對這個文件的數據結構進行了改變,首先是舊的文件名不包含 bin 字樣

    如果將原來的文件重命名後,會被同步爲重置後的數據,而不是用這個文件的數據替換掉雲端的數據。
    哪怕是在 Windows 8 重裝遊戲,然後進行文件替換,依然沒有改變。

    現在最後的恢復方法就是,試圖將原來在 VHD 文件的重新啓動,然後看看結果。但這個方法我還沒有嘗試。

    不過發現在嘗試過程的一些方法,能夠在相當程度上實現備份。包括:

    1. 在測試賬號中,先備份已有 SyncedData.bin.ark, 然後在遊戲選項中將積分重置,并關閉遊戲,接著將備份文件替換回來,重啓遊戲會看到要求選擇以雲端存檔爲主,還是以本地存檔爲主提示。
    2. 可以將虛擬機中的 SyncedData.bin.ark 複製到物理機,而且積分依然有效。

    所以基於這一發現,考慮如果在將自己力所能及範圍内的方法全部試盡,依然無法恢復,退而求其次至少可以備份現有數據,以備不時之需。

    重要説明

    1. 此方法並沒有做大範圍測試,不能保證對所用遊戲或者其他應用有效。

    2. 即便是目前有效,但不能保證當應用更新後,依然有效。比如 Microsoft Mahjong 遇到的情況。

    3. 如果不是特別需要,不建議隨意用備份覆蓋已有文件。因爲依據應用的不同,一些應用可以直接全部覆蓋,而不影響運行,有些應用祇能覆蓋部分文件,全部覆蓋會導致無法運行。可能需要重裝應用才能恢復。

    4. 不同的應用,所對應的核心數據文件的位置以及文件名是不一樣的。
    比如 Microsoft Mahjong ,是在 LocalState 目錄下的 SyncedData.bin.ark,而 Jetpack Joyride 的則是在 LocalState 下的 xuid_??????? 和 offlineProfile 内。
    至於哪些文件有效,需要自行測試驗證。

    5. 如果需要使用這種方法恢復,需要特別留意,在恢復過程中是否存在位于多個不同設備的該應用依然在運行。最好是在沒有回復完成前不運行應用。

    6. 不保證此恢復方法不被服務器視爲作弊,導致損失,比如賬號被 ban。

    7. 不明確在遊戲過程中通過此批處理程序進行的備份,是否數據完整。

    8. 雖然備份目標目錄是通過時間戳命名的,但依然存在同名目錄或文件而導致備份失敗。

    9. 雖然已經考慮了可能存在的本地化問題,但是存在因爲某個本地化問題,而導致程序不能正常運行情況的發生。

    10. 沒有在 Windows RT 系統下進行測試, 但是理論上應該不成問題。

    以下爲批處理程序:

    @echo off
    pushd %cd%
    
    if not defined localAppData (goto ERR_PATH)
    
    set pkgFile=%~f1
    if not defined pkgFile (goto Err_ARG) 
    dir %pkgFile% /a-d 2> nul 1> nul
    if %errorlevel% neq 0 (goto ERR_PATH)
    
    set backupTarget=%~f2
    if not defined backupTarget (goto ERR_ARG)
    
    dir %backupTarget% /ad 2> nul 1> nul
    if %errorlevel% neq 0 (goto ERR_PATH)
    
    echo 准备备份当前用户指定 Windows Apps 
    echo 的 LocalState 子目录到备份位置 %backuptarget%
    
    for /f "tokens=1,2" %%a in ('echo %date%') do (
    	echo %%a | findstr /r/c:"[-/]" 2> nul 1> nul
    	if %errorlevel% equ 0 (
    		set ymd=%%a
    	) else (
    		set ymd=%%b
    	)
    )
    set ymd=%ymd:-=_%
    set ymd=%ymd:/=_%
    set hism=%time::=_%
    set hism=%hism:.=_%
    set parentPath=%ymd%_%hism%
    
    cd /d %localappdata%\Packages
    echo 正在列目录...
    for /f "tokens=*" %%n in ('type %pkgFile% ^| find /c /v ""') do echo 共包含 %%n 个目录(应用)
    echo.
    
    echo 开始备份
    
    set /a rc=1
    setlocal enabledelayedexpansion
    for /f "tokens=*" %%n in (!pkgFile!) do (
    	dir "%%n\LocalState"/ad/b 2> nul 1> nul
    	if !errorlevel! equ 0 (	
    		echo [!rc!] 正在备份 "%%n" 的子目录 LocalState ...
    		echo d | xcopy /hrkse "%%n\LocalState" "!backupTarget!\!parentPath!\%%n\LocalState" 1> nul
    		if !errorlevel! neq 0 (
    			echo 在备份 "%%n" 的子目录 LocalState 发生错误 1>&2
    		)		
    	) else (
    		echo "%%n" 不存在子目录 LocalState,跳过 1>&2
    	)
    	set /a rc=!rc!+1
    )
    endlocal 
    
    goto exit
    
    :ERR_ARG
    echo copyright 2013 repl
    echo 用法: bakuls packagesfile bakuptarget
    echo.
    echo 	packagesfile 为包含要备份应用的目录名
    echo		backuptarge  为备份目标目录路径
    
    :ERR_PATH
    
    net helpmsg 3 1>&2
    exit /b 3
    
    :exit
    popd
    net helpmsg 0
    exit /b 0
    


    Folding@Home

    2013年11月3日 15:22

全部回复

  • 以下是增加了通過  7z 打包壓縮的腳本.

    @echo off
    pushd %cd%
    
    if not defined localAppData (goto ERR_PATH)
    
    set pkgFile=%~f1
    if not defined pkgFile (goto Err_ARG) 
    dir %pkgFile% /a-d 2> nul 1> nul
    if %errorlevel% neq 0 (goto ERR_PATH)
    
    set backupTarget=%~f2
    if not defined backupTarget (goto ERR_ARG)
    
    dir %backupTarget% /ad 2> nul 1> nul
    if %errorlevel% neq 0 (goto ERR_PATH)
    
    echo 准备备份当前用户指定 Windows Apps 
    echo 的 LocalState 子目录到备份位置 "%backuptarget%"
    
    for /f "tokens=1,2" %%a in ('echo %date%') do (
    	echo %%a | findstr /r/c:"[-/]" 2> nul 1> nul
    	if %errorlevel% equ 0 (
    		set ymd=%%a
    	) else (
    		set ymd=%%b
    	)
    )
    set ymd=%ymd:-=_%
    set ymd=%ymd:/=_%
    set hism=%time::=_%
    set hism=%hism:.=_%
    set parentPath=%ymd%_%hism%
    
    cd /d %localappdata%\Packages
    echo 正在列目录...
    for /f "tokens=*" %%n in ('type %pkgFile% ^| find /c /v ""') do echo 共包含 %%n 个目录(应用)
    echo.
    
    echo 开始备份
    
    set /a rc=1
    setlocal enabledelayedexpansion
    for /f "tokens=*" %%n in (!pkgFile!) do (
    	dir "%%n\LocalState"/ad/b 2> nul 1> nul
    	if !errorlevel! equ 0 (	
    		echo [!rc!] 正在备份 "%%n" 的子目录 LocalState ...
    		echo d | xcopy /hrkse "%%n\LocalState" "!backupTarget!\!parentPath!\%%n\LocalState" 1> nul
    		if !errorlevel! neq 0 (
    			echo 在备份 "%%n" 的子目录 LocalState 发生错误 1>&2
    		)		
    	) else (
    		echo "%%n" 不存在子目录 LocalState,跳过 1>&2
    	)
    	dir "%%n\Settings"/ad/b 2> nul 1> nul
    	if !errorlevel! equ 0 (	
    		echo [!rc!] 正在备份 "%%n" 的子目录 Settings ...
    		echo d | xcopy /hrkse "%%n\Settings" "!backupTarget!\!parentPath!\%%n\Settings" 1> nul
    		if !errorlevel! neq 0 (
    			echo 在备份 "%%n" 的子目录 Settings 发生错误 1>&2
    		)		
    	) else (
    		echo "%%n" 不存在子目录 Settings,跳过 1>&2
    	)
    	set /a rc=!rc!+1
    )
    
    echo.
    echo 正在压缩目录 "!backupTarget!\!parentPath!"
    
    7z a "!backupTarget!\!parentPath!.7z" "!backupTarget!\!parentPath!" 2> nul 1> nul
    
    if !errorlevel! neq 0 (
    	echo 压缩目录失败
    	if exist "!backupTarget!\!parentPath!.7z" (del "!backupTarget!\!parentPath!.7z" /a/f/q)
    ) else (
    	echo 成功生成压缩文件 "!backupTarget!\!parentPath!.7z"
    	echo 刪除目录 "!backupTarget!\!parentPath!"
    	rmdir "!backupTarget!\!parentPath!" /s/q
    )
    
    endlocal 
    
    goto exit
    
    :ERR_ARG
    echo copyright 2013 repl
    echo 用法: bakuls packagesfile bakuptarget
    echo.
    echo 	packagesfile 为包含要备份应用的目录名
    echo		backuptarge  为备份目标目录路径
    
    :ERR_PATH
    
    net helpmsg 3 1>&2
    exit /b 3
    
    :exit
    popd
    net helpmsg 0
    exit /b 0
    


    Folding@Home

    2014年10月23日 13:12