line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
###############################################################################
|
2
|
|
|
|
|
|
|
#UTF.pm
|
3
|
|
|
|
|
|
|
#Last Change: 2008-12-08
|
4
|
|
|
|
|
|
|
#Copyright (c) 2008 Marc-Seabstian "Maluku" Lucksch
|
5
|
|
|
|
|
|
|
#Version 0.21
|
6
|
|
|
|
|
|
|
####################
|
7
|
|
|
|
|
|
|
#This file is part of the Plasma project, a parser library for an all-purpose
|
8
|
|
|
|
|
|
|
#ASCII file format. More information can be found on the project web site
|
9
|
|
|
|
|
|
|
#at http://plasma.sf.net/ .
|
10
|
|
|
|
|
|
|
#
|
11
|
|
|
|
|
|
|
#UTF.pm is published under the terms of the MIT license, which basically
|
12
|
|
|
|
|
|
|
#means "Do with it whatever you want". For more information, see the license.txt
|
13
|
|
|
|
|
|
|
#file that should be enclosed with plasma distributions. A copy of the license
|
14
|
|
|
|
|
|
|
#is (at the time of this writing) also available at
|
15
|
|
|
|
|
|
|
#http://www.opensource.org/licenses/mit-license.php .
|
16
|
|
|
|
|
|
|
###############################################################################
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package Games::Freelancer::UTF;
|
19
|
1
|
|
|
1
|
|
19566
|
use Exporter;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
46
|
|
20
|
|
|
|
|
|
|
{
|
21
|
1
|
|
|
1
|
|
5
|
no warnings qw/portable/; #Why is this so hard... :(
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
42
|
|
22
|
|
|
|
|
|
|
local $^W=0; #Grrr @ Tie::InsertOrderHash;
|
23
|
|
|
|
|
|
|
require Tie::InsertOrderHash;
|
24
|
|
|
|
|
|
|
}
|
25
|
1
|
|
|
1
|
|
4
|
use strict;
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
25
|
|
26
|
1
|
|
|
1
|
|
4
|
use warnings;
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
43
|
|
27
|
1
|
|
|
1
|
|
10
|
use warnings::register;
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
149
|
|
28
|
|
|
|
|
|
|
|
29
|
1
|
|
|
1
|
|
5
|
use Carp;
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
520
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $VERSION = 1.001;
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head1 NAME
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
Games::Freelancer::UTF - Perl extension for working with Microsoft UTF Files used in the Game Freelancer.
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head1 Synopsis
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
use Games::Freelancer::UTF;
|
40
|
|
|
|
|
|
|
open FILE,"model.cmp"; #or .utf, .3db, .txm, .mat, .ale, .vms, .dfm or maybe some more
|
41
|
|
|
|
|
|
|
binmode FILE;
|
42
|
|
|
|
|
|
|
my $content = do {local $/; };
|
43
|
|
|
|
|
|
|
close FILE;
|
44
|
|
|
|
|
|
|
my $tree=UTFread($content);
|
45
|
|
|
|
|
|
|
$code = UTFwriteUTF($tree);
|
46
|
|
|
|
|
|
|
open FILE, ">out.cmp"
|
47
|
|
|
|
|
|
|
binmode FILE;
|
48
|
|
|
|
|
|
|
print FILE, $code;
|
49
|
|
|
|
|
|
|
close FILE;
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
This Module provides the ability to decode UTF files for the Mircrosoft game "Freelancer"
|
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Those are named UTF files because of their header.
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
They are just trees that are encoded in binary, there might be a possibility that these files are used somewhere else, too.
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
In "Freelancer" they are used for models, meshes, materials, textures, effects and a lot more.
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
You can even use this to save hashes of hashes, but I highly recommend using something else, like L for this.
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 WARNING
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
The read routines return a tied InsertOrderHash, so just keep the reference you got and work with it instead of using
|
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
my %mysuperhash = %{UTFread($crypted)}
|
69
|
|
|
|
|
|
|
%mysuperhash{NewEntry} = "Data"
|
70
|
|
|
|
|
|
|
# Now the order is destroyed
|
71
|
|
|
|
|
|
|
# This is better:
|
72
|
|
|
|
|
|
|
my $tree=UTFread($crypted);
|
73
|
|
|
|
|
|
|
$tree->{NewEntry} = "Data"
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
I have no idea how important the order of the elements is for Freelancer, but better keep it this way.
|
77
|
|
|
|
|
|
|
Of course all subhashes are also tied
|
78
|
|
|
|
|
|
|
#Bad code example
|
79
|
|
|
|
|
|
|
$tree->{copyme}={%{$tree}};
|
80
|
|
|
|
|
|
|
#Good code:
|
81
|
|
|
|
|
|
|
tie my %newhash,'Tie::InsertOrderHash';
|
82
|
|
|
|
|
|
|
%newhash=(%{$tree}) #Copy tree, preserves order, at least last time I tested
|
83
|
|
|
|
|
|
|
$tree->{copyme}=\%newhash;
|
84
|
|
|
|
|
|
|
#not this:
|
85
|
|
|
|
|
|
|
$tree->{copyme}={%newhash}; #Looses tiedness too.
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=head1 FUNCTIONS
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=cut
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Perl-Port by Maluku (fl@maluku.de)
|
92
|
|
|
|
|
|
|
our @ISA=qw/Exporter/;
|
93
|
|
|
|
|
|
|
our @EXPORT=qw/UTFread UTFwrite/;
|
94
|
|
|
|
|
|
|
our @EXPORT_OK=qw/UTFread UTFwrite/;
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
my $datas;
|
97
|
|
|
|
|
|
|
my $strings;
|
98
|
|
|
|
|
|
|
my $pointer;
|
99
|
|
|
|
|
|
|
my %strings;
|
100
|
|
|
|
|
|
|
my %datas;
|
101
|
|
|
|
|
|
|
my %offsets;
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head2 $tree = UTFread ($data);
|
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
Reads an UTF file content into a tied tree:
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
use Games::Freelancer::UTF;
|
109
|
|
|
|
|
|
|
use Data::Dumper;
|
110
|
|
|
|
|
|
|
open FILE,"model.cmp";
|
111
|
|
|
|
|
|
|
binmode FILE;
|
112
|
|
|
|
|
|
|
my $content = do {local $/; };
|
113
|
|
|
|
|
|
|
close FILE;
|
114
|
|
|
|
|
|
|
my $tree=UTFread($content);
|
115
|
|
|
|
|
|
|
print Data::Dumper->Dump([$tree]);
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=cut
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub UTFread {
|
120
|
1
|
|
|
1
|
1
|
298
|
my $d = UTFreadUTF(@_);
|
121
|
1
|
|
|
|
|
40
|
return $d;
|
122
|
|
|
|
|
|
|
}
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 $data = UTFwrite ($tree);
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Reads an UTF file content into a tied tree:
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
use Games::Freelancer::UTF;
|
130
|
|
|
|
|
|
|
open FILE,"model.cmp";
|
131
|
|
|
|
|
|
|
binmode FILE;
|
132
|
|
|
|
|
|
|
my $content = do {local $/; };
|
133
|
|
|
|
|
|
|
close FILE;
|
134
|
|
|
|
|
|
|
my $tree=UTFread($content);
|
135
|
|
|
|
|
|
|
#... Do something with $tree ..., for example moving a hardpoint:
|
136
|
|
|
|
|
|
|
foreach my $entr (grep /\.3db/,keys %{$tree->{"\\"}}) {
|
137
|
|
|
|
|
|
|
foreach (keys %{$tree->{"\\"}->{$entr}->{Hardpoints}->{"Fixed"}}) {
|
138
|
|
|
|
|
|
|
#moves all fixed hardpoints along (0.2,0.2,0.2):
|
139
|
|
|
|
|
|
|
$tree->{"\\"}->{$entr}->{Hardpoints}->{"Fixed"}->{$_}->{Position}=pack("f*",map {$_+0.2} unpack("f*",$tree->{"\\"}->{$entr}->{Hardpoints}->{"Fixed"}->{$_}->{Position}));
|
140
|
|
|
|
|
|
|
}
|
141
|
|
|
|
|
|
|
}
|
142
|
|
|
|
|
|
|
# Now write it down.
|
143
|
|
|
|
|
|
|
open FILE,">model2.cmp";
|
144
|
|
|
|
|
|
|
binmode FILE;
|
145
|
|
|
|
|
|
|
print FILE UTFwrite($tree);
|
146
|
|
|
|
|
|
|
close FILE;
|
147
|
|
|
|
|
|
|
=cut
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub UTFwrite {
|
150
|
1
|
|
|
1
|
1
|
713
|
return UTFwriteUTF(@_);
|
151
|
|
|
|
|
|
|
}
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 INTERNAL FUNCTIONS (use with care)
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=head2 get (SOURCE, OFFSET, LENTGH)
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Extracts a string of LENGTH at OFFSET of the SOURCE.
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Used for general file reading.
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
#Internal functions:
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
#$chars = get ($string,$offset,$amount);
|
166
|
|
|
|
|
|
|
#returns $amount (or less) chars of $string starting from $offset.
|
167
|
|
|
|
|
|
|
#Also increases $offset.
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub get {
|
171
|
1
|
|
|
1
|
1
|
2
|
my $off=$_[1];
|
172
|
1
|
|
|
|
|
2
|
$_[1]+=$_[2];
|
173
|
1
|
|
|
|
|
7
|
return substr($_[0],$off,$_[2]);
|
174
|
|
|
|
|
|
|
}
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head2 string (OFFSET)
|
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Extracts a string \0 delimited string at OFFSET out of the stringlibrary of the file.
|
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
Used for key names
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=cut
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
#my $string = string($offset)
|
185
|
|
|
|
|
|
|
#Returns a string of stringlib, which is a compilation of \0 strings starting a $offset.
|
186
|
|
|
|
|
|
|
#Returns string without the trailing \0.
|
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub string {
|
189
|
32
|
50
|
|
32
|
1
|
64
|
if ($_[0] >= length $strings) { #THIS IS AN ERROR IN THE UTF FILE
|
190
|
0
|
|
|
|
|
0
|
warnings::warnif("Requested a string from outside of the string lib, this is an error in the file or something the parser doesn't know about.");
|
191
|
0
|
|
|
|
|
0
|
return "" ;
|
192
|
|
|
|
|
|
|
}
|
193
|
32
|
|
|
|
|
48
|
my $shift=index($strings,"\0",$_[0]) - $_[0];
|
194
|
32
|
|
|
|
|
150
|
return substr($strings,$_[0],$shift);
|
195
|
|
|
|
|
|
|
}
|
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=head2 data (OFFSET, LENGTH)
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Extracts data from an OFFSET with specific LENGTH out of the datalibrary of the file.
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Used for data nodes
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=cut
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
#my $data=data($start,$length);
|
206
|
|
|
|
|
|
|
#Returns a string of the datalib, which is just a bunch of data, starting at $start with a length of $length.
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub data {
|
209
|
17
|
50
|
|
17
|
1
|
33
|
if ($_[0] >= length $datas) {#THIS IS AN ERROR IN THE UTF FILE
|
210
|
0
|
|
|
|
|
0
|
warnings::warnif("Requested data from outside of the data lib, this is an error in the file or something the parser doesn't know about.");
|
211
|
0
|
|
|
|
|
0
|
return "" ;
|
212
|
|
|
|
|
|
|
}
|
213
|
17
|
|
|
|
|
42
|
return substr($datas,$_[0],$_[1]);
|
214
|
|
|
|
|
|
|
}
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head2 UTFreadUTFrek( TREE, NODEID )
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Parses a node with NODEID out of the binary TREE, then calls itself with all the childnodes and siblingnodes
|
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
#Parses a UTF node recursive:
|
224
|
|
|
|
|
|
|
#node
|
225
|
|
|
|
|
|
|
#{
|
226
|
|
|
|
|
|
|
# dword sibling_offset
|
227
|
|
|
|
|
|
|
# dword string_offset
|
228
|
|
|
|
|
|
|
# dword flags
|
229
|
|
|
|
|
|
|
# dword zero (seems to be always zero, meaning unknown)
|
230
|
|
|
|
|
|
|
# dword child_offset
|
231
|
|
|
|
|
|
|
# dword allocated_size
|
232
|
|
|
|
|
|
|
# dword size1
|
233
|
|
|
|
|
|
|
# dword size2
|
234
|
|
|
|
|
|
|
# dword time1
|
235
|
|
|
|
|
|
|
# dword time2
|
236
|
|
|
|
|
|
|
# dword time3
|
237
|
|
|
|
|
|
|
#} = 44 bytes
|
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub UTFreadUTFrek {
|
242
|
1
|
|
|
1
|
|
5
|
no warnings 'recursion';
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
442
|
|
243
|
32
|
|
|
32
|
1
|
30
|
local $_;
|
244
|
32
|
|
|
|
|
36
|
my $tree=shift;
|
245
|
32
|
|
|
|
|
31
|
my $i=shift;
|
246
|
32
|
50
|
|
|
|
98
|
if ($offsets{$i}++) {
|
247
|
0
|
|
|
|
|
0
|
warnings::warnif("Somehow the file managed to request the same node again, this is ignored");
|
248
|
0
|
|
|
|
|
0
|
return {};
|
249
|
|
|
|
|
|
|
}
|
250
|
32
|
50
|
|
|
|
66
|
if ($i > length($tree)-44) {
|
251
|
0
|
|
|
|
|
0
|
warnings::warnif("Requested a node from outside of the TREE, this is an error in the file or something the parser doesn't know about.");
|
252
|
0
|
|
|
|
|
0
|
return {};
|
253
|
|
|
|
|
|
|
}
|
254
|
32
|
|
|
|
|
108
|
tie my %data => 'Tie::InsertOrderHash';
|
255
|
32
|
|
|
|
|
301
|
my ($silb, $name, $flags, $z, $childoffset, $alloc, $size, $size2, $time1, $time2, $time3) = unpack("VVVVVVVVVVV", substr($tree,$i));
|
256
|
|
|
|
|
|
|
# Now we must use all the stuff here or warning will annoy us:
|
257
|
32
|
|
|
|
|
49
|
($time1, $time2, $time3) = ($time1, $time2, $time3);
|
258
|
32
|
50
|
|
|
|
61
|
$size=$size2 if ($size2 < $size);
|
259
|
32
|
100
|
66
|
|
|
101
|
if ($flags & 0x10 and not $flags & 0x80) {
|
260
|
15
|
|
|
|
|
46
|
$data{string($name)}=UTFreadUTFrek($tree,$childoffset);
|
261
|
|
|
|
|
|
|
}
|
262
|
|
|
|
|
|
|
else {
|
263
|
17
|
|
|
|
|
27
|
$data{string($name)}=data($childoffset,$size);
|
264
|
|
|
|
|
|
|
}
|
265
|
32
|
100
|
|
|
|
356
|
if ($silb) {
|
266
|
16
|
|
|
|
|
42
|
my $data=UTFreadUTFrek($tree,$silb);
|
267
|
|
|
|
|
|
|
#print $data,"->$i-->$silb\n";
|
268
|
16
|
|
|
|
|
54
|
%data=(%data,%{$data});
|
|
16
|
|
|
|
|
236
|
|
269
|
|
|
|
|
|
|
}
|
270
|
32
|
|
|
|
|
1094
|
return \%data;
|
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
}
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=head2 packString (STRING)
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Saves a STRING to the stringlib and returns an offset. Tests if the string already exists.
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
Used for writing Nodenames (keys)
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=cut
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
#$offset = packString($string)
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
#Packs a string and returns the offset.
|
285
|
|
|
|
|
|
|
#Also adds the string to the stringlib
|
286
|
|
|
|
|
|
|
sub packString {
|
287
|
32
|
|
|
32
|
1
|
39
|
my $string = shift;
|
288
|
32
|
100
|
|
|
|
54
|
if (exists $strings{$string}) {
|
289
|
7
|
|
|
|
|
20
|
return $strings{$string};
|
290
|
|
|
|
|
|
|
}
|
291
|
|
|
|
|
|
|
else {
|
292
|
25
|
|
|
|
|
49
|
$strings{$string} = length($strings);
|
293
|
25
|
|
|
|
|
37
|
$strings.=$string."\0";
|
294
|
25
|
|
|
|
|
173
|
return $strings{$string};
|
295
|
|
|
|
|
|
|
}
|
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
}
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 packData (DATA)
|
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
Saves a string with DATA to the datalib and returns an offset. Tests if the data already exists.
|
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
Used for writing nodedata (values)
|
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=cut
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
#$offset = packData($data)
|
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
#Packs a piece of data and returns the offset.
|
310
|
|
|
|
|
|
|
#Also adds the data to the datalib
|
311
|
|
|
|
|
|
|
sub packData {
|
312
|
17
|
|
|
17
|
1
|
22
|
my $data = shift;
|
313
|
17
|
100
|
|
|
|
33
|
if (exists $datas{$data}) {
|
314
|
3
|
|
|
|
|
26
|
return $datas{$data};
|
315
|
|
|
|
|
|
|
}
|
316
|
|
|
|
|
|
|
else {
|
317
|
14
|
|
|
|
|
577
|
$datas{$data} = length($datas);
|
318
|
|
|
|
|
|
|
#$datas.=$data."\0";
|
319
|
14
|
|
|
|
|
23
|
$datas.=$data;
|
320
|
14
|
|
|
|
|
83
|
return $datas{$data};
|
321
|
|
|
|
|
|
|
}
|
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
}
|
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
#Writes UTF nodes recursive.
|
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head2 UTFwriteUTFrek (DATA, NAME, SIBLING)
|
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Writes hashref or a scalar into the tree.
|
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
SIBLING is true if there is a next sibling node (different output on nodes without a next one)
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Calls itself again for each entry of a hashref. (Writes the children)
|
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=cut
|
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub UTFwriteUTFrek {
|
338
|
1
|
|
|
1
|
|
5
|
no warnings 'recursion';
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
667
|
|
339
|
32
|
|
|
32
|
1
|
35
|
local $_;
|
340
|
32
|
|
|
|
|
42
|
my $tree=shift;
|
341
|
32
|
|
|
|
|
74
|
my $name=shift;
|
342
|
32
|
|
|
|
|
40
|
my $silb=shift;
|
343
|
32
|
|
|
|
|
37
|
$pointer+=44;
|
344
|
32
|
|
|
|
|
29
|
my $start=$pointer;
|
345
|
32
|
50
|
66
|
|
|
107
|
croak "Can't pack other then scalar or Hashrefs" if ref $tree and ref $tree ne "HASH";
|
346
|
32
|
100
|
|
|
|
88
|
return pack ("VVVVVVVVVVV",$silb?$pointer:0, packString($name), 0x80, 0, packData($tree), length($tree), length($tree), length($tree), 0, 0, 0) unless ref $tree;
|
|
|
100
|
|
|
|
|
|
347
|
15
|
|
|
|
|
22
|
my $code="";
|
348
|
15
|
|
|
|
|
50
|
my @list = keys(%$tree);
|
349
|
|
|
|
|
|
|
#print "name = $name, pointer = $pointer, data = ".(ref ($tree) || "scalar")."\n";
|
350
|
15
|
|
|
|
|
35
|
foreach (0 .. $#list) {
|
351
|
31
|
|
|
|
|
172
|
$code.=UTFwriteUTFrek($tree->{$list[$_]},$list[$_],($_!=$#list));
|
352
|
|
|
|
|
|
|
}
|
353
|
|
|
|
|
|
|
#print "name = $name, pointer = $pointer, data = ".(ref ($tree) || "scalar")."\n";
|
354
|
15
|
100
|
|
|
|
43
|
return pack ("VVVVVVVVVVV",$silb?$pointer:0, packString($name), 0x10, 0, $start, 0,0,0,0,0,0).$code;
|
355
|
|
|
|
|
|
|
}
|
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
#header
|
359
|
|
|
|
|
|
|
#{
|
360
|
|
|
|
|
|
|
# dword "UTF "
|
361
|
|
|
|
|
|
|
# dword 0x101
|
362
|
|
|
|
|
|
|
# dword tree_segment_offset
|
363
|
|
|
|
|
|
|
# dword size_of_tree_segment
|
364
|
|
|
|
|
|
|
# dword header_offset? (0) ##I think its here also a First element of the treeoffset
|
365
|
|
|
|
|
|
|
# dword size_of_header (44) ##I think it is more a size of entry
|
366
|
|
|
|
|
|
|
# dword string_segment_offset
|
367
|
|
|
|
|
|
|
# dword space_allocated_for_string_segment
|
368
|
|
|
|
|
|
|
# dword size_of_string_segment_actually_used
|
369
|
|
|
|
|
|
|
# dword data_segment_offset
|
370
|
|
|
|
|
|
|
# dword unknown (seems to be zero most of the times) #Possible first entry of data segment (after deletion of an entry)
|
371
|
|
|
|
|
|
|
#} = 44 bytes
|
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
#Reads the UTF header, extracts data and string libraries and starts parsing the nodes
|
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head2 UTFreadUTF ( DATA )
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
Extracts and parses an UTF header from the scalar DATA.
|
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Splits the file in TREE, STRINGLIB and DATALIB according to the header and then calls UTFreadUTFrek on the TREE.
|
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=cut
|
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub UTFreadUTF{
|
384
|
1
|
|
|
1
|
1
|
2
|
my $code=shift;
|
385
|
1
|
|
|
|
|
2
|
my $i=0;
|
386
|
1
|
|
|
|
|
2
|
%offsets = ();
|
387
|
1
|
50
|
|
|
|
5
|
if (substr($code,$i,4) eq "UTF ") {
|
388
|
1
|
|
|
|
|
2
|
$i+=4;
|
389
|
1
|
|
|
|
|
4
|
my ($ver,$treeoffset,$treesize,$treefirst,$treeelemsize,$stringoffset,$stringspace,$stringsize,$dataoffset,$datafirst)=unpack("VVVVVVVVVV",get($code,$i,40));
|
390
|
|
|
|
|
|
|
# We don't use those now, not sure what they are for anyway
|
391
|
1
|
50
|
|
|
|
3
|
$datafirst = 0 unless $datafirst;
|
392
|
1
|
50
|
|
|
|
3
|
$treefirst = 0 unless $treefirst;
|
393
|
1
|
50
|
|
|
|
2
|
$treeelemsize = 0 unless $treeelemsize;
|
394
|
|
|
|
|
|
|
# We don't need this one either, do we?
|
395
|
1
|
50
|
|
|
|
2
|
$stringsize = 0 unless $stringsize;
|
396
|
|
|
|
|
|
|
# Splitting the parts
|
397
|
1
|
|
|
|
|
2
|
$strings=substr($code,$stringoffset,$stringspace);
|
398
|
1
|
|
|
|
|
3
|
$datas=substr($code,$dataoffset);
|
399
|
1
|
|
|
|
|
3
|
my $tree=substr($code,$treeoffset,$treesize);
|
400
|
1
|
|
|
|
|
4
|
return UTFreadUTFrek($tree,0);
|
401
|
|
|
|
|
|
|
}
|
402
|
|
|
|
|
|
|
else {
|
403
|
0
|
|
|
|
|
0
|
croak "NOT a UTF File";
|
404
|
|
|
|
|
|
|
}
|
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
}
|
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
#Writes an UTF file with header and nodes.
|
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=head2 UTFwriteUTF (TREE(HASHREF) )
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
Calls UTFwriteUTFrek and then return the header, TREE, STRINGLIB and DATALIB
|
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=cut
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
sub UTFwriteUTF{
|
417
|
1
|
|
|
1
|
1
|
3
|
my $tree=shift;
|
418
|
1
|
|
|
|
|
3
|
my $i=0;
|
419
|
1
|
|
|
|
|
2
|
$strings="";
|
420
|
1
|
|
|
|
|
1
|
$datas="";
|
421
|
1
|
|
|
|
|
2
|
my $code = "";
|
422
|
1
|
|
|
|
|
3
|
%strings = ();
|
423
|
1
|
|
|
|
|
2
|
%datas = ();
|
424
|
1
|
|
|
|
|
2
|
$pointer=0;
|
425
|
1
|
|
|
|
|
5
|
my @list = keys(%$tree);
|
426
|
1
|
|
|
|
|
4
|
foreach (0 .. $#list) {
|
427
|
1
|
|
|
|
|
7
|
$code.=UTFwriteUTFrek($tree->{$list[$_]},$list[$_],($_!=$#list));
|
428
|
|
|
|
|
|
|
}
|
429
|
1
|
|
|
|
|
15
|
my $string=$strings;
|
430
|
1
|
|
|
|
|
11
|
$string.="\0" for(length($strings) .. (int(length($strings)/32)+1)*32); #Just some fun stuff.
|
431
|
1
|
|
|
|
|
10
|
return "UTF ".pack("VVVVVVVVVV",0x101,44+12,length($code),0,44,44+12+length($code),length($string),length($strings),44+12+length($code)+length($string),0)."000000000000".$code.$string.$datas;
|
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
}
|
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
1;
|
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
__END__
|