Updating MAME CHDs
by
, 03-09-2018 at 12:18 PM (451 Views)
My MAME CHDs have been a mess for years. I had a complete set at one stage, but the project moved from version 4 to version 5 several years ago and I never performed an update. Today I decided that I wanted a little green tick next to the set in clrmamepro and decided to convert what I had so I could start the ~500GiB torrent with some percentage already complete. Converting by hand would be a pain as there are so many of them and it turned out that at one stage I must have converted some of them manually, so I decided to automate the task.
The easiest way to achieve this I decided would be with a perl script that would walk through the directory containing the files recursively and convert each CHD that it encountered. As some were already converted, I would need to test them first and skip any that were already version 5. This was easy to achieve using perl with a minimum of modules needing to be used. The only modules I used are File::Copy for moving the original and converted files around, File::Stat so I could skip zero length files, and I used Capture::Tiny so I could capture the STDERR output from the chdman command in order to handle errors.
The script consists of two subroutines, the first being:
Perl Code:
sub test_chd { my $ver; my ( $stdout, $stderr ) = capture{ }; if ($_ =~ /^File/) { $ver = $_; $ver =~ s/.*:\s//; } } }
This subroutine simply takes a file path as an argument and then checks it using chdman. STDOUT and STDERR are captured using Capture::Tiny. A greedy regex then tests if STDERR contains the word 'error' and returns 0 if it does which informs the main loop that an error has occured.
The output from a successful chd check looks something like:
Code:chdman - MAME Compressed Hunks of Data (CHD) manager 0.195 (unknown) Input file: /mnt/storage/torrents/pd/MAME 0.175 CHDs/gauntdl/gauntd24.chd File Version: 4 Logical size: 2,287,467,520 bytes Hunk Size: 4,096 bytes Total Hunks: 558,464 Unit Size: 512 bytes Total Units: 4,467,710 Compression: zlib (Deflate) CHD size: 1,602,514,166 bytes Ratio: 70.1% SHA1: 3e055794d23d62680732e906cfaf9154765de698 Data SHA1: d6d9b15f3e20e3456431a6799aceeb2c0b4336aa Metadata: Tag='GDDD' Index=0 Length=35 bytes CYLS:34367,HEADS:5,SECS:26,BPS:512.
So the line we are interested in is 'File Version: 4'. We iterate over this info line by line until we hit a regex match on the word 'File' and push the line into $ver. The variable is then cleaned up leaving us with the integer only which is then handed back to the main loop.
The second subroutine is:
Perl Code:
This takes the file path as an argument and then hands it chdman which performs the conversion storing the new file in the configured location. Again we use Capture::Tiny, this time we are only interested in capturing STDERR, so we use the capture_stderr method. We return 1 if our regex matches the word 'error' so the main loop can handle the failure.
The main loop of the script is simply a recursive walk through the given directory. When we come across a file with the extension '.chd' we start our testing. If the file has a zero length we skip it and move on to the next one, otherwise we pass it to the test_chd subroutine. If test_chd returns a 0, null, or non-integer value then we move on to the next file. If the return value is an integer we test that it is less than $want_ver ($want_ver = 5 in our case) we then pass the file to the convert_chd subroutine.
Once conversion is finished we pass the converted file to test_chd to ensure the conversion worked. If the conversion was a success we move the new file to the appropriate directory. If $keep_old is set to 1 then we append '.old' to the original file before we move the new one into place. If the conversion was not a success then we move on to the next file.
The whole script is as follows:
Perl Code:
#!/usr/bin/perl use strict; use warnings; use Capture::Tiny qw/:all/; use File::Copy qw/move/; my $chdman = "/usr/local/bin/chdman"; my $tmpfile = "/tmp/out.chd"; my $want_ver = 5; my $keep_old = 0; my @dirs = (@ARGV); my %seen; my $chdver; # Strip trailing slashes my $x = 0; foreach ( @dirs ) { $x++; } sub test_chd { my $ver; my ( $stdout, $stderr ) = capture{ }; if ($_ =~ /^File/) { $ver = $_; $ver =~ s/.*:\s//; } } } sub convert_chd { my $retval = 0; $retval = 1 if $stderr =~ /error/g; } { foreach my $file ( @files ) { if ( -d "$pwd/$file" and ( $file !~ /^\.\.?$/ ) and not $seen{$file} ) { $seen{$file} = 1; } next if ($file !~ /\.chd$/i); next; } $chdver = test_chd("$pwd/$file"); { next if not $chdver =~ /^-?\d+\z/; if ( $chdver eq 0 ) { next; } } else { next; } if ( $chdver < $want_ver ) { my $r = convert_chd( "$pwd/$file" ); if ( $r eq 1 ) { next; } my $test = test_chd($tmpfile); if ( $test ne $want_ver ) { } else { if ( $keep_old eq 1 ) { move "$pwd/$file", "$pwd/$file.old"; } else { } move $tmpfile, "$pwd/$file"; } } else { } } }