The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Games::Freelancer::UTF - Perl extension for working with Microsoft UTF Files used in the Game Freelancer.

Synopsis

use Games::Freelancer::UTF;
open FILE,"model.cmp"; #or .utf, .3db, .txm, .mat, .ale, .vms, .dfm or maybe some more
binmode FILE;
my $content = do {local $/; <FILE>};
close FILE;
my $tree=UTFread($content);
$code = UTFwriteUTF($tree);
open FILE, ">out.cmp"
binmode FILE;
print FILE, $code;
close FILE;

DESCRIPTION

This Module provides the ability to decode UTF files for the Mircrosoft game "Freelancer"

Those are named UTF files because of their header.

They are just trees that are encoded in binary, there might be a possibility that these files are used somewhere else, too.

In "Freelancer" they are used for models, meshes, materials, textures, effects and a lot more.

You can even use this to save hashes of hashes, but I highly recommend using something else, like Storable for this.

WARNING

The read routines return a tied InsertOrderHash, so just keep the reference you got and work with it instead of using

my %mysuperhash = %{UTFread($crypted)}
%mysuperhash{NewEntry} = "Data"
# Now the order is destroyed
# This is better:
my $tree=UTFread($crypted);
$tree->{NewEntry} = "Data"

I have no idea how important the order of the elements is for Freelancer, but better keep it this way. Of course all subhashes are also tied #Bad code example $tree->{copyme}={%{$tree}}; #Good code: tie my %newhash,'Tie::InsertOrderHash'; %newhash=(%{$tree}) #Copy tree, preserves order, at least last time I tested $tree->{copyme}=\%newhash; #not this: $tree->{copyme}={%newhash}; #Looses tiedness too.

FUNCTIONS

$tree = UTFread ($data);

Reads an UTF file content into a tied tree:

use Games::Freelancer::UTF;
use Data::Dumper;
open FILE,"model.cmp";
binmode FILE;
my $content = do {local $/; <FILE>};
close FILE;
my $tree=UTFread($content);
print Data::Dumper->Dump([$tree]);

$data = UTFwrite ($tree);

Reads an UTF file content into a tied tree:

use Games::Freelancer::UTF;
open FILE,"model.cmp";
binmode FILE;
my $content = do {local $/; <FILE>};
close FILE;
my $tree=UTFread($content);
#... Do something with $tree ..., for example moving a hardpoint:
foreach my $entr (grep /\.3db/,keys %{$tree->{"\\"}}) {
	foreach (keys %{$tree->{"\\"}->{$entr}->{Hardpoints}->{"Fixed"}}) {
		#moves all fixed hardpoints along (0.2,0.2,0.2):
		$tree->{"\\"}->{$entr}->{Hardpoints}->{"Fixed"}->{$_}->{Position}=pack("f*",map {$_+0.2} unpack("f*",$tree->{"\\"}->{$entr}->{Hardpoints}->{"Fixed"}->{$_}->{Position})); 
	}
}
# Now write it down.
open FILE,">model2.cmp";
binmode FILE;
print FILE UTFwrite($tree);
close FILE;

Example tree

This is an example of deparsed tree of a very basic model a friend of mine made:

$tree = {
	  '\\' => {
		    'VMeshLibrary' => {
					'jc_defender.lod0.vms' => {
								    'VMeshData' => 'Some Vmeshdata' #Removed by me because you can't see anything useful here and its large.
								  }
				      },
		    'Cmpnd' => {
				 'Root' => {
					     'File name' => 'jc_defender.3db�',
					     'Index' => '��������',
					     'Object name' => 'Root�'
					   }
			       },
		    'jc_defender.3db' => {
					   'Hardpoints' => {
							     'Fixed' => {
									  'HpEngine01' => {
											    'Orientation' => '��€?��������������€?��������������€?', #These are just packed vectors, can be easily decoded using unpack("f*",this)
											    'Position' => '����IK�¿×A' #also a vector: unpack("f*",this)
											  },
 
									}
							     'Revolute' => {
									     'HpWeapon01' => {
											       'Axis' => '������€?����', #also a vector: unpack("f*",this)
											       'Max' => '’
†>����', #an angle in radians: unpack("f*",this)
											       'Min' => '’
†¾����',
											       'Orientation' => 'Z|¿âÐ1¾���€âÐ1>Z|¿�����������€��€?', #also a vector: unpack("f*",this)
											       'Position' => '­L€?Œø®>¿+±À' #also a vector: unpack("f*",this)
											     },
									     'HpWeapon02' => {
											       'Axis' => '������€?����',
											       'Max' => '’
†>����',
											       'Min' => '’
†¾����',
											       'Orientation' => 'Z|¿âÐ1>����âÐ1¾Z|¿��������������€?', 
											       'Position' => 'r¿Œø®>¿+±À'
											     }
									   }
							   },
					   'MultiLevel' => {
							     'Level0' => {
									   'VMeshPart' => {
											    'VMeshRef' => '<���Úý��x��n
����¯Ë@Ä�ËÀ“°š?d÷¤¿-¬Aï7Á�`Ž:m$½€ð¢½ä�,A'
											  }
									 }
							   }
					 }
		  }
	};

I cut out the VMesh part which is just binary data, but it can be read using Games::Freelancer::VMesh

Games::Freelancer::VMesh for in UTF files included VMeshData and VMeshRef values.

Games::Freelancer::BINI for another type of file used by Freelancer.

AUTHOR

Marc "Maluku" Sebastian Lucksch perl@marc-s.de

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 369:

Non-ASCII character seen before =encoding in ''��€?��������������€?��������������€?','. Assuming CP1252

Around line 409:

Unknown directive: =head