#!/usr/bin/perl =head1 NAME art2bmp - Converts "VGA Art Studio files" to BMP =head1 SYNOPSIS B I =head1 DESCRIPTION Converts "VGA Art Studio" (1991) files to BMP files. Not to be confused with the numerous other formats using the ".art" extension. If you have a .art file, it's almost certainly a different format. Does not handle any mask data if present. =head1 AUTHOR Lee Pumphret =cut use strict; use warnings; my @files = glob(shift @ARGV); foreach my $file (@files) { next and warn "Mismatched extension in filename [$file]... skipping\n" unless $file=~/\.art$/i; print "processing $file...\n"; open( IN, "<$file" ) or die "Couldn't open $file :$!"; binmode(IN); my $buffer; my $ART_HEADER_SIZE = 8; # .art file header size # Read the header ( read( IN, $buffer, $ART_HEADER_SIZE ) == $ART_HEADER_SIZE ) or die "Error reading header data!"; my ( $ident, $mask, $x, $y, $im_compressed, $mask_compressed, ) = unpack( "CCssCC", $buffer ); unless ( $ident == 123 ) { die "Doesn't look like an ART file! [$ident]"; } # Read the palette data (rgb triplets) my @pal; for ( 0 .. 255 ) { read( IN, $buffer, 3 ) == 3 or die "Error reading palette!"; my @rgb = unpack( "CCC", $buffer ); push @pal, pack( "CCC", reverse @rgb ); } # Read image data my @imdata; my $pixel_count = 0; if ($im_compressed) { warn "Decompressing image [$x x $y]....\n"; while ( $pixel_count < $x * $y ) { read( IN, $buffer, 1 ) == 1 or die "Couldn't read! (have $pixel_count pixels $#imdata) $!"; my $byte = unpack( "C", $buffer ); if ( $byte < 128 ) { $imdata[ $pixel_count++ ] = $pal[$byte]; } else { # It's RLE my $repeat = $byte - 128; read( IN, $buffer, 1 ) == 1 or die "Couldn't read! :$!"; $byte = unpack( "C", $buffer ); push @imdata, ( $pal[$byte] ) x ( $repeat + 1 ); $pixel_count += $repeat + 1; } } } else { warn "$file is Uncompressed!"; read( IN, $buffer, $x * $y ) == $x * $y or die "Short read on $file"; @imdata = map { $pal[$_] } unpack( "C*", $buffer ); } if ($mask){ warn "Image has mask data, ignoring..."; } # Write out the bmp file ( my $outfile = $file ) =~ s/art$/bmp/i; my $written = $#imdata; open( OUT, ">$outfile" ) or die "Couldn't open output!"; binmode(OUT); # BMP 3.1 Header print OUT pack "C C l a a a a l l l l s s l l l l l l", 0x42, 0x4D, 54 + ( $x * 3 ) * $y, 'l', 'e', 'e', 'p', 54, 0x28, $x, $y, 1, 24, 0, 0, 0, 0, 0, 0; # Write out the image data, bottom to top while ( $written > 0 ) { print OUT @imdata[ $written - ( $x - 1 ) .. $written ]; $written -= $x; } close OUT; } __END__ A snippet of documentation from "VGA Art Studio" * Note, there were some typos, errors in the doc which I have corrected. Appendix A: VGA Art Studio .ART and .PAL file formats ========================================================= The .ART file ------------- All VGA Art Studio picture/cutout files are stored in this format. In fact, there is no distinction between a picture or a cutout - a picture can be pasted in as a cutting, and a cutting can be loaded in as a file. There are essentially two image formats - compressed and uncompressed. Before saving a file, VGA Art Studio works out which format will actually result in a smaller file, and then saves the file in this format. Actually, it is not quite as simple as that - each image may need a 'mask', and whether or not they the mask is compressed is independent of whether or not the picture itself is compressed. However, the file format is still relatively easy to use, and is as follows : +----------+-----------------------------------------------------+-------+ | Offset | Description | Bytes | +----------+-----------------------------------------------------+-------+ | 0 | 123 dec. (acts as a file identifier) | 1 | | 1 | Mask included? (1=yes, 0=no) | 1 | | 2 | Size (x) in pixels of the image - INTEL format | 2 | | 4 | Size (y) in pixels of the image - INTEL format | 2 | | 6 | Image data compressed? (1=yes, 0=no) | 1 | | 7 | Mask data compressed ? (1=yes, 0=no) | 1 | | 8 | Palette data - 256 x (R,G,B) triplets, with | 768 | | | individual intensities from 0..255 | | | 776 | Image data (may be compressed) | ??? | | ??? | (optional) Mask data (may be compressed) | ??? | +----------------------------------------------------------------+-------+ As mentioned, both the image data and the mask data may be compressed depending on the values in bytes 6 and 7 of the header. The two formats are the same for both the mask and the image, and work as follows: The data in its uncompressed form consists of 64000 bytes, in a straight forward raster format - with 1 byte per pixel - (acting as an index into the palette in the case of image data - in the case of mask data, a non-zero value indicates that the corresponding image pixel IS to be displayed). The bytes are stored row by row, with each row running from left to right, in standard raster format. In the compressed format, the algorithm for uncompressing the data runs as follows : +-----------------------------+ | Read the next byte of data | | from the file, (unless we |<-------------------------------------------+ | now have data for 64000 | | | pixels, in which case stop).| | +-----------------------------+ | | | \ / | +--------------------+ Y +----------------------------------+ | | Is the byte <128 ? |--------->| Use this byte for the next pixel |----+ +--------------------+ +----------------------------------+ ^ | N | \ / | +--------------------------------------------------------------------+ | | Subtract 128 from the value of the byte - call this C. Then read | | | the next byte - this gives you the colour of the next (C+1) |-----+ | pixels. | +--------------------------------------------------------------------+ Note: Compressed data is stored a line at a time. Compression using a byte >128 as a repeat count MUST NOT wrap round from one line to the beginning of the next. The .PAL file ------------- This is very very straightforward. Each file is 769 bytes long, and consists of the following data: +----------+-----------------------------------------------------+-------+ | Offset | Description | Bytes | +----------+-----------------------------------------------------+-------+ | 0 | 124 dec. (acts as a file identifier) | 1 | | 1 | Palette data - 256 x (R,G,B) triplets, with | 768 | | | individual intensities from 0..255 | | +----------------------------------------------------------------+-------+ ---------------------------------------------------------------------------- Appendix D : About Mooose Software ================================== "Founded" in 1990 by : Ben Stragnell Graham Sanderson Samer Abdallah \ \ / \/ ___// \_ / // \] //~~~ \\ ]] // // \__\ _]_\_ _\\ __/\// __ _____\ /_\// _ __ _/ \/~~~~~~\/ \__ // _/ ] ] \/ /] / \ ] / ](0 0)] / ] ] ____________~ ] ] \ < > / / \______/ ] ] ] ] The moose is without doubt one of the finest animals ever invented, its majestic antlers towering skyward, as it stands proudly, one of Nature's most cunning creations. It's also got a really cool name, and you can put an extra 'o' in it, to make it last just that little bit longer. Oh yes. Keep an eye out for more releases from Mooose Software, and please support the Public Domain!