http://qs321.pair.com?node_id=666943
Category: Utility Scripts
Author/Contact Info Lee Pumphret
Description: Converts obscure ".art" files that were generated from "VGA Art Studio" into bmps. If you have a ".art" file, it is almost certainly a different format. A quick hack, but it works.
#!/usr/bin/perl
=head1 NAME

art2bmp - Converts "VGA Art Studio files" to BMP

=head1 SYNOPSIS

B<art2bmp.pl> I<file.art>

=head1 DESCRIPTION

Converts "VGA Art Studio" (1991) files to BMP files. Not to be confuse
+d 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]... skippin
+g\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 $#imdat
+a) $!";
            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, 2
+4, 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 correcte
+d.

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 res
+ult 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                                         | Byt
+es |
+----------+-----------------------------------------------------+----
+---+
|      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         |  76
+8  |
|          | 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 straig
+ht
forward raster format - with 1 byte per pixel - (acting as an index in
+to the
palette in the case of image data - in the case of mask data, a non-ze
+ro 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 standa
+rd raster
format.

In the compressed format, the algorithm for uncompressing the data run
+s 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 co
+nsists of
the following data:

+----------+-----------------------------------------------------+----
+---+
|  Offset  | Description                                         | Byt
+es |
+----------+-----------------------------------------------------+----
+---+
|      0   | 124 dec. (acts as a file identifier)                |   1
+   |
|      1   | Palette data - 256 x (R,G,B) triplets, with         |  76
+8  |
|          | 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, it
+s
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 ye
+s.

Keep an eye out for more releases from Mooose Software, and please sup
+port
the Public Domain!