Wednesday, December 01, 2010

Dropbox Sync Script

Recently, I started using Steam, and while Steam can install each of your games on each of your computers, it doesn't keep the game saves in sync.  I wanted to be able to pick up where I had left off on any computer I wanted, not needing to worry about copying over save files, so I turned to Dropbox, which I've been using for a while. 

I had created a set of batch scripts that I used on Windows to set up the sync folders, but since I was adding several folders, and the number of scripts was getting to be large, I decided to combine them all into a single flexible and streamlined script.  I also decided to make it as generic as possible, so that other people could use it with minimal fuss.  Basically the script has a function that moves files and folders located outside of the Dropbox folder inside, and then creates a symlink (or folder junction), so that the program accessing the files doesn't need to be reconfigured in order to keep accessing the files.  If the item is already in the Dropbox folder, the original is kept under a different name, and a link is created to the existing synced content.  If the item doesn't exist in either place, it gets created in Dropbox, and linked to in the specified location.

The script is populated with things that I want synced, which will probably be different from what you want to sync, but it should be a simple matter to change that.  If you add items, the script is set up so that you can re-run it without creating more and more symlinks.

It's worth noting that the mklink command requires Windows Vista or later.  If you're running an earlier version such as XP, it will use the linkd command, which comes with the Windows 2003 Resource Kit. (You don't need to be running 2003 to install it.)

So, without further ado, here is the script:
@echo off
:: This script creates symlinks (NTFS junctions) in order to sync the contents via Dropbox
:: Some of these items require Administrator privileges to execute because of where the items are located.

:: Copyright 2010 Tim "burndive" of http://burndive.blogspot.com/ and http://tuxbox.blogspot.com/
:: This software is licensed under the Creative Commons GNU GPL version 2.0 or later.
:: License informattion: http://creativecommons.org/licenses/GPL/2.0/
:: This script was obtained from here:
:: http://tuxbox.blogspot.com/2010/12/dropbox-sync-script.html

:: Set this to your Steam user ID
set STEAM_USER=burndive

:: The path to your Documents folder
set DOCUMENTS=%USERPROFILE%\My Documents
if exist "%USERPROFILE%\Documents" (
  set DROPBOX=%USERPROFILE%\Documents
)

:: The path to your Dropbox folder
set DROPBOX=%DOCUMENTS%\My Dropbox
if exist "%USERPROFILE%\Dropbox" (
  set DROPBOX=%USERPROFILE%\Dropbox
)

:: Determine which command to use for making Folder Junctions
ver | findstr "5." > nul
if errorlevel 1 (
  :: This requires Windows Vista or later
  set JUNCTION_CMD=mklink /j
) else (
  :: This requires the Windows 2003 Resource Kit Tools
  set JUNCTION_CMD=linkd
)

:: Find the correct Flash SharedObjects folder name if localhost subfolder exists
set FLASH_LOCAL=
for /F "tokens=*" %%I in ('dir /b "%APPDATA%\Macromedia\Flash Player\#SharedObjects"') do (
  if exist "%APPDATA%\Macromedia\Flash Player\#SharedObjects\%%I\localhost" (
    set FLASH_LOCAL=%APPDATA%\Macromedia\Flash Player\#SharedObjects\%%I\localhost
  )
)

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: The following items require only User privileges to execute
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: Make localhost Flash Shared Objects dir architecture neutral
if not "%FLASH_LOCAL%" == "" (
  if exist "%FLASH_LOCAL%\Program Files (x86)" (
    call:LinkFolder "%FLASH_LOCAL%", "Program Files", "%FLASH_LOCAL%\Program Files (x86)"
  )
)

:: Digital Blasphemy wallpapers
call:LinkFolder "%USERPROFILE%\Pictures\wallpaper", "db-fs", "%DROPBOX%\images\digital-blasphemy\db-fs"
call:LinkFolder "%USERPROFILE%\Pictures\wallpaper", "db-ws", "%DROPBOX%\images\digital-blasphemy\db-ws"
call:LinkFolder "%USERPROFILE%\Pictures\wallpaper", "db-preview", "%DROPBOX%\images\digital-blasphemy\db-preview"

:: Images
call:LinkFolder "%USERPROFILE%\Pictures", "images", "%DROPBOX%\images"

:: Pidgin Instant Messenger
call:LinkFolder "%APPDATA%\.purple", "icons", "%DROPBOX%\app-files\pidgin\icons"
call:LinkFolder "%APPDATA%\.purple", "logs", "%DROPBOX%\app-files\pidgin\logs"

:: DVRMSToolbox Commercials XML files
call:LinkFolder "%PUBLIC%\DvrmsToolbox", "CommercialsXml", "%DROPBOX%\app-files\CommercialsXml"

::::::::::::::::::::::
:: Humble Bundle Games
::::::::::::::::::::::
:: Penumbra Overture
call:LinkFolder "%DOCUMENTS%\Penumbra Overture\Episode1", "save", "%DROPBOX%\app-files\game-saves\penumbra-overture"
:: Samarost2 : TODO
:: World of Goo
call:LinkFolder "%LOCALAPPDATA%\2DBoy", "WorldOfGoo", "%DROPBOX%\app-files\game-saves\world-of-goo"
:: Aquaria : TODO
:: Gish : TODO
:: Lugaru : TODO

::::::::::::::::::::::::
:: Humble Bundle 2 Games
::::::::::::::::::::::::
:: Braid
call:LinkFolder "%APPDATA%", "Braid", "%DROPBOX%\app-files\game-saves\braid"
:: Machinarium
if not "%FLASH_LOCAL%" == "" (
  if exist "%FLASH_LOCAL%\Program Files" (
    call:LinkFolder "%FLASH_LOCAL%\Program Files\Machinarium", "machinarium.exe", "%DROPBOX%\app-files\game-saves\machinarium"
  )
)
:: Osmos
call:LinkFolder "%DOCUMENTS%", "Osmos", "%DROPBOX%\app-files\game-saves\osmos"
:: Cortex Command : TODO
:: Revenge of the Titans HIB
call:LinkFolder "%USERPROFILE%\Revenge of the Titans 1.71", "slots", "%DROPBOX%\app-files\game-saves\revenge-of-the-titans-1.71"
call:LinkFolder "%USERPROFILE%\Revenge of the Titans 1.72", "slots", "%DROPBOX%\app-files\game-saves\revenge-of-the-titans-1.72"

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: The following items require Administrator privileges to execute
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: Eclipse workspace
call:LinkFolder "%USERPROFILE%", "workspace", "%DROPBOX%\app-files\workspace"

:: gVim config file
call:LinkFile "%USERPROFILE%", "_gvimrc", "%DROPBOX%\config\windows\_gvimrc"

:: Steam Game save folders
if exist "%PROGRAMFILES(X86)%" (
  :: Note: The caret is an escape character
  set STEAM_DIR=C:\Program Files ^(x86^)\Steam\steamapps
) else (
  set STEAM_DIR=%PROGRAMFILES%\Steam\steamapps
)
call:LinkFolder "%STEAM_DIR%\%STEAM_USER%\half-life 2\hl2", "SAVE", "%DROPBOX%\app-files\game-saves\half-life-2-steam"
call:LinkFolder "%STEAM_DIR%\%STEAM_USER%\half-life 2 episode one\episodic", "SAVE", "%DROPBOX%\app-files\game-saves\half-life-2-ep1-steam"
call:LinkFolder "%STEAM_DIR%\%STEAM_USER%\half-life 2 episode two\ep2", "SAVE", "%DROPBOX%\app-files\game-saves\half-life-2-ep2-steam"
call:LinkFolder "%STEAM_DIR%\%STEAM_USER%\half-life 2 lostcoast\lostcoast", "SAVE", "%DROPBOX%\app-files\game-saves\half-life-2-lostcoast-steam"
call:LinkFolder "%STEAM_DIR%\%STEAM_USER%\portal\portal", "SAVE", "%DROPBOX%\app-files\game-saves\portal-steam"

:: Pause so the user can review output
pause

:: End of execution
goto:eof

::::::::::::
:: Functions
::::::::::::

:LinkFolder
setlocal
:: Arguments
set LINK_PATH=%~1
set LINK_NAME=%~2
set TARGET=%~3
::echo Link: %LINK_PATH%\%LINK_NAME%
::echo Target: %TARGET%

if not exist "%LINK_PATH%" (
  mkdir "%LINK_PATH%"
)
cd "%LINK_PATH%"
:: If the folder is already a link, just delete it
dir | findstr /i "%LINK_NAME%" | findstr "<JUNCTION>" > NUL
if not errorlevel 1 (
  rmdir "%LINK_NAME%"
)
if exist "%LINK_NAME%" (
  if exist "%TARGET%" (
    echo Backing up conflicting copy
    move "%LINK_NAME%" "%LINK_NAME%-orig"
  ) else (
    :: Move the original if target does not exist
    echo Moving original folder to link location
    echo move "%LINK_NAME%" "%TARGET%"
    move "%LINK_NAME%" "%TARGET%"
  )
) else (
  if not exist "%TARGET%" (
    :: If neither exist, create target
    echo Creating target folder
    mkdir "%TARGET%"
  )
)
%JUNCTION_CMD% "%LINK_NAME%" "%TARGET%"

endlocal
goto:eof

:LinkFile
setlocal
:: Arguments
set LINK_PATH=%~1
set LINK_NAME=%~2
set TARGET=%~3
::echo Link: %LINK_PATH%\%LINK_NAME%
::echo Target: %TARGET%

if not exist "%LINK_PATH%" (
  mkdir "%LINK_PATH%"
)
cd "%LINK_PATH%"
:: If the folder is already a link, just delete it
dir | findstr /i "%LINK_NAME%" | findstr "<SYMLINK>" > NUL
if not errorlevel 1 (
  del "%LINK_NAME%"
)
if exist "%LINK_NAME%" (
  if exist "%TARGET%" (
    echo Backing up conflicting copy
    move "%LINK_NAME%" "%LINK_NAME%-orig"
  ) else (
    :: Move the original if target does not exist
    echo Moving original file to target location
    move "%LINK_NAME%" "%TARGET%"
  )
) else (
  if not exist "%TARGET%" (
    :: If neither exist, create target
    echo Creating empty target file
    echo "" > "%TARGET%"
  )
)
:: linkd will not work for files, only folders
mklink "%LINK_NAME%" "%TARGET%"

endlocal
goto:eof
CC-GNU GPL
This software is licensed under the CC-GNU GPL version 2.0 or later.

PS: If you use my code, I appreciate comments to let me know, and any feedback you may have, especially if it's not working right for you, but also just to say thanks.

For convenience, you can download this script from my server.

No comments:

Post a Comment