=head1 NAME

Mac::Types - Macintosh Toolbox Types and conversions.

=head1 DESCRIPTION

Access to Inside Macintosh is essential for proper use of these functions.
Explanations of terms, processes and procedures are provided there.
Any attempt to use these functions without guidance can cause severe errors in 
your machine, including corruption of data. B<You have been warned.>

=cut

use strict;

package Mac::Types;

use MacPerl 'MakeFSSpec';

BEGIN {
	use Exporter   ();
	use DynaLoader ();
	use Carp;

	use vars qw($VERSION @ISA @EXPORT %MacPack %MacUnpack);
	$VERSION = '1.02';
	@ISA = qw(Exporter DynaLoader);
	
	@EXPORT = qw(
		Debugger
		%MacPack
		%MacUnpack
		MacPack
		MacUnpack
	);
}

=head2 Functions

=over 4

=cut
sub _Identity {
	return wantarray ? @_ : shift;
}

sub _Packer {
	my($template) = @_;
	
	return sub { return pack($template, @_); };
}

sub _Unpacker {
	my($template) = @_;
	
	return sub { return unpack($template, $_[0] || ''); };
}

sub _PackPStr {
	my($string) = @_;
	
	return pack("Ca*", length($string), $string);
}

sub _UnpackPStr {
	my($string) = @_;
	
	return "" unless defined($string) && length($string);
	
	my ($length, $cstr) = unpack("Ca*", $string);
	
	return substr($cstr, 0, $length);
}

sub _PackPStrList {
	my($list) = pack("s", scalar(@_));
	for (@_) {
		$list .= _PackPStr($_);
	}
	return $list;
}

sub _UnpackPStrList {
	my($data) = @_;
	my($count, @strings) = unpack("s", $data);
	$data = substr($data,2);
	while ($count--) {
		my($str) = _UnpackPStr($data);
		$data = substr($data, length($str)+1);
		push(@strings, $str);
	}
	return @strings;
}

sub _PackFSSpec {
	my($spec) = MacPerl::MakeFSSpec($_[0]);
	my($packed) = pack("SL", hex(substr($spec, 1, 4)), hex(substr($spec, 5, 8))) 
		. _PackPStr(substr($spec, 14));
	return $packed . ("\0" x (70-length($packed)));
}

sub _UnpackFSSpec {
	my($spec) = @_;
	
	return 
		MacPerl::MakeFSSpec(sprintf(
			"\021%04x%08x:%s", 
			unpack("SL", $spec), 
			_UnpackPStr(substr($spec, 6))));
}

%MacPack = (
	TEXT => \&_Identity,
	enum => _Packer("A4"),
	type => _Packer("A4"),
	keyw => _Packer("A4"),
	sign => _Packer("A4"),
	bool => _Packer("c"),
	shor => _Packer("s"),
	long => _Packer("l"),
	sing => _Packer("f"),
	doub => _Packer("d"),
	magn => _Packer("L"),
	qdrt => _Packer("s4"),
	cRGB => _Packer("s3"),
	
	'STR ' => \&_PackPStr,
	'STR#' => \&_PackPStrList,
	'fss ' => \&_PackFSSpec,
);

%MacUnpack = (
	TEXT => \&_Identity,
	enum => _Unpacker("a4"),
	type => _Unpacker("a4"),
	keyw => _Unpacker("a4"),
	sign => _Unpacker("a4"),
	bool => _Unpacker("c"),
	shor => _Unpacker("s"),
	long => _Unpacker("l"),
	sing => _Unpacker("f"),
	doub => _Unpacker("d"),
	magn => _Unpacker("L"),
	qdrt => _Unpacker("s4"),
	cRGB => _Unpacker("S3"),
	
	'STR ' => \&_UnpackPStr,
	'STR#' => \&_UnpackPStrList,
	'fss ' => \&_UnpackFSSpec,
);

sub _MacConvert {
	my($type) = shift;
	my(@methods) = ();
	
	while (ref($type) eq "HASH") {
		unshift @methods, $type;
		$type = shift;
	}
	my($table,$code);
	for $table (@methods) {
		$code = $table->{$type};
		if (ref($code) eq "CODE") {
			return &{$code}(@_);
		}
	}
	croak "Don't know about type Ò$typeÓ";
}

=item MacPack [ CONVERTERS ...] CODE, DATA ...

Convert a perl value into a Mac toolbox type. Predefined codes are:

=over 4

=item TEXT 

Text (an identity operation).

=item enum

=item type 

=item keyw

A 4-byte string.

=item bool 

A boolean.

=item shor 

A short integer.

=item long 

A long integer.

=item sing 

A single precision float.

=item doub 

A double precision float.

=item magn 

An unsigned long.

=item qdrt

A QuickDraw C<Rect>.

=item 'STR ' 

A pascal style string.

=item 'STR#' 

A string list.

=item 'fss '

A file specification record.

=back

You can pass further code mappings as hash references.

=cut
sub MacPack {
	_MacConvert(\%MacPack, @_);
}

=item MacUnpack [ CONVERTERS ...] CODE, DATA

Convert a Mac toolbox type into a perl value. Predefined codes are as for 
C<MacPack>. You can pass further code mappings as hash references.

=cut
sub MacUnpack {
	_MacConvert(\%MacUnpack, @_);
}

bootstrap Mac::Types;

=back

=include Types.xs

=head1 AUTHOR

Written by Matthias Ulrich Neeracher E<lt>neeracher@mac.comE<gt>.
Currently maintained by Chris Nandor E<lt>pudge@pobox.comE<gt>.

=cut

1;

__END__
