summaryrefslogtreecommitdiffstats
path: root/src/helpers/rosegarden-project-package
diff options
context:
space:
mode:
Diffstat (limited to 'src/helpers/rosegarden-project-package')
-rwxr-xr-xsrc/helpers/rosegarden-project-package839
1 files changed, 839 insertions, 0 deletions
diff --git a/src/helpers/rosegarden-project-package b/src/helpers/rosegarden-project-package
new file mode 100755
index 0000000..d9f2cb8
--- /dev/null
+++ b/src/helpers/rosegarden-project-package
@@ -0,0 +1,839 @@
+#!/bin/bash
+#
+# Check Perl requirements before executing the program proper
+if [ "$1" = "--conftest" ]; then
+ reqd=
+ if ! type -path perl >/dev/null; then
+ reqd="Perl 5"
+ else
+ for x in Getopt::Long File::Copy File::Basename XML::Twig; do
+ if ! perl -e "require $x" 2>/dev/null; then
+ reqd="$reqd, Perl module $x"
+ fi
+ done
+ fi
+ if [ -n "$reqd" ]; then
+ echo "Required: ${reqd#, }"
+ exit 1
+ fi
+fi
+exec perl -x "$0" "$@"
+
+#!/usr/bin/perl -w
+# -*- cperl-indent-level: 4 -*-
+#
+# Rosegarden Project file packager
+# Copyright 2005-2008 Chris Cannam and Fervent Software Ltd.
+# Distributed under the GNU General Public License.
+#
+# This program converts between Rosegarden (.rg) files and Rosegarden
+# Project (.rgp) files.
+#
+# A Rosegarden file (.rg) is a single file (in gzipped XML format)
+# that defines a Rosegarden composition in terms of events, segments,
+# programs, plugin data, references to audio files, and so on. Some
+# of these types of information depend on the presence of external
+# files such as the audio files or plugin preset data, without which
+# the file cannot be used.
+#
+# A Rosegarden Project (.rgp) file is a bundle of files, including the
+# Rosegarden file itself, as well as any audio file or other external
+# data it requires, in compressed form. A Rosegarden Project file is
+# intended to be portable and self-contained, but the Rosegarden
+# program itself does not (at the time of writing) understand these
+# files directly.
+#
+# This program can take a Rosegarden file, examine it to find out
+# which other data files it uses, and pack it to create the
+# corresponding Rosegarden Project file. Conversely, it can also take
+# a Rosegarden Project file and unpack it as a project directory for
+# use with Rosegarden.
+
+# TODO:
+# - quiet
+# - versioning
+# - graceful recovery from running out of disk space
+# - mucho tidying
+
+use strict;
+
+use XML::Twig;
+use File::Copy;
+use File::Basename;
+use Getopt::Long;
+
+print STDERR "Rosegarden Project Packager v0.25 (c) Fervent Software Ltd 2005-2008\n";
+print STDERR "This program is Free Software under the GNU General Public License.\n";
+
+my $dcop = undef;
+sub usage {
+ print STDERR "Usage: rosegarden-project-package [--quiet] [--pack] file.rg [file.rgp]\n";
+ print STDERR " rosegarden-project-package [--quiet] --unpack file.rgp\n";
+ print STDERR " rosegarden-project-package [--quiet] --rg file.rgp\n";
+ print STDERR " rosegarden-project-package --conftest\n";
+ if (defined $dcop) {
+ `dcop "$dcop" close`;
+ }
+ exit 2;
+}
+
+my $kdialog = "kdialog --title \"Rosegarden Project\" --icon \"rosegarden\"";
+
+my $pack = 0;
+my $unpack = 0;
+my $conftest = 0;
+my $rg = 0;
+my $quiet = 0;
+my $result = GetOptions("pack" => \$pack,
+ "unpack" => \$unpack,
+ "conftest" => \$conftest,
+ "rg" => \$rg,
+ "quiet" => \$quiet);
+if ($result eq "") {
+ usage;
+}
+
+if (!$conftest) {
+ $dcop = `kdialog --title \"Rosegarden Project Progress\" --icon \"rosegarden\" --progressbar "Please wait..." 100`;
+ chomp $dcop;
+}
+
+my @temporaries = ();
+$SIG{__DIE__} = sub {
+ my $error = shift;
+ $error =~ s/ at .*$/./;
+ `dcop "$dcop" close`;
+ map { system qq{rm -rf $_} } @temporaries;
+ system qq{kdialog --error "$error"};
+ return 1;
+};
+
+sub conftest {
+ my @required = ();
+ system "kdialog -v >/dev/null" and push @required, "kdialog";
+ system "flac --help >/dev/null" and push @required, "flac";
+ # sndfile-convert --help always returns 1
+ system "which sndfile-convert >/dev/null" and push @required, "sndfile-convert";
+# system "oggenc -h >/dev/null 2>&1" and push @required, "oggenc";
+# system "oggdec -h >/dev/null 2>&1" and push @required, "oggdec";
+ system "dcop --help >/dev/null" and push @required, "dcop";
+ return @required;
+}
+
+sub canonicalise {
+ my $b = shift;
+ chomp $b;
+ my $p = shift;
+ chomp $p;
+ $p =~ s,^~/,$ENV{"HOME"}/,;
+ $p =~ s,^~$,$ENV{"HOME"},;
+ $p =~ s,^([^/]),$b/$1,;
+ $p =~ s,/./,/,g;
+ $p =~ s,/$,,;
+ return $p;
+}
+
+sub relativise {
+ my $b = shift;
+ chomp $b;
+ my $p = shift;
+ chomp $p;
+ if ($p =~ m,^$b/,) {
+ $p =~ s,^$b/,,;
+ }
+ return $p;
+}
+
+sub name {
+ my $rgFile = shift;
+ my $n = shift;
+ my $base = basename $rgFile;
+ $base =~ s/\.rg$//i;
+
+ if ($n =~ m,^RG-AUDIO-\d+\.wav(.pk)?$,i or
+ $n =~ m,/RG-AUDIO-\d+\.wav(.pk)?$,i) {
+
+ $n =~ s,RG-AUDIO-(\d+)\.wav,$base-rg-$1.wav,;
+
+ } elsif ($n =~ m,^rg-[0-9-]+.wav(.pk)?$, or
+ $n =~ m,/rg-[0-9-]+.wav(.pk)?$,) {
+
+ $n =~ s,rg-([0-9-]+).wav,$base-rg-$1.wav,;
+ }
+
+ return $n;
+}
+
+sub convertType {
+ my $file = shift;
+ if ($file =~ m/\.(wav|w64)$/i) {
+ return 'flac';
+ } else {
+ return 'copy';
+ }
+}
+
+sub contrib { # Approximate amount of contribution to overall processing time
+ my $file = shift;
+ my $size = (stat $file)[7];
+ if (!defined $size) {
+ return 10.0;
+ }
+ if (convertType($file) eq 'flac' or convertType($file) eq 'ogg') {
+ return $size / 1000.0;
+ } else {
+ return $size / 50000.0;
+ }
+}
+
+sub locate {
+ my $file = shift;
+ if (-f $file) {
+ return $file;
+ }
+ my $code = system qq{$kdialog --warningyesnocancel 'File not found:\n$file\nDo you want to browse for this file now?'};
+ if ($code == 512) { # cancel
+ die "File $file not found.";
+ } elsif ($code == 256) { # no
+ return "";
+ }
+ my $dir = dirname $file;
+ if (! -d $dir) { $dir = $ENV{'HOME'}; }
+ $file = basename $file;
+ $file = `$kdialog --getopenfilename "$dir" "$file"`;
+ chomp $file;
+ return $file;
+}
+
+sub convert {
+ my $source = shift;
+ my $target = shift;
+ print STDERR "$source -> $target\n";
+ $source = locate $source;
+ return if ($source eq "");
+ my $d = dirname $target;
+ print STDERR "Creating directory $d\n";
+ system qq{mkdir -p "$d"} and die "Failed to create directory $d: $!";
+ print STDERR "Convert type is " . convertType($source) . "\n";
+ if (convertType($source) eq 'flac') {
+ print STDERR "Running flac -o $target.rgp.flac $source\n";
+ system qq{flac -s -o "$target.rgp.flac" "$source"} and do {
+ # Conversion failed. First let's assume that's because the
+ # input is a type of WAV file flac doesn't like.
+ system qq{sndfile-convert -pcm24 "$source" "$target.rgp.wav"} and do {
+ die "Failed to convert $source to PCM-24 intermediate file: $!";
+ };
+ system qq{flac -s -o "$target.rgp.flac" "$target.rgp.wav"} and do {
+ # OK, that didn't help.
+ system qq{rm -f "$target.rgp.wav"};
+ die "Failed to convert PCM-24 intermediate file $target.rgp.wav to $target.rgp.flac: $!"; # warn via dialog & recover
+ };
+ # Looks good, remove intermediate file
+ system qq{rm -f "$target.rgp.wav"};
+ };
+ } elsif (convertType($source) eq 'ogg') {
+ print STDERR "Running oggenc -o $target $source\n";
+ system qq{oggenc -o "$target.rgp.ogg" "$source"} and
+ die "Failed to convert $source to $target.rgp.ogg"; # warn via dialog & recover
+ } else {
+ print STDERR "Copying $source to $target\n";
+ copy $source, $target or die "Failed to copy $source to $target: $!";
+ }
+ return 1;
+}
+
+sub rgPack {
+
+ my ($d, $an, $pn);
+ $an = 0;
+ $pn = 0;
+ my $projectDir = "";
+ my %audioFiles = ();
+ my %unusedAudioFiles = ();
+ my @possibles = ();
+ my @indices = ();
+
+ my $rgFile = shift;
+ my $projectFile = shift;
+
+ $rgFile = canonicalise `pwd`, $rgFile;
+ $projectFile = canonicalise `pwd`, $projectFile;
+
+ if (-d $projectFile) {
+ die "Project file $projectFile exists and is a directory -- not overwriting it";
+ }
+
+ if (-f $projectFile) {
+ if (system qq{$kdialog --warningyesno "Project file \"$projectFile\" already exists. Overwrite it?"}) {
+ die "Not overwriting existing project file $projectFile";
+ }
+ }
+
+ my $targetDir = $projectFile . ".d";
+ if (-f $targetDir or -d $targetDir) {
+ system qq{$kdialog --sorry "Packaging directory \"`pwd`/$targetDir\" already exists.\nCannot continue to create project file."};
+ die "Not overwriting existing packaging directory $targetDir";
+ }
+
+ `dcop "$dcop" setLabel "Reading Rosegarden file..."`;
+
+ my $dir = '.';
+ if ($rgFile =~ m,/,) {
+ $dir = $rgFile;
+ $dir =~ s,[^/]*$,,;
+ }
+
+ if ($dir =~ m,^[^/],) {
+ my $wd = `pwd`;
+ chomp $wd;
+ $dir = "$wd/$dir";
+ }
+
+ $dir =~ s,/\.?/,/,g;
+
+ print STDERR "dir is $dir\n";
+
+ my $twig = XML::Twig->new();
+
+ open INZIP, "-|", "gunzip -c $rgFile";
+
+ eval {
+ $twig->parse(\*INZIP);
+ };
+
+ close INZIP;
+
+ my $root = $twig->root;
+ if (!defined $root) { die "No root"; }
+
+ if ($root->gi ne 'rosegarden-data') {
+ die "Not a Rosegarden file";
+ }
+
+ my $audioFilesNode = $root->first_child('audiofiles');
+
+ my $audioPathNode = $audioFilesNode->first_child('audioPath');
+ $projectDir = canonicalise $dir, $audioPathNode->att('value');
+
+ my @audioNodes = $audioFilesNode->children('audio');
+ foreach my $audioNode (@audioNodes) {
+ my $file = $audioNode->att('file');
+ my $id = $audioNode->att('id');
+ my $done = 0;
+ foreach my $segment ($root->children('segment')) {
+ if (defined $segment->att('type') and
+ $segment->att('type') eq "audio" and
+ $segment->att('file') == $id) {
+ $audioFiles{$id} = $file;
+ $done = 1;
+ last;
+ }
+ }
+ if (!$done) { $unusedAudioFiles{$id} = $file; }
+ }
+
+ if (%unusedAudioFiles) {
+ my @args;
+ map {
+ my $id = $_;
+ my $file = $unusedAudioFiles{$id};
+
+ my $haveElsewhere = 0;
+ foreach my $usedFile (values %audioFiles) {
+ if ($usedFile eq $file) {
+ $haveElsewhere = 1;
+ last;
+ }
+ }
+
+ if (!$haveElsewhere) {
+
+ my $fn = canonicalise $projectDir, $file;
+ my $desc = `file "$fn"`;
+ my $sz = `wc -c "$fn"`;
+ chomp $desc;
+ chomp $sz;
+ $sz =~ s,\s+.*,,;
+ $desc =~ s,^$fn: ,,;
+ $desc =~ s,\([^\)]*\)\s+,,g;
+ $fn =~ s,^$projectDir/,[PROJECT]/,;
+ push @args, ($id, "\"$fn - $sz bytes - $desc\"", "off");
+ }
+
+ } sort { $a <=> $b } keys %unusedAudioFiles;
+
+ print STDERR "args are @args\n";
+ my $result = `$kdialog --checklist "The following audio files are referred to in the Rosegarden file,\nbut are not used in any segment.\n\nBy default these will not be included in the project file.\n\nPlease select any that you wish to include." @args`;
+ if ($? == 256) {
+ die "Operation cancelled";
+ }
+ chomp $result;
+ print STDERR "result is $result\n";
+ @indices =
+ map { s/^"([^"]*)"$/$1/; $_ }
+ split '\s', $result;
+ }
+
+ map { $audioFiles{$_} = $unusedAudioFiles{$_} } @indices;
+ @indices = ();
+
+ my @peakFiles = ();
+ map {
+ my $peakFile = canonicalise($projectDir, $_) . ".pk";
+ -f $peakFile and push @peakFiles, $peakFile;
+ } values %audioFiles;
+
+ my %possibleMap;
+
+ my $studioNode = $root->first_child('studio');
+ my @deviceNodes = $studioNode->children('device');
+ foreach my $deviceNode (@deviceNodes) {
+ my @instrumentNodes = $deviceNode->children('instrument');
+ foreach my $instrumentNode (@instrumentNodes) {
+ my @pluginNodes = $instrumentNode->children('plugin');
+ push @pluginNodes, $instrumentNode->children('synth');
+ my @configureNodes;
+ map { push @configureNodes, $_->children('configure') } @pluginNodes;
+ map {
+ my $possible = $_->att('value');
+ my $loc = canonicalise $projectDir, $possible;
+ if (-f $loc && -r $loc) {
+ $possibleMap{$possible} = 1;
+ }
+ } @configureNodes;
+ }
+ }
+
+ @possibles = sort keys %possibleMap;
+
+ `dcop "$dcop" setProgress 5`;
+
+ print STDERR "Possibles: @possibles\n";
+
+ if (@possibles) {
+ print STDERR "$#possibles possibles\n";
+ my @args;
+ my $n = 0;
+ map {
+ my $possible = $_;
+ my $fn = canonicalise $projectDir, $possible;
+ my $desc = `file "$fn"`;
+ my $sz = `wc -c "$fn"`;
+ my $status = "off";
+ chomp $desc;
+ chomp $sz;
+ $sz =~ s,\s+.*,,;
+ $desc =~ s,^$fn: ,,;
+ $desc =~ s,\([^\)]*\)\s+,,g;
+ if ($fn =~ m,^$projectDir/[^/]*$,) {
+ $status = "on";
+ }
+ $fn =~ s,^$projectDir/,(PROJECT)/,;
+ push @args, ($n, "\"$fn - $sz bytes - $desc\"", $status);
+ ++$n;
+ } @possibles;
+ print STDERR "args are @args\n";
+ my $result = `$kdialog --checklist "The following files may be required for use by plugins used in this composition.\n\nPlease select any that you wish to include in the project file." @args`;
+ if ($? == 256) {
+ die "Operation cancelled";
+ }
+ chomp $result;
+ print STDERR "result is $result\n";
+ @indices =
+ map { s/^"([^"]*)"$/$1/; $_ }
+ split '\s', $result;
+ }
+
+ mkdir $targetDir or die "Cannot create packaging directory $targetDir: $!";
+ push @temporaries, $targetDir;
+
+ my @sourceFiles = sort values %audioFiles;
+ push @sourceFiles, @peakFiles;
+ map { push @sourceFiles, $possibles[$_] } @indices;
+
+ print STDERR "Source files: @sourceFiles\n";
+
+ `dcop "$dcop" setProgress 10`;
+
+ my %fileMap;
+
+ my $totalContrib = 0.0;
+ map { $totalContrib += contrib canonicalise $projectDir, $_; } @sourceFiles;
+ if ($totalContrib == 0) {
+ $totalContrib = 10;
+ }
+
+ my $contribCount = 0.0;
+
+ print STDERR "Total contribution: $totalContrib\n";
+
+ my $newProjectDir = basename $rgFile;
+ $newProjectDir =~ s/.rg$//i;
+ if ($newProjectDir eq basename $rgFile) {
+ $newProjectDir = $newProjectDir . ".d";
+ }
+
+ foreach my $origSourceFile (@sourceFiles) {
+ my $sourceCanonical = canonicalise $projectDir, $origSourceFile;
+ my $sourceRelative = relativise $projectDir, $sourceCanonical;
+ my $renamedRelative = name $rgFile, $sourceRelative;
+ if ($sourceRelative =~ m,^/,) {
+ $renamedRelative = basename $renamedRelative;
+ }
+ my $sourceFile = canonicalise $projectDir, $sourceRelative;
+ my $targetFile = canonicalise($targetDir . "/" . $newProjectDir, $renamedRelative);
+ print STDERR "first guess: $sourceFile -> $targetFile (renamedRelative is $renamedRelative)\n";
+ my $count = 1;
+ while (-f $targetFile or -d $targetFile) {
+ print STDERR "$targetFile exists... ";
+ if ($renamedRelative =~ m,\.([^.]+)$,) {
+ $renamedRelative =~ s,^(.*)\.([^.]+)$,$1_$count.$2,;
+ } else {
+ $renamedRelative = $renamedRelative . "_$count";
+ }
+ $targetFile = canonicalise($targetDir . "/" . $newProjectDir, $renamedRelative);
+ ++$count;
+ print STDERR "trying $targetFile\n";
+ }
+ if (convertType($sourceFile) eq 'flac' or
+ convertType($sourceFile) eq 'ogg') {
+ `dcop "$dcop" setLabel "Converting $renamedRelative..."`;
+ } else {
+ `dcop "$dcop" setLabel "Including $renamedRelative..."`;
+ }
+ $fileMap{$origSourceFile} = $renamedRelative;
+ convert $sourceFile, $targetFile;
+ $contribCount += contrib $sourceFile;
+ my $progress = int(10.0 + $contribCount * 82.0 / $totalContrib);
+ `dcop "$dcop" setProgress $progress`;
+ }
+
+ my $code;
+ while (($code = system qq{$kdialog --yesno "Do you want to include any additional files in this project?"}) == 0) {
+ my $file = `$kdialog --getopenfilename "$dir" "*"`;
+ chomp $file;
+ my $targetFile = canonicalise($targetDir . "/" . $newProjectDir, basename $file);
+ convert $file, $targetFile;
+ }
+
+ `dcop "$dcop" setLabel "Converting Rosegarden file..."`;
+
+ $audioPathNode->set_att('value', $newProjectDir);
+
+ foreach my $audioNode (@audioNodes) {
+ if (exists $audioFiles{$audioNode->att('id')}) {
+ if (exists $fileMap{$audioNode->att('file')}) {
+ my $before = $audioNode->att('file');
+ my $after = $fileMap{$before};
+ print STDERR "Renaming $before to $after in XML\n";
+ $audioNode->set_att('file', $after);
+ }
+ } else {
+ $audioNode->cut;
+ }
+ }
+
+ foreach my $deviceNode (@deviceNodes) {
+ my @instrumentNodes = $deviceNode->children('instrument');
+ foreach my $instrumentNode (@instrumentNodes) {
+ my @pluginNodes = $instrumentNode->children('plugin');
+ push @pluginNodes, $instrumentNode->children('synth');
+ my @configureNodes;
+ map { push @configureNodes, $_->children('configure') } @pluginNodes;
+ foreach my $configureNode (@configureNodes) {
+ if (exists $fileMap{$configureNode->att('value')}) {
+ my $before = $configureNode->att('value');
+ my $after = $fileMap{$before};
+ print STDERR "Renaming $before to $after in configure XML\n";
+ $configureNode->set_att('value', $after);
+ }
+ }
+ }
+ }
+
+ my $targetRgFile = $targetDir . "/" . basename $rgFile;
+ my $targetRgXml = $targetRgFile . ".xml";
+
+ print STDERR "out is $targetRgFile\n";
+
+ open OUT, "| gzip -c > $targetRgFile" or die "Failed to open $targetRgFile for writing via gzip";
+
+ $twig->print(\*OUT);
+
+ close OUT;
+
+ `dcop "$dcop" setLabel "Packaging..."`;
+ `dcop "$dcop" setProgress 95`;
+
+ system qq{rm -f "$projectFile"} and die "Overwriting old project file failed: $!";
+
+ my $baseTarget = basename $targetDir;
+ my $baseRg = basename $targetRgFile;
+ my $baseProject = basename $projectFile;
+ print STDERR "dir is $targetDir\nbaseTarget is $baseTarget\nbaseRg is $baseRg\nnewProjectDir is $newProjectDir\n";
+ system qq{mkdir -p "$targetDir/$newProjectDir"};
+ system qq{cd "$targetDir"; tar cf - "$baseRg" "$newProjectDir" | gzip --fast -c > "$baseProject" ; mv "$baseProject" ..} and die "Making tarball failed";
+
+ `dcop "$dcop" setLabel "Done"`;
+ `dcop "$dcop" setProgress 100`;
+
+ system qq{rm -r "$targetDir"} and die "Cleaning up packaging directory failed: $!";
+
+ sleep 1;
+
+ my $completeProject = canonicalise $targetDir, "../$baseProject";
+ system qq{$kdialog --msgbox "Packaging complete."};
+
+ `dcop "$dcop" close`;
+
+ return 1;
+}
+
+sub rgUnpack {
+
+ my $rgFile = shift;
+ my $projectFile = shift;
+
+ $rgFile = canonicalise `pwd`, $rgFile;
+ $projectFile = canonicalise `pwd`, $projectFile;
+
+ if (! -f $projectFile) {
+ die "Project file $projectFile not found";
+ }
+
+ $rgFile =~ /.rg$/i or $rgFile = "$rgFile.rg";
+
+ # directory containing target rg file
+ my $rgDir = dirname $rgFile;
+
+ my $origRgFile = basename $projectFile;
+ $origRgFile =~ s/\.rg\.rgp$/.rg/i;
+ $origRgFile =~ s/\.rgp$/.rg/i;
+
+ if (-d $rgFile) {
+ die "Target Rosegarden file $rgFile exists and is a directory -- not overwriting";
+ }
+
+ if (-f $rgFile) {
+ if (system qq{$kdialog --warningyesno "Rosegarden file \"$rgFile\" already exists. Overwrite it?"}) {
+ die "Not overwriting existing Rosegarden file $rgFile";
+ }
+ }
+
+ my $projectDir = $rgFile;
+ $projectDir =~ s/\.[^\.\/]*$//;
+ if ($projectDir eq $rgFile) {
+ $projectDir = "$projectDir.d";
+ }
+ # projectDir is now absolute or relative to cwd
+
+ if (-f $projectDir) {
+
+ die "Project directory $projectDir already exists and is a file -- not overwriting it";
+
+ } elsif (-d $projectDir) {
+
+ if (system qq{$kdialog --warningyesno "Project directory \"$projectDir\" already exists. Overwrite any duplicate files?"}) {
+ die "Not overwriting existing project directory $projectDir";
+ }
+
+ } else {
+ push @temporaries, $projectDir;
+ system qq{mkdir -p "$projectDir"} and die "Failed to create target project directory $projectDir";
+ }
+
+ my %dirs;
+ map { chomp; s,/.*$,,; $dirs{$_} = 1; } `gunzip -c "$projectFile" | tar tf -`;
+
+ my $origProjectDir = "";
+ for my $targetDir (keys %dirs) {
+ if ($targetDir =~ /.rg$/ and !($targetDir =~ m,/,)) {
+ $origRgFile = $targetDir;
+ next;
+ }
+ if ($targetDir eq $origRgFile) {
+ next;
+ }
+ if ($origProjectDir eq "") {
+ $origProjectDir = $targetDir;
+ next;
+ }
+ }
+ # origProjectDir is now relative to the base path
+ # origRgFile is a base name only (no path)
+
+ # We have:
+ # rgFile -> target to unpack rg file to
+ # projectFile -> original project file
+ # rgDir -> target directory to contain rg file
+ # origRgFile -> original rg file (virtual) in same dir as project file
+ # projectDir -> target to unpack project files to (rgFile.d)
+ # origProjectDir -> original name of project directory (probably origRgFile.d)
+
+ # procedure:
+ # create a temporary directory tmpdir
+ # go there and unpack the original project file
+ # this will create original rg file and original project dir in tmpdir
+ # move original project dir from tmpdir to rgDir
+ # unpack audio files in new project dir
+ # move original rg file from tmpdir to rgFile
+ # if the project dir basename differs from the original project dir basename,
+ # edit the rgFile (just the audioPath node) appropriately.
+ # !!! extra above: show error and exit if rgFile exists and is a directory
+
+ my $tmpdir = "rgp_tmp_$$"; ##!!! clean up
+ if (-e $tmpdir) { die "Temporary directory $tmpdir already exists, abandoning"; }
+ system qq{ mkdir "$tmpdir" } and die "Failed to create temporary directory $tmpdir";
+
+ push @temporaries, $tmpdir;
+
+ `dcop "$dcop" setLabel "Unpacking..."`;
+ `gunzip -c "$projectFile" | ( cd "$tmpdir"; tar xf - )`;
+
+ `dcop "$dcop" setProgress 20`;
+
+ for my $item (glob "$tmpdir/$origProjectDir/*") {
+ system qq{mv "$item" "$projectDir/"} and die "Failed to move $item to $projectDir";
+ }
+
+ my @encFiles = `ls $projectDir/*.rgp.flac $projectDir/*.rgp.ogg 2>/dev/null`;
+ my $count = 0;
+ foreach my $encFile (@encFiles) {
+ chomp $encFile;
+ my $origFile = $encFile;
+ $origFile =~ s/\.rgp\.(flac|ogg)$//;
+ my $decoder = 'flac -s -f -d';
+ if ($encFile =~ /ogg$/) {
+ $decoder = 'oggdec';
+ }
+ if (system qq{$decoder -o "$origFile" "$encFile"}) {
+ die "Failed to unpack $origFile"; # should warn & continue
+ } else {
+ system qq{rm "$encFile"};
+ }
+ my $progress = @encFiles;
+ $progress = int(25 + $count * 75 / $progress);
+ ++$count;
+ `dcop "$dcop" setProgress $progress`;
+ }
+
+ # we now have the target project directory set up correctly:
+ # all we need is the rg file
+
+ open INZIP, "-|", "gunzip -c $tmpdir/$origRgFile" or die "Failed to open original Rosegarden file $tmpdir/$origRgFile";
+ open OUTZIP, "| gzip -c > $rgFile" or die "Failed to open target Rosegarden file $rgFile for writing";
+ my $encProjDir = basename $projectDir;
+ while (<INZIP>) {
+ if (/<audioPath\s/) {
+ s/<audioPath\s+value=\"[^\"]*"\s*\/>/<audioPath value="$encProjDir"\/>/;
+ }
+ print OUTZIP;
+ }
+ close INZIP;
+ close OUTZIP;
+
+ `dcop "$dcop" setLabel "Done"`;
+ `dcop "$dcop" setProgress 100`;
+
+ sleep 1;
+
+ system qq{rm -rf "$tmpdir"};
+ `dcop "$dcop" close`;
+
+ @temporaries = ();
+ return 1;
+}
+
+
+if (!$pack && !$unpack && !$conftest && !$rg) {
+ if (exists $ARGV[0]) {
+ if ($ARGV[0] =~ /.rg$/i) {
+ $pack = 1;
+ } elsif ($ARGV[0] =~ /.rgp$/i) {
+ $unpack = 1;
+ } else {
+ usage;
+ }
+ } else {
+ usage;
+ }
+}
+
+my @required = conftest;
+if (@required) {
+ print STDERR "The following additional packages are required but not available in the PATH:\n";
+ print STDERR join ' ', @required;
+ print STDERR "\n";
+ if ($conftest) {
+ print "Required: ";
+ print join ', ', @required;
+ print "\n";
+ exit 1;
+ } else {
+ die "Configuration requirements not met\nThe following helper applications were not found: " . join(', ', @required);
+ }
+}
+
+if ($conftest) {
+ exit 0;
+}
+
+if ($pack) {
+
+ if ($unpack || $conftest || $rg) { usage; }
+
+ my ($rgFile, $projectFile);
+
+ if (exists $ARGV[0]) {
+ $rgFile = $ARGV[0];
+ chomp $rgFile;
+ }
+ if (! -f $rgFile) {
+ die "$rgFile: No such file or directory\n";
+ }
+
+ if (exists $ARGV[1]) {
+ $projectFile = $ARGV[1];
+ chomp $projectFile;
+ } else {
+ $projectFile = $rgFile;
+ $projectFile =~ s/\.rg$/.rgp/;
+ if ($projectFile eq $rgFile) {
+ $projectFile = $rgFile . ".rgp";
+ }
+ }
+
+ rgPack $rgFile, $projectFile;
+
+} elsif ($unpack || $rg) {
+
+ if ($pack || $conftest) { usage; }
+ if ($unpack && $rg) { usage; }
+
+ my $projectFile;
+
+ if (exists $ARGV[0]) {
+ $projectFile = $ARGV[0];
+ chomp $projectFile;
+ }
+ if (! -f $projectFile) {
+ die "$projectFile: No such file or directory\n";
+ }
+
+ my $rgFile;
+ if (exists $ARGV[1]) {
+ $rgFile = $ARGV[1];
+ chomp $rgFile;
+ } else {
+ $rgFile = $projectFile;
+ $rgFile =~ s/\.rg\.rgp$/.rg/i;
+ $rgFile =~ s/\.rgp$/.rg/i;
+ }
+
+ rgUnpack $rgFile, $projectFile;
+
+ if ($rg) {
+ system qq{rosegarden "$rgFile"};
+ }
+}
+