From 18b8c17f24b9cd6d9600b212cbd615deb69fe500 Mon Sep 17 00:00:00 2001
From: Bob Igo <bob@stormlogic.com>
Date: Sat, 17 Jan 2009 12:53:44 -0500
Subject: Expanded Tweaker tar file into individual files, and adjusted
 installation process to reflect this.  Further integration will be required
 before this is ready for R6.

---
 abs/core-testing/tweaker/PKGBUILD                  |  33 +-
 abs/core-testing/tweaker/bin/tweaker.pl            | 333 ++++++++++++
 abs/core-testing/tweaker/bin/twk_EXAMPLE.pl        | 134 +++++
 abs/core-testing/tweaker/bin/twk_RAM.pl            |  78 +++
 abs/core-testing/tweaker/bin/twk_audio.pl          | 376 ++++++++++++++
 abs/core-testing/tweaker/bin/twk_audio_RP.pl       | 307 +++++++++++
 abs/core-testing/tweaker/bin/twk_cpu.pl            | 132 +++++
 abs/core-testing/tweaker/bin/twk_dragon.pl         |  66 +++
 .../tweaker/bin/twk_fingerprint_hardware.sh        |  98 ++++
 abs/core-testing/tweaker/bin/twk_general.pl        | 541 ++++++++++++++++++++
 abs/core-testing/tweaker/bin/twk_graphics.pl       | 149 ++++++
 abs/core-testing/tweaker/bin/twk_keymap.sh         |  74 +++
 abs/core-testing/tweaker/bin/twk_linux.pl          |  65 +++
 abs/core-testing/tweaker/bin/twk_localization.pl   |  70 +++
 abs/core-testing/tweaker/bin/twk_misc.pl           |  13 +
 abs/core-testing/tweaker/bin/twk_scrub_sql.pl      |  96 ++++
 abs/core-testing/tweaker/bin/twk_tuners.pl         | 488 ++++++++++++++++++
 abs/core-testing/tweaker/bin/twk_tuners_notes.txt  |  55 ++
 abs/core-testing/tweaker/bin/twk_upgrade.pl        | 190 +++++++
 .../tweaker/bin/twk_what_has_changed.sh            | 183 +++++++
 abs/core-testing/tweaker/fs/etc/asound.conf        |  15 +
 .../tweaker/fs/var/lib/alsa/ALC888.asound.state    | 354 +++++++++++++
 .../tweaker/fs/var/lib/alsa/AV710.asound.state     | 567 +++++++++++++++++++++
 .../tweaker/lib/Tweaker/Definitions.pm             |  66 +++
 abs/core-testing/tweaker/lib/Tweaker/Script.pm     | 353 +++++++++++++
 abs/core-testing/tweaker/tcf/EXAMPLE.tcf           |  38 ++
 abs/core-testing/tweaker/tcf/focus.tcf             | 113 ++++
 abs/core-testing/tweaker/tcf/os.tcf                |  10 +
 abs/core-testing/tweaker/tcf/tcf.dtd               |  11 +
 abs/core-testing/tweaker/tcf/tweaker-core.tcf      |  47 ++
 abs/core-testing/tweaker/tcf/userland.tcf          |   0
 abs/core-testing/tweaker/tweaker.tar.bz2           | Bin 37143 -> 0 bytes
 32 files changed, 5040 insertions(+), 15 deletions(-)
 create mode 100755 abs/core-testing/tweaker/bin/tweaker.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_EXAMPLE.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_RAM.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_audio.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_audio_RP.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_cpu.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_dragon.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_fingerprint_hardware.sh
 create mode 100755 abs/core-testing/tweaker/bin/twk_general.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_graphics.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_keymap.sh
 create mode 100755 abs/core-testing/tweaker/bin/twk_linux.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_localization.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_misc.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_scrub_sql.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_tuners.pl
 create mode 100644 abs/core-testing/tweaker/bin/twk_tuners_notes.txt
 create mode 100755 abs/core-testing/tweaker/bin/twk_upgrade.pl
 create mode 100755 abs/core-testing/tweaker/bin/twk_what_has_changed.sh
 create mode 100644 abs/core-testing/tweaker/fs/etc/asound.conf
 create mode 100644 abs/core-testing/tweaker/fs/var/lib/alsa/ALC888.asound.state
 create mode 100644 abs/core-testing/tweaker/fs/var/lib/alsa/AV710.asound.state
 create mode 100644 abs/core-testing/tweaker/lib/Tweaker/Definitions.pm
 create mode 100644 abs/core-testing/tweaker/lib/Tweaker/Script.pm
 create mode 100644 abs/core-testing/tweaker/tcf/EXAMPLE.tcf
 create mode 100644 abs/core-testing/tweaker/tcf/focus.tcf
 create mode 100644 abs/core-testing/tweaker/tcf/os.tcf
 create mode 100644 abs/core-testing/tweaker/tcf/tcf.dtd
 create mode 100644 abs/core-testing/tweaker/tcf/tweaker-core.tcf
 create mode 100644 abs/core-testing/tweaker/tcf/userland.tcf
 delete mode 100644 abs/core-testing/tweaker/tweaker.tar.bz2

diff --git a/abs/core-testing/tweaker/PKGBUILD b/abs/core-testing/tweaker/PKGBUILD
index a11643b..bb5a401 100644
--- a/abs/core-testing/tweaker/PKGBUILD
+++ b/abs/core-testing/tweaker/PKGBUILD
@@ -6,7 +6,7 @@ arch=('i686' 'x86_64')
 
 depends=('bash' 'perl' 'perl-dbi' 'perl-exception-class' 'perl-log-log4perl' 'perl-log-dispatch' 'perl-getopt-lucid' 'perl-list-member' 'perl-class-data-inheritable' 'perl-devel-stacktrace' 'perl-xml-twig') 
 
-source=(${pkgname}.tar.bz2 tweaker.sh log4perl.conf)
+source=(tweaker.sh log4perl.conf bin/tweaker.pl bin/twk_audio.pl bin/twk_audio_RP.pl bin/twk_cpu.pl bin/twk_dragon.pl bin/twk_EXAMPLE.pl bin/twk_fingerprint_hardware.sh bin/twk_general.pl bin/twk_graphics.pl bin/twk_keymap.sh bin/twk_linux.pl bin/twk_localization.pl bin/twk_misc.pl bin/twk_RAM.pl bin/twk_scrub_sql.pl bin/twk_tuners.pl bin/twk_upgrade.pl bin/twk_what_has_changed.sh tcf/EXAMPLE.tcf tcf/focus.tcf tcf/os.tcf tcf/tcf.dtd tcf/tweaker-core.tcf tcf/userland.tcf fs/etc/asound.conf fs/var/lib/alsa/ALC888.asound.state fs/var/lib/alsa/AV710.asound.state lib/Tweaker/Definitions.pm lib/Tweaker/Script.pm)
 
 license=('GPL2')
 #groups=('pvr')
@@ -14,21 +14,24 @@ license=('GPL2')
 
 #install=tweaker.install
 build() {
-   TWEAKER_ROOT=$startdir/pkg/usr/LH/tweaker
-   cd $startdir/src/ || return 1
-   mkdir -p $TWEAKER_ROOT/bin
+   TWEAKER_ROOT=/usr/LH/tweaker
+   mkdir -p $TWEAKER_ROOT
+   cd $TWEAKER_ROOT
+   # executables
+   install -m0777 -d bin $startdir/bin
+   # parallel root directory structure used for seeding configuration files
+   install -m0777 -d fs $startdir/fs
+   # Tweaker configuration files
+   install -m0555 -d tcf $startdir/tcf
+   # Tweaker-centric log4perl configuration
+   cd /etc
+   install -m0755 log4perl.conf $startdir/log4perl.conf
+
+   # Ensure shell variables are configured at start; ensure $TWEAKER_ROOT/bin is in $PATH
    mkdir -p $startdir/pkg/etc/profile.d
    install -m0755 tweaker.sh $startdir/pkg/etc/profile.d/tweaker.sh
-   install -m0755 log4perl.conf $TWEAKER_ROOT/log4perl.conf
-
-   #copy binary files
-   cd usr/local/bin
-   install -m0755 *  $TWEAKER_ROOT/bin/
-   rm -f  $TWEAKER_ROOT/bin/tweaker-installer.sh
-
-#copied lib from km 5.5 because it was missing from the tar file
-  mkdir -p $TWEAKER_ROOT
-  cd $startdir/src/usr/LH/tweaker
-  cp -rp  * $TWEAKER_ROOT
 
+   # Link our tweaker Perl modules for general use
+   /bin/ln -fs $TWEAKER_ROOT/lib/Tweaker /etc/perl/
+   /bin/cp $startdir/fs/etc/log4perl.conf /etc
 }
diff --git a/abs/core-testing/tweaker/bin/tweaker.pl b/abs/core-testing/tweaker/bin/tweaker.pl
new file mode 100755
index 0000000..28519df
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/tweaker.pl
@@ -0,0 +1,333 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package Tweaker;
+
+use strict;
+use Getopt::Lucid qw( :all );
+# see http://search.cpan.org/~dagolden/Getopt-Lucid-0.16/lib/Getopt/Lucid.pm for usage details
+use XML::Twig;
+# see http://xmltwig.com for usage details
+use Log::Log4perl qw(get_logger);
+
+# see http://search.cpan.org/~mschilli/Log-Log4perl-1.14/lib/Log/Log4perl.pm for usage details
+# http://www.perl.com/pub/a/2002/09/11/log4perl.html is highly recommended as well
+
+use Tweaker::Script;
+use Tweaker::Definitions;
+
+# To install the above modules:
+# --
+# sudo cpan install Getopt::Lucid XML::Twig Log::Log4perl List::Member
+# choose the defaults for all options, agree to install all dependencies
+# Copy TweakerDefinitions.pm to /etc/perl
+
+use vars qw($debug);
+use vars qw($interactivity);
+use vars qw($logfile);
+use vars qw($twig);
+
+my $bottom = Tweaker::Definitions::get_global_variable_value("bottom");
+my $optional = Tweaker::Definitions::get_global_variable_value("optional");
+my $minimal = Tweaker::Definitions::get_global_variable_value("minimal");
+my $null = Tweaker::Definitions::get_global_variable_value("null");
+
+# As each tweak tag is processed, this subroutine is called.  Here we will
+# see if any previous tweak tag had the same name attribute.  If so, we will
+# replace the previous tweak tag with this new tweak tag's contents.
+sub uniquify_tweaks {
+    my ($twig, $this_tweak) = @_;
+    my $tweak_name = $this_tweak->att('name');
+    my $previous_tweak = $this_tweak->prev_sibling( qq{tweak[\@name="$tweak_name"]});
+    my $logger = get_logger('tweaker');
+
+    # If the tweak's name is found elsewhere, replace the first
+    # instance with the latest one, then delete the latest one.
+    if ($previous_tweak) {
+	my $log_entry;
+	# Requirement 6.3.1
+	$log_entry = sprintf("\tReplacing\n\t\t%s\n\twith\n\t\t%s\n", $previous_tweak->sprint, $this_tweak->sprint);
+	$logger->info($log_entry);
+	$this_tweak->cut;
+	$this_tweak->replace($previous_tweak);
+    }
+}
+
+$twig = XML::Twig->new(load_DTD => 1,
+		       expand_external_ents => 1,
+		       twig_handlers => { 'tweak' => \&uniquify_tweaks }
+    );
+
+# Requirement 4.2
+# Print advice on usage and invocation.
+sub help () {
+    my $logger = get_logger('tweaker');
+    $logger->fatal("USAGE:\n$0 --tcf file1.tcf [--tcf file2.tcf ...] [--help]");
+    exit;
+}
+
+sub print_tcf {
+    my(@tweaks) = @_;
+    my $logger = get_logger('tweaker');
+
+    $logger->debug('<?xml version="1.0"?>');   # print the XML declaration
+    $logger->debug('<!DOCTYPE stats SYSTEM "tcf.dtd">');
+    $logger->debug('<tcf>');                   # then the root element start tag
+
+    foreach my $tweak (@tweaks) {    # the list of tweaks
+	$logger->debug($tweak->sprint);               # print the xml content of the element 
+    }
+
+    $logger->debug("</tcf>\n");                # close the document
+}
+
+# For each tweak, invoke the option that was selected.
+# Requirement 9.1.5
+# Requirement 13
+sub invoke_selected_options {
+    my @tweaks= $twig->root->children;   # get the tweak list
+    my $logger = get_logger('tweaker');
+    my $has_selected_option;
+    my $recommendationlevel = $bottom;
+
+    foreach my $tweak (@tweaks) {    # the list of tweaks
+	if ($tweak->att('name')) {
+	    $has_selected_option=0;
+	    $logger->debug("TWEAK ", $tweak->att('name'));
+	    my @options = $tweak->descendants('option');
+	    foreach my $option (@options) {
+		if ($option->first_child('selected')) {
+		    $has_selected_option++;
+		    $recommendationlevel = $option->first_child('selected')->text;
+		    $logger->info("\t", $tweak->att('name'), " : ", $option->att('name'), " is selected (recommendationlevel is ", $recommendationlevel, ")");
+		    # If the selected option is merely $optional, and we are in $minimal interactivity mode, we
+		    # really don't know if this option is useful for the user.  Just skip it.
+		    if (($recommendationlevel == $optional) && ($interactivity eq $minimal)) { # UI Requirement 9.1.4
+			$logger->debug("\tSKIPPING");
+			next;
+		    } elsif (($tweak->first_child('script')->text) &&
+			     ($tweak->first_child('script')->text) ne $null) {
+			$logger->debug("\tRUNNING");
+			my $command = sprintf("%s --implement %s", $tweak->first_child('script')->text, $option->att('name'));
+			$logger->info("\t\tRunning '$command' to run a tweak.");
+			
+			open(COMMAND, "$command|");
+			while(<COMMAND>) { # should only be one line of results
+			    if ($_) {
+				$logger->debug("script returned text: ", $_);
+			    } else {
+				$logger->debug("no return value from script");
+			    }
+			}
+			close(COMMAND);
+		    } else {
+			$logger->debug("\tNO SCRIPT TO RUN");
+		    }
+		}
+	    }
+	    if ($has_selected_option == 0) {
+		$logger->debug("\tNO OPTION SELECTED");
+	    }
+	}
+    }
+}
+
+
+# Parses an XML file in TCF format, loading its information into memory.
+# Rely on the DTD to mandate required tags.
+sub parse_tcf {
+    my $this_tcf = shift(@_);
+    my $logger = get_logger('tweaker');
+
+    $logger->info("Parsing $this_tcf...");
+    $twig->safe_parsefile($this_tcf) || $logger->fatal("Bad TCF $this_tcf: $@");
+    if ($@) {
+	return 0;
+    }
+    # ??? For Requirement 5.1, we need to catch parse errors here
+    # $twig now has the parsed $this_tcf
+    $logger->info("DONE parsing $this_tcf");
+
+    my $root= $twig->root;         # get the root of the twig (tcf)
+    my @tweaks= $root->children;   # get the tweak list
+
+    print_tcf(@tweaks) if $debug;
+
+    return 1; # successfully parsed $this_tcf
+}
+
+sub parse_core_tcf {
+    my ($core_tcf_pathname) = @_;
+    my $logger = get_logger('tweaker');
+
+    $logger->debug("core TCF pathname: $core_tcf_pathname");
+
+    # Requirement 5.1
+    if (!(parse_tcf($core_tcf_pathname))) {
+	# Part of Requirement 6.4.1
+	$logger->fatal("$0 requires at least one valid .tcf file.");
+	$logger->fatal("$core_tcf_pathname, or a .tcf that it includes, did not parse.");
+	exit;    
+    }
+    #    $twig->root->print;
+}
+
+sub process_parameters () {
+    # Accept these parameters:
+    # --
+    my @parameters = (
+	Switch("help")->anycase,  # Requirement 4.2
+	Switch("debug")->anycase,
+	# part of Requirement 8
+	Param("interactivity")->default($minimal), # side-effect of UI Requirement 9 for v0.7
+	);
+
+    my $opt = Getopt::Lucid->getopt( \@parameters );
+    my $help = $opt->get_help;
+    $debug = $opt->get_debug;
+    $interactivity = $opt->get_interactivity;
+
+    if ($interactivity ne $minimal) { # side-effect of UI Requirement 9 for v0.7
+	my $logger = get_logger('tweaker');
+	$logger->warn("This version of Tweaker ignores requests for $interactivity interactivity and defaults to $minimal.");
+	$interactivity = $minimal;
+    }
+
+    if ( $help > 0 ) {
+	help;
+    }
+}
+
+# Requirement 6.6: Tweaker shall determine Recommendation Levels and default Options
+# Requirements 6.6.1 and 6.6.2
+sub post_process_tweaks {
+    my $logger = get_logger('tweaker');
+
+    # Requirement 6.6.1
+
+    my $root= $twig->root;         # get the root of the twig (tcf)
+    my @tweaks= $root->children;   # get the tweak list
+
+    foreach my $tweak (@tweaks) {
+	$logger->debug("#######");
+	my @options = $tweak->descendants('option');
+
+	my $name_of_most_recommended_option="";
+	my $most_recommended_option;
+	my $highest_recommendationlevel=$bottom;
+
+	foreach my $option (@options) {
+	    $logger->debug("===== OPTION");
+	    $logger->debug($option->sprint);
+
+	    my $recommendationlevel = $option->first_child('recommendationlevel');
+	    if ($recommendationlevel) {
+		# Requirement 6.6.1.1
+		$logger->debug("\t+++");
+		$logger->debug("\tUsing predefined recommendation level:", $recommendationlevel->text);
+	    } else {
+		# Requirement 6.6.1.4
+		my $result="optional";
+		my $explanation="";
+
+		if (($tweak->first_child('script')->text) &&
+		    ($tweak->first_child('script')->text) ne $null) {
+		    # Requirement 6.6.1.2
+		    $logger->debug("\t---");
+		    $logger->debug("\tThis has no defined recommendation level.");
+
+		    # Get the name of the script for this tweak and invoke it with the name of the option.
+		    my $command = sprintf("%s --poll %s", $tweak->first_child('script')->text, $option->att('name'));
+		    $logger->debug("\t\tRunning '$command' to see what it should be.");
+		    # Create a recommendationlevel element and populate it with the script's return value.
+
+		    open(COMMAND, "$command|");
+		    while(<COMMAND>) { # should only be one line of results
+			$logger->debug("Got this: ", $_);
+			if ($_) {
+			    chop;
+			    my @text = split(/\|/);
+			    $result = $text[0];
+			    if ($text[1]) {
+				$explanation = $text[1];
+			    }
+			}
+		    }
+		    close(COMMAND);
+		}
+		# Requirement 6.6.1.3
+		$option->set_field ( 'recommendationlevel', $result );
+		if ($explanation) {
+		    $option->set_field ( 'explanation', $explanation );
+		}
+		#$logger->debug("*** Paste results: ", $option->sprint);
+	    }
+	    # Requirement 6.6.2 : Auto-select the Option with the highest non-negative Recommendation Level.
+	    # Requirement 6.6.2.1 : If there is a tie, Tweaker shall auto-select the first Option with the highest Recommendation Level.
+	    $recommendationlevel = $option->first_child('recommendationlevel');
+	    if ($recommendationlevel) {
+		if ($name_of_most_recommended_option) {
+		    $logger->debug("comparing ", $name_of_most_recommended_option, "'s recommendationlevel ($highest_recommendationlevel) to ", $recommendationlevel->text, " (",Tweaker::Definitions::get_global_variable_value($recommendationlevel->text),")");
+		} else {
+		    $logger->debug("considering recommendationlevel ", $recommendationlevel->text, " (",Tweaker::Definitions::get_global_variable_value($recommendationlevel->text),")");
+		}
+		if (($highest_recommendationlevel < Tweaker::Definitions::get_global_variable_value($recommendationlevel->text))
+		    && (Tweaker::Definitions::get_global_variable_value($recommendationlevel->text) >= 0)) {
+		    $highest_recommendationlevel = Tweaker::Definitions::get_global_variable_value($recommendationlevel->text);
+		    $name_of_most_recommended_option = $option->att('name');
+		    $most_recommended_option = $option;
+		    $logger->debug("now recommending: ", $name_of_most_recommended_option, " at level ", $highest_recommendationlevel);
+		}
+	    } else {
+		$logger->error("No recommendationlevel defined for ", $option->att('name'));
+	    }
+	    # Select the best option, based on recommendation level
+	}
+	if ($most_recommended_option) {
+	    $logger->debug("(1) BEST OPTION: ", $most_recommended_option->sprint);
+	    $most_recommended_option->set_field ( 'selected', $highest_recommendationlevel );
+	}
+    }
+}
+
+sub main () {
+    my $tweaker_root;
+
+    if (!($tweaker_root = Tweaker::Script::get_environment_variable("\$TWEAKER_ROOT"))) {
+	Log::Log4perl->easy_init();
+	my $logger = get_logger();
+	$logger->fatal("\$TWEAKER_ROOT not defined.  Exiting.");
+	exit -1;
+    }
+    my $core_tcf_path = "$tweaker_root/tcf";
+    my $core_tcf = "tweaker-core.tcf";
+    my $core_tcf_pathname = "$core_tcf_path/$core_tcf";
+    my $log4perl_conf = "$tweaker_root/log4perl.conf";
+
+    Log::Log4perl::init_and_watch($log4perl_conf,10);
+    
+    process_parameters;
+    parse_core_tcf($core_tcf_pathname);
+    post_process_tweaks;
+    # ??? need to add Requirement 11 here
+    invoke_selected_options;
+}
+
+main;
+
+#my @tweaks= $twig->root->children;   # get the tweak list
+#print_tcf(@tweaks) if $debug;
diff --git a/abs/core-testing/tweaker/bin/twk_EXAMPLE.pl b/abs/core-testing/tweaker/bin/twk_EXAMPLE.pl
new file mode 100755
index 0000000..4fc401a
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_EXAMPLE.pl
@@ -0,0 +1,134 @@
+# This is not an executable.  It provides an example of how you would implement a
+# Tweaker Script.  For details on the functions provided by Tweaker::Script,
+# such as execute_shell_command, get_environment_variable, connect_to_db, etc.
+# see Tweaker/Script.pm
+#
+# See the corresponding EXAMPLE.tcf for the Tweak that would correspond to this script.
+
+#
+# BEGIN EXAMPLE
+#
+
+#!/usr/bin/perl -w
+
+# Copyright 2008 YOUR NAME HERE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+# List all the options that this script supports.  Make sure it matches what's in
+# the corresponding .tcf entry.
+set_known_options( 'option1', 'option2', 'option3' );
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    # If you need a subroutine that is specific to the task of implementing an option,
+    # define it inside the above subroutine.
+    sub my_subroutune {
+	# ...
+    }
+    
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) { # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+	switch ($option) {
+	    # List all the options that this script supports.  You can have as many as you like.
+	    case "option1" {
+		# Perform the actions necessary to implement option1.
+		# You may want to call do_query, change_or_make_setting, or change_or_make_entry to make changes to the MySQL database.
+		# You may want to call execute_shell_command to make changes from the shell.
+	    }
+	    case "option2" {
+		# Perform the actions necessary to implement option2.
+		# You may want to call do_query, change_or_make_setting, or change_or_make_entry to make changes to the MySQL database.
+		# You may want to call execute_shell_command to make changes from the shell.
+	    }
+	    case "option3" {
+		# Perform the actions necessary to implement option3.
+		# You may want to call do_query, change_or_make_setting, or change_or_make_entry to make changes to the MySQL database.
+		# You may want to call execute_shell_command to make changes from the shell.
+	    }
+	}
+    } else { # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+	my $logger = get_logger('tweaker.script');
+	$logger->error("ERROR: Unable to connect to mythconverg database");
+	$logger->error("ERROR: Unable to implement option $option.");
+	exit -1; # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+    } # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+    disconnect_from_db(); # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+}
+
+# Poll the system to see what recommendationlevel the given option has on the system.
+sub poll_options {
+    my($option) = @_;
+
+    # If you need a subroutine that is specific to the task of implementing an option,
+    # define it inside the above subroutine.
+    sub my_subroutune {
+	# ...
+    }
+    
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) { # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+	switch ($option) {
+	    # List all the options that this script supports.  You can have as many as you like.
+	    case "option1" {
+		# Perform the actions necessary to determine a recommendation level for option1.
+		# You may want to call do_query to query to the MySQL database.
+		# You may want to call execute_shell_command to check things in the shell.
+		# When you're done, you can use the recommendation_level subroutine to return the
+		#   resulting recommendation level.
+	    }
+	    case "option2" {
+		# Perform the actions necessary to determine a recommendation level for option2.
+		# You may want to call do_query to query to the MySQL database.
+		# You may want to call execute_shell_command to check things in the shell.
+		# When you're done, you can use the recommendation_level subroutine to return the
+		#   resulting recommendation level.
+	    }
+	    case "option3" {
+		# Perform the actions necessary to determine a recommendation level for option3.
+		# You may want to call do_query to query to the MySQL database.
+		# You may want to call execute_shell_command to check things in the shell.
+		# When you're done, you can use the recommendation_level subroutine to return the
+		#   resulting recommendation level.
+	    }
+	}
+    } else { # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+	exit -1; # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+    } # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+    disconnect_from_db(); # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters; # mandatory
+
+#
+# END EXAMPLE
+#
diff --git a/abs/core-testing/tweaker/bin/twk_RAM.pl b/abs/core-testing/tweaker/bin/twk_RAM.pl
new file mode 100755
index 0000000..ead3e3c
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_RAM.pl
@@ -0,0 +1,78 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'low', 'medium', 'high' );
+
+# ??? Need to make this update a class variable so it only ever needs to run once.
+sub determine_RAM_size() {
+    my $result = execute_shell_command("cat /proc/meminfo | grep MemTotal");
+    if ($result =~ m/.*MemTotal:\s*(\d+) .*/) {
+	return $1;
+    }
+    return 0;
+}
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	# MythTV's GUI allows setting this value in multiples of 4700kB.
+	# We will arbitrarily set the HDRingbufferSize to the value that is closest to
+	# 3.3% of the total RAM.
+	my $RAM_size = determine_RAM_size();
+	my $ringbuffer_size = int($RAM_size * 0.033 / 4700) * 4700;
+	change_or_make_setting('HDRingbufferSize', $ringbuffer_size) || exit -1;
+
+	# change any existing mplayer cache setting to one based on available RAM size
+	# We will arbitrarily set the cache size to 1/16 of available RAM
+	my $cachesize = int($RAM_size / 16);
+	execute_shell_command("sed -i 's/cache.*=.*/cache=$cachesize/g' /etc/mplayer/mplayer.conf") || exit -1;
+    } else {
+	exit -1; # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+    } # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+    disconnect_from_db(); # You may not have to do this.  It's only when you need to change MySQL settings for MythTV.
+}
+
+# Poll the system to see what recommendationlevel the given option has on the system.
+sub poll_options {
+    my($option) = @_;
+    my $fudge_factor = .985;
+    my $low_RAM = int($fudge_factor * 512*1024);     # ~512MB
+    my $medium_RAM = int($fudge_factor * 1024*1024); # ~1GB
+    my $high_RAM = int($fudge_factor * 2048*1024);   # ~2GB
+
+    threshold_test($option, determine_RAM_size(), "RAM", $low_RAM, $medium_RAM, $high_RAM);
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_audio.pl b/abs/core-testing/tweaker/bin/twk_audio.pl
new file mode 100755
index 0000000..72b9c86
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_audio.pl
@@ -0,0 +1,376 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'analogstereo', 'analogsurround', 'digital' );
+
+# Poll the system to find the digital output device.
+sub poll_for_digital_output_device {
+    my $card=-1;
+    my $device=-1;
+    my $poll_command = "aplay -l | grep card";
+
+    my @digital_matches = ( "digital", "IEC958" );
+    
+    my $results = execute_shell_command($poll_command);
+    foreach my $digital_match (@digital_matches) {
+	if ($results =~ /card (\d):.*device (\d).*$digital_match.*/i) {
+	    $card = $1;
+	    $device = $2;
+	}
+    }
+    return ($card, $device);
+}
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+    use vars qw($card);
+    use vars qw($device);
+    ($card, $device) = poll_for_digital_output_device;
+    use vars qw($asound_conf);
+    $asound_conf = "/etc/asound.conf";
+    use vars qw($mplayer_conf);
+    $mplayer_conf = "/etc/mplayer/mplayer.conf";
+    use vars qw($xine_conf);
+    $xine_conf = "/home/mythtv/.xine/config";
+    
+    sub generate_asound_conf {
+	my($option) = @_;
+	my $command1;
+	my $command2;
+
+	switch ($option) {
+	    case "analogstereo" {
+		# Analog stereo usually needs no asound.conf, but sometimes it needs to be there to
+		# support more than one app using sound simultaneously.
+		$command1 = "[ -e $asound_conf ] && /bin/cp $asound_conf $asound_conf-TWEAKERBACKUP";
+		# Assumption: The proper analog device is always device 0.
+		if ($card >= 0) {
+		    $command2 = "[ -e $asound_conf ] && sed -i 's/hw:.,./hw:$card,0/g' $asound_conf";
+		} else {
+		    $command2 = "[ -e $asound_conf ] && sed -i 's/hw:.,./hw:0,0/g' $asound_conf";
+		}
+	    }
+	    case "analogsurround" {
+		# Not supported yet.
+	    }
+	    case "digital" {
+		# Digital audio starts with an asound.conf that references the
+		# hardware address on the sound device corresponding to digital
+		# output.
+		$command1 = "/bin/cp \$TWEAKER_ROOT/fs$asound_conf /etc/";
+		if (($card >= 0) && ($device >= 0)) {
+		    $command2 = "sed -i 's/hw:.,./hw:$card,$device/g' $asound_conf";
+		} else {
+		    my $logger = get_logger('tweaker.script');
+		    $logger->error("ERROR: Unable to poll for digital sound output device.");
+		    exit(-1);
+		}
+	    }
+	}
+	if (my $error = execute_shell_command($command1)) {
+	    my $logger = get_logger('tweaker.script');
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	if (my $error = execute_shell_command($command2)) {
+	    my $logger = get_logger('tweaker.script');
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	my $logger = get_logger('tweaker.script');
+	$logger->info("Generated $asound_conf for $option audio.");
+    }
+
+    sub edit_mplayer_conf {
+	my($option) = @_;
+	# delete any old entries that Tweaker made, relevant to this particular edit
+	my $delete_old_tweaker_edits = "sed -i '/^.*a[o,c].*=.*#TWEAKER/d' $mplayer_conf";
+	# comment out old entries that some other process may have made
+	my $comment_out_external_edits = "sed -i 's/^\\(a[o,c].*=.*\\)/#\\1/g' $mplayer_conf";
+	my $command1;
+	my $command2="";
+	($card, $device) = poll_for_digital_output_device;
+	my $logger = get_logger('tweaker.script');
+
+	if (my $error = execute_shell_command("$delete_old_tweaker_edits && $comment_out_external_edits")) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+
+	switch($option) {
+	    case "analogstereo" {
+		# No additional work needed; the above commands comment out ao and ac entries,
+		# leaving a baseline which works with analog stereo.
+	    }
+	    case "analogsurround" {
+		# Not supported yet.
+	    }
+	    case "digital" {
+		$command2 = "echo -e 'ac=hwac3,hwdts, #TWEAKER\nao=alsa:device=plughw=$card.$device #TWEAKER' >> $mplayer_conf";
+		if (my $error = execute_shell_command($command2)) {
+		    $logger->error("ERROR: $error");
+		    $logger->error("ERROR: Unable to implement option $option.");
+		    exit(-1);
+		}
+
+		my @digital_audio_device_patterns = (
+		    [
+		     # Asus T3-M2NC51PV onboard audio
+		     ".*0403.*10de.*026c.*1043.*821f", "T3-M2NC51PV", "sed -i 's/plughw.* #TWEAKER/spdif #TWEAKER/g' $mplayer_conf"
+		    ]
+		    );
+
+		foreach my $pattern_and_workaround_command (@digital_audio_device_patterns) {
+		    my $pattern=@$pattern_and_workaround_command[0];
+		    my $name=@$pattern_and_workaround_command[1];
+		    my $workaround_command=@$pattern_and_workaround_command[2];
+		    
+		    if (execute_shell_command('lspci -mn | grep -e "'.$pattern.'"')) {
+			$logger->info("Applying workaround for $name audio");
+			execute_shell_command($workaround_command);
+			last;
+		    }
+		}
+	    }
+	}
+	$logger->info("Edited $mplayer_conf for $option audio.");
+    }
+
+    sub edit_xine_conf {
+	my($option)=@_;
+	# delete any old entries that Tweaker made, relevant to this particular edit
+	my $delete_old_tweaker_edits = "sed -i -e '/^.*audio.output.speaker_arrangement.*#TWEAKER/d' -e '/^.*audio.synchronization.passthrough_offset.*#TWEAKER/d' $xine_conf";
+	# comment out entries that some other process may have made
+	my $comment_out_external_edits = "sed -i -e 's/^\\(audio.output.speaker_arrangement.*\\)/#\\1/g' -e 's/^\\(audio.synchronization.passthrough_offset.*\\)/#\\1/g' $xine_conf";
+	my $logger = get_logger('tweaker.script');
+
+	if (my $error = execute_shell_command("$delete_old_tweaker_edits && $comment_out_external_edits")) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+
+	my $command1;
+
+	switch($option) {
+	    case "analogstereo" {
+		$command1 = "echo 'audio.output.speaker_arrangement:Stereo 2.0 #TWEAKER' >> $xine_conf";
+	    }
+	    case "analogsurround" {
+		# Not supported yet.
+	    }
+	    case "digital" {
+		$command1 = "echo -e 'audio.output.speaker_arrangement:Pass Through #TWEAKER\naudio.synchronization.passthrough_offset:$device #TWEAKER' >> $xine_conf";
+	    }
+	}
+	if (my $error = execute_shell_command($command1)) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	$logger->info("Edited $xine_conf for $option audio.");
+    }
+
+    sub reload_modules {
+	my($option) = @_;
+	my $command = "update-modules; depmod -a; rmmod snd-pcm-oss; modprobe snd-pcm-oss";
+	my $logger = get_logger('tweaker.script');
+
+	if (my $error = execute_shell_command($command)) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	$logger->info("Reloaded sound modules for $option audio.");
+    }
+    
+    sub set_mixer_values {
+	my($option) = @_;
+	my $logger = get_logger('tweaker.script');
+
+	# Some sound devices work poorly in their default state, so we will load in a known-good
+	# state for them.
+
+	my @digital_audio_device_patterns = (
+	    [
+	     # Chaintech AV-710
+	     ".*0401.*1412.*1724.*1412.*1724", "AV710"
+	    ],
+	    [
+	     # Realtek ALC888 High Definition onboard Audio
+	     ".*0403.*10de.*055c.*1565.*820c", "ALC888"
+	    ]
+	    );
+	
+	foreach my $pattern_and_name_pairing (@digital_audio_device_patterns) {
+	    my $name=@$pattern_and_name_pairing[1];
+	    my $pattern=@$pattern_and_name_pairing[0];
+	    
+	    if (execute_shell_command('lspci -mn | grep -e "'.$pattern.'"')) {
+		$logger->info("Applying ALSA state workaround for $name audio");
+		execute_shell_command("/bin/cp \$TWEAKER_ROOT/fs/var/lib/alsa/$name.asound.state /var/lib/alsa/asound.state");
+		execute_shell_command("alsactl restore");
+		last;
+	    }
+	}
+
+	# Now, do what works for all sound devices.
+	
+	my $command = "su - mythtv -c \"aumix -v 70 -m 0 -l 0 -l R -w 70\""; # ok for analog and digital
+
+	if (my $error = execute_shell_command($command)) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+
+	if ("$option" eq "digital") {
+	    # Poll system for IEC958 controls, and try to turn them all 'on'
+	    $command = "amixer scontrols | grep IEC958";
+	    my @results = split('\n', execute_shell_command($command));
+	    foreach my $line (@results) {
+		if ($line =~ /Simple mixer control (.*),.*/i) {
+		    $command = "su - mythtv -c \"amixer set $1 on\""; # Tries to set all IEC958 devices to 'on'
+		    # but some are just placeholders and can't be turned 'on', therefore don't error out if we fail
+		    execute_shell_command($command);
+		}
+	    }
+	}
+	execute_shell_command("alsactl store"); # persist the above change(s)
+	$logger->info("Reset mixer volume levels for $option audio.");
+    }
+
+    sub edit_mythtv_configuration {
+	my($option)=@_;
+
+	$dbconnectionstring = get_mythtv_connection_string();
+	
+	if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	    switch ($option) {
+		case "analogstereo" {
+		    change_or_make_setting('AC3PassThru', '0') || exit -1;
+		    change_or_make_setting('DTSPassThru', '0') || exit -1;
+		    change_or_make_setting('MTDac3Flag', '0') || exit -1;
+		    change_or_make_setting('MythControlsVolume', '1') || exit -1;
+		}
+		case "analogsurround" {
+		    # Not supported yet.
+		}
+		case "digital" {
+		    change_or_make_setting('AC3PassThru', '1') || exit -1;
+		    change_or_make_setting('DTSPassThru', '1') || exit -1;
+		    change_or_make_setting('MTDac3Flag', '1') || exit -1;
+		    change_or_make_setting('MythControlsVolume', '0') || exit -1;
+		}
+	    }
+	    change_or_make_setting('AudioOutputDevice', 'ALSA:default') || exit -1;
+	    change_or_make_setting('MixerDevice', 'ALSA:default') || exit -1;
+	    change_or_make_setting('MusicAudioDevice', 'default') || exit -1;
+	} else {
+	    exit -1;
+	}
+	disconnect_from_db();
+    }
+
+    generate_asound_conf($option);
+    edit_mplayer_conf($option);
+    edit_xine_conf($option);
+    reload_modules($option);
+    set_mixer_values($option);
+    edit_mythtv_configuration($option);
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+    my @digital_audio_device_patterns = (
+	[
+	 # Pattern matches for PCI IDs of perfect devices, comma-separated within the brackets
+	 [ ".*0403.*8086.*284b.*8086.*2504", # Intel DG965WH onboard audio
+	   ".*0403.*10de.*026c.*1043.*821f", # Asus T3-M2NC51PV onboard audio
+	   ".*0403.*8086.*293e.*8086.*3001"  # Intel AusDragon onboard audio
+	 ],
+	 "optional|Your sound device works very well in digital mode.  Unless you don't have a receiver that can accept digital inputs, you should use digital mode.",
+	],
+	[
+	 # Pattern matches for PCI IDs of suboptimal devices, comma-separated within the brackets
+	 [ ".*0401.*1412.*1724.*1412.*1724", # Chaintech AV-710 PCI card (regressions in R5.5)
+	 ],
+	 "inadvisable|Your sound device is known to have some problems in digital mode, but you may try it if you like, starting at low volume in your receiver.",
+	],
+	[
+	 [ ], # Leave blank for unknown devices
+	 "optional|Your sound device may or may not work well in digital mode.  Please tell us if it works, and how well.",
+	]
+	);
+
+    switch ($option) {
+	case "analogstereo" {
+	    my ($card, $device) = poll_for_digital_output_device;
+	    if (($card >= 0) && ($device >= 0)) { # A digital output device was detected.
+		recommendation_level("optional");
+	    } else {
+		recommendation_level("recommended", "You seem to have no digital output option.  If this is not true, please tell us.");
+	    }
+	}
+	case "analogsurround" {
+	    recommendation_level("unsupported", "No configuration data exists yet for this option.");
+	}
+	case "digital" {
+	    my ($card, $device) = poll_for_digital_output_device;
+	    if (($card >= 0) && ($device >= 0)) { # A digital output device was detected.
+		my $recommendation_return_string;
+		foreach my $pattern_and_recommendation_pairing (@digital_audio_device_patterns) {
+		    $recommendation_return_string=@$pattern_and_recommendation_pairing[1];
+		    foreach my $patterns (@$pattern_and_recommendation_pairing[0]) {
+			foreach my $pattern (@$patterns) {
+			    if (execute_shell_command('lspci -mn | grep -e "'.$pattern.'"')) {
+				recommendation_level($recommendation_return_string);
+				return;
+			    }
+			}
+		    }
+		}
+		# Because we didn't return a recommendation level above, return a default recommendation level.
+		recommendation_level($recommendation_return_string);
+		return;
+	    } else { # No digital output device was detected.
+		recommendation_level("not available");
+	    }
+	}
+    }
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_audio_RP.pl b/abs/core-testing/tweaker/bin/twk_audio_RP.pl
new file mode 100755
index 0000000..2642fda
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_audio_RP.pl
@@ -0,0 +1,307 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'analogstereo', 'analogsurround', 'digital' );
+
+# Poll the system to find the digital output device.
+sub poll_for_digital_output_device {
+    my $card=-1;
+    my $device=-1;
+    my $poll_command = "aplay -l | grep card";
+
+    my @digital_matches = ( "digital", "IEC958" );
+    
+    my $results = execute_shell_command($poll_command);
+    foreach my $digital_match (@digital_matches) {
+	if ($results =~ /card (\d):.*device (\d).*$digital_match.*/i) {
+	    $card = $1;
+	    $device = $2;
+	}
+    }
+    return ($card, $device);
+}
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+    use vars qw($card);
+    use vars qw($device);
+    ($card, $device) = poll_for_digital_output_device;
+    use vars qw($asound_conf);
+    $asound_conf = "/etc/asound.conf";
+    use vars qw($mplayer_conf);
+    $mplayer_conf = "/etc/mplayer/mplayer.conf";
+    use vars qw($xine_conf);
+    $xine_conf = "/home/mythtv/.xine/config";
+    
+    sub generate_asound_conf {
+	my($option) = @_;
+	my $command1;
+	my $command2;
+
+	switch ($option) {
+	    case "analogstereo" {
+		# Analog stereo usually needs no asound.conf, but sometimes it needs to be there to
+		# support more than one app using sound simultaneously.
+		$command1 = "[ -e $asound_conf ] && /bin/cp $asound_conf $asound_conf-TWEAKERBACKUP";
+		# Assumption: The proper analog device is always device 0.
+		if ($card >= 0) {
+		    $command2 = "sed -i 's/hw:.,./hw:$card,0/g' $asound_conf";
+		} else {
+		    $command2 = "sed -i 's/hw:.,./hw:0,0/g' $asound_conf";
+		}
+	    }
+	    case "analogsurround" {
+		# Not supported yet.
+	    }
+	    case "digital" {
+		# Digital audio starts with an asound.conf that references the
+		# hardware address on the sound device corresponding to digital
+		# output.
+		$command1 = "/bin/cp \$TWEAKER_ROOT/fs$asound_conf /etc/";
+		if (($card >= 0) && ($device >= 0)) {
+		    $command2 = "sed -i 's/hw:.,./hw:$card,$device/g' $asound_conf";
+		} else {
+		    my $logger = get_logger('tweaker.script');
+		    $logger->error("ERROR: Unable to poll for digital sound output device.");
+		    exit(-1);
+		}
+	    }
+	}
+	if (my $error = execute_shell_command($command1)) {
+	    my $logger = get_logger('tweaker.script');
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	if (my $error = execute_shell_command($command2)) {
+	    my $logger = get_logger('tweaker.script');
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	my $logger = get_logger('tweaker.script');
+	$logger->info("Generated $asound_conf for $option audio.");
+    }
+
+    sub edit_mplayer_conf {
+	my($option) = @_;
+	# delete any old entries that Tweaker made, relevant to this particular edit
+	my $delete_old_tweaker_edits = "sed -i '/^.*a[o,c].*=.*#TWEAKER/d' $mplayer_conf";
+	# comment out old entries that some other process may have made
+	my $comment_out_external_edits = "sed -i 's/^\\(a[o,c].*=.*\\)/#\\1/g' $mplayer_conf";
+	my $command1;
+	my $command2="";
+	my $logger = get_logger('tweaker.script');
+
+	if (my $error = execute_shell_command("$delete_old_tweaker_edits && $comment_out_external_edits")) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+
+	switch($option) {
+	    case "analogstereo" {
+		# No additional work needed; the above commands comment out ao and ac entries,
+		# leaving a baseline which works with analog stereo.
+	    }
+	    case "analogsurround" {
+		# Not supported yet.
+	    }
+	    case "digital" {
+		$command2 = "echo -e 'ac=hwac3,hwdts, #TWEAKER\nao=oss:/dev/adsp #TWEAKER' >> $mplayer_conf";
+		if (my $error = execute_shell_command($command2)) {
+		    $logger->error("ERROR: $error");
+		    $logger->error("ERROR: Unable to implement option $option.");
+		    exit(-1);
+		}
+	    }
+	}
+	$logger->info("Edited $mplayer_conf for $option audio.");
+    }
+
+    sub edit_xine_conf {
+	my($option)=@_;
+	# delete any old entries that Tweaker made, relevant to this particular edit
+	my $delete_old_tweaker_edits = "sed -i -e '/^.*audio.output.speaker_arrangement.*#TWEAKER/d' -e '/^.*audio.synchronization.passthrough_offset.*#TWEAKER/d' $xine_conf";
+	# comment out entries that some other process may have made
+	my $comment_out_external_edits = "sed -i -e 's/^\\(audio.output.speaker_arrangement.*\\)/#\\1/g' -e 's/^\\(audio.synchronization.passthrough_offset.*\\)/#\\1/g' $xine_conf";
+	my $logger = get_logger('tweaker.script');
+
+	if (my $error = execute_shell_command("$delete_old_tweaker_edits && $comment_out_external_edits")) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+
+	my $command1;
+
+	switch($option) {
+	    case "analogstereo" {
+		$command1 = "echo 'audio.output.speaker_arrangement:Stereo 2.0 #TWEAKER' >> $xine_conf";
+	    }
+	    case "analogsurround" {
+		# Not supported yet.
+	    }
+	    case "digital" {
+		$command1 = "echo -e 'audio.output.speaker_arrangement:Pass Through #TWEAKER\naudio.synchronization.passthrough_offset:$device #TWEAKER' >> $xine_conf";
+	    }
+	}
+	if (my $error = execute_shell_command($command1)) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	$logger->info("Edited $xine_conf for $option audio.");
+    }
+
+    sub reload_modules {
+	my($option) = @_;
+	my $command = "update-modules; depmod -a; rmmod snd-pcm-oss; modprobe snd-pcm-oss";
+	my $logger = get_logger('tweaker.script');
+
+	if (my $error = execute_shell_command($command)) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	$logger->info("Reloaded sound modules for $option audio.");
+    }
+    
+    sub set_mixer_values {
+	my($option) = @_;
+	my $command = "aumix -v 70 -m 0 -l 0 -l R -w 70";
+	my $logger = get_logger('tweaker.script');
+
+	if (my $error = execute_shell_command($command)) {
+	    $logger->error("ERROR: $error");
+	    $logger->error("ERROR: Unable to implement option $option.");
+	    exit(-1);
+	}
+	$logger->info("Reset mixer volume levels for $option audio.");
+    }
+
+    sub edit_mythtv_configuration {
+	my($option)=@_;
+
+	$dbconnectionstring = get_mythtv_connection_string();
+	
+	if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	    switch ($option) {
+		case "analogstereo" {
+		    change_or_make_setting('AudioOutputDevice', '/dev/dsp') || exit -1;
+		    change_or_make_setting('AC3PassThru', '0') || exit -1;
+		    change_or_make_setting('DTSPassThru', '0') || exit -1;
+		    change_or_make_setting('MTDac3Flag', '0') || exit -1;
+
+		}
+		case "analogsurround" {
+		    # Not supported yet.
+		}
+		case "digital" {
+		    change_or_make_setting('AudioOutputDevice', '/dev/adsp') || exit -1;
+		    change_or_make_setting('AC3PassThru', '1') || exit -1;
+		    change_or_make_setting('DTSPassThru', '1') || exit -1;
+		    change_or_make_setting('MTDac3Flag', '1') || exit -1;
+		}
+	    }
+	    change_or_make_setting('MusicAudioDevice', 'default') || exit -1;
+	    change_or_make_setting('MythControlsVolume', '0') || exit -1;	    
+	} else {
+	    exit -1;
+	}
+	disconnect_from_db();
+    }
+
+    generate_asound_conf($option);
+    edit_mplayer_conf($option);
+    edit_xine_conf($option);
+    reload_modules($option);
+    set_mixer_values($option);
+    edit_mythtv_configuration($option);
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+    my @digital_audio_device_patterns = (
+	[
+	 [ ".*0401.*1412.*1724.*1412.*1724" ],   # Pattern matches for PCI IDs of perfect devices, comma-separated within the brackets
+	 "optional|Your sound device works very well in digital mode.  Unless you don't have a receiver that can accept digital inputs, you should use digital mode.",
+	],
+	[
+	 [ ".*0403.*8086.*284b.*8086.*2504" ],   # Pattern matches for PCI IDs of suboptimal devices, comma-separated within the brackets
+	 "inadvisable|Your sound device is known to have some problems in digital mode, but you may try it if you like, starting at low volume in your receiver.",
+	],
+	[
+	 [ ], # Leave blank for unknown devices
+	 "optional|Your sound device may or may not work well in digital mode.  Please tell us if it works, and how well.",
+	]
+	);
+
+    switch ($option) {
+	case "analogstereo" {
+	    my ($card, $device) = poll_for_digital_output_device;
+	    if (($card >= 0) && ($device >= 0)) { # A digital output device was detected.
+		recommendation_level("optional");
+	    } else {
+		recommendation_level("recommended", "You seem to have no digital output option.  If this is not true, please tell us.");
+	    }
+	}
+	case "analogsurround" {
+	    recommendation_level("unsupported", "No configuration data exists yet for this option.");
+	}
+	case "digital" {
+	    my ($card, $device) = poll_for_digital_output_device;
+	    if (($card >= 0) && ($device >= 0)) { # A digital output device was detected.
+		my $recommendation_return_string;
+		foreach my $pattern_and_recommendation_pairing (@digital_audio_device_patterns) {
+		    $recommendation_return_string=@$pattern_and_recommendation_pairing[1];
+		    foreach my $patterns (@$pattern_and_recommendation_pairing[0]) {
+			foreach my $pattern (@$patterns) {
+			    if (execute_shell_command('lspci -mn | grep -e "'.$pattern.'"')) {
+				recommendation_level($recommendation_return_string);
+				return;
+			    }
+			}
+		    }
+		}
+		# Because we didn't return a recommendation level above, return a default recommendation level.
+		recommendation_level($recommendation_return_string);
+		return;
+	    } else { # No digital output device was detected.
+		recommendation_level("not available");
+	    }
+	}
+    }
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_cpu.pl b/abs/core-testing/tweaker/bin/twk_cpu.pl
new file mode 100755
index 0000000..04a57cc
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_cpu.pl
@@ -0,0 +1,132 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'low', 'medium', 'high' );
+
+# These values are in BOGOMIPS.  They are arbitrary and will be refined, if possible,
+# as more data is collected.  Obviously, BOGOMIPS are not the best CPU benchmarking
+# tool.
+my $rrd_threshold = 1650; # bogomips at or below this mean rrdtool is too much of a strain
+#my $rrd_threshold = 3365;
+
+my $low_threshold = 2500;
+my $medium_threshold = 4050;
+my $high_threshold = 7400;
+
+# Poll the system to find the CPU type.  Currently, we use bogomips (the bogosity of which
+# is in the name itself) to guess how powerful the CPU is.
+sub get_CPU_strength {
+    my $bogomips=0;
+    my @results = split("\n", execute_shell_command("cat /proc/cpuinfo | grep bogomips"));
+    foreach my $result (@results) { # Count the bogomips for each core.  Again, this is rough.
+	if ($result =~ /bogomips\s*:\s*(\d+.\d+)/) {
+	    $bogomips+=$1;
+	}
+    }
+    return $bogomips;
+}
+
+# ??? Need to test for storage groups and LVM
+# Specific to KnoppMyth
+sub get_HDD_size {
+    my @HDD_info = split(" +", execute_shell_command("df -h /myth | grep myth"));
+
+    chop($HDD_info[1]);
+    return($HDD_info[1] || 0);
+}
+
+#sub disable_rrdtool {
+#    
+#}
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    # ??? This will need some work to properly integrate with MythTV 0.21's own CPU-based playback settings.
+    # For now, set profile to CPU++ no matter what CPU type, and just change what CPU++ provides.
+
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	switch ($option) {
+	    case "low" {
+		change_or_make_setting('AutoCommflagWhileRecording', '0') || exit -1;
+		change_or_make_setting('Theme', 'Iulius') || exit -1; # Low eye candy, high performance
+		#change_or_make_setting('PreferredMPEG2Decoder', 'libmpeg2') || exit -1; # Least CPU usage, lowest quality
+		#change_or_make_entry("displayprofiles", [["pref_decoder", "libmpeg2"]], [["profilegroupid", "1"], ["profileid", "1"]]) || exit -1; # Least CPU usage, lowest quality
+		change_or_make_setting('DefaultVideoPlaybackProfile', 'CPU--') || exit -1; # best playback defaults for weak hardware
+		# Should libmpeg2/ffmpeg be triggered based on CPU type?  libmpeg2 is usually
+		# recommended for AMD CPUs.
+		# ??? when to use xvmc?
+
+		# Weak CPUs may actually be too weak to run rrdtool without causing problems.
+#		if (get_CPU_strength() <= $rrd_threshold) {
+#		    disable_rrdtool();
+#		}
+	    }
+	    case "medium" {
+		change_or_make_setting('AutoCommflagWhileRecording', '0') || exit -1;
+		change_or_make_setting('Theme', 'blootubelite-wide') || exit -1; # Moderate eye candy, moderate performance
+		#change_or_make_setting('PreferredMPEG2Decoder', 'libmpeg2') || exit -1; # Least CPU usage, lowest quality
+		#change_or_make_entry("displayprofiles", [["pref_decoder", "libmpeg2"]], [["profilegroupid", "1"], ["profileid", "1"]]) || exit -1; # Least CPU usage, lowest quality
+		change_or_make_setting('DefaultVideoPlaybackProfile', 'CPU+') || exit -1; # best playback defaults for average hardware
+		# ??? when to use xvmc?
+	    }
+	    case "high" {
+		change_or_make_setting('AutoCommflagWhileRecording', '1') || exit -1;
+		# ??? Interacts with screen aspect ratio
+		change_or_make_setting('Theme', 'blootube-wide') || exit -1; # High eye candy, potentially low performance
+		#change_or_make_entry("displayprofiles", [["pref_decoder", "ffmpeg"]], [["profilegroupid", "1"], ["profileid", "1"]]) || exit -1; # Most CPU usage, best quality
+		#change_or_make_setting('PreferredMPEG2Decoder', 'ffmpeg') || exit -1; # Most CPU usage, best quality
+		change_or_make_setting('DefaultVideoPlaybackProfile', 'CPU++') || exit -1; # best playback defaults for powerful hardware
+		# ??? when to use xvmc?
+	    }
+	}
+	change_or_make_setting('UseXvMcVld', '0') || exit -1;
+	#change_or_make_entry("displayprofiles", [["pref_deint0", "onefield"]], [["profilegroupid", "1"], ["profileid", "1"]]) || exit -1; # Most CPU usage, best quality
+	change_or_make_setting('OSDTheme', 'blootube-osd') || exit -1;
+	change_or_make_setting('PlayBoxShading', '0') || exit -1;
+	change_or_make_setting('PlaybackExitPrompt', '2') || exit -1;
+    } else {
+	exit -1;
+    }
+    disconnect_from_db();
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+
+    threshold_test($option, get_CPU_strength(), "CPU", $low_threshold, $medium_threshold, $high_threshold);
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_dragon.pl b/abs/core-testing/tweaker/bin/twk_dragon.pl
new file mode 100755
index 0000000..8bfccca
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_dragon.pl
@@ -0,0 +1,66 @@
+#!/usr/bin/perl -w
+
+# Copyright 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+# List all the options that this script supports.  Make sure it matches what's in
+# the corresponding .tcf entry.
+set_known_options( 'all' );
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+    my $logger = get_logger('tweaker.script');
+
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	# Overrides SQL changes made in twk_general.pl
+	change_or_make_setting('DVDPlayerCommand', 'mplayer dvd:\/\/ -dvd-device %d -fs -zoom -vc mpeg12,ffmpeg12-vo xv -vf pp=lb') || return -1;
+	change_or_make_setting('VCDPlayerCommand', 'mplayer vcd:\/\/ -cdrom-device %d -fs -zoom -vo xv -vf pp=lb') || return -1;
+
+	# Overrides SQL changes made in twk_tuners.pl
+	# WARNING: Very big harccoded hack.
+	do_query("UPDATE cardinput SET sourceid='10' WHERE sourceid='20'") || return -1;
+    } else {
+	my $logger = get_logger('tweaker.script');
+	$logger->error("ERROR: Unable to connect to mythconverg database");
+	$logger->error("ERROR: Unable to implement option $option.");
+	exit -1;
+    }
+    disconnect_from_db();
+}
+
+# Poll the system to see what recommendationlevel the given option has on the system.
+sub poll_options {
+    my($option) = @_;
+    recommendation_level("recommended", "These tweaks benefit all users.");
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_fingerprint_hardware.sh b/abs/core-testing/tweaker/bin/twk_fingerprint_hardware.sh
new file mode 100755
index 0000000..25111dd
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_fingerprint_hardware.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This script will grab USB and PCI data and dump it to a file for the
+# user to post.
+
+output_file=/tmp/fingerprint.txt
+maintainer="Human"
+thread=""
+
+Usage() {
+    echo "USAGE:"
+    echo `basename $0` " [-a]"
+    echo "-a: advanced mode"
+    echo
+    echo "EXAMPLE: $0"
+    exit 3
+}
+
+while getopts "a" FLAG ; do
+    case "$FLAG" in
+	a) ADVANCED_MODE=1;;
+	*) Usage;;
+    esac
+done
+
+check_for_root() {
+    if [ `whoami` != "root" ]; then
+	echo -n `basename $0`
+	echo " must be run as root.  Exiting.";
+	exit;
+    fi
+}
+
+poll_PCI() {
+    echo \
+"#####
+# lspci -vv
+#####" >> $output_file
+    lspci -vv >> $output_file
+    echo ""  >> $output_file
+    
+    echo \
+"#####
+# lspci -mn
+#####" >> $output_file
+    lspci -mn >> $output_file
+    echo ""  >> $output_file
+}
+
+poll_USB() {
+    echo \
+"#####
+# /proc/bus/usb/devices
+#####" >> $output_file
+cat /proc/bus/usb/devices >> $output_file
+}
+
+instruct() {
+    echo "Your hardware fingerprint is in $output_file"
+    echo -n "Please PM it to $maintainer"
+    if [ "$thread" != "" ]; then
+	echo " or post it to"
+	echo -n "$thread"
+    fi
+    echo "."
+    echo "It should be accompanied by a list of changes that you made to the baseline
+installation in order to improve MythTV on your hardware."
+    
+    echo "If you feel up to the task, feel free to prune out any entries that are for
+very low-level devices like memory controllers, USB subsystems, etc. before
+sending the fingerprint."
+}
+
+main() {
+    check_for_root
+    > $output_file
+    poll_PCI
+    poll_USB
+    instruct
+}
+
+main
+
diff --git a/abs/core-testing/tweaker/bin/twk_general.pl b/abs/core-testing/tweaker/bin/twk_general.pl
new file mode 100755
index 0000000..86da85d
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_general.pl
@@ -0,0 +1,541 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'all' );
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    # KnoppMyth-centric file-level tweaks
+    sub file_tweaks {
+	my($option) = @_;
+
+	my @commands = (
+	    # keymap tweaks
+	    "twk_keymap.sh",
+	    # fix distro name in various html
+	    "fix_index",
+	    # fix 'motion' index to use our hostname
+	    "sed -i 's/MythTVhost/`hostname`/g' /var/www/motion/index.html",
+	    #
+	    # These may or may not be necessary after GF21
+	    #
+
+	    # Change from "sid" to "stable" repository
+	    "sed -i \"s/sid/stable/g\" /etc/apt/sources.list",
+	    
+	    # Fix bizarre ownership of files:
+	    "chown -f root: /usr/bin/get_dual.sh",
+	    "chown -fR root: /usr/lib/krp",
+	    "chown -fR root: /usr/local/bin",
+	    "chown -f root: /usr/share/man/man1/tv_grab_au.1.gz",
+	    "chown -f root: /usr/share/xmltv/tv_grab_au/channel_ids",
+	    "chown -fR root:src /usr/src/",
+
+	    # Install extra software
+	    #"apt-get update && apt-get -y -q=2 install frozen-bubble &",
+	    # prevent fluxbox toolbar from appearing
+	    "fix_toolbar.sh"
+	    );
+	
+	foreach my $command (@commands) {
+	    if (my $error = execute_shell_command($command)) {
+		my $logger = get_logger('tweaker.script');
+		$logger->error("$error");
+		$logger->error("Unable to implement option $option with command $command.");
+	    }
+	}
+	return 1;
+    }
+    
+    # Enhance the default MythTV SQL
+    sub SQL_tweaks {
+	my($option) = @_;
+
+	$dbconnectionstring = get_mythtv_connection_string();
+	
+	if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	    # ??? This also exists in mkmythdir - it should be centralized
+	    my $SQL_DirData="/myth";			# Top level dir. of structure,
+	    my $SQL_DirTV="$SQL_DirData/tv";		# TV Recordings,
+	    my $SQL_DirMusic="$SQL_DirData/music/";	# Music storage,
+	    my $SQL_DirGames="$SQL_DirData/games";	# Games dir,
+	    my $SQL_DirPics="$SQL_DirData/gallery";	# Pictures directory.
+	    my $SQL_DirTmp="$SQL_DirData/tmp";		# DVD temp
+	    my $SQL_DirVideo="$SQL_DirData/video";	# videos
+	    
+	    # ??? Eventually, put these into a text file so that 1) people can contribute without having to code, and 2) the data
+	    # stays separate from the code and can be re-used by other (non-Perl) implementations
+
+	    #
+	    # keybindings
+	    #
+	    change_or_make_entry("keybindings", [["keylist", "l"]], [["context", "Gallery"], ["action","PLAY"]]);
+	    change_or_make_entry("keybindings", [["keylist", ">"]], [["context", "Music"], ["action","FFWD"]]);
+	    change_or_make_entry("keybindings", [["keylist", "<"]], [["context", "Music"], ["action","RWND"]]);
+	    change_or_make_entry("keybindings", [["keylist", "PgDown,.,Z,End"]], [["context", "Music"], ["action","NEXTTTRACK"]]);
+	    change_or_make_entry("keybindings", [["keylist", "PgUp,Q,Home"]], [["context", "Music"], ["action","PREVTRACK"]]);
+	    change_or_make_entry("keybindings", [["keylist", "P,l"]], [["context", "Music"], ["action","PAUSE"]]);
+
+	    change_or_make_entry("keybindings", [["keylist", ">"]], [["context", "Stream"], ["action","FORWARD"]]);
+	    change_or_make_entry("keybindings", [["keylist", "<"]], [["context", "Stream"], ["action","REWIND"]]);
+	    change_or_make_entry("keybindings", [["keylist", "F"]], [["context", "Stream"], ["action","FULLSCREEN"]]);
+	    change_or_make_entry("keybindings", [["keylist", "|,\\,F9,Volume Mute"]], [["context", "Stream"], ["action","MUTE"]]);
+	    change_or_make_entry("keybindings", [["keylist", "Y"]], [["context", "Stream"], ["action","STOREMARKED"]]);
+	    
+	    change_or_make_entry("keybindings", [["keylist", "l"]], [["context", "TV Frontend"], ["action","PLAYBACK"]]);
+	    
+	    change_or_make_entry("keybindings", [["keylist", "l"]], [["context", "TV Playback"], ["action","PLAY"]]);
+	    change_or_make_entry("keybindings", [["keylist", "P"]], [["context", "TV Playback"], ["action","PAUSE"]]); # default, but here for completeness
+
+	    #
+	    # better awareness of different video extensions
+	    #
+	    change_or_make_entry("videotypes", [["playcommand", "mplayer-resumer.pl -fs -zoom -vc theora,fftheora, -vo xv %s"]], [["extension", "ogg"]]);
+	    change_or_make_entry("videotypes", [["playcommand", "mplayer-resumer.pl -fs -zoom -vc theora,fftheora, -vo xv %s"]], [["extension", "theora"]]);
+	    foreach my $video_ext ("mp2", "tp", "ts", "m2p", "nuv") {
+		change_or_make_entry("videotypes", [["playcommand", "Internal"]], [["extension", $video_ext]]);
+	    }
+	    foreach my $non_video_ext ("jpg", "par2") {
+		change_or_make_entry("videotypes", [["f_ignore", "1"]], [["extension", $non_video_ext]]);
+	    }
+	    
+	    #
+	    # games, game players, and emulators
+	    #
+	    my @players = (
+		 [ 'SDLMAME', '/myth/games/xmame/roms', '/myth/games/xmame/screenshots', '/usr/games/mame -rp /myth/games/xmame/roms %s', 'MAME' ],
+		 [ 'ZSNES','/myth/games/snes/roms','/myth/games/snes/screens','/usr/bin/zsnes','SNES' ],
+		 [ 'FCEU','/myth/games/nes/roms','/myth/games/nes/screens','/usr/games/fceu','NES' ],
+		 [ 'Frozen Bubble', '', '', '/usr/games/frozen-bubble --fullscreen','PC' ]
+		);
+
+	    foreach my $player (@players) {
+		# These INSERTs will fail if the playername is already present, but we don't error out if it happens.
+		do_query("INSERT INTO gameplayers (playername, rompath, screenshots, commandline, gametype) VALUES ('".join("','",@$player)."');");
+	    }
+	    # somewhat hardwired, but make sure Frozen Bubble shows up in the list of playable games.
+	    do_query("INSERT INTO gamemetadata (system, romname, gamename, genre, year, publisher, rompath, gametype) VALUES ('".
+		     join("','",($players[3][0], $players[3][3], $players[3][0], "action/puzzle", "2006", "Frozen Bubble Team", "/usr/games", $players[3][4]))."');");
+
+	    #
+	    # smart music playlists
+	    #
+	    # categoryid, name
+	    change_or_make_entry("music_smartplaylist_categories", [["name", "Decades"]], [["categoryid", 1]]);
+	    change_or_make_entry("music_smartplaylist_categories", [["name", "Favorite Tracks"]], [["categoryid", 2]]);
+	    change_or_make_entry("music_smartplaylist_categories", [["name", "New Tracks"]], [["categoryid", 3]]);
+
+	    foreach my $decade (60, 70, 80, 90, 100) {
+		my $id = ($decade / 10) - 5;
+		my $query = "INSERT INTO music_smartplaylist_items (smartplaylistid, field, operator, value1, value2) VALUES ($id, 'Year', 'is between'," . ($decade+1900) . "," . ($decade+1909) . ");";
+		do_query($query);
+		$query = "INSERT INTO music_smartplaylists (name, categoryid, matchtype, orderby, limitto) VALUES ('". ($decade+1900) . "\\'s', 1, 'All', 'Artist (A)', 0);";
+		do_query($query);
+	    }
+
+	    my @other_lists = (
+		[
+		 ( 'Rating', 'is greater than', '7', 'Favorite Tracks', 2, 'Artist (A), Album (A)', 0 ),
+		 ( 'Play Count', 'is greater than', '0', '100 Most Played Tracks', 2, 'Play Count (D)', 100 ),
+		 ( 'Play Count', 'is equal to', '0', 'Never Played Tracks', 3, 'Artist (A), Album (A)', 0 )
+		]);
+
+	    my $id=6;
+	    foreach my $other_list (@other_lists) {
+		change_or_make_entry("music_smartplaylist_items", [["field", @$other_list[0]], ["operator", @$other_list[1]], ["value1", @$other_list[2]]], [["smartplaylistid", $id]]);
+		change_or_make_entry("music_smartplaylists", [["name", @$other_list[3]], ["categoryid", @$other_list[4]], ["matchtype", "All"], ["orderby", @$other_list[5]], ["limitto", @$other_list[6]]], [["smartplaylistid", $id]]);
+		$id++;
+	    }
+
+	    #
+	    # default playgroup with time-related settings
+	    #
+	    change_or_make_entry("playgroup", [["skipahead", 10], ["skipback", 5], ["timestretch", 100], ["jump", 1]], [["name", "Default"]]);
+
+	    #
+	    # useful recording profiles and transcoding options
+	    #
+	    change_or_make_entry("profilegroups", [["name", "Software Encoders (v4l based)"], ["cardtype", "V4L"], ["is_default", 1]], [["id", 1]]);
+	    change_or_make_entry("profilegroups", [["name", "MPEG-2 Encoders (PVR-x50, PVR-500)"], ["cardtype", "MPEG"], ["is_default", 1]], [["id", 2]]);
+	    change_or_make_entry("profilegroups", [["name", "Hardware MJPEG Encoders (Matrox G200-TV, Miro DC10, etc)"], ["cardtype", "MJPEG"], ["is_default", 1]], [["id", 3]]);
+	    change_or_make_entry("profilegroups", [["name", "Hardware HDTV"], ["cardtype", "HDTV"], ["is_default", 1]], [["id", 4]]);
+	    change_or_make_entry("profilegroups", [["name", "Hardware DVB Encoders"], ["cardtype", "DVB"], ["is_default", 1]], [["id", 5]]);
+	    change_or_make_entry("profilegroups", [["name", "Transcoders"], ["cardtype", "TRANSCODE"], ["is_default", 1]], [["id", 6]]);
+	    change_or_make_entry("profilegroups", [["name", "FireWire Input"], ["cardtype", "FIREWIRE"], ["is_default", 1]], [["id", 7]]);
+	    change_or_make_entry("profilegroups", [["name", "USB Mpeg-4 Encoder (Plextor ConvertX, etc)"], ["cardtype", "GO7007"], ["is_default", 1]], [["id", 8]]);
+	    change_or_make_entry("profilegroups", [["name", "DBOX2 Input"], ["cardtype", "DBOX2"], ["is_default", 1]], [["id", 9]]);
+	    change_or_make_entry("profilegroups", [["name", "Freebox Input"], ["cardtype", "Freebox"], ["is_default", 1]], [["id", 10]]);
+	    change_or_make_entry("profilegroups", [["name", "HDHomeRun Recorders"], ["cardtype", "HDHOMERUN"], ["is_default", 1]], [["id", 11]]);
+	    change_or_make_entry("profilegroups", [["name", "CRC IP Recorders"], ["cardtype", "CRC_IP"], ["is_default", 1]], [["id", 12]]);
+
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'transcodelossless', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'transcoderesize', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4bitrate', 2200)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4maxquality', 2)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4minquality', 15)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4qualdiff', 3)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'scalebitrate', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4optionvhq', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4option4mv', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4optionidct', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg4optionime', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'encodingthreadcount', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg2bitrate', 4500)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'hardwaremjpegquality', 100)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'hardwaremjpeghdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'hardwaremjpegvdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg2streamtype', 'MPEG-2 PS')");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'mpeg2maxbitrate', 6000)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'samplerate', 48000)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (21, 'volume', 100)");
+
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'transcodelossless', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'transcoderesize', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4bitrate', 2200)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4maxquality', 2)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4minquality', 15)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4qualdiff', 3)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'scalebitrate', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4optionvhq', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4option4mv', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4optionidct', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg4optionime', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'encodingthreadcount', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg2bitrate', 4500)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'hardwaremjpegquality', 100)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'hardwaremjpeghdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'hardwaremjpegvdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg2streamtype', 'MPEG-2 PS')");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'mpeg2maxbitrate', 6000)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'samplerate', 48000)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (22, 'volume', 100)");
+
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (27, 'transcodelossless', 1)");
+
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'transcodelossless', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'transcoderesize', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4bitrate', 2200)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4maxquality', 2)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4minquality', 15)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4qualdiff', 3)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'scalebitrate', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4optionvhq', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4option4mv', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4optionidct', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg4optionime', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'encodingthreadcount', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg2bitrate', 4500)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'hardwaremjpegquality', 100)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'hardwaremjpeghdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'hardwaremjpegvdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg2streamtype', 'MPEG-2 PS')");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'mpeg2maxbitrate', 6000)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'samplerate', 48000)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (28, 'volume', 100)");
+
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'transcodelossless', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'transcoderesize', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4bitrate', 1500)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4maxquality', 2)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4minquality', 15)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4qualdiff', 3)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'scalebitrate', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4optionvhq', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4option4mv', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4optionidct', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg4optionime', 0)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'encodingthreadcount', 1)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg2bitrate', 4500)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'hardwaremjpegquality', 100)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'hardwaremjpeghdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'hardwaremjpegvdecimation', 4)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg2streamtype', 'MPEG-2 PS')");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mpeg2maxbitrate', 6000)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'samplerate', 44100)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'mp3quality', 7)");
+	    do_query("INSERT INTO codecparams (profile, name, value) VALUES (29, 'volume', 100)");
+
+	    # I don't know why we have so many of these.  Only profilegroup 6 seems to matter.
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 1]], [["id", 1]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 1]], [["id", 2]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 1]], [["id", 3]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 1]], [["id", 4]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 2]], [["id", 5]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 2]], [["id", 6]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 2]], [["id", 7]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 2]], [["id", 8]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 3]], [["id", 9]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 3]], [["id", 10]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 3]], [["id", 11]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 3]], [["id", 12]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 4]], [["id", 13]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 4]], [["id", 14]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 4]], [["id", 15]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 4]], [["id", 16]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 5]], [["id", 17]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 5]], [["id", 18]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 5]], [["id", 19]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 5]], [["id", 20]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'RTjpeg/MPEG4'], ["profilegroup", 6], ["videocodec", "MPEG-4"], ["audiocodec", "Uncompressed"]], [["id", 21]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'MPEG2'], ["profilegroup", 6], ["videocodec", "MPEG-4"], ["audiocodec", "Uncompressed"]], [["id", 22]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 8]], [["id", 23]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 8]], [["id", 24]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 8]], [["id", 25]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 8]], [["id", 26]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 6]], [["id", 27]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Medium Quality'], ["profilegroup", 6], ["videocodec", "MPEG-4"], ["audiocodec", "Uncompressed"]], [["id", 28]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 6], ["videocodec", "MPEG-4"], ["audiocodec", "MP3"]], [["id", 29]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 10]], [["id", 30]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 10]], [["id", 31]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 10]], [["id", 32]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 10]], [["id", 33]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 11]], [["id", 34]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 11]], [["id", 35]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 11]], [["id", 36]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 11]], [["id", 37]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 12]], [["id", 38]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 12]], [["id", 39]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 12]], [["id", 40]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 12]], [["id", 41]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 7]], [["id", 42]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 7]], [["id", 43]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 7]], [["id", 44]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 7]], [["id", 45]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Default'], ["profilegroup", 9]], [["id", 46]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Live TV'], ["profilegroup", 9]], [["id", 47]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'High Quality'], ["profilegroup", 9]], [["id", 48]]);
+	    change_or_make_entry("recordingprofiles", [["name", 'Low Quality'], ["profilegroup", 9]], [["id", 49]]);
+
+	    #
+	    # settings
+	    #
+	    change_or_make_setting('AutoCommercialSkip', '1');
+	    change_or_make_setting('AutoExpireWatchedPriority', '1');
+	    change_or_make_setting('BackendServerPort', '6543');
+	    change_or_make_setting('BackendStatusPort', '6544');
+	    change_or_make_setting('CDWriterDevice', 'ATA:1,0,0');
+#	    change_or_make_setting('ChannelOrdering', 'chanid');
+	    change_or_make_setting('CommercialSkipMethod', '255');
+	    change_or_make_setting('DVDBookmarkPrompt', '1');
+	    change_or_make_setting('DVDPlayerCommand', 'xine -pfhq --no-splash dvd:\/\/');
+	    change_or_make_setting('DVDRipLocation', $SQL_DirTmp);
+	    change_or_make_setting('DefaultRipQuality', '1');
+	    change_or_make_setting('DefaultTranscoder', '28'); # change this number if you redefine the transcoders above
+	    change_or_make_setting('Deinterlace', '1');
+	    change_or_make_setting('EITCrawIdleStart','60');
+	    change_or_make_setting('EITIgnoresSource','0');
+	    change_or_make_setting('EITTimeOffset','Auto');
+	    change_or_make_setting('EITTransportTimeout','5');
+	    change_or_make_setting('EPGEnableJumpToChannel','1');
+	    change_or_make_setting('EPGFillType','12');
+	    change_or_make_setting('EnableDVDBookmark','1');
+	    change_or_make_setting('EndOfRecordingExitPrompt','1');
+	    change_or_make_setting('GalleryDir', $SQL_DirPics);
+	    change_or_make_setting('GalleryRecursiveSlideshow', '1');
+	    change_or_make_setting('HaltCommand', 'sudo halt');
+	    change_or_make_setting('JobAllowCommFlag', '1');
+	    change_or_make_setting('JobAllowTranscode', '1');
+	    change_or_make_setting('JobAllowUserJob1', '1');
+	    change_or_make_setting('JobAllowUserJob2', '2');
+	    change_or_make_setting('JobQueueCPU','0');
+	    change_or_make_setting('JobQueueCheckFrequency','60');
+	    change_or_make_setting('JobQueueCommFlagCommand','mythcommflag');
+	    change_or_make_setting('JobQueueMaxSimultaneousJobs','1');
+	    change_or_make_setting('JobQueueTranscodeCommand','mythtranscode');
+	    change_or_make_setting('JobQueueWindowEnd','23:59');
+	    change_or_make_setting('JobQueueWindowStart','00:00');
+	    change_or_make_setting('JobsRunOnRecordHost','0');
+	    change_or_make_setting('LiveTVInAllPrograms','1');
+	    change_or_make_setting('MediaChangeEvents','1');
+	    change_or_make_setting('MonitorDrives', '1');
+	    change_or_make_setting('MusicLocation', $SQL_DirMusic);
+	    change_or_make_setting('MythArchivePng2yuvCmd', 'png2yuv'); # ??? still used?
+	    change_or_make_setting('MythArchiveShareDir','/myth/archive/');
+	    change_or_make_setting('MythArchiveTempDir', $SQL_DirTmp);
+	    change_or_make_setting('MythFillDatabaseArgs', '--quiet');
+	    change_or_make_setting('MythFillDatabaseLog', '/var/log/mythtv/mythfilldatabase.log');
+	    change_or_make_setting('MythFillDatabasePath', '/usr/bin/nice -n 19 /usr/bin/mythfilldatabase');
+	    change_or_make_setting('MythFillEnabled', '1');
+	    change_or_make_setting('MythTVtv', $SQL_DirTV);
+	    change_or_make_setting('PVR350VideoDev', '/dev/video16');
+	    change_or_make_setting('PlayBoxShading', '0');
+	    change_or_make_setting('PlayMode', 'none');
+	    change_or_make_setting('PlaybackExitPrompt', '2');
+	    change_or_make_setting('PlaybackPreviewLowCPU', '1');
+#	    change_or_make_setting('PlaybackReturnPrompt', '1');
+#	    change_or_make_setting('PlaybackReturnPrompt', '2');
+	    change_or_make_setting('RecordFilePrefix',$SQL_DirTV);
+	    change_or_make_setting('SelectChangesChannel', '1');
+	    change_or_make_setting('ShowWholeTree', '1');
+	    change_or_make_setting('SmartChannelChange', '1');
+	    change_or_make_setting('TruncateDeletesSlowly', '1');
+	    change_or_make_setting('UserJob1', 'myth2ipod -cut \"%DIR%\" \"%FILE%\"');
+	    change_or_make_setting('UserJob2', 'myt2xvid3 -cut \"%DIR%\" \"%FILE%\"');
+	    change_or_make_setting('UserJobDesc1', 'Encode for iPod');
+	    change_or_make_setting('UserJobDesc2', 'Transcode to XviD');
+	    change_or_make_setting('UserJobDesc3', 'User Job #3');
+	    change_or_make_setting('UserJobDesc4', 'User Job #4');
+	    change_or_make_setting('VCDPlayerCommand', 'xine -pfhq --no-splash vcd://');
+	    change_or_make_setting('VideoArtworkDir', "$SQL_DirVideo/.covers");
+	    change_or_make_setting('VideoBrowserNoDB', '1');
+	    change_or_make_setting('VideoDefaultPlayer', 'mplayer-resumer.pl -fs -zoom -vo xv %s');
+	    change_or_make_setting('VideoGalleryNoDB', '1');
+	    change_or_make_setting('VideoStartupDir', $SQL_DirVideo);
+	    change_or_make_setting('VideoTreeNoDB', '1');
+	    change_or_make_setting('VisualMode','BumpScope;Gears;Goom;MonoScope;Squares;StereoScope;Synaesthesia;LibVisual-bumpscope;LibVisual-corona;LibVisual-infinite;LibVisual-jakdaw;LibVisual-jess;AlbumArt');
+	    change_or_make_setting('weatherbackgroundfetch', '1');
+	    change_or_make_setting('WebBrowserCommand','/usr/bin/mythbrowser');
+	    change_or_make_setting('WebBrowserHideScrollbars','0');
+	    change_or_make_setting('WebBrowserScrollMode','1');
+	    change_or_make_setting('WebBrowserScrollSpeed','4');
+	    change_or_make_setting('WebBrowserZoomLevel','20');
+	    change_or_make_setting('WebcamDevice','/dev/video');
+	    change_or_make_setting('blockSDWUwithoutClient','1');
+	    change_or_make_setting('mythdvd.DVDPlayerCommand', 'xine -pfhq --no-splash dvd://');
+	    change_or_make_setting('mythvideo.VideoTreeRemember', '1');
+	    change_or_make_setting('mythfilldatabaseLastRunStart','');
+	    change_or_make_setting('mythfilldatabaseLastRunEnd','');
+	    change_or_make_setting('mythfilldatabaseLastRunStatus','');
+
+	    
+
+#	    # As much of MythWeather as we can automate for now
+	    my $units = 0;
+	    my $metric_units = do_query("SELECT * FROM settings WHERE value='SIUnits' AND data='YES'");
+	    if ($metric_units) {
+		$units=0;
+	    } else {
+		$units=1;
+	    }
+#	    change_or_make_entry("weatherscreens", [["draworder", "0"], ["container", "Six Day Forecast"], ["units", $units]], [["screen_id", 1]]);
+#	    change_or_make_entry("weatherdatalayout", [["location", "0"], ["dataitem", "6dlocation"], ["weatherscreens_screen_id", 1]], [["weathersourcesettings_sourceid", 4]]);
+#	    for (my $increment=0; $increment < 6; $increment++) {
+#		change_or_make_entry("weatherdatalayout", [["location", "0"], ["dataitem", "date-$increment"], ["weatherscreens_screen_id", 1]], [["weathersourcesettings_sourceid", 4]]);
+#		change_or_make_entry("weatherdatalayout", [["location", "0"], ["dataitem", "high-$increment"], ["weatherscreens_screen_id", 1]], [["weathersourcesettings_sourceid", 4]]);
+#		change_or_make_entry("weatherdatalayout", [["location", "0"], ["dataitem", "icon-$increment"], ["weatherscreens_screen_id", 1]], [["weathersourcesettings_sourceid", 4]]);
+#		change_or_make_entry("weatherdatalayout", [["location", "0"], ["dataitem", "low-$increment"], ["weatherscreens_screen_id", 1]], [["weathersourcesettings_sourceid", 4]]);
+#	    }
+#	    change_or_make_entry("weatherdatalayout", [["location", "0"], ["dataitem", "updatetime"], ["weatherscreens_screen_id", 1]], [["weathersourcesettings_sourceid", 4]]);
+#
+#	    change_or_make_entry("weathersourcesettings", [["sourceid", "1"], ["source_name", "BBC-Current-XML"], ["update_timeout", "7200"], ["retrieve_timeout", "30"], ["path", "/usr/share/mythtv/mythweather/scripts/bbccurrentxml.pl"], ["author", "Stuart Morgan"], ["version", "0.1"], ["email", "stuart\@tase.co.uk"], ["types", "cclocation,station_id,copyright,observation_time,weather,temp,relative_humidity,wind_dir,pressure,visibility,weather_icon,appt,wind_spdgst"]]);
+#	    change_or_make_entry("weathersourcesettings", [["sourceid", "2"], ["source_name", "BBC-3day-XML"], ["update_timeout", "21600"], ["retrieve_timeout", "30"], ["path", "/usr/share/mythtv/mythweather/scripts/bbcthreedayxml.pl"], ["author", "Stuart Morgan"], ["version", "0.1"], ["email", "stuart\@tase.co.uk"], ["types", "3dlocation,station_id,copyright,weather_icon,date-0,icon-0,low-0,high-0,date-1,icon-1,low-1,high-1,date-2,icon-2,low-2,high-2,updatetime"]]);
+#	    change_or_make_entry("weathersourcesettings", [["sourceid", "3"], ["source_name", "ENVCAN"], ["update_timeout", "900"], ["retrieve_timeout", "30"], ["path", "/usr/share/mythtv/mythweather/scripts/ca_envcan/envcan.pl"], ["author", "Joe Ripley"], ["version", "0.4"], ["email", "vitaminjoe\@gmail.com"], ["types", "cclocation,station_id,copyright,observation_time,observation_time_rfc822,weather,temp,relative_humidity,wind_dir,wind_degrees,wind_speed,wind_gust,pressure,dewpoint,heat_index,windchill,visibility,weather_icon,appt,wind_spdgst,3dlocation,6dlocation,date-0,icon-0,low-0,high-0,date-1,icon-1,low-1,high-1,date-2,icon-2,low-2,high-2,updatetime,date-3,icon-3,low-3,high-3,date-4,icon-4,low-4,high-4,date-5,icon-5,low-5,high-5"]]);
+#	    change_or_make_entry("weathersourcesettings", [["sourceid", "4"], ["source_name", "NDFD-6_day"], ["update_timeout", "900"], ["retrieve_timeout", "30"], ["path", "/usr/share/mythtv/mythweather/scripts/ndfd.pl"], ["author", "Lucien Dunning"], ["version", "0.1"], ["email", "ldunning\@gmail.com"], ["types", "3dlocation,6dlocation,updatetime,high-0,high-1,high-2,high-3,high-4,high-5,low-0,low-1,low-2,low-3,low-4,low-5,icon-0,icon-1,icon-2,icon-3,icon-4,icon-5,date-0,date-1,date-2,date-3,date-4,date-5"]]);
+#	    change_or_make_entry("weathersourcesettings", [["sourceid", "5"], ["source_name", "NDFD-18_hour"], ["update_timeout", "900"], ["retrieve_timeout", "30"], ["path", "/usr/share/mythtv/mythweather/scripts/ndfd18.pl"], ["author", "Lucien Dunning"], ["version", "0.1"], ["email", "ldunning\@gmail.com"], ["types", "18hrlocation,updatetime,temp-0,temp-1,temp-2,temp-3,temp-4,temp-5,18icon-0,18icon-1,18icon-2,18icon-3,18icon-4,18icon-5,pop-0,pop-1,pop-2,pop-3,pop-4,pop-5,time-0,time-1,time-2,time-3,time-4,time-5"]]);
+#	    change_or_make_entry("weathersourcesettings", [["sourceid", "6"], ["source_name", "NWS-Alerts"], ["update_timeout", "600"], ["retrieve_timeout", "30"], ["path", "/usr/share/mythtv/mythweather/scripts/nws-alert.pl"], ["author", "Lucien Dunning"], ["version", "0.1"], ["email", "ldunning\@gmail.com"], ["types", "swlocation,updatetime,alerts"]]);
+#	    change_or_make_entry("weathersourcesettings", [["sourceid", "7"], ["source_name", "NWS-XML"], ["update_timeout", "900"], ["retrieve_timeout", "30"], ["path", "/usr/share/mythtv/mythweather/scripts/nwsxml.pl"], ["author", "Lucien Dunning"], ["version", "0.2"], ["email", "ldunning\@gmail.com"], ["types", "cclocation,station_id,latitude,longitude,observation_time,observation_time_rfc822,weather,temperature_string,temp,relative_humidity,wind_string,wind_dir,wind_degrees,wind_speed,wind_gust,pressure_string,pressure,dewpoint_string,dewpoint,heat_index_string,heat_index,windchill_string,windchill,visibility,weather_icon,appt,wind_spdgst"]]);
+	    
+	    my $ipaddress = execute_shell_command("ifconfig | grep inet.addr | head -1");
+	    $ipaddress =~ s/.*inet addr:(\d+.\d+.\d+.\d+)\s.*/$1/g;
+
+	    # Change from the generic IP address to the real one.
+	    do_query("UPDATE settings SET data='$ipaddress' where data='127.0.0.1'");
+	    do_query("UPDATE settings SET data='$ipaddress' where data='MythTVip'"); # ??? needed?
+	    
+	    my $hostname = execute_shell_command("hostname") || "localhost";
+	    # one table at a time, replace hostname with our actual hostname
+	    # ??? can this be done all at once in MySQL?
+	    foreach my $table ("capturecard", "inuseprograms", "jobqueue", "jumppoints", "keybindings", "music_playlists",
+			       "musicplaylist", "recorded", "settings", "weatherscreens") {
+		do_query("UPDATE $table SET hostname='$hostname'");
+	    }
+
+	    # Some entries in 'settings' should stay NULL:  http://www.mythtv.org/wiki/index.php/Settings_table
+	    # There are fewer entries that stay NULL than there are that should have the hostname set, so while
+	    # it duplicates some effort to change them from NULL to the hostname and them back to NULL, it's
+	    # easier to maintain and more future-proof.
+	    foreach my $entry ("mythfilldatabaseLastRunStart", "mythfilldatabaseLastRunEnd", "mythfilldatabaseLastRunStatus",
+			       "DataDirectMessage", "HaveRepeats", "DBSchemaVer", "DefaultTranscoder", "MythFillSuggestedRunTime",
+			       "MythFillGrabberSuggestsTime", "MasterServerIP", "MasterServerPort", "TVFormat", "VbiFormat", "FreqTable",
+			       "TimeOffset", "MasterBackendOverride", "DeletesFollowLinks", "HDRingbufferSize", "EITTransportTimeout",
+			       "EITIgnoresSource", "EITCrawIdleStart", "startupCommand", "blockSDWUwithoutClient", "idleWaitForRecordingTime",
+			       "StartupSecsBeforeRecording", "WakeupTimeFormat", "SetWakeuptimeCommand", "ServerHaltCommand", "preSDWUCheckCommand",
+			       "WOLbackendConnectRetry", "WOLbackendCommand", "WOLslaveBackendsCommand", "JobsRunOnRecordHost",
+			       "AutoCommflagWhileRecording", "JobQueueCommFlagCommand", "JobQueueTranscodeCommand",
+			       "AutoTranscodeBeforeAutoCommflag", "SaveTranscoding", "UserJobDesc1", "UserJob1", "UserJobDesc2", "UserJob2",
+			       "UserJobDesc3", "UserJob3", "UserJobDesc4", "UserJob4", "PreviewPixmapOffset", "AllRecGroupPassword",
+			       "MaximumCommercialSkip", "CommSkipAllBlanks", "LastFreeCard", "LiveTVPriority", "AutoExpireMethod",
+			       "AutoExpireDefault", "RerecordWatched", "AutoExpireWatchedPriority", "AutoExpireLiveTVMaxAge",
+			       "AutoExpireDayPriority", "AutoExpireExtraSpace", "AutoExpireInsteadOfDelete", "DeletedFifoOrder",
+			       "CommercialSkipMethod", "AggressiveCommDetect", "AutoCommercialFlag", "AutoTranscode", "AutoRunUserJob1",
+			       "AutoRunUserJob2", "AutoRunUserJob3", "AutoRunUserJob4", "OverTimeCategory", "CategoryOverTime",
+			       "EPGEnableJumpToChannel", "LogEnabled", "MythFillEnabled", "MythFillDatabasePath", "MythFillDatabaseArgs",
+			       "MythFillDatabaseLog", "MythFillPeriod", "MythFillMinHour", "MythFillMaxHour", "SchedMoveHigher", "SchedOpenEnd",
+			       "ComplexPriority", "PrefInputPriority", "SingleRecordRecPriority", "FindOneRecordRecPriority", "ArchiveDBSchemaVer",
+			       "FlixDBSchemaVer", "GalleryDBSchemaVer", "GameDBSchemaVer", "MusicDBSchemaVer", "PhoneDBSchemaVer",
+			       "mythvideo.DBSchemaVer", "WeatherDBSchemaVer") {
+		do_query("UPDATE settings SET hostname=NULL WHERE value='$entry'");
+	    }
+	    
+	    change_or_make_setting('MasterBackendOverride','1'); # I don't remember why, but making the hostname NULL is important here
+	    do_query("UPDATE settings SET hostname=NULL WHERE value='MasterBackendOverride'");
+	    
+	    # storagegroup
+	    change_or_make_entry("storagegroup", [["groupname", "Default"], ["hostname", $hostname], ["dirname", $SQL_DirTV]], [["id", 1]]);
+	    
+	    disconnect_from_db();
+
+	    # Fix hostname for iPod feed URLs
+	    $command = "sed -i \"s/hostname\\//$hostname\\//g\" /usr/local/bin/myth2ipod";
+	    execute_shell_command($command);
+	    
+	    # Fix hostname for XViD feed URLs
+	    $command = "sed -i \"s/192.168.0.222\\//$hostname\\//g\" /usr/local/bin/myt2xvid3";
+	    execute_shell_command($command);
+
+	    # Customize default MythTV library.xml to reflect KnoppMyth's wider selection of
+	    # online stream options.
+	    #$command = "sed -i \"/<type>STREAM<\\/type>\$/{N; N; N; N; s/text>.*<\\/text/text>Online Streams<\\/text/; s/action>.*<\\/action/action>MENU is.xml<\\/action/; s/<depends.*depends>//; }\" /usr/share/mythtv/library.xml";
+	    $command = "/bin/cp /usr/share/mythtv/library.xml.km /usr/share/mythtv/library.xml";
+	    execute_shell_command($command);
+	    
+	} else {
+	    my $logger = get_logger('tweaker.script');
+	    $logger->error("Unable to connect to mythconverg database");
+	    $logger->error("Unable to implement option $option.");
+	    return -1;
+	}
+	return 1;
+    }
+
+    file_tweaks($option) || exit -1;
+    SQL_tweaks($option) || exit -1;
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+    recommendation_level("recommended", "These tweaks benefit all users.");
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_graphics.pl b/abs/core-testing/tweaker/bin/twk_graphics.pl
new file mode 100755
index 0000000..b2fdb17
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_graphics.pl
@@ -0,0 +1,149 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+# List all the options that this script supports.  Make sure it matches what's in
+# the corresponding .tcf entry.
+set_known_options( 'low', 'medium', 'high' );
+
+sub check_for_GL() {
+    # ??? Need to make this update a class variable instead of a temp file.
+    my $fps = execute_shell_command("[ -e /tmp/fps ] && cat /tmp/fps") || -1;
+
+    # We think that any nVidia cards at or below NV25 are not well-supported
+    # for GL.  Newer cards are NV34 or above, or have a different starting letter.
+    my $generation = execute_shell_command("lspci -v | grep \"nVidia Corporation\" | grep VGA | awk -FCorporation '{ print \$2 }' | awk '{ print \$1 }'");
+    my $letters = substr($generation,0,2);
+    if (("$letters" eq "NV") && ("$generation" lt "NV24")) {
+	$fps = 0;
+    }
+    
+    if ($fps == -1) {
+	my $result = execute_shell_command("glxinfo | grep 'direct rendering'");
+	if ($result =~ m/direct rendering: Yes/) {
+	    my $logger = get_logger('tweaker.script');
+	    $logger->info("Hardware OpenGL rendering ability detected.");
+	    # Now poll the strength, returning the FPS from glxgears, run at the default resolution.
+	    # I would love to be able to poll this in a way that 1) doesn't pop up a GUI window and
+	    # 2) doesn't rely on manually making a database that maps video cards to performance levels.
+	    $result = execute_shell_command("script -q -c \"glxgears -fullscreen &  sleep 11 ;  pkill -15 glxgears\" | tail -1");
+	    if ($result =~ m/ (\d+)\.\d+ FPS/) {
+		$fps = $1;
+		execute_shell_command("echo $fps > /tmp/fps");
+	    }
+	} else {
+	    $fps = 0;
+	    execute_shell_command("echo $fps > /tmp/fps");
+	}
+    }
+    return $fps;
+}
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+    
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	switch ($option) {
+	    # List all the options that this script supports.  You can have as many as you like.
+	    case "low" {
+		change_or_make_setting('ThemePainter', 'Qt') || exit -1;
+		change_or_make_setting('SlideshowUseOpenGL', '0') || exit -1;
+		change_or_make_setting('SlideshowOpenGLTransition', 'none') || exit -1;
+		change_or_make_setting('SlideshowTransition', 'none') || exit -1;
+	    }
+	    case "medium" {
+		change_or_make_setting('ThemePainter', 'Qt') || exit -1;
+		change_or_make_setting('SlideshowUseOpenGL', '1') || exit -1;
+		change_or_make_setting('SlideshowOpenGLTransition', 'random (gl)') || exit -1;
+		change_or_make_setting('SlideshowTransition', 'random') || exit -1;
+	    }
+	    case "high" {
+		change_or_make_setting('ThemePainter', 'opengl') || exit -1;
+		change_or_make_setting('SlideshowUseOpenGL', '1') || exit -1;
+		change_or_make_setting('SlideshowOpenGLTransition', 'random (gl)') || exit -1;
+		change_or_make_setting('SlideshowTransition', 'random') || exit -1;
+	    }
+
+	    # In all cases, do the following:
+
+	    # enable anti-aliased fonts
+	    my $qtrc="/home/mythtv/.qt/qtrc";
+	    # clean out old Xft settings
+	    my $command = "sed -i 's/.*Xft=.*//g' $qtrc";
+
+	    if (my $error = execute_shell_command($command)) {
+		my $logger = get_logger('tweaker.script');
+		$logger->error("ERROR: $error");
+		$logger->error("ERROR: Unable to implement option $option.");
+		exit(-1);
+	    } else {
+		# Add Xft settings
+		my $command = "sed -i 's/embedFonts=true/embedFonts=true\nenableXft=true\nuseXft=true/g' $qtrc";
+		if  (my $error = execute_shell_command($command)) {
+		    my $logger = get_logger('tweaker.script');
+		    $logger->error("ERROR: $error");
+		    $logger->error("ERROR: Unable to implement option $option.");
+		    exit(-1);
+		} else {
+		    # Make sure to use an AA-capable font
+		    my $command = "sed -i 's/font=.*/font=Sans Serif,12,-1,5,50,0,0,0,0,0/g' $qtrc";
+		    if  (my $error = execute_shell_command($command)) {
+			my $logger = get_logger('tweaker.script');
+			$logger->error("ERROR: $error");
+			$logger->error("ERROR: Unable to implement option $option.");
+			exit(-1);
+		    }
+		}
+	    }
+	}
+    } else {
+	exit -1;
+    }
+    disconnect_from_db();
+}
+
+# Poll the system to see what recommendationlevel the given option has on the system.
+sub poll_options {
+    my($option) = @_;
+    
+    # These are somewhat arbitrary at this point.  Note that they correspond to
+    # _full screen_ glxgears results in Frames Per Second (FPS), since GL is used
+    # at full screen resolution in MythTV.
+    my $low_threshold = 350; # at or below $low_threshold FPS, GL is not considered good enough to use in MythTV
+    my $medium_threshold = 425; # GL is pretty usable here
+    my $high_threshold = 500;
+
+    threshold_test($option, check_for_GL(), "video card", $low_threshold, $medium_threshold, $high_threshold);
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_keymap.sh b/abs/core-testing/tweaker/bin/twk_keymap.sh
new file mode 100755
index 0000000..41341bf
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_keymap.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This script is called from twk_general.pl to implement keymap standardization in KnoppMyth
+
+KNOPPMYTH_SHARE=/usr/local/share/knoppmyth
+
+####################
+# Unify key mappings
+####################
+
+# Universal keybindings:
+# Function	Remote Key	Keyboard Key
+# ------------------------------------------
+# Skip Back	<<		PgUp
+# Skip Forward	>>		PgDn
+# Play		|>		l
+# Pause		||		p
+# Stop		[]		ESC
+
+#Make xine use keymappings that get along with mplayer, MythMusic, and ATI remote button mappings.
+#NOTE: "Prior" is PageUp and "Next" is PageDown
+if [[ -f $MYTH_HOME/.xine/keymap ]]; then
+    sed -i "/Mute {$/{N; s/key = .*/key = VOID/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/MrlBrowser {$/{N; s/key = .*/key = VOID/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/NextMrl {$/{N; s/key = .*/key = VOID/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/PriorMrl {$/{N; s/key = .*/key = VOID/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/AudioVideoDecay+ {$/{N; s/key = .*/key = VOID/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/ToggleLoopMode {$/{N; s/key = .*/key = VOID/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/PlaylistStop {$/{N; s/key = .*/key = VOID/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/Mute {$/{N; N; s/modifier = .*/modifier = none/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/Quit {$/{N; s/key = .*/key = Escape/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/^Menu {$/{N; s/key = .*/key = m/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/Play {$/{N; s/key = .*/key = l/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/Pause {$/{N; s/key = .*/key = p/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/SeekRelative+60 {$/{N; s/key = .*/key = Next/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/SeekRelative-60 {$/{N; s/key = .*/key = Prior/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/Volume+ {$/{N; s/key = .*/key = ]/; }" $MYTH_HOME/.xine/keymap
+    sed -i "/Volume- {$/{N; s/key = .*/key = [/; }" $MYTH_HOME/.xine/keymap
+else
+    mkdir -p $MYTH_HOME/.xine/
+    /bin/cp $KNOPPMYTH_SHARE/xine_keymap $MYTH_HOME/.xine/keymap
+fi    
+chown -fR mythtv:mythtv $MYTH_HOME/.xine
+
+
+#Make mplayer use keymappings that get along with xine, MythMusic, and ATI remote button mappings.
+#NOTE: PGUP is PageUp and PGDWN is PageDown
+if [[ -f $MYTH_HOME/.mplayer/input.conf ]]; then
+    sed -i "s/^PGUP .*/PGUP seek -60/" $MYTH_HOME/.mplayer/input.conf
+    sed -i "s/^PGDWN .*/PGDWN seek +60/" $MYTH_HOME/.mplayer/input.conf
+    sed -i "s/^p .*/p pause/" $MYTH_HOME/.mplayer/input.conf
+    sed -i "s/^l .*/l pause/" $MYTH_HOME/.mplayer/input.conf
+else
+    mkdir -p $MYTH_HOME/.mplayer/
+    /bin/cp $KNOPPMYTH_SHARE/mplayer_keymap $MYTH_HOME/.mplayer/input.conf
+fi
+chown -fR mythtv:mythtv $MYTH_HOME/.mplayer
+
+echo "Tweaked keymap"
\ No newline at end of file
diff --git a/abs/core-testing/tweaker/bin/twk_linux.pl b/abs/core-testing/tweaker/bin/twk_linux.pl
new file mode 100755
index 0000000..107b4d9
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_linux.pl
@@ -0,0 +1,65 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'all' );
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    my $command;
+    my $apacheconf = "/etc/apache2/apache2.conf";
+    # Linux-centric tweaks
+
+    my @commands = (
+	# erases any previous ServerName and adds a new one
+	"sed -i 's/ServerName.*//g' $apacheconf && echo -n \"ServerName \" >> $apacheconf && hostname >> $apacheconf",
+	# make autofs run automatically
+	"/etc/init.d/autofs stop && /etc/init.d/autofs start && /usr/sbin/update-rc.d -f autofs remove && /usr/sbin/update-rc.d autofs start 45 3 5 .",
+	# set up NTP
+	"/etc/init.d/ntp restart && /usr/sbin/update-rc.d -f ntp remove && /usr/sbin/update-rc.d ntp defaults 85"
+	);
+    
+    foreach my $command (@commands) {
+	if (my $result = execute_shell_command($command)) {
+	    my $logger = get_logger('tweaker.script');
+	    $logger->info("result: $result");
+	}
+    }
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+    recommendation_level("recommended", "These tweaks benefit all users.");
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_localization.pl b/abs/core-testing/tweaker/bin/twk_localization.pl
new file mode 100755
index 0000000..f517ff4
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_localization.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'US_English', 'GB_English' ); # Should eventually expand to handle all translations available for MythTV.
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	# The entire planet except the US uses metric measurements.
+	change_or_make_setting('SIUnits', 'YES') || exit -1;
+	switch ($option) {
+	    case "US_English" {
+		# Uses Imperial measurements for things like weather.
+		change_or_make_setting('SIUnits', 'NO') || exit -1;
+		change_or_make_setting('Language', 'EN') || exit -1;
+		change_or_make_setting('ISO639Language0', 'eng') || exit -1;
+		change_or_make_setting('ISO639Language1', 'eng') || exit -1;
+	    }
+	    case "GB_English" {
+		change_or_make_setting('Language', 'EN_GB') || exit -1;
+		change_or_make_setting('ISO639Language0', 'eng') || exit -1;
+		change_or_make_setting('ISO639Language1', 'eng') || exit -1;
+	    }
+	}
+    } else {
+	exit -1;
+    }
+    disconnect_from_db();
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+
+    recommendation_level("optional", "We don't yet have any way to know what your preferred language is.  This option is as valid as any other.");
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_misc.pl b/abs/core-testing/tweaker/bin/twk_misc.pl
new file mode 100755
index 0000000..96771a6
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_misc.pl
@@ -0,0 +1,13 @@
+# Placeholder for tweaks not yet implemented
+
+# Screen size / viewing distance
+change_or_make_setting('chanPerPage', '4') || exit -1;
+change_or_make_setting('timePerPage', '3') || exit -1;
+
+# Disk space / sound quality / compatibility
+change_or_make_setting('DefaultRipQuality', '2') || exit -1;
+change_or_make_setting('Mp3UseVBR', '1') || exit -1;
+
+# Localization / tuner type
+# N. America
+change_or_make_setting('VbiFormat', 'NTSC Closed Caption') || exit -1;
diff --git a/abs/core-testing/tweaker/bin/twk_scrub_sql.pl b/abs/core-testing/tweaker/bin/twk_scrub_sql.pl
new file mode 100755
index 0000000..2012d4d
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_scrub_sql.pl
@@ -0,0 +1,96 @@
+#!/usr/bin/perl -w
+
+# Copyright 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+# List all the options that this script supports.  Make sure it matches what's in
+# the corresponding .tcf entry.
+set_known_options( 'scrub', 'protect' );
+my $beginning_of_user_index=65;
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+
+	my @table_list = (
+	    [ 'capturecard', 'cardid' ], # tuner-related
+	    [ 'cardinput', 'cardinputid' ],   # tuner-related
+	    [ 'videosource', 'sourceid' ], # tuner-related
+
+	    [ 'dvdinput', 'intid' ],
+	    [ 'dvdtranscode', 'intid' ],
+	    [ 'gameplayers', 'gameplayerid' ],
+	    [ 'music_smartplaylist_categories', 'categoryid' ],
+	    [ 'music_smartplaylist_items', 'smartplaylistitemid' ],
+	    [ 'music_smartplaylists', 'smartplaylistid' ],
+	    [ 'phonedirectory', 'intid' ],
+	    [ 'profilegroups', 'id' ],
+	    [ 'recordingprofiles', 'id' ],
+	    [ 'storagegroup', 'id' ],
+	    [ 'videotypes', 'intid' ],
+	    );
+
+	switch ($option) {
+	    case "scrub" { # delete the rows from the table
+		foreach my $table (@table_list) {
+		    do_query("DELETE FROM @$table[0] WHERE @$table[1] < $beginning_of_user_index;");
+		    do_query("ALTER TABLE @$table[0] AUTO_INCREMENT = 0;");
+		}
+	    }
+	    case "protect" {
+		foreach my $table (@table_list) {
+		    do_query("ALTER TABLE @$table[0] AUTO_INCREMENT = $beginning_of_user_index;");
+		}
+	    }
+	}
+    } else {
+	exit -1;
+    }
+    disconnect_from_db();
+}
+
+sub poll_options {
+    my($option) = @_;
+    switch ($option) {
+	case "scrub" {
+	    recommendation_level("recommended", "This is required to ensure that other KnoppMyth SQL Tweaks work.");
+	}
+	case "protect" {
+	    recommendation_level("recommended", "This is required to ensure that other KnoppMyth SQL Tweaks do not clobber user-made changes.");
+	}
+    }
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
+
+
diff --git a/abs/core-testing/tweaker/bin/twk_tuners.pl b/abs/core-testing/tweaker/bin/twk_tuners.pl
new file mode 100755
index 0000000..ab7f029
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_tuners.pl
@@ -0,0 +1,488 @@
+#!/usr/bin/perl -w
+
+# Copyright 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'all' ); # When Tweaker handles multiple instances of a script, this will be expanded to
+# handle options for a given card, based on what it supports, e.g. ATSC, QAM, NTSC-coax, NTSC-Svideo, etc.
+# The current version of this script will run once and set up defaults for all detected cards, choosing
+# a default configuration based on some assumptions of common usage.  Modes in this array should be listed
+# from least desirable to most desirable.  E.g. ["NTSC", "ATSC", "QAM"]
+
+my @capture_card_patterns = (
+    # DIGITAL
+    # -------
+    # ATSC-only devices
+    [ "pcHDTV hd-2000", [".*0400.*109e.*036e.*r11.*7063.*2000.*"],
+      ["DVB", ["ATSC"]]],
+    [ "BBTI Air2PC v2", [".*0280.*13d0.*2103.*r02.*13d0.*2103.*"], # ??? dupe 1; mihanson
+      ["DVB", ["ATSC"]]],
+    
+    # DVB-T devices
+    [ "DViCO FusionHDTV DVB-T Lite", [".*0400.*109e.*036e.*r11.*18ac.*db10.*"],
+      # secondary device "0480" "109e" "0878" -r11 "18ac" "d500"
+      ["DVB", ["DVB-T"]]],
+    [ "DViCO FusionHDTV DVB-T1", [".*0400.*14f1.*8800.*18ac.*db00.*"],
+      ["DVB", ["DVB-T"]]], # also has composite and S-Video in for frame grabbing
+    [ "DViCO FusionHDTV DVB-T Plus", [".*0400.*14f1.*8800.*8800.*db10.*"],
+      ["DVB", ["DVB-T"]]],
+    [ "DViCO FusionHDTV dual Express", [".*0400.*14f1.*8852.*18ac.*db78.*"],
+      ["DVB", ["DVB-T"]]],
+    [ "Twinhan VisionPlus DVB-T", [".*0400.*109e.*036e.*r11.*1822.*0001.*"], # ??? dupe 2; jbman, Girkers
+      ["DVB", ["DVB-T"]]],
+#    [ "EU Hauppauge PVR-500 (DVB-T)", [".*0400.*4444.*0016.*0070.*e807.*"],
+#      ["DVB", ["DVB-T"]]],
+    [ "Avermedia A777", [".*0480.*1131.*7133.*1461.*2c05.*", ".*0480.*1131.*7134.*1461.*2c05.*"],
+      ["DVB", ["DVB-T"]]],
+    [ "Compro T300", [".*0480.*1131.*7134.*185b.*c900.*", ".*0480.*1131.*7134.*7134.*c900.*"],
+      ["DVB", ["DVB-T"]]],
+
+    # DVB-C devices
+    [ "Siemens DVB-C", [".*0480.*1131.*7146.*110a.*0000.*"],
+      ["DVB", ["DVB-C"]]],
+
+    # DVB-S devices
+    [ "VisionPlus 1020A", [".*0480.*109e.*0878.*1822.*0001.*"],
+      ["DVB", ["DVB-S"]]],
+    [ "Technisat Skystar2", [".*0280.*13d0.*2103.*r01.*13d0.*2103.*"], # ??? dupe 1; nbdwt73, neutron68
+      ["DVB", ["DVB-S"]]],
+    [ "Twinhan 102g", [".*0400.*109e.*036e.*r11.*1822.*0001.*"], # ??? dupe 2; neutron68
+      # secondary device "0480" "109e" "0878" -r11 "1822" "0001"
+      ["DVB", ["DVB-S"]]],
+    
+    # ATSC/QAM devices
+    [ "Kworld ATSC-110", [".*0480.*1131.*7133.*17de.*"],
+      ["DVB", ["ATSC", "QAM"]]],
+    [ "pcHDTV hd-3000", [".*0400.*14f1.*8800.*3000.*"],
+      ["DVB", ["ATSC", "QAM"]]], # the hd-3000 has several sub-devices; this pattern matches the digital tuner
+    [ "BBTI HD5000AV / AirStar 2 TV", [".*0280.*13d0.*2103.*r02.*13d0.*2103.*"], # ??? dupe 1; manicmike
+      ["DVB", ["ATSC", "QAM", "DVB-T"]]],
+    [ "pcHDTV hd-5500", [".*0400.*14f1.*8800.*7063.*5500.*"],
+      ["DVB", ["ATSC", "QAM"]]],
+    [ "Hauppauge WinTV-HVR-1800", [".*0400.*14f1.*8880.*0070.*7801.*"],
+      ["DVB", ["ATSC", "QAM"]]],
+    [ "DViCO Fusion Express", [".*0400.*14f1.*8852.*r02.*18ac.*d500.*"],
+      ["DVB", ["ATSC", "QAM"]]],
+
+    # card has one sub-device; this pattern matches the primary device
+    [ "DViCO Fusion HDTV5 Lite", [".*0400.*109e.*036e.*r11.*18ac.*d500.*"],
+      # secondary device "0480" "109e" "0878" -r11 "18ac" "d500"
+      ["DVB", ["ATSC", "QAM"]]],
+    
+    # ANALOG
+    # -------
+    # NTSC cards
+    # MPEG-2 type
+    [ "Hauppauge PVR-150", [".*0400.*4444.*0016.*0070.*8003.*", ".*0400.*4444.*0016.*0070.*8801.*"],
+      ["MPEG", ["NTSC"]]],
+    [ "Hauppauge PVR-250", [".*0200.*10ec.*8139.*1799.*5000.*", ".*0400.*4444.*0016.*0070.*4009.*", ".*0400.*4444.*0016.*0070.*f7f9.*"],
+      ["MPEG", ["NTSC"]]],
+    [ "Hauppauge PVR-350", [".*0400.*4444.*0803.*0070.*4000.*"],
+      ["MPEG", ["NTSC"]]],
+
+    # NTSC/PAL cards
+    # MPEG-2 type
+    [ "Hauppauge PVR-500", [".*0400.*4444.*0016.*0070.*e817.*", ".*0400.*4444.*0016.*0070.*e807.*"], # look for each of two devices on the card, since it's dual-tuner
+      ["MPEG", ["NTSC"]]],
+    
+    # Frame grabbers
+#    [ "bt878-based frame grabbers", [".*0400.*109e.*036e.*", ".*0480.*109e.*0878.*"],
+#      ["V4L", ["NTSC"]]],
+    
+    );
+
+# USB patterns
+
+# "Hauppauge Nova-T 500 Dual DVB-T"
+
+#T:  Bus=08 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480 MxCh= 0
+#D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
+#P:  Vendor=2040 ProdID=9941 Rev= 1.00
+#S:  Manufacturer=Hauppauge
+#S:  Product=WinTV Nova-DT
+#S:  SerialNumber=4027351140
+#C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=500mA
+#I:* If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=00 Prot=00 Driver=dvb_usb_dib0700
+#E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=125us
+#E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+#E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+#E:  Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 
+
+# "Hauppauge Nova-T-500"
+
+#T:  Bus=08 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480 MxCh= 0
+#D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
+#P:  Vendor=2040 ProdID=9950 Rev= 1.00
+#S:  Manufacturer=Hauppauge
+#S:  Product=WinTV Nova-DT
+#S:  SerialNumber=4027353863
+#C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
+#I:  If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=00 Prot=00 Driver=dvb_usb_dib0700
+#E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=125us
+#E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+#E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+#E:  Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+    
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	my $logger = get_logger('tweaker.script');
+	
+	# Get a list of all PCI devices for later processing
+	my @lspci = split('\n', execute_shell_command("lspci -mn"));
+
+	# Iterate through the list of known tuner cards, then the list of known network tuners.
+	# For each known tuner, configure a safe default for it if it's found.  Set up
+	# well-named video sources for any mode supported by any tuner, and pick a default.
+
+	# Determine the recording priority for a given device using the following priority rules,
+	# from top to bottom, the top being the most significant, and the bottom being the least
+	# significant
+	
+	# Input types in order of decreasing priority:
+	# Digital cable, digital satellite, digital OTA, analog cable, analog line-in, analog OTA
+
+	# Device types in order of decreasing priority:
+	# local device, remote device
+
+	# Capture types in order of decreasing priority:
+	# MPEG-2 creators (or passthrough of MPEG-2), MPEG-4 creators, frame grabbers
+	
+	# For example, if device1 supports a better input type than device2, device1 is always preferred.
+	# Ties are broken by the next level down.  For example, if device1 and device2 both support the
+	# same input type, the tie is broken based on whether it's a local or remote device.  Finally,
+	# ties of input type and device type are broken by the output of the device.  It should be
+	# noted that it's possible to tie in all categories, and MythTV will break ties based on the
+	# order in which the devices are defined.
+
+
+	# For the following subroutines, $relative_device_count represents which device this is in
+	# the context of the total devices of its type.  $global_device_count represents which device
+	# this is amongst all capture devices.  Each returns a list of row names and values
+	# for the capturecard table, appropriate for the relevant type of device.
+	
+	# Create an entry in the capturecard table for the given tuner.
+	sub make_capturecard_SQL {
+	    my($global_device_count, $relative_device_count, $cardtype, $tuner_number)=@_;
+	    # $tuner_number is only used for HDHomeRuns
+	    my $defaultinput="";
+	    my $hostname = execute_shell_command("hostname") || "localhost";
+	    my $checkfields = [["cardid", "$global_device_count"],
+			       ["hostname", "$hostname"],
+			       ["cardtype", "$cardtype"]];
+	    my $setfields;
+	    my $logger = get_logger('tweaker.script');
+	    
+	    # Because other devices use the /dev/video* and /dev/vbi* device
+	    # files, the "device count" is used to determine which number follows the base
+	    # device file name, e.g. /dev/video1, /dev/vbi1
+	    sub make_V4L_capturecard_SQL {
+		my($relative_device_count)=@_; # 0-indexed
+		
+		return [["videodevice", "/dev/video$relative_device_count"],
+			["audiodevice", "/dev/dsp"],
+			["vbidevice", "/dev/vbi$relative_device_count"],
+			["audioratelimit", "0"]];
+	    }
+	    
+	    # Because other devices use the /dev/video* and /dev/vbi* device
+	    # files, the "device count" is used to determine which number follows the base
+	    # device file name, e.g. /dev/video1, /dev/vbi1
+	    sub make_MPEG_capturecard_SQL {
+		my($relative_device_count, $defaultinput)=@_; # 0-indexed
+		
+		return [["videodevice", "/dev/video$relative_device_count"],
+			["defaultinput", $defaultinput]];
+	    }
+	    
+	    sub make_DVB_capturecard_SQL {
+		my($relative_device_count, # 0-indexed
+		   $defaultinput)=@_;
+		my $logger = get_logger('tweaker.script');
+
+		$logger->debug("DEFAULTINPUT: $defaultinput");
+		
+		return [["videodevice", "$relative_device_count"], # Rather than being a device file, it's a 0-indexed value indicating
+			# which of the N available DVB devices this is.  Since a card can have more than one DVB device on it, there may
+			# be more DVB "videodevice"s on a system than there are distinct "cardid"s.
+			["defaultinput", $defaultinput],
+			["dvb_on_demand", "1"]];
+	    }
+
+	    # special case of DVB device
+	    sub make_HDHOMERUN_capturecard_SQL {
+		my($hdhr_hex_id, $tuner_number, $defaultinput)=@_;
+
+		return [["videodevice", "$hdhr_hex_id"],
+			["defaultinput", $defaultinput],
+			["dvb_on_demand", "0"],
+			["dbox2_port", $tuner_number] # 0 or 1
+		    ];
+	    }
+
+	    $logger->debug("CARD TYPE: $cardtype");
+	    switch($cardtype) {
+		case "V4L" {
+		    $setfields = make_V4L_capturecard_SQL($relative_device_count);
+		}
+		case "DVB" {
+		    $defaultinput = "DVBInput";
+		    $setfields = make_DVB_capturecard_SQL($relative_device_count, $defaultinput);
+		}
+		case "HDHOMERUN" {
+		    $defaultinput = "MPEG2TS";
+		    $setfields = make_HDHOMERUN_capturecard_SQL($relative_device_count, $tuner_number, $defaultinput);
+		}
+		case "MPEG" {
+		    $defaultinput = "Tuner 1";
+		    $setfields = make_MPEG_capturecard_SQL($relative_device_count, $defaultinput);
+		}
+	    }
+
+	    change_or_make_entry("capturecard", $setfields, $checkfields);
+	    return $defaultinput;
+	}
+
+	# Create an entry in videosource corresponding to the sub-type of input this device takes
+	# Return the sourceid for the entry we just made, which is a rough metric of desirability.
+	sub verify_or_make_videosource_SQL {
+	    # sub-types are NTSC, PAL, ATSC, QAM, DVB-S, DVB-T, DVB-C, etc.
+	    my($sub_type)=@_;
+	    my $logger = get_logger('tweaker.script');
+
+	    $logger->debug("\t\t\tUpdating or adding videosource for sub-type: $sub_type");
+	    
+	    switch($sub_type) {
+		# North American options, from least desirable to most desirable (this is slightly arbitrary)
+		case "NTSC" {
+		    change_or_make_entry("videosource", [["name", "analog_broadcast"], ["freqtable", "try-all"]], [["sourceid", 1]]);
+		    # This is one of two cases where a sub_type can be used in two ways.  Both videosources are made, but the preferred one is
+		    # cable, the most common option.
+		    change_or_make_entry("videosource", [["name", "analog_cable"], ["freqtable", "try-all"]], [["sourceid", 5]]);
+		    return 5;
+		}
+		case "ATSC" {
+		    change_or_make_entry("videosource", [["name", "digital_broadcast"], ["freqtable", "try-all"]], [["sourceid", 10]]);
+		    return 10;
+		}
+		case "QAM" {
+		    change_or_make_entry("videosource", [["name", "digital_cable"], ["freqtable", "try-all"]], [["sourceid", 20]]);
+		    return 20;
+		}
+
+		# Options for the rest of the planet
+		case "PAL" {
+		    change_or_make_entry("videosource", [["name", "analog_broadcast"], ["freqtable", "try-all"]], [["sourceid", 1]]);
+		    # This is one of two cases where a sub_type can be used in two ways.  Both videosources are made, but the preferred one is
+		    # cable, the most common option.
+		    change_or_make_entry("videosource", [["name", "analog_cable"], ["freqtable", "try-all"]], [["sourceid", 5]]);
+		    return 5;
+		}
+		case "DVB-T" {
+		    change_or_make_entry("videosource", [["name", "digital_broadcast"], ["freqtable", "try-all"]], [["sourceid", 10]]);
+		    return 10;
+		}
+		case "DVB-C" {
+		    change_or_make_entry("videosource", [["name", "digital_cable"], ["freqtable", "try-all"]], [["sourceid", 20]]);
+		    return 20;
+		}
+
+		# Planet-wide options
+		case "DVB-S" {
+		    change_or_make_entry("videosource", [["name", "digital_satellite"], ["freqtable", "try-all"]], [["sourceid", 15]]);
+		    return 15;
+		}
+	    }
+	}
+
+	sub make_cardinput_SQL {
+	    my($tuner_card_number, $sourceid, $inputname, $priority_modifier)=@_;
+
+	    change_or_make_entry("cardinput", [["sourceid", $sourceid], ["cardid", $tuner_card_number],
+					       ["inputname", $inputname], ["preference", $sourceid], ["tunechan", ""],
+					       ["startchan", "Please add"], ["freetoaironly", "1"],
+					       ["recpriority", $sourceid+$priority_modifier]],
+				 [["cardinputid", $tuner_card_number]]);
+	}
+
+	sub max {
+	    my($a, $b)=@_;
+	    if ($a > $b) {
+		return $a;
+	    } else {
+		return $b;
+	    }
+	}
+	
+	my $global_device_count=0;
+
+	# Configure each supported tuner/capture device detected on the system.
+
+	# built-in, PCI, or PCI Express
+	$logger->debug("Processing local devices...");
+	for my $pci_device (@lspci) {
+	    $logger->debug("DEVICE: $pci_device");
+
+	    for my $pci_device_data (@capture_card_patterns) {
+		$logger->debug("\tIs there a @$pci_device_data[0] at this location?");
+		for my $patterns (@$pci_device_data[1]) {
+		    for my $pattern (@$patterns) {
+			my $match=0;
+			$logger->debug("\t\tPATTERN: $pattern");
+			$match += ($pci_device =~ m/$pattern/i);
+			if ($match) {
+			    $global_device_count++; # 1-indexed
+						    
+			    $logger->debug("\t\tfound one!");
+			    # Each device has a device type (e.g. MPEG, DVB, V4L) that it supports, and a
+			    # list of sub_types (e.g. PAL, NTSC, DVB-S, DVB-T, DVB-C, ATSC, QAM).
+			    # The device type is used to configure the capturecard table and the cardinput table.
+			    # The sub_types are used to populate the videosource table.
+			    # The device type and "best" sub_type are used to set the 'sourceid' field of the cardinput table.
+
+			    for my $typeblock (@$pci_device_data[2]) {
+				my $pci_device_type = @$typeblock[0];
+				$logger->debug("\t\tDEVICE TYPE: $pci_device_type"); # e.g. "DVB", "MPEG", etc.
+				$cardtypes{$pci_device_type}++;
+				my $sourceid = -1;
+				for my $sub_types (@$typeblock[1]) {
+				    for my $sub_type (@$sub_types) {
+					$logger->debug("\t\tSUB-TYPE: $sub_type\n"); # e.g. "DVB-S", "QAM", etc.
+					# ensure that the videosource table has an entry for each sub_type
+					# that this device supports; use the "best" one for the device by
+					# default
+
+					#
+					# POPULATE videosource table
+					#
+					$sourceid = max(verify_or_make_videosource_SQL($sub_type), $sourceid);
+				    }
+				}
+				$logger->debug("\t\t\"BEST\" SOURCE ID: $sourceid\n");
+
+				#
+				# POPULATE capturecard table
+				#
+				my $defaultinput = make_capturecard_SQL($global_device_count, $cardtypes{$pci_device_type}-1, $pci_device_type, -1);
+
+				#
+				# POPULATE cardinput table
+				#
+				make_cardinput_SQL($global_device_count, $sourceid, $defaultinput, 0);
+			    }
+			}
+		    }
+		}
+	    }
+	}
+
+	# network tuner devices
+	$logger->debug("Processing network devices...");
+
+	# get our IP address
+	my $ip_address;
+	open(SHELL, "ifconfig | grep \"inet addr\" | grep -v 127 |");
+	while(<SHELL>) {
+	    my @line = split(/:| +/);
+	    $ip_address = $line[3];
+	}
+	close(SHELL);
+
+	# ??? eventually, just add LocalIPCheck.pl's code to Tweaker/Script.pm and avoid bizarre perl-to-shell-to-perl calls at this level
+	my $ip_class = execute_shell_command("echo $ip_address | LocalIPCheck.pl; echo \$?");
+
+	if ($ip_class > 0) { # we're on a LAN, not the internet
+	    # Look for HDHomeRun, a DVB ATSC/QAM device
+	    $sourceid=0;
+	    $logger->debug("\tLooking for SiliconDust HDHomeRun...");
+	    open(SHELL2, "hdhomerun_config discover |");
+	    while(<SHELL2>) {
+		if (m/\d+\.\d+\.\d+\.\d+/) {
+		    my @line = split(/ /);
+		    my $hdhr_hex_id = $line[2];
+
+		    $logger->debug("\tfound $hdhr_hex_id");
+		    $global_device_count++; # 1-indexed
+		    $cardtypes{"DVB"}++;
+		    #
+		    # POPULATE videosource table
+		    #
+		    for my $sub_type ("ATSC", "QAM") {
+			$sourceid = max(verify_or_make_videosource_SQL($sub_type), $sourceid);
+		    }
+		    $logger->debug("\t\t\"BEST\" SOURCE ID: $sourceid\n");
+		    
+		    #
+		    # POPULATE capturecard table
+		    #
+		    
+		    # there are two tuners per HDHomeRun
+		    for (my $sub_tuner_count = 0; $sub_tuner_count < 2; $sub_tuner_count++) {
+			my $defaultinput = make_capturecard_SQL($global_device_count+$sub_tuner_count, $hdhr_hex_id, "HDHOMERUN", $sub_tuner_count);
+			#
+			# POPULATE cardinput table
+			#
+			make_cardinput_SQL($global_device_count+$sub_tuner_count, $sourceid, $defaultinput, -4); # the -4 is to make it less desirable
+			# than a local device
+		    }
+		    $global_device_count++;
+		    
+		}
+	    }
+	    close(SHELL2);
+	} # else: don't scan the internet!
+	
+	# USB devices
+	$logger->debug("Processing USB devices...");
+	$logger->debug("\t(no USB devices supported yet)");
+
+	# Filesystem Tweaks for tuners
+	# This only works with the Nova-T-500 card, but it doesn't hurt any other cards
+	execute_shell_command("echo \"#switch on onboard amplifier on Nova-T-500 card\" > /etc/modprobe.d/dvb-usb-dib0700");
+	execute_shell_command("echo \"options dvb-usb-dib0700 force_lna_activation=1\" >> /etc/modprobe.d/dvb-usb-dib0700");
+    } else {
+	my $logger = get_logger('tweaker.script');
+	$logger->error("Unable to connect to mythconverg database");
+	$logger->error("Unable to implement option $option.");
+	return -1;
+    }
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+
+    recommendation_level("recommended", "Everyone should scan for tuners to autoconfigure.");
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_tuners_notes.txt b/abs/core-testing/tweaker/bin/twk_tuners_notes.txt
new file mode 100644
index 0000000..1f2e283
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_tuners_notes.txt
@@ -0,0 +1,55 @@
+# Some PCI devices have the same lspci -mn fingerprint, yet users are identifying them as having different functions.  The example below is reported as an ATSC(DVB-T) card by one user and as a DVB-S card by two other users.  lspci -vvxxx provides more fingerprint data, and I'm trying to see if there's a definitive way to tell the cards apart from it.
+
+    [ "BBTI Air2PC v2", [".*0280.*13d0.*2103.*r02.*13d0.*2103.*"], # ??? dupe 1; mihanson
+      ["DVB", ["ATSC"]]],
+    # mihanson lspci -vvxxx info:
+
+#00: d0 13 03 21 07 01 00 04 02 00 80 02 00 40 00 00
+#10: 00 00 64 ed 81 70 00 00 00 00 00 00 00 00 00 00
+#20: 00 00 00 00 00 00 00 00 00 00 00 00 d0 13 03 21
+#30: 00 00 00 00 00 00 00 00 00 00 00 00 0c 01 00 00 
+
+#00: d0 13 03 21 07 01 00 04 02 00 80 02 00 40 00 00
+#10: 00 00 65 ed a1 70 00 00 00 00 00 00 00 00 00 00
+#20: 00 00 00 00 00 00 00 00 00 00 00 00 d0 13 03 21
+#30: 00 00 00 00 00 00 00 00 00 00 00 00 0b 01 00 00 
+
+#00: d0 13 03 21 07 01 00 04 02 00 80 02 00 40 00 00
+#10: 00 00 66 ed c1 70 00 00 00 00 00 00 00 00 00 00
+#20: 00 00 00 00 00 00 00 00 00 00 00 00 d0 13 03 21
+#30: 00 00 00 00 00 00 00 00 00 00 00 00 0a 01 00 00
+
+#00: d0 13 03 21 07 01 00 04 02 00 80 02 00 40 00 00
+#10: 00 00 67 ed e1 70 00 00 00 00 00 00 00 00 00 00
+#20: 00 00 00 00 00 00 00 00 00 00 00 00 d0 13 03 21
+#30: 00 00 00 00 00 00 00 00 00 00 00 00 0c 01 00 00
+
+# Among the above, there are only three sections that
+# differ, shown by XX:
+#10: -- -- XX -- XX -- -- -- -- -- -- -- -- -- -- --
+#30: -- -- -- -- -- -- -- -- -- -- -- -- XX -- -- --
+
+
+    [ "Technisat Skystar2", [".*0280.*13d0.*2103.*r01.*13d0.*2103.*"], # ??? dupe 1; nbdwt73, neutron68
+      ["DVB", ["DVB-S"]]],
+    # Neutron68 lspci -vvxxx info:
+#00: d0 13 03 21 07 01 00 04 01 00 80 02 00 40 00 00
+#10: 00 00 ae fa 81 c8 00 00 00 00 00 00 00 00 00 00
+#20: 00 00 00 00 00 00 00 00 00 00 00 00 d0 13 03 21
+#30: 00 00 00 00 00 00 00 00 00 00 00 00 05 01 00 00
+
+# when compared with mihanson's blocks, these entries differ:
+#00: -- -- -- -- -- -- -- -- -- -- -- XX -- -- -- --
+#10: -- -- XX XX XX XX XX -- -- -- -- -- -- -- -- --
+#20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+#30: -- -- -- -- -- -- -- -- -- -- -- -- XX -- -- --
+
+# eliminating the entries that even differ among mihanson's devices, we get:
+#00: -- -- -- -- -- -- -- -- -- -- -- XX -- -- -- --
+#10: -- -- -- XX -- XX XX -- -- -- -- -- -- -- -- --
+#20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+#30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+# Without more feedback, it's not clear whether or not this pattern is representative
+# of the differences between the DVB-S and ATSC versions of this device.
+
diff --git a/abs/core-testing/tweaker/bin/twk_upgrade.pl b/abs/core-testing/tweaker/bin/twk_upgrade.pl
new file mode 100755
index 0000000..479a675
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_upgrade.pl
@@ -0,0 +1,190 @@
+#!/usr/bin/perl -w
+
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#
+# NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE 
+#
+# Tweaker 0.7 is not interactive, so these post-upgrade changes are a stopgap
+# measure to make sure people upgrading from R5F27 get sensible settings
+# for things that MythTV 0.21 doesn't know how to handle properly.  This file
+# should shrink or disappear entirely once Tweaker becomes interactive.
+#
+# NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE 
+#
+
+use Switch;
+use Tweaker::Script;
+package Tweaker::Script;
+
+set_known_options( 'all' );
+
+# Try to implement the given option.
+sub implement_option {
+    my($option) = @_;
+
+    $dbconnectionstring = get_mythtv_connection_string();
+	
+    if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
+	# use poll results from twk_cpu.pl to set playback decoder options
+	my @levels = ("low", "medium", "high");
+	foreach my $level (@levels) {
+	    my @results = split(/\|/, execute_shell_command("twk_cpu.pl --poll $level"));
+	    foreach my $result (@results) {
+		if ($result eq "recommended") {
+		    if ("$level" eq "low") {
+			change_or_make_setting('DefaultVideoPlaybackProfile', 'CPU--') || exit -1; # best playback defaults for weak hardware
+		    } elsif ("$level" eq "medium") {
+			change_or_make_setting('DefaultVideoPlaybackProfile', 'CPU+') || exit -1; # best playback defaults for average hardware
+		    } elsif ("$level" eq "high") {
+			change_or_make_setting('DefaultVideoPlaybackProfile', 'CPU++') || exit -1; # best playback defaults for powerful hardware
+		    }
+		}
+	    }
+	}
+
+	# Update visualization modes for 0.21
+	change_or_make_setting('VisualMode','BumpScope;Gears;Goom;MonoScope;Squares;StereoScope;Synaesthesia;LibVisual-bumpscope;LibVisual-corona;LibVisual-infinite;LibVisual-jakdaw;LibVisual-jess;AlbumArt');
+
+	# Remove possibly obsolete and conflicting theme cache entries.
+	execute_shell_command("/bin/rm -rf /home/mythtv/.mythtv/themecache/");
+
+	# Fix bizarre ownership of files:
+	execute_shell_command("chown -f root: /usr/bin/get_dual.sh");
+	execute_shell_command("chown -fR root: /usr/lib/krp");
+	execute_shell_command("chown -fR root: /usr/local/bin");
+	execute_shell_command("chown -f root: /usr/share/man/man1/tv_grab_au.1.gz");
+	execute_shell_command("chown -f root: /usr/share/xmltv/tv_grab_au/channel_ids");
+	execute_shell_command("chown -fR root:src /usr/src/");
+
+	# Fix video problems for some nVidia users
+	execute_shell_command("grep nvidia /etc/X11/xorg.conf && sed -i -e 's/^[ \t]*Option.*\"XvmcUsesTextures\".*/#REMOVEME/g' -e 's/^[ \t]*Option.*\"UseEvents\".*/#REMOVEME/g' /etc/X11/xorg.conf"); # clear out old entries, if present
+	execute_shell_command("grep nvidia /etc/X11/xorg.conf && sed -i -n '1h;2,\$H;\${g;s/#REMOVEME\\n//g;p}' /etc/X11/xorg.conf"); # clear out old entries, if present
+	execute_shell_command("grep nvidia /etc/X11/xorg.conf && sed -i 's/\\(^[ \t]*Driver.*\"nvidia\".*\\)/\\1\\n\tOption \"XvmcUsesTextures\" \"false\"\\n\tOption \"UseEvents\" \"true\"/g' /etc/X11/xorg.conf");
+	execute_shell_command("if [ -e /home/mythtv/.nvidia-settings-rc ] ; then sed -i -e \"s'.*XVideoTextureSyncToVBlank.*'#REMOVEME'g\" -e \"s'.*XVideoBlitterSyncToVBlank.*'#REMOVEME'g\" /home/mythtv/.nvidia-settings-rc; fi");
+	execute_shell_command("grep nvidia /etc/X11/xorg.conf && echo 0/XVideoTextureSyncToVBlank=0 >> /home/mythtv/.nvidia-settings-rc && echo 0/XVideoBlitterSyncToVBlank=0 >> /home/mythtv/.nvidia-settings-rc && sed -i -n '1h;2,\$H;\${g;s/#REMOVEME\\n//g;p}' /home/mythtv/.nvidia-settings-rc");
+
+
+	# Enable any digital audio out device we can.
+	my $command = "amixer scontrols | grep IEC958";
+	my @results = split('\n', execute_shell_command($command));
+	foreach my $line (@results) {
+	    if ($line =~ /Simple mixer control (.*),.*/i) {
+		$command = "su - mythtv -c \"amixer set $1 on\""; # Tries to set all IEC958 devices to 'on'
+		# but some are just placeholders and can't be turned 'on', therefore don't error out if we fail
+		execute_shell_command($command);
+	    }
+	}
+	execute_shell_command("alsactl store"); # persist the above change(s)
+
+	# The Chaintech AV-710 was generally configured for digital audio in R5F27 in a way that worked
+	# great there but which is failing utterly in R5.5.  Try to detect this old configuration, and if
+	# it's present, implement the new settings which seem to work for all digital audio devices.
+	# This may only match 80% of the AV-710 users, but the rest of them can just run the necessary
+	# tweaker command by hand to get the same result.
+	if (execute_shell_command('lspci -mn | grep -e ".*0401.*1412.*1724.*1412.*1724"')) {
+	    # found an AV-710, now see if it's configured for digital audio
+	    if (do_query("SELECT * FROM settings WHERE value='AudioOutputDevice' AND data='/dev/adsp'")) {
+		# The user was using this AV-710's spdif output, so give them the new good digital configuration
+		execute_shell_command("twk_audio.pl --implement digital");
+	    }
+	}
+	change_or_make_setting('MixerDevice', 'ALSA:default') || exit -1;
+
+	# Undo obsolete mplayer workaround for aspect ratio
+	execute_shell_command("sed -i 's/.*monitoraspect.*//g' /home/mythtv/.mplayer/config");
+	
+	my $hostname = execute_shell_command("hostname") || "localhost";
+	# one table at a time, replace hostname with our actual hostname
+	# ??? can this be done all at once in MySQL?
+	foreach my $table ("capturecard", "inuseprograms", "jobqueue", "jumppoints", "keybindings", "music_playlists",
+			   "musicplaylist", "recorded", "settings", "weatherscreens") {
+	    do_query("UPDATE $table SET hostname='$hostname'");
+	}
+	
+	# Some entries in 'settings' should stay NULL:  http://www.mythtv.org/wiki/index.php/Settings_table
+	# There are fewer entries that stay NULL than there are that should have the hostname set, so while
+	# it duplicates some effort to change them from NULL to the hostname and them back to NULL, it's
+	# easier to maintain and more future-proof.
+	foreach my $entry ("mythfilldatabaseLastRunStart", "mythfilldatabaseLastRunEnd", "mythfilldatabaseLastRunStatus",
+			   "DataDirectMessage", "HaveRepeats", "DBSchemaVer", "DefaultTranscoder", "MythFillSuggestedRunTime",
+			   "MythFillGrabberSuggestsTime", "MasterServerIP", "MasterServerPort", "TVFormat", "VbiFormat", "FreqTable",
+			   "TimeOffset", "MasterBackendOverride", "DeletesFollowLinks", "HDRingbufferSize", "EITTransportTimeout",
+			   "EITIgnoresSource", "EITCrawIdleStart", "startupCommand", "blockSDWUwithoutClient", "idleWaitForRecordingTime",
+			   "StartupSecsBeforeRecording", "WakeupTimeFormat", "SetWakeuptimeCommand", "ServerHaltCommand", "preSDWUCheckCommand",
+			   "WOLbackendConnectRetry", "WOLbackendCommand", "WOLslaveBackendsCommand", "JobsRunOnRecordHost",
+			   "AutoCommflagWhileRecording", "JobQueueCommFlagCommand", "JobQueueTranscodeCommand",
+			   "AutoTranscodeBeforeAutoCommflag", "SaveTranscoding", "UserJobDesc1", "UserJob1", "UserJobDesc2", "UserJob2",
+			   "UserJobDesc3", "UserJob3", "UserJobDesc4", "UserJob4", "PreviewPixmapOffset", "AllRecGroupPassword",
+			   "MaximumCommercialSkip", "CommSkipAllBlanks", "LastFreeCard", "LiveTVPriority", "AutoExpireMethod",
+			   "AutoExpireDefault", "RerecordWatched", "AutoExpireWatchedPriority", "AutoExpireLiveTVMaxAge",
+			   "AutoExpireDayPriority", "AutoExpireExtraSpace", "AutoExpireInsteadOfDelete", "DeletedFifoOrder",
+			   "CommercialSkipMethod", "AggressiveCommDetect", "AutoCommercialFlag", "AutoTranscode", "AutoRunUserJob1",
+			   "AutoRunUserJob2", "AutoRunUserJob3", "AutoRunUserJob4", "OverTimeCategory", "CategoryOverTime",
+			   "EPGEnableJumpToChannel", "LogEnabled", "MythFillEnabled", "MythFillDatabasePath", "MythFillDatabaseArgs",
+			   "MythFillDatabaseLog", "MythFillPeriod", "MythFillMinHour", "MythFillMaxHour", "SchedMoveHigher", "SchedOpenEnd",
+			   "ComplexPriority", "PrefInputPriority", "SingleRecordRecPriority", "FindOneRecordRecPriority", "ArchiveDBSchemaVer",
+			   "FlixDBSchemaVer", "GalleryDBSchemaVer", "GameDBSchemaVer", "MusicDBSchemaVer", "PhoneDBSchemaVer",
+			   "mythvideo.DBSchemaVer", "WeatherDBSchemaVer") {
+	    do_query("UPDATE settings SET hostname=NULL WHERE value='$entry'");
+	}
+
+	change_or_make_setting('MythFillDatabaseLog', '/var/log/mythtv/mythfilldatabase.log');
+	change_or_make_setting('MasterBackendOverride','1'); # I don't remember why, but making the hostname NULL is important here
+	do_query("UPDATE settings SET hostname=NULL WHERE value='MasterBackendOverride'");
+
+	disconnect_from_db();
+
+	# Fix hostname for iPod feed URLs
+	$command = "sed -i \"s/hostname\\//$hostname\\//g\" /usr/local/bin/myth2ipod";
+	execute_shell_command($command);
+	
+	# Fix hostname for XViD feed URLs
+	$command = "sed -i \"s/192.168.0.222\\//$hostname\\//g\" /usr/local/bin/myt2xvid3";
+	execute_shell_command($command);
+
+	# Customize default MythTV library.xml to reflect KnoppMyth's wider selection of
+	# online stream options.
+	#$command = "sed -i \"/<type>STREAM<\\/type>\$/{N; N; N; N; s/text>.*<\\/text/text>Online Streams<\\/text/; s/action>.*<\\/action/action>MENU is.xml<\\/action/; s/<depends.*depends>//; }\" /usr/share/mythtv/library.xml";
+	$command = "/bin/cp /usr/share/mythtv/library.xml.km /usr/share/mythtv/library.xml"
+	execute_shell_command($command);
+	
+    } else {
+	my $logger = get_logger('tweaker.script');
+	$logger->error("ERROR: Unable to connect to mythconverg database");
+	$logger->error("ERROR: Unable to implement option $option.");
+	exit -1;
+    }
+    disconnect_from_db();
+}
+
+# Try to get a Recommendation Level for $option.
+sub poll_options {
+    my($option) = @_;
+    recommendation_level("recommended", "These tweaks benefit all users.");
+}
+
+# Unimplemented in 0.7
+sub check_option {
+    help;
+}
+
+# Unimplemented in 0.7
+sub count_iterations {
+    help;
+}
+
+process_parameters;
diff --git a/abs/core-testing/tweaker/bin/twk_what_has_changed.sh b/abs/core-testing/tweaker/bin/twk_what_has_changed.sh
new file mode 100755
index 0000000..deff7cf
--- /dev/null
+++ b/abs/core-testing/tweaker/bin/twk_what_has_changed.sh
@@ -0,0 +1,183 @@
+#!/bin/bash
+
+# Copyright 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv
+# and Tom Culliton of <affiliation>.  Most of restore_baseline_SQL taken from
+# KnoppMyth R5F27's /usr/local/bin/KnoppMyth-run script (multiple authors)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+#----------------------------------------------------------------------------
+. /usr/local/bin/backupcommon || {
+    echo "Can not find common settings!"
+    exit 1
+}
+#----------------------------------------------------------------------------
+
+# Target output file for our results
+DIFF_RESULTS=/tmp/database_changes.txt
+LOGFILE=/var/log/twk_what_has_changed.log
+WORKING_DIR=/tmp/
+
+# Restore the SQL to the way it would have been after R5F27 was installed
+# and before the user made any changes to MythTV settings.
+# This is KnoppMyth-centric and does not apply to general MythTV distros,
+# however it is possible to generalize this to apply to any MythTV distro
+# including generic Linux distros in which MythTV has been installed.
+#
+# Preconditions: MySQL daemon is running
+#
+# Postconditions: 'mythconverg' is deleted and re-created, just as it
+# would be during the course of a normal installation.
+restore_baseline_SQL() {
+    echo "restoring baseline SQL" >> $LOGFILE
+    KRP_RESULT=-1
+    KRPAutoDetect.pl &> /dev/null # be quiet
+    KRP_RESULT=$?;
+
+    # Parts from R5F27's KnoppMyth-run
+    KNOPPMYTH_SQL_FILE="/usr/local/share/knoppmyth/KnoppMyth.sql"
+    
+    # Try to detect and configure settings for various tuner
+    # and capture devices.
+    /usr/local/bin/TunerConfig.sh $KNOPPMYTH_SQL_FILE >> /dev/null
+    /usr/local/bin/ren_host.sh
+    GlobalSQLtweaker.sh $KNOPPMYTH_SQL_FILE
+
+    if [[ ($KRP_RESULT != 1) && ($KRP_RESULT != 2) && ($KRP_RESULT != 3) && ($KRP_RESULT != 10) && (-n "$IS_AUS") ]]; then # not Dragon
+	/usr/local/bin/epia_sql.sh
+    fi
+    
+    # General MythTV SQL settings for all boxes
+    if [ -f "/usr/local/bin/MythTV-sql" ]; then
+	# Interject platform-specific SQL settings before we populate the SQL
+	# database.
+	if [[ ($KRP_RESULT == 1) || ($KRP_RESULT == 2) || ($KRP_RESULT == 3) || ($KRP_RESULT == 10) ]]; then # Dragon
+	    DragonSQLtweaker.sh $KNOPPMYTH_SQL_FILE
+	fi
+	# Now use the (possibly modified) KnoppMyth.sql to populate initial
+	# MythTV settings.
+	
+	# This wipes out the current mythconverg and replaces it with what's in $KNOPPMYTH_SQL_FILE
+	sh /usr/local/bin/MythTV-sql $KNOPPMYTH_SQL_FILE
+    fi
+
+    echo \
+	"MythTV will re-launch in a tiny window.  If it asks you about
+upgrading the schema, agree.  When the schema upgrade is complete,
+exit MythTV.
+You can exit MythTV immediately if you aren't asked to do a schema
+upgrade."
+    echo "###"
+    echo "Press ENTER to re-launch MythTV."
+    read CONTINUE;
+    restart_mythtv "--geometry 640x480"
+}
+
+# Call mysql and only dump the requested data in tab seperated columns
+get_data () {
+    mysql mythconverg -u root -sNBe "$*"
+}
+
+# Compares $MYTHTV_EXPECTED_DB_NAME to $USERS_DB_NAME
+compare_databases() {
+    echo "comparing databases" >> $LOGFILE
+    tables_to_compare="capturecard cardinput codecparams displayprofilegroups displayprofiles dtv_multiplex keybindings playgroups profilegroups recordingprofiles settings videotypes weatherdatalayout weatherscreens weathersourcesettings"
+    hostnames=$(get_data "select distinct hostname from settings where hostname is not null") 
+
+    /etc/init.d/mysql start >> $LOGFILE
+    # Do some MySQL magic to compare $tables_to_compare between
+    # $MYTHTV_EXPECTED_DB_NAME (baseline) and $USERS_DB_NAME (user's version)
+    # ???
+}
+
+# This is somewhat KnoppMyth-centric, though minor modification would
+# make it work generally.
+function restart_mythtv {
+    echo "restarting MythTV" >> $LOGFILE
+    EXTRA_PARAMETERS=$1 ; shift
+    /etc/init.d/mythtv-backend start >> $LOGFILE
+    killall mythfrontend
+    # re-launches the frontend and gives the user a chance to upgrade to latest schema
+    su - mythtv -c "export DISPLAY=:0.0 ; mythfrontend --logfile /var/log/mythtv/mythfrontend.log $EXTRA_PARAMETERS" >> $LOGFILE
+}
+
+function rename_database {
+    db_source=$1 ; shift
+    db_target=$1 ; shift
+
+    echo "renaming database $db_source to $db_target" >> $LOGFILE
+
+    # Exit the frontend before messing with the SQL
+    killall mythfrontend
+    # If we stop MySQL before stopping the MythTV backend, we'll probably trigger badness
+    /etc/init.d/mythtv-backend stop >> $LOGFILE
+    # MySQL needs to stop before we do this.
+    /etc/init.d/mysql stop >> $LOGFILE
+    /bin/mv $db_source $db_target
+}
+
+main() {
+    echo -n "$0 started running: " >> $LOGFILE
+    date >> $LOGFILE
+
+    echo "STEP 0..."
+    echo "STEP 0" >> $LOGFILE
+    # 0) acquire system settings
+    MYTHTV_EXPECTED_DB_NAME=`grep DBName /etc/mythtv/mysql.txt | awk -F= '{ print $2 }'`
+    USERS_DB_NAME="mythconverg-copy"
+    /etc/init.d/mysql start >> $LOGFILE
+    # MySQL needs to be running or mysqladmin won't return anything
+    SQL_HOME=`mysqladmin variables | grep datadir | awk -F\| '{ print $3 }' | sed 's/ //g'`
+
+    echo "STEP 1..."
+    echo "STEP 1" >> $LOGFILE
+    # 1) rename the current MythTV MySQL database
+    rename_database $SQL_HOME$MYTHTV_EXPECTED_DB_NAME $WORKING_DIR$USERS_DB_NAME
+
+    echo "STEP 2..."
+    echo "STEP 2" >> $LOGFILE
+    # 2) re-create the R5F27 baseline SQL.
+    /etc/init.d/mysql start >> $LOGFILE
+    restore_baseline_SQL
+
+    echo "STEP 3..."
+    echo "STEP 3" >> $LOGFILE
+    # 3) compare the fresh baseline DB with the user's modified DB, and save the
+    # diffs to a text file
+    # ???
+
+    echo "STEP 4..."
+    echo "STEP 4" >> $LOGFILE
+    # 4) restore the user's database
+    rename_database $WORKING_DIR$USERS_DB_NAME $SQL_HOME$MYTHTV_EXPECTED_DB_NAME
+    /etc/init.d/mysql start >> $LOGFILE
+
+    echo "STEP 5..."
+    echo "STEP 5" >> $LOGFILE
+    # 5) Tell them the comparison is done, tell them where to find the results,
+    # and prompt them to restart MythTV.
+    echo "Your results are in $DIFF_RESULTS"
+    echo "###"
+    echo "Press ENTER to re-launch MythTV."
+    read CONTINUE;
+
+    restart_mythtv "&"
+    echo "DONE"
+    echo -n "$0 stopped running: " >> $LOGFILE
+    date >> $LOGFILE
+}
+
+main
+
diff --git a/abs/core-testing/tweaker/fs/etc/asound.conf b/abs/core-testing/tweaker/fs/etc/asound.conf
new file mode 100644
index 0000000..ffb665d
--- /dev/null
+++ b/abs/core-testing/tweaker/fs/etc/asound.conf
@@ -0,0 +1,15 @@
+pcm.spdifdmix {
+        type dmix
+        ipc_key 1337
+        slave {
+                pcm "hw:0,1"
+		format S32_LE
+        }
+}
+
+pcm.!default {
+        type plug
+        slave {
+		pcm spdifdmix
+        }
+}
diff --git a/abs/core-testing/tweaker/fs/var/lib/alsa/ALC888.asound.state b/abs/core-testing/tweaker/fs/var/lib/alsa/ALC888.asound.state
new file mode 100644
index 0000000..5540338
--- /dev/null
+++ b/abs/core-testing/tweaker/fs/var/lib/alsa/ALC888.asound.state
@@ -0,0 +1,354 @@
+state.NVidia {
+	control.1 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Front Playback Volume'
+		value.0 25
+		value.1 25
+	}
+	control.2 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Front Playback Switch'
+		value.0 true
+		value.1 true
+	}
+	control.3 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Surround Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.4 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Surround Playback Switch'
+		value.0 true
+		value.1 true
+	}
+	control.5 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Center Playback Volume'
+		value 0
+	}
+	control.6 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'LFE Playback Volume'
+		value 0
+	}
+	control.7 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Center Playback Switch'
+		value true
+	}
+	control.8 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'LFE Playback Switch'
+		value true
+	}
+	control.9 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Side Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.10 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Side Playback Switch'
+		value.0 true
+		value.1 true
+	}
+	control.11 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Headphone Playback Switch'
+		value.0 true
+		value.1 true
+	}
+	control.12 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Mic Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.13 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Mic Playback Switch'
+		value.0 false
+		value.1 false
+	}
+	control.14 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Front Mic Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.15 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Front Mic Playback Switch'
+		value.0 false
+		value.1 false
+	}
+	control.16 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Line Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.17 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Line Playback Switch'
+		value.0 false
+		value.1 false
+	}
+	control.18 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 3'
+		comment.dbmin 0
+		comment.dbmax 3000
+		iface MIXER
+		name 'Mic Boost'
+		value.0 0
+		value.1 0
+	}
+	control.19 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 3'
+		comment.dbmin 0
+		comment.dbmax 3000
+		iface MIXER
+		name 'Front Mic Boost'
+		value.0 0
+		value.1 0
+	}
+	control.20 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -1650
+		comment.dbmax 3000
+		iface MIXER
+		name 'Capture Volume'
+		value.0 0
+		value.1 0
+	}
+	control.21 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Capture Switch'
+		value.0 true
+		value.1 true
+	}
+	control.22 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -1650
+		comment.dbmax 3000
+		iface MIXER
+		name 'Capture Volume'
+		index 1
+		value.0 0
+		value.1 0
+	}
+	control.23 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Capture Switch'
+		index 1
+		value.0 true
+		value.1 true
+	}
+	control.24 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 Mic
+		comment.item.1 'Front Mic'
+		comment.item.2 Line
+		iface MIXER
+		name 'Input Source'
+		value Mic
+	}
+	control.25 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 Mic
+		comment.item.1 'Front Mic'
+		comment.item.2 Line
+		iface MIXER
+		name 'Input Source'
+		index 1
+		value Mic
+	}
+	control.26 {
+		comment.access read
+		comment.type IEC958
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Playback Con Mask'
+		value '0fff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+	}
+	control.27 {
+		comment.access read
+		comment.type IEC958
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Playback Pro Mask'
+		value '0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+	}
+	control.28 {
+		comment.access 'read write'
+		comment.type IEC958
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Playback Default'
+		value '0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+	}
+	control.29 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Playback Switch'
+		value false
+	}
+	control.30 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Default PCM Playback Switch'
+		value true
+	}
+	control.31 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Capture Switch'
+		value false
+	}
+	control.32 {
+		comment.access read
+		comment.type IEC958
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Capture Default'
+		value '0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+	}
+	control.33 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Master Playback Volume'
+		value 31
+	}
+	control.34 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Master Playback Switch'
+		value true
+	}
+	control.35 {
+		comment.access 'read write user'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 255'
+		comment.tlv '0000000100000008ffffec1400000014'
+		comment.dbmin -5100
+		comment.dbmax 0
+		iface MIXER
+		name 'PCM Playback Volume'
+		value.0 163
+		value.1 163
+	}
+}
diff --git a/abs/core-testing/tweaker/fs/var/lib/alsa/AV710.asound.state b/abs/core-testing/tweaker/fs/var/lib/alsa/AV710.asound.state
new file mode 100644
index 0000000..7e57108
--- /dev/null
+++ b/abs/core-testing/tweaker/fs/var/lib/alsa/AV710.asound.state
@@ -0,0 +1,567 @@
+state.AV710 {
+	control.1 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Master Playback Switch'
+		value true
+	}
+	control.2 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Master Playback Volume'
+		value.0 22
+		value.1 22
+	}
+	control.3 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Center Playback Switch'
+		value false
+	}
+	control.4 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Center Playback Volume'
+		value 31
+	}
+	control.5 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'LFE Playback Switch'
+		value false
+	}
+	control.6 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'LFE Playback Volume'
+		value 0
+	}
+	control.7 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 2
+		iface MIXER
+		name 'Surround Playback Switch'
+		value.0 false
+		value.1 false
+	}
+	control.8 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Surround Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.9 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Headphone Playback Switch'
+		value true
+	}
+	control.10 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Headphone Playback Volume'
+		value.0 22
+		value.1 22
+	}
+	control.11 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Master Mono Playback Switch'
+		value true
+	}
+	control.12 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -4650
+		comment.dbmax 0
+		iface MIXER
+		name 'Master Mono Playback Volume'
+		value 25
+	}
+	control.13 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'PC Speaker Playback Switch'
+		value false
+	}
+	control.14 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 15'
+		comment.dbmin -4500
+		comment.dbmax 0
+		iface MIXER
+		name 'PC Speaker Playback Volume'
+		value 0
+	}
+	control.15 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Phone Playback Switch'
+		value false
+	}
+	control.16 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Phone Playback Volume'
+		value 0
+	}
+	control.17 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Mic Playback Switch'
+		value false
+	}
+	control.18 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Mic Playback Volume'
+		value 0
+	}
+	control.19 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Mic Boost (+20dB)'
+		value false
+	}
+	control.20 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Line Playback Switch'
+		value false
+	}
+	control.21 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Line Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.22 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'CD Playback Switch'
+		value true
+	}
+	control.23 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'CD Playback Volume'
+		value.0 25
+		value.1 25
+	}
+	control.24 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Video Playback Switch'
+		value false
+	}
+	control.25 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Video Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.26 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Aux Playback Switch'
+		value false
+	}
+	control.27 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'Aux Playback Volume'
+		value.0 0
+		value.1 0
+	}
+	control.28 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'PCM Playback Switch'
+		value true
+	}
+	control.29 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 31'
+		comment.dbmin -3450
+		comment.dbmax 1200
+		iface MIXER
+		name 'PCM Playback Volume'
+		value.0 22
+		value.1 22
+	}
+	control.30 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 2
+		comment.item.0 Mic
+		comment.item.1 CD
+		comment.item.2 Video
+		comment.item.3 Aux
+		comment.item.4 Line
+		comment.item.5 Mix
+		comment.item.6 'Mix Mono'
+		comment.item.7 Phone
+		iface MIXER
+		name 'Capture Source'
+		value.0 Line
+		value.1 Line
+	}
+	control.31 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Capture Switch'
+		value true
+	}
+	control.32 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 2
+		comment.range '0 - 15'
+		comment.dbmin 0
+		comment.dbmax 2250
+		iface MIXER
+		name 'Capture Volume'
+		value.0 0
+		value.1 0
+	}
+	control.33 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name '3D Control - Switch'
+		value false
+	}
+	control.34 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 Mix
+		comment.item.1 Mic
+		iface MIXER
+		name 'Mono Output Select'
+		value Mix
+	}
+	control.35 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 Mic1
+		comment.item.1 Mic2
+		iface MIXER
+		name 'Mic Select'
+		value Mic1
+	}
+	control.36 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 15'
+		iface MIXER
+		name '3D Control - Center'
+		value 0
+	}
+	control.37 {
+		comment.access 'read write'
+		comment.type INTEGER
+		comment.count 1
+		comment.range '0 - 15'
+		iface MIXER
+		name '3D Control - Depth'
+		value 0
+	}
+	control.38 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Alternate Level to Surround Out'
+		value false
+	}
+	control.39 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Downmix LFE and Center to Front'
+		value false
+	}
+	control.40 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Downmix Surround to Front'
+		value false
+	}
+	control.41 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'External Amplifier'
+		value true
+	}
+	control.42 {
+		comment.access read
+		comment.type BYTES
+		comment.count 52
+		iface CARD
+		name 'ICE1724 EEPROM'
+		value '172414121c01020210c1ff0000ff0000ff0000000101010001000000000000000000000000000000ff000000ff000000ff000000'
+	}
+	control.43 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 '8000'
+		comment.item.1 '9600'
+		comment.item.2 '11025'
+		comment.item.3 '12000'
+		comment.item.4 '16000'
+		comment.item.5 '22050'
+		comment.item.6 '24000'
+		comment.item.7 '32000'
+		comment.item.8 '44100'
+		comment.item.9 '48000'
+		comment.item.10 '64000'
+		comment.item.11 '88200'
+		comment.item.12 '96000'
+		comment.item.13 '176400'
+		comment.item.14 '192000'
+		comment.item.15 'IEC958 Input'
+		iface MIXER
+		name 'Multi Track Internal Clock'
+		value '44100'
+	}
+	control.44 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Multi Track Rate Locking'
+		value false
+	}
+	control.45 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'Multi Track Rate Reset'
+		value true
+	}
+	control.46 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 'PCM Out'
+		comment.item.1 'H/W In 0'
+		comment.item.2 'H/W In 1'
+		comment.item.3 'IEC958 In L'
+		comment.item.4 'IEC958 In R'
+		iface MIXER
+		name 'H/W Playback Route'
+		value 'PCM Out'
+	}
+	control.47 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 'PCM Out'
+		comment.item.1 'H/W In 0'
+		comment.item.2 'H/W In 1'
+		comment.item.3 'IEC958 In L'
+		comment.item.4 'IEC958 In R'
+		iface MIXER
+		name 'H/W Playback Route'
+		index 1
+		value 'PCM Out'
+	}
+	control.48 {
+		comment.access read
+		comment.type INTEGER
+		comment.count 22
+		comment.range '0 - 255'
+		iface MIXER
+		name 'Multi Track Peak'
+		value.0 0
+		value.1 0
+		value.2 0
+		value.3 0
+		value.4 0
+		value.5 0
+		value.6 0
+		value.7 0
+		value.8 255
+		value.9 255
+		value.10 0
+		value.11 0
+		value.12 0
+		value.13 0
+		value.14 0
+		value.15 0
+		value.16 0
+		value.17 0
+		value.18 0
+		value.19 0
+		value.20 0
+		value.21 0
+	}
+	control.49 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 'PCM Out'
+		comment.item.1 'H/W In 0'
+		comment.item.2 'H/W In 1'
+		comment.item.3 'IEC958 In L'
+		comment.item.4 'IEC958 In R'
+		iface MIXER
+		name 'IEC958 Playback Route'
+		value 'PCM Out'
+	}
+	control.50 {
+		comment.access 'read write'
+		comment.type ENUMERATED
+		comment.count 1
+		comment.item.0 'PCM Out'
+		comment.item.1 'H/W In 0'
+		comment.item.2 'H/W In 1'
+		comment.item.3 'IEC958 In L'
+		comment.item.4 'IEC958 In R'
+		iface MIXER
+		name 'IEC958 Playback Route'
+		index 1
+		value 'PCM Out'
+	}
+	control.51 {
+		comment.access 'read write'
+		comment.type BOOLEAN
+		comment.count 1
+		iface MIXER
+		name 'IEC958 Output Switch'
+		value true
+	}
+	control.52 {
+		comment.access 'read write'
+		comment.type IEC958
+		comment.count 1
+		iface PCM
+		device 1
+		name 'IEC958 Playback Default'
+		value '0000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+	}
+	control.53 {
+		comment.access read
+		comment.type IEC958
+		comment.count 1
+		iface PCM
+		device 1
+		name 'IEC958 Playback Con Mask'
+		value '3fff000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+	}
+	control.54 {
+		comment.access read
+		comment.type IEC958
+		comment.count 1
+		iface PCM
+		device 1
+		name 'IEC958 Playback Pro Mask'
+		value df00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+	}
+}
diff --git a/abs/core-testing/tweaker/lib/Tweaker/Definitions.pm b/abs/core-testing/tweaker/lib/Tweaker/Definitions.pm
new file mode 100644
index 0000000..cbadf29
--- /dev/null
+++ b/abs/core-testing/tweaker/lib/Tweaker/Definitions.pm
@@ -0,0 +1,66 @@
+# Copyright 2007 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package Tweaker::Definitions;
+use List::Member;
+use Log::Log4perl qw(:easy);
+
+# To install the above modules:
+# --
+# sudo cpan install List::Member
+
+# Recommendation Levels
+my $bottom = -200; # a reserved value, under which no recommendationlevel can go
+my $not_available = -100;
+my $unsupported = -99;
+my $inadvisable = -50;
+my $optional = 0;
+my $recommended = 100;
+
+# Interactivity Levels
+my $guided = "guided";
+my $minimal = "minimal";
+
+# Special variables
+my $null = "null"; # reserved as the script name for special Tweaks that define Tweaker behavior
+
+my %global_variable_hash = (
+    "bottom" => $bottom,
+    "not available" => $not_available,
+    "unsupported" => $unsupported,
+    "inadvisable" => $inadvisable,
+    "optional" => $optional,
+    "recommended" => $recommended,
+    "guided" => $guided,
+    "minimal" => $minimal,
+    "null" => $null
+);
+
+sub get_global_variable_value {
+    my ($variable) = @_;
+
+    my @known_variables = keys %global_variable_hash;
+    if (member($variable, @known_variables) + 1) {
+	if ($global_variable_hash{$variable}) {
+	    return $global_variable_hash{$variable};
+	}
+    } else {
+	my $logger = get_logger();
+	$logger->error("No Tweaker Definition for variable named $variable");
+	return $bottom;
+    }
+}
+
+1;
diff --git a/abs/core-testing/tweaker/lib/Tweaker/Script.pm b/abs/core-testing/tweaker/lib/Tweaker/Script.pm
new file mode 100644
index 0000000..ec3a42a
--- /dev/null
+++ b/abs/core-testing/tweaker/lib/Tweaker/Script.pm
@@ -0,0 +1,353 @@
+# Copyright 2007, 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package Tweaker::Script;
+
+use strict;
+use DBI;
+use Switch;
+use Tweaker::Definitions;
+
+# To install the above modules:
+# --
+# sudo apt-get install libdbi-perl liblog-log4perl-perl
+# sudo cpan install Getopt::Lucid List::Member
+# choose the defaults for all options, agree to install all dependencies
+
+use Getopt::Lucid qw( :all );
+# see http://search.cpan.org/~dagolden/Getopt-Lucid-0.16/lib/Getopt/Lucid.pm for usage details
+use Log::Log4perl qw(get_logger);
+# see http://search.cpan.org/~mschilli/Log-Log4perl-1.14/lib/Log/Log4perl.pm for usage details
+# http://www.perl.com/pub/a/2002/09/11/log4perl.html is highly recommended as well
+
+use List::Member;
+
+my $dbh; # the database we connect to
+my $sth; # 
+my $TWEAKER_ROOT;
+my @known_options;
+
+# Print advice on usage and invocation.
+sub help () {
+    my $logger = get_logger('tweaker.script');
+    $logger->fatal("USAGE:\n$0 [--check Option] [--implement Option] [--poll Option] [--iterations] [--help]");
+    $logger->fatal("Valid values for Option are: ", join(" ",@known_options));
+    $logger->fatal("Only one of the above parameters may be passed.");
+    $logger->fatal("The --check and --iterations parameters are not yet supported.");
+    exit;
+}
+
+sub execute_shell_command {
+    my($command) = @_;
+    my $results="";
+    my $logger = get_logger('tweaker.script');
+
+    open(SHELL, "$command 2>&1 |");
+
+    while(<SHELL>) {
+	$results=$results."".$_;
+    }
+    close(SHELL);
+    chop($results);
+    $logger->debug("Command '$command' produced output '$results'");
+    return $results;
+}
+
+# Simple way to get the value of an environment variable from the executing shell.
+sub get_environment_variable {
+    my($envvar) = @_;
+    open(SHELL, "echo $envvar|");
+    while(<SHELL>) {
+	chop;
+	return $_;
+    }
+    close(SHELL);
+}
+
+sub get_mythtv_connection_string {
+    # we want something like mythconverg:localhost
+    my $dbname = "";
+    my $dbhostname = "";
+    open(MYSQLTXT, "< /home/mythtv/.mythtv/mysql.txt");
+    while(<MYSQLTXT>) {
+	if (/DBName=(.*)/) {
+	    $dbname=$1;
+	} elsif (/DBHostName=(.*)/) {
+	    $dbhostname=$1;
+	}
+    }
+    close(MYSQLTXT);
+    
+    return "$dbname:$dbhostname";
+}
+
+sub get_mythtv_authentication {
+    # we want something like ['root', '']
+    my $dbusername = "";
+    my $dbpassword = "";
+
+    open(MYSQLTXT, "< /home/mythtv/.mythtv/mysql.txt");
+    while(<MYSQLTXT>) {
+	if (/DBUserName=(.*)/) {
+	    $dbusername=$1;
+	} elsif (/DBPassword=(.*)/) {
+	    $dbpassword=$1;
+	}
+    }
+    close(MYSQLTXT);
+    
+    return ($dbusername, $dbpassword);
+}
+
+# Database routines.
+sub connect_to_db {
+    my ($db) = @_;
+    my $logger = get_logger('tweaker.script');
+    my ($dbusername, $dbpassword) = get_mythtv_authentication();
+    if (!($dbh = DBI->connect($db, $dbusername, $dbpassword))) {
+	$logger->error("Couldn't connect to database: ", DBI->errstr);
+	return -1;
+    }
+    return 1;
+}
+
+sub disconnect_from_db {
+    $dbh->disconnect;
+}
+
+sub do_query {
+    my ($query) =  @_;
+    my $logger = get_logger('tweaker.script');
+    my $rv="";
+    
+    $logger->debug("Processing statement: ", $query);
+    
+    if (!($sth = $dbh->prepare($query))) {
+	$logger->error("Couldn't prepare statement: ", $dbh->errstr);
+	return -1;
+    }
+    $rv = $sth->execute(); # Returns an integer when rows were affected; returns -1 when there's an
+    # error; returns 0E0 (true) when no error but no rows affected.
+
+    if (!$rv) {
+	$logger->error("Couldn't execute statement: ", $sth->errstr);
+	return -1;
+    }
+    return $rv;
+}
+
+# Make sure the option passed to this script is handled.
+sub validate_option {
+    my($option) = @_;
+
+    if (!(member($option, @known_options) + 1)) {
+	my $logger = get_logger('tweaker.script');
+	$logger->fatal("Option '$option' is not known to $0.");
+	$logger->fatal("Valid Options are: ", join(", ",@known_options));
+	exit -1;
+    }
+    return 1;
+}
+
+# Prints out each option that the script handles, separated by '|'.
+# This allows for a minimal .tcf entry.
+sub get_options {
+    print join("|",@known_options),"\n";
+}
+
+# Prints out the passed Recommendation Level first, followed by any other
+# strings, separated by '|'.  This allows the author of a Tweaker Script to
+# return explanatory information along with a Recommendation Level.
+sub recommendation_level {
+    print join("|",@_),"\n";
+}
+
+sub process_parameters () {
+    # Accept these parameters:
+    # --
+    my @parameters = (
+	Switch("help")->anycase,
+	Param("check")->anycase,  # Requirement 1.1
+	Param("implement")->anycase,  # Requirement 1.2
+	Param("poll")->anycase, # Requirement 1.3
+	Switch("iterations")->anycase, # Requirement 1.4
+	Switch("getoptions")->anycase # Requirement 1.5
+	);
+    my $opt = Getopt::Lucid->getopt( \@parameters );
+    
+    if (!(my $TWEAKER_ROOT = get_environment_variable("\$TWEAKER_ROOT"))) {
+	Log::Log4perl->easy_init();
+	my $logger = get_logger();
+	$logger->fatal("ERROR: \$TWEAKER_ROOT environment variable is not set.");
+	exit -1;
+    } else {
+	my $log4perl_conf = "$TWEAKER_ROOT/log4perl.conf";
+	Log::Log4perl::init_and_watch($log4perl_conf,10);
+	my $logger = get_logger('tweaker.script');
+	$logger->info("\$TWEAKER_ROOT is '$TWEAKER_ROOT'.");
+    }
+
+    if ($opt->get_help > 0) {
+	help;
+    }
+    
+    my $check = $opt->get_check;
+    my $implement = $opt->get_implement;
+    my $poll = $opt->get_poll;
+    my $iterations = $opt->get_iterations;
+    my $getoptions = $opt->get_getoptions;
+
+    # Requirement 1.6
+    if ($check) {
+	if ($implement || $poll || $iterations || $getoptions) {
+	    help;
+	}
+	validate_option($check); # exits with an error if option is invalid
+	check_option($check); # Requirement 1.1
+    } elsif ($implement) {
+	if ($poll || $iterations || $getoptions) {
+	    help;
+	}
+	validate_option($implement); # exits with an error if option is invalid
+	implement_option($implement); # Requirement 1.2
+    } elsif ($poll) {
+	if ($iterations || $getoptions) {
+	    help;
+	}
+	validate_option($poll); # exits with an error if option is invalid
+	poll_options($poll); # Requirement 1.3
+    } elsif ($iterations) {
+	if ($getoptions) {
+	    help;
+	}
+	#count_iterations; # Requirement 1.4
+    } elsif ($getoptions) {
+	get_options; # Requirement 1.5
+    } else {
+	help;
+    }
+}
+
+sub set_known_options {
+    my(@options) = @_;
+    @known_options = @_;
+}
+
+# These entries may or may not already exist.  First, try updating them, and if that fails, insert them.
+# Pass in array references for setfields and checkfields.  Each must
+# reference an array of lists, where each list is a key, value pair, e.g.
+# [["data", "somedata"], ["name", "skippy"]]
+sub change_or_make_entry {
+    my($table, $setfields, $checkfields) = @_;
+    my $query_string = "UPDATE $table SET ";
+
+    my $fields="";
+    foreach my $sets (@$setfields) {
+	if ($fields) {
+	    $fields = $fields . ", ";
+	}
+	$fields = $fields . "@$sets[0]='@$sets[1]' ";
+    }
+    $query_string = $query_string . $fields . "WHERE ";
+
+    my $checkstring="";
+    foreach my $checks (@$checkfields) {
+	if ($checkstring) {
+	    $checkstring = $checkstring . "AND ";
+	}
+	$checkstring = $checkstring . "@$checks[0]='@$checks[1]' ";
+    }
+    $query_string = $query_string . $checkstring;
+    
+    my $rv = do_query($query_string);
+    if (($rv == 0E0) || ($rv < 1)) { # UPDATE didn't apply; do an insert
+	my $fields="";
+	my $values="";
+	foreach my $sets (@$setfields) {
+	    if ($fields) {
+		$fields = $fields . ", ";
+		$values = $values . ", ";
+	    }
+	    $fields = $fields . "@$sets[0]";
+	    $values = $values . "'@$sets[1]'";
+	}
+	foreach my $sets (@$checkfields) {
+	    if ($fields) {
+		$fields = $fields . ", ";
+		$values = $values . ", ";
+	    }
+	    $fields = $fields . "@$sets[0]";
+	    $values = $values . "'@$sets[1]'";
+	}
+	
+	$query_string = "INSERT INTO $table (". $fields . ") VALUES (" . $values . ")";
+	
+	$rv = do_query($query_string);
+    }
+    return $rv;
+}
+
+# We update so many entries in the settings table that a subroutine makes coding and readability easier.
+sub change_or_make_setting {
+    my($value, $data) = @_;
+
+    return(change_or_make_entry("settings", [["data", $data]], [["value", $value]]));
+}
+
+# Benchmark-driven tests for low, medium, or high "performance" often look the same.
+# If your test falls into this pattern, you can use this subroutine to simplify your
+# Tweaker Script's poll_options subroutine.
+# NOTE: This only handles options for low, medium, or high right now.
+# NOTE: You don't have to use this!  Only use it if your poll_options subroutine
+# would look like this anyway.  Don't shoehorn it to fit!
+sub threshold_test {
+    my($option, $benchmark_number, $name_of_benchmarked_device, $low_threshold, $medium_threshold, $high_threshold) = @_;
+    # e.g. ("medium", 512, "video card", 350, 425, 500)
+
+    my $logger = get_logger('tweaker.script');
+    $logger->debug("Threshold test for option '$option' ($name_of_benchmarked_device) with benchmark of $benchmark_number, where: low = $low_threshold, medium = $medium_threshold, high = $high_threshold");
+    
+    switch ($option) {
+	case "low" {
+	    if ($benchmark_number <= $low_threshold) {
+		recommendation_level("recommended", "Your $name_of_benchmarked_device seems to be unable to handle higher usage than this.");
+	    } elsif ($benchmark_number <= $medium_threshold) {
+		recommendation_level("optional", "Your $name_of_benchmarked_device seems to be able to handle higher usage than this, but select this option if you want to reduce overall $name_of_benchmarked_device usage.");
+	    } elsif ($benchmark_number >= $high_threshold) {
+		recommendation_level("optional", "Your $name_of_benchmarked_device seems to be quite capable of this setting, but select this option if you want to reduce overall $name_of_benchmarked_device usage.");
+	    }
+	}
+	case "medium" {
+	    if ($benchmark_number <= $low_threshold) {
+		recommendation_level("inadvisable", "Your $name_of_benchmarked_device seems to be unable to handle this usage level, but you are free to try.");
+	    } elsif ($benchmark_number <= $medium_threshold) {
+		recommendation_level("recommended", "Your $name_of_benchmarked_device seems to be unable to handle higher usage than this.");
+	    } elsif ($benchmark_number >= $high_threshold) {
+		recommendation_level("optional", "Your $name_of_benchmarked_device seems to be able to handle higher usage than this, but select this option if you want to reduce overall $name_of_benchmarked_device usage.");
+	    }
+	}
+	case "high" {
+	    if ($benchmark_number <= $low_threshold) {
+		recommendation_level("inadvisable", "Your $name_of_benchmarked_device seems to be unable to handle this usage level, and it is not recommended that you try.");
+	    } elsif ($benchmark_number <= $medium_threshold) {
+		recommendation_level("inadvisable", "Your $name_of_benchmarked_device seems to be unable to handle this usage level, but you are free to try.");
+	    } elsif ($benchmark_number >= $high_threshold) {
+		recommendation_level("recommended", "Your $name_of_benchmarked_device seems to be quite capable of this setting.");
+	    }
+	}
+    }
+}
+
+1;
diff --git a/abs/core-testing/tweaker/tcf/EXAMPLE.tcf b/abs/core-testing/tweaker/tcf/EXAMPLE.tcf
new file mode 100644
index 0000000..0064fba
--- /dev/null
+++ b/abs/core-testing/tweaker/tcf/EXAMPLE.tcf
@@ -0,0 +1,38 @@
+<!-- This is an example TCF.  See TCFProgrammingSpecifications.odt for details.
+     //-->
+<tweak name = "Put a human-readable name for the tweak here.  Make sure it's unique."> <!-- REQUIRED //-->
+  <description>Put a human-readable description of the tweak here.</description> <!-- OPTIONAL but usually a good idea, unless the user
+										      will never see the description.
+										      //-->
+  <complexity>Put a complexity level here.</complexity> <!-- OPTIONAL human-readable assessment of how complex this Tweak is to understand.
+							     A complete list of complexity levels is listed in tweaker-core.tcf
+							     //-->
+  <script>twk_EXAMPLE.pl</script> <!-- REQUIRED: An executable script in the $PATH of the shell that invoked Tweaker.
+				       For consistency, adhere to the naming convention by beginning the script name with
+				       'twk_'.  You can make the tweaker script in any language you wish, but Perl is
+				       supported well.
+				       //-->
+  <alwaysaskuser>boolean</alwaysaskuser> <!-- OPTIONAL field with values: yes, no - not used in Tweaker v0.7
+					 //-->
+  <options> <!-- REQUIRED //-->
+    <option name = "Human-readable name for this option"> <!-- REQUIRED: Option names must match those handled in the
+							       corresponding tweaker script.
+							       //-->
+      <description>Human-readable description of what this option does.</description> <!-- OPTIONAL //-->
+      <complexity>Put a complexity level here.</complexity> <!-- OPTIONAL human-readable assessment of how complex this Tweak is to
+								 understand.  A complete list of complexity levels is listed in
+								 tweaker-core.tcf
+								 //-->
+      <recommendationlevel>recommendationlevel</recommendationlevel> <!-- OPTIONAL human-readable recommendation level for this tweak.
+									  If this field is absent, the script will be run to acquire
+									  the recommendationlevel.  The default recommendationlevel
+									  is $optional if it cannot otherwise be determined.  For a
+									  complete list of recommendation levels, see Tweaker/Definitions.pm
+									  //-->
+    </option>
+    <!-- Put any additional options here.  If you only have one option and it ends up with a recommendationlevel at or above $optional,
+	 it will be selected automatically.  Otherwise, the option with the highest recommendationlevel above $optional will be selected.
+	 //-->
+  </options>
+</tweak>
+<!-- Put any additional tweaks here. //-->
diff --git a/abs/core-testing/tweaker/tcf/focus.tcf b/abs/core-testing/tweaker/tcf/focus.tcf
new file mode 100644
index 0000000..353662c
--- /dev/null
+++ b/abs/core-testing/tweaker/tcf/focus.tcf
@@ -0,0 +1,113 @@
+<tweak name = "SQL table scrubber">
+	<script>twk_scrub_sql.pl</script>
+	<description>Scrubs the sections of mythconverg that KnoppMyth controls, preparing for the Tweaks below.</description>
+	<options>
+		<option name = "scrub">
+			<description>Everyone should run this, or some the Tweaks below may fail.</description>
+			<recommendationlevel>recommended</recommendationlevel>
+		</option>
+	</options>
+</tweak>
+<tweak name = "Audio">
+	<script>twk_audio.pl</script>
+	<description>Configures MythTV, xine, and mplayer to output audio the way you want.</description>
+	<options>
+		<option name = "analogstereo">
+			<description>Suitable for output to headphones, amplified speakers, or analog inputs on a receiver.</description>
+			<recommendationlevel>recommended</recommendationlevel>
+		</option>
+		<option name = "analogsurround">
+			<description>Suitable for direct output to multiple individual speakers.</description>
+		</option>
+		<option name = "digital">
+			<description>The best option if your hardware supports it.  Sends audio directly to a reciever to be processed.</description>
+		</option>
+	</options>
+</tweak>
+<tweak name = "CPU">
+	<script>twk_cpu.pl</script>
+	<description>Configures MythTV to use CPU-appropriate features and themes.</description>
+	<options>
+		<option name = "low">
+			<description>Uses the least amount of CPU but enables the fewest extra options.</description>
+		</option>
+		<option name = "medium">
+			<description>Uses a moderate amount of CPU and enables typical extra options.</description>
+		</option>
+		<option name = "high">
+			<description>Uses the most amount of CPU of the above options but enables the most extra options.</description>
+		</option>
+	</options>
+</tweak>
+<tweak name = "RAM">
+	<script>twk_RAM.pl</script>
+	<description>Configures MythTV to use the right-sized RAM cache for various options.</description>
+	<options>
+		<option name = "low">
+			<description>Uses the least amount of RAM but may cause buffering delays.</description>
+		</option>
+		<option name = "medium">
+			<description>Uses a moderate amount of RAM as a tradeoff between buffering and RAM usage.</description>
+		</option>
+		<option name = "high">
+			<description>Uses the most amount of RAM to reduce buffering and improve performance.</description>
+		</option>
+	</options>
+</tweak>
+<tweak name = "graphics">
+	<script>twk_graphics.pl</script>
+	<description>Configures MythTV to use graphical options that are appropriate for your video card.</description>
+	<options>
+		<option name = "low">
+			<description>Puts little stress on the video card.</description>
+		</option>
+		<option name = "medium">
+			<description>Puts the video card to work a bit and enables some eye candy.</description>
+		</option>
+		<option name = "high">
+			<description>Puts the video card hardest to work and enables more eye candy.</description>
+		</option>
+	</options>
+</tweak>
+<tweak name = "localization">
+	<script>twk_localization.pl</script>
+	<description>Configures the system to use your native language, system of measurements, etc.</description>
+	<options>
+		<option name = "US_English">
+			<description>This is for most North Americans.  Any units seen are in imperial units, such as Fahrenheit, pounds, feet, etc.</description>
+		</option>
+		<option name = "GB_English">
+			<description>This is for most UK residents.  Any units seen are in metric, such as Celsius, kilograms, meters, etc.</description>
+		</option>
+	</options>
+</tweak>
+<tweak name = "Tuner Configuration">
+	<script>twk_tuners.pl</script>
+	<description>Recommended for everyone.</description>
+	<options>
+		<option name = "all">
+			<description>Detects and auto-configures many tuner devices.</description>
+			<recommendationlevel>recommended</recommendationlevel>
+		</option>
+	</options>
+</tweak>
+<tweak name = "KnoppMyth Customizations">
+	<script>twk_general.pl</script>
+	<description>Recommended for everyone.</description>
+	<options>
+		<option name = "all">
+			<description>Changes made to the MythTV and KnoppMyth baseline settings for integration and functionality.</description>
+			<recommendationlevel>recommended</recommendationlevel>
+		</option>
+	</options>
+</tweak>
+<tweak name = "SQL table protecter">
+	<script>twk_scrub_sql.pl</script>
+	<description>Protects the sections of mythconverg that KnoppMyth controls, keeping our changes separate from user-made changes.</description>
+	<options>
+		<option name = "protect">
+			<description>Everyone should run this, or user-made changes might get clobbered.</description>
+			<recommendationlevel>recommended</recommendationlevel>
+		</option>
+	</options>
+</tweak>
diff --git a/abs/core-testing/tweaker/tcf/os.tcf b/abs/core-testing/tweaker/tcf/os.tcf
new file mode 100644
index 0000000..e70f4ee
--- /dev/null
+++ b/abs/core-testing/tweaker/tcf/os.tcf
@@ -0,0 +1,10 @@
+<tweak name = "Linux-related Customizations">
+	<script>twk_linux.pl</script>
+	<description>Recommended for everyone.</description>
+	<options>
+		<option name = "all">
+			<description>Changes made to the Linux baseline settings for integration and functionality.</description>
+			<recommendationlevel>recommended</recommendationlevel>
+		</option>
+	</options>
+</tweak>
diff --git a/abs/core-testing/tweaker/tcf/tcf.dtd b/abs/core-testing/tweaker/tcf/tcf.dtd
new file mode 100644
index 0000000..64ce67e
--- /dev/null
+++ b/abs/core-testing/tweaker/tcf/tcf.dtd
@@ -0,0 +1,11 @@
+<!ELEMENT tcf (tweak+)>
+<!ELEMENT tweak (description?, complexity?, script, alwaysaskuser?, options)>
+  <!ATTLIST tweak name CDATA #REQUIRED>
+<!ELEMENT description (#PCDATA)>
+<!ELEMENT complexity (#PCDATA)>
+<!ELEMENT script (#PCDATA)>
+<!ELEMENT alwaysaskuser (#PCDATA)>
+<!ELEMENT options (option+)>
+<!ELEMENT option (description?, complexity?, recommendationlevel?)>
+  <!ATTLIST option name CDATA #REQUIRED>
+<!ELEMENT recommendationlevel (#PCDATA)>
diff --git a/abs/core-testing/tweaker/tcf/tweaker-core.tcf b/abs/core-testing/tweaker/tcf/tweaker-core.tcf
new file mode 100644
index 0000000..d9c4bb6
--- /dev/null
+++ b/abs/core-testing/tweaker/tcf/tweaker-core.tcf
@@ -0,0 +1,47 @@
+<?xml version="1.0" ?>
+<!DOCTYPE tcf SYSTEM "/usr/LH/tweaker/tcf/tcf.dtd" [
+	  <!ENTITY os SYSTEM "/usr/LH/tweaker/tcf/os.tcf">
+<!--	  <!ENTITY distro SYSTEM "/usr/LH/tweaker/tcf/distro.tcf"> //-->
+	  <!ENTITY focus SYSTEM "/usr/LH/tweaker/tcf/focus.tcf">
+	  <!ENTITY userland SYSTEM "/usr/LH/tweaker/tcf/userland.tcf">
+	  ]>
+<tcf>
+  <tweak name = "Complexity Level">
+    <description>Some configuration options require more understanding than others.</description>
+    <complexity>basic</complexity>
+    <script>null</script>
+    <options>
+      <option name = "Basic">
+	<description>Show only the most basic configuration options.  This is best for new Users.</description>
+	<recommendationlevel>recommended</recommendationlevel>
+      </option>
+      <option name = "Intermediate">
+	<description>Show the basic configuration options as well as the options that veteran Users will understand.</description>
+      </option>
+      <option name = "Advanced">
+	<description>Show all configuration options, including those that only advanced Users will understand.</description>
+	<recommendationlevel>inadvisable</recommendationlevel>
+      </option>
+    </options>
+  </tweak>
+  <tweak name = "Interactivity">
+    <description>Tweaker can guess answers to some configuration options.  Interactivity determines whether or not these guesses are used automatically, or whether you have a chance to intervene.</description>
+    <complexity>basic</complexity>
+    <script>null</script>
+    <options>
+      <option name = "Minimal">
+	<description>Show only the most basic configuration options.  This is best for new Users.</description>
+	<recommendationlevel>inadvisable</recommendationlevel>
+      </option>
+      <option name = "Guided">
+	<description>Show the basic configuration options as well as the options that veteran Users will understand.</description>
+	<recommendationlevel>recommended</recommendationlevel>
+      </option>
+    </options>
+  </tweak>
+  &os;
+  &distro;
+  &focus;
+  &userland;
+</tcf>
+
diff --git a/abs/core-testing/tweaker/tcf/userland.tcf b/abs/core-testing/tweaker/tcf/userland.tcf
new file mode 100644
index 0000000..e69de29
diff --git a/abs/core-testing/tweaker/tweaker.tar.bz2 b/abs/core-testing/tweaker/tweaker.tar.bz2
deleted file mode 100644
index 6fd6a92..0000000
Binary files a/abs/core-testing/tweaker/tweaker.tar.bz2 and /dev/null differ
-- 
cgit v0.12