#!/usr/bin/perl

############################################################################
#                                                                          #
# MAME XML-to-MythTV database parsing script v1.0                          #
#                                                                          #
# Copyright (C) 2008 Lazor Software                                        #
#                                                                          #
#  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 2 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.                            #
#                                                                          #
#  The full GPL license can be found on the GNU web site:                  #
#  http://www.gnu.org/licenses/gpl.txt                                     #
#                                                                          #
############################################################################


use Config::Tiny;
use DBI;
use XML::Parser;
use strict;

############################################################################
########################### VARIABLES START HERE ###########################
############################################################################

#
# Database Settings.
#
# Change these to match your MythTV configuration
#
my $db_server   = "localhost";
my $db_database = "mythtv";
my $db_username = "mythtv";
my $db_password = "mythtv";

#
# XML File settings
#
# The two variables below tell the script where to find the XML game data.
# If you have an XML file already generated by MAME, simply change the first
# variable ($xml_file) to the path/filename of the file, then remove the #
# in front of the last line. Otherwise, the script will run the given
# command to generate the XML file.
#
my $xml_file = "/tmp/mame.xml";
my $mame    = "/usr/local/bin/mame64 -listxml 2>/dev/null >$xml_file";
#my $mame    = "";

#
# CatVer.ini settings (if applicable)
#
# If you have a copy of catver.ini available (the simple version format,
# NOT MAME32), you can specify that file here. The categories will be
# extracted for each game from this file. The Version Added info will
# also be extracted. If you do not have this file, then remove the # in
# front of the last line.
# 
my $catver = "/usr/local/share/xmame/catver.ini";
#my $catver = "";


############################################################################
############# CODE STARTS HERE (DO NOT MODIFY BELOW THIS LINE) #############
############################################################################

# force buffer flushing (otherwise the progress dots won't appear)
local $| = 1;

my $DEBUG = 0;
my $count = 0;
my $catver_ini;
my $total_changed = 0;

my %game_attr    = (
                    'name'    => '',
                    'genre'   => '',
                    'cloneof' => ''
                   );
my %content_vars = (
                    'description'   => '',
                    'year'          => '',
                    'manufacturer'  => ''
                   );

# connect to database
my $dbh = DBI->connect("dbi:mysql:$db_database:$db_server", $db_username, $db_password, {AutoCommit => 1})
    or die("Cannot connect to MySQL database! Please check your settings.");


# generate the XML file, if told to do so
if ($mame ne "")
{
    print "Generating MAME XML ...\n";
    system $mame;
}

# read in the INI file, if set
if (-f $catver)
{
    print "Parsing catver.ini ...\n";
    $catver_ini = Config::Tiny->read($catver);
}

die "Can't find file '$xml_file'" unless -f $xml_file;
    
my $parser = new XML::Parser(ErrorContext => 2);
$parser->setHandlers(
                    Start => \&start_tag_handler,
                    End   => \&end_tag_handler,
                    Char  => \&content_handler
                    );

print "Parsing XML and updating the database ...\n";
$parser->parsefile($xml_file);

print "\nDone!\n";
print "Total Roms found:   $count\n";
print "Total Rows updated: $total_changed\n";


sub start_tag_handler
{

    my $p       = shift;
    my $element = shift;

    my $parent = $p->current_element;

    # we found the game node. grab the info
    if ($parent eq "mame" && $element eq "game")
    {

        print "Found element: $parent::$element\n" if ($DEBUG == 2);

        # clear out the old game attributes
        foreach my $key (keys %content_vars) { $content_vars{$key} = ''; }

        # Deal with attributes

        my %attr;
        my $name;
        while (@_)
        {
            $name = shift;
            $attr{$name} = shift;

            print "  Found attributes: $name=$attr{$name}\n" if ($DEBUG == 2);
        }

        $game_attr{'name'}    = $attr{'name'};
        $game_attr{'cloneof'} = $attr{'cloneof'};
        $game_attr{'genre'}   = $catver_ini->{'Category'}->{$attr{'name'}};
        $game_attr{'version'} = $catver_ini->{'VerAdded'}->{$attr{'name'}};

    }

}

sub end_tag_handler
{

    my $p       = shift;
    my $element = shift;

    my $parent = $p->current_element;

    # we have reached the end of the game node. process pending info
    if ($parent eq "mame" && $element eq "game")
    {
        process_pending();
    }

}

sub content_handler
{

    my ($p, $data) = @_;
    my $parent = $p->current_element;

    if (exists $content_vars{$parent})
    {
        # a weird bug in the XML parsing sometimes causes a single XML node to be broken
        # into two nodes
        $content_vars{$parent} .= $data;
        print "Found CONTENT: $parent=$content_vars{$parent}\n" if ($DEBUG == 2);
    }

}

sub process_pending
{

    # this method will process any pending ROM data by putting it in the database
    if ($game_attr{'name'} ne "")
    {
        $count++;
        print "----------------------------------------\n" if ($DEBUG == 1);
        print "Game:    $game_attr{'name'}\n"              if ($DEBUG == 1);
        print "Cloneof: $game_attr{'cloneof'}\n"           if ($DEBUG == 1);
        print "Desc:    $content_vars{'description'}\n"    if ($DEBUG == 1);
        print "Year:    $content_vars{'year'}\n"           if ($DEBUG == 1);
        print "Manf:    $content_vars{'manufacturer'}\n"   if ($DEBUG == 1);
        print "----------------------------------------\n" if ($DEBUG == 1);
        print "." if ($count % 50 == 0);

        # we don't prepare the query because it makes debugging easier if we need to
        # echo out the query later
        my $query = "UPDATE gamemetadata SET " .
                    "year     = " . $dbh->quote($content_vars{'year'}) . ", " .
                    "gamename = " . $dbh->quote($content_vars{'description'}) . ", " .
                    "version  = " . $dbh->quote($game_attr{'version'}) . ", " .
                    "genre    = " . $dbh->quote($game_attr{'genre'}) . " " .
                    "WHERE " .
                    "gametype = 'MAME' AND " .
                    "romname LIKE " . $dbh->quote($game_attr{'name'} . '.zip');

        if ($DEBUG == 0)
        {
            my $affected_rows = $dbh->do($query) or die("Error executing query: " . $dbh->errstr . "\n");
            $total_changed += $affected_rows;
        }
        else
        {
            print "QUERY: $query\n" if ($DEBUG == 1);
        }
    }

}
