Torchlight 2 Toon Archiving: The Sequel

The kids left me alone last night, so I decided I’d goof around with my Torchlight  2 character archiving.  Recent reading around Git has convinced me that Git stores deltas of its binary file commits, so my last objection to using Git over Subversion for binary files has just been laid to rest.

Also, the original Perl script was a bit brain-dead in that it was good at automatically committing save files that had changed, but additions and deletions were still things I’d have to manually tell the repository about.

As these things tend to go, the lion’s share of the script was written in maybe the first 15 minutes.  The rest of the night was spent tweaking and testing the command template constants, destroying and re-creating Git archives until I had myself convinced that the script was automatically committing exact replicas of the save-game directory, regardless of additions, deletions and modifications.

The script turned out to be pretty short, and is included at the end of this blog post in all its perlesque glory.  Now that I have the trick of it though, the pattern is pretty much applicable to any save-game directory I might want to subject to version control.  The human-readable formula is basically:

  1. Delete all contents of the directory within your version control repository that contains the copied image of your save-game directory.
  2. Fully (recursively) copy the contents of that save-game directory into  the recently emptied archive directory.
  3. Ask the repository to identify all files that have been deleted from the  copied file set that it is currently archiving.  If there are any deleted files, stage these files to also be deleted in the repository on the next commit.
  4. Ask the repository to identify all new files that it is currently not archiving. If there are any new files, stage these  files to be added into the repository on the next commit.
  5. Have the repository apply in a single commit, all deletions, additions and modifications identified.  

# An archive utility for Torchlight 2 characters. Works by commiting the current
# save-game contents to a GIT repository housed in the parent directory to the script.
# (c) 2013, Lindsay Bradford, released under the Creative Commons Attribution licence.
# The parent directory has two subdirectories, being "bin", and "toons".
#  The "bin" directory contains this script.
#  The "toons" directory contains the save-game content of the game.
# Usage:
# <"optional commit message string"">
# Modify the constants below to suit your own environment

package ArchiveTL2Toons;

use strict;

###### constants for directory locations and command templates below #####

use constant GAME_SAVE_DIR =>
	"/home/linds/.wine/drive_c/users/linds/My Documents/My Games/Runic Games/Torchlight 2/save/76561198044661040/.";

use constant ARCHIVE_TOON_DIR =>

	sprintf "rm -rf '%s'", ARCHIVE_TOON_DIR;

use constant COPY_COMMAND =>
	sprintf "cp -r '%s' '%s'", GAME_SAVE_DIR, ARCHIVE_TOON_DIR;

	sprintf "git ls-files --deleted \"%s\" | xargs -r git rm --quiet", ARCHIVE_TOON_DIR;

	sprintf "git add --all \"%s\"", ARCHIVE_TOON_DIR;

use constant COMMIT_ARCHIVE_COMMAND => "git commit --quiet --m \"%s\"";

##### Methods below #####

# Bootstrap method.


# Archives the current set of Torchlight 2 toons by
# deleting the archive contents, taking a recursive
# copy of the save directory back into the directory
# and commiting a snapshot of the copied content.

sub archiveTL2toons() {
  my $commandLineComment = $_[0];

  if ($commandLineComment eq "") {
  	$commandLineComment = "Commit of current save state.";

    "Clearing archive content...",

    "Copying Torchlight 2 save game content to archive...",


# Commits a snapshot of the current content
# of the archive, assuming that the current content
# is exactly what the commit should contain.  Specifically:
#   * Any files missing  from the archive are deleted in the commit
#   * Any new files found are automatically added with the commit
#   * All modified files are commited as-is.

sub snapshotArchive() {
 my $commandLineComment = $_[0];

    "Staging removal of missing files from archive...",

   "Staging addition of untracked new files to archive...",

  my $message = &getNowTimestamp . " | " . $commandLineComment;

  my $commitCommand = sprintf COMMIT_ARCHIVE_COMMAND, $message;

    "Commiting staged snapshot of save-directory to archive...",

# Generates a timestamp of the current time.

sub getNowTimestamp() {
 my ($sec, $min, $hr, $day, $mon, $year) = localtime;
 return sprintf("%04d-%02d-%02d %02d:%02d",
       1900 + $year, $mon + 1, $day, $hr, $min);

# Simple method that prints the $message supplied,
# runs the $command specified, and prints any results
# the command generates.

sub runCommand() {
  my ($message, $command) = @_;

  print "$message\n";

  my $result = `$command`;
  print $result;

On a final note, I installed EPIC for Eclipse to modify the script, so despite my intense dislike for the lack of automated refactoring, I’m begrudgingly having to admit that it’s working better for me than doing it in a text editor.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s