1#!perl -w
2#
3#v 0.3 Last Edited: 12 June 2011
4###########################################################
5package level;
6use strict;
7use stkutils::chunked_file;
8use stkutils::ini_file;
9#use stkutils::lzhuf;
10use Cwd;
11sub new {
12	my $class = shift;
13	my $self = {};
14	$self->{fsl_header} = {};
15	$self->{fsl_shaders} = {};
16	$self->{fsl_visuals} = {};
17	$self->{fsl_portals} = {};
18	$self->{fsl_light_dynamic} = {};
19	$self->{fsl_glows} = {};
20	$self->{fsl_sectors} = {};
21	$self->{fsl_vertex_buffer} = {};
22	$self->{fsl_index_buffer} = {};
23	$self->{fsl_swis} = {};
24	$self->{compressed} = {};
25	bless($self, $class);
26	return $self;
27}
28sub read {
29	my $self = shift;
30	my ($fn) = @_;
31
32	my $fh = stkutils::chunked_file->new($fn, 'r') or die "$fn: $!\n";
33	while (1) {
34		my ($index, $size) = $fh->r_chunk_open();
35		defined $index or last;
36		last unless $index != 0;
37		my $data = $fh->r_chunk_data();
38#		if ($index & 0x80000000) {
39#			print "chunk compressed!\n";
40#			my $dec_data = $self->decompress($data, $size);
41#			$index -= 0x80000000;
42#			print "$index\n";
43#			$data = $dec_data;
44#		}
45		SWITCH: {
46			$index == 0x1 && do {$self->fsl_header::read($data); last SWITCH;};
47			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_TEXTURES') && do {$self->fsl_textures::read($data); last SWITCH;};
48			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_SHADERS') && do {$self->fsl_shaders::read($data); last SWITCH;};
49			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_CFORM') && do {$self->fsl_cform::read($data); last SWITCH; };
50			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_LIGHT_KEY_FRAMES') && do {$self->fsl_light_key_frames::read($data); last SWITCH;};
51			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_SHADER_CONSTANT') && do {$self->fsl_shader_constant::read($data); last SWITCH;};
52			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_VISUALS') && do {$self->fsl_visuals::read($data); last SWITCH;};
53			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_PORTALS') && do {$self->fsl_portals::read($data); last SWITCH;};
54			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_LIGHT_DYNAMIC') && do {$self->fsl_light_dynamic::read($data); last SWITCH;};
55			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_GLOWS') && do {$self->fsl_glows::read($data); last SWITCH; };
56			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_SECTORS') && do {$self->fsl_sectors::read($data); last SWITCH;};
57			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_VB') && do {$self->fsl_vertex_buffer::read($data); last SWITCH;};
58			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_VB_OLD') && do {$self->fsl_vertex_buffer::read($data); last SWITCH;};
59			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_IB') && do {$self->fsl_index_buffer::read($data); last SWITCH;};
60			(chunks::get_name($index, $self->{fsl_header}->{xrlc_version}) eq 'FSL_SWIS') && do {$self->fsl_swis::read($data); last SWITCH;};
61			($index &0x80000000) && do {$index -= 0x80000000; $self->compressed::read($index, $data); last SWITCH; };
62			printf "unexpected chunk %04x size %d\n", $index, $size;
63			die;
64		}
65		$fh->r_chunk_close();
66	}
67}
68sub decompress {
69	my $self = shift;
70	my ($data, $size) = @_;
71	my $huff = stkutils::lzhuf->new($data);
72	$huff->decode($size);
73	return $huff->{dec_data};
74}
75sub write {
76	my $self = shift;
77	my ($fn) = @_;
78
79	my $fh = stkutils::chunked_file->new($fn, 'w') or die "$fn: $!\n";
80	$self->fsl_header::write($fh);
81	if ($self->{fsl_header}->{xrlc_version} == 10 || $self->{fsl_header}->{xrlc_version} == 11) {
82		if (defined $self->{fsl_portals}->{raw_data}) {
83			$self->fsl_portals::write($fh);
84		} elsif (defined $self->{compressed}->{chunks::get_index('FSL_PORTALS', $self->{fsl_header}->{xrlc_version})}) {
85			$self->compressed::write(chunks::get_index('FSL_PORTALS', $self->{fsl_header}->{xrlc_version}), $fh);
86		}
87		if (defined $self->{fsl_cform}->{raw_data}) {
88			$self->fsl_cform::write($fh);
89		} elsif (defined $self->{compressed}->{chunks::get_index('FSL_CFORM', $self->{fsl_header}->{xrlc_version})}) {
90			$self->compressed::write(chunks::get_index('FSL_CFORM', $self->{fsl_header}->{xrlc_version}), $fh);
91		}
92	} else {
93		if (defined $self->{fsl_cform}->{raw_data}) {
94			$self->fsl_cform::write($fh);
95		} elsif (defined $self->{compressed}->{chunks::get_index('FSL_CFORM', $self->{fsl_header}->{xrlc_version})}) {
96			$self->compressed::write(chunks::get_index('FSL_CFORM', $self->{fsl_header}->{xrlc_version}), $fh);
97		}
98		if (defined $self->{fsl_portals}->{raw_data}) {
99			$self->fsl_portals::write($fh);
100		} elsif (chunks::get_index('FSL_PORTALS', $self->{fsl_header}->{xrlc_version}) && defined $self->{compressed}->{chunks::get_index('FSL_PORTALS', $self->{fsl_header}->{xrlc_version})}) {
101			$self->compressed::write(chunks::get_index('FSL_PORTALS', $self->{fsl_header}->{xrlc_version}), $fh);
102		}
103	}
104	if (defined $self->{fsl_shader_constant}->{raw_data}) {
105		$self->fsl_shader_constant::write($fh);
106	} elsif (chunks::get_index('FSL_SHADER_CONSTANT', $self->{fsl_header}->{xrlc_version}) && defined $self->{compressed}->{chunks::get_index('FSL_SHADER_CONSTANT', $self->{fsl_header}->{xrlc_version})}) {
107		$self->compressed::write(chunks::get_index('FSL_SHADER_CONSTANT', $self->{fsl_header}->{xrlc_version}), $fh);
108	}
109	$self->fsl_light_dynamic::write($fh);
110	if ($self->{fsl_header}->{xrlc_version} < 8) {
111		if (defined $self->{fsl_light_key_frames}->{raw_data}) {
112			$self->fsl_light_key_frames::write($fh);
113		} elsif (defined $self->{compressed}->{chunks::get_index('FSL_LIGHT_KEY_FRAMES', $self->{fsl_header}->{xrlc_version})}) {
114			$self->compressed::write(chunks::get_index('FSL_LIGHT_KEY_FRAMES', $self->{fsl_header}->{xrlc_version}), $fh);
115		}
116	}
117	$self->fsl_glows::write($fh);
118	if (defined $self->{fsl_visuals}->{raw_data}) {
119		$self->fsl_visuals::write($fh);
120	} elsif (defined $self->{compressed}->{chunks::get_index('FSL_VISUALS', $self->{fsl_header}->{xrlc_version})}) {
121		$self->compressed::write(chunks::get_index('FSL_VISUALS', $self->{fsl_header}->{xrlc_version}), $fh);
122	}
123	if (defined $self->{fsl_vertex_buffer}->{raw_data}) {
124		$self->fsl_vertex_buffer::write($fh);
125	} elsif (chunks::get_index('FSL_VB', $self->{fsl_header}->{xrlc_version}) && defined $self->{compressed}->{chunks::get_index('FSL_VB', $self->{fsl_header}->{xrlc_version})}) {
126		$self->compressed::write(chunks::get_index('FSL_VB', $self->{fsl_header}->{xrlc_version}), $fh);
127	} elsif (defined $self->{compressed}->{chunks::get_index('FSL_VB_OLD', $self->{fsl_header}->{xrlc_version})}) {
128		$self->compressed::write(chunks::get_index('FSL_VB_OLD', $self->{fsl_header}->{xrlc_version}), $fh);
129	}
130	if ($self->{fsl_header}->{xrlc_version} >= 12) {
131		if (defined $self->{fsl_swis}->{raw_data}) {
132			$self->fsl_swis::write($fh);
133		} elsif (defined $self->{compressed}->{11}) {
134			$self->compressed::write(11, $fh);
135		}
136	}
137	if (defined $self->{fsl_index_buffer}->{raw_data}) {
138		$self->fsl_index_buffer::write($fh);
139	} elsif (chunks::get_index('FSL_IB', $self->{fsl_header}->{xrlc_version}) && defined $self->{compressed}->{chunks::get_index('FSL_IB', $self->{fsl_header}->{xrlc_version})}) {
140		$self->compressed::write(chunks::get_index('FSL_IB', $self->{fsl_header}->{xrlc_version}), $fh);
141	}
142	if (defined $self->{fsl_textures}->{count}) {
143		$self->fsl_textures::write($fh);
144	}
145	$self->fsl_shaders::write($fh);
146	if (defined $self->{fsl_sectors}->{raw_data}) {
147		$self->fsl_sectors::write($fh);
148	} elsif (defined $self->{compressed}->{chunks::get_index('FSL_SECTORS', $self->{fsl_header}->{xrlc_version})}) {
149		$self->compressed::write(chunks::get_index('FSL_SECTORS', $self->{fsl_header}->{xrlc_version}), $fh);
150	}
151}
152sub import {
153	my $self = shift;
154	my $imf = stkutils::ini_file->new('FSL_HEADER.ltx', 'r');
155	$self->fsl_header::import($imf);
156	$imf->close();
157	$imf = stkutils::ini_file->new('FSL_SHADERS.ltx', 'r');
158	$self->fsl_shaders::import($imf);
159	$imf->close();
160	$imf = stkutils::ini_file->new('FSL_LIGHT_DYNAMIC.ltx', 'r');
161	$self->fsl_light_dynamic::import($imf);
162	$imf->close();
163	$imf = stkutils::ini_file->new('FSL_GLOWS.ltx', 'r');
164	$self->fsl_glows::import($imf);
165	$imf->close();
166	if ($imf = IO::File->new('FSL_VISUALS.bin', 'r')) {
167		binmode $imf;
168		$self->fsl_visuals::import($imf);
169		$imf->close();
170	}
171	if ($imf = IO::File->new('FSL_PORTALS.bin', 'r')) {
172		binmode $imf;
173		$self->fsl_portals::import($imf);
174		$imf->close();
175	}
176	if ($imf = IO::File->new('FSL_CFORM.bin', 'r')) {
177		binmode $imf;
178		$self->fsl_cform::import($imf);
179		$imf->close();
180	}
181	if ($imf = stkutils::ini_file->new('FSL_TEXTURES.ltx', 'r')) {
182		$self->fsl_textures::import($imf);
183		$imf->close();
184	}
185	if ($imf = IO::File->new('FSL_LIGHT_KEY_FRAMES.bin', 'r')) {
186		binmode $imf;
187		$self->fsl_light_key_frames::import($imf);
188		$imf->close();
189	}
190	if ($imf = IO::File->new('FSL_SHADER_CONSTANT.bin', 'r')) {
191		binmode $imf;
192		$self->fsl_shader_constant::import($imf);
193		$imf->close();
194	}
195	if ($imf = IO::File->new('FSL_SECTORS.bin', 'r')) {
196		binmode $imf;
197		$self->fsl_sectors::import($imf);
198		$imf->close();
199	}
200	if ($imf = IO::File->new('FSL_VERTEX_BUFFER.bin', 'r')) {
201		binmode $imf;
202		$self->fsl_vertex_buffer::import($imf);
203		$imf->close();
204	}
205	if ($imf = IO::File->new('FSL_INDEX_BUFFER.bin', 'r')) {
206		binmode $imf;
207		$self->fsl_index_buffer::import($imf);
208		$imf->close();
209	}
210	if ($imf = IO::File->new('FSL_SWIS.bin', 'r')) {
211		binmode $imf;
212		$self->fsl_swis::import($imf);
213		$imf->close();
214	}
215	my ($dirhandle, $compressed);
216	opendir $dirhandle, getcwd() or die;
217	while ($compressed = readdir $dirhandle) {
218		if ($compressed =~ /^(COMPRESSED_)(\w+)(.bin)$/) {
219			my $imf = IO::File->new($compressed, 'r');
220			binmode $imf;
221			$self->compressed::import($imf, $2);
222			$imf->close();
223		}
224	}
225	closedir $dirhandle;
226}
227sub export {
228	my $self = shift;
229	my $outf = IO::File->new('FSL_HEADER.ltx', 'w');
230	$self->fsl_header::export($outf);
231	$outf->close();
232	$outf = IO::File->new('FSL_SHADERS.ltx', 'w');
233	$self->fsl_shaders::export($outf);
234	$outf->close();
235	$outf = IO::File->new('FSL_LIGHT_DYNAMIC.ltx', 'w');
236	$self->fsl_light_dynamic::export($outf);
237	$outf->close();
238	$outf = IO::File->new('FSL_GLOWS.ltx', 'w');
239	$self->fsl_glows::export($outf);
240	$outf->close();
241	if (defined $self->{fsl_textures}->{count}) {
242		$outf = IO::File->new('FSL_TEXTURES.ltx', 'w');
243		$self->fsl_textures::export($outf);
244		$outf->close();
245	}
246	if (defined $self->{fsl_cform}->{raw_data}) {
247		$outf = IO::File->new('FSL_CFORM.bin', 'w');
248		$self->fsl_cform::export($outf);
249		$outf->close();
250	}
251	if (defined $self->{fsl_shader_constant}->{raw_data}) {
252		$outf = IO::File->new('FSL_SHADER_CONSTANT.bin', 'w');
253		$self->fsl_shader_constant::export($outf);
254		$outf->close();
255	}
256	if (defined $self->{fsl_light_key_frames}->{raw_data}) {
257		$outf = IO::File->new('FSL_LIGHT_KEY_FRAMES.bin', 'w');
258		$self->fsl_light_key_frames::export($outf);
259		$outf->close();
260	}
261	if (defined $self->{fsl_visuals}->{raw_data}) {
262		$outf = IO::File->new('FSL_VISUALS.bin', 'w');
263		$self->fsl_visuals::export($outf);
264		$outf->close();
265	}
266	if (defined $self->{fsl_portals}->{raw_data}) {
267		$outf = IO::File->new('FSL_PORTALS.bin', 'w');
268		$self->fsl_portals::export($outf);
269		$outf->close();
270	}
271	if (defined $self->{fsl_sectors}->{raw_data}) {
272		$outf = IO::File->new('FSL_SECTORS.bin', 'w');
273		$self->fsl_sectors::export($outf);
274		$outf->close();
275	}
276	if (defined $self->{fsl_vertex_buffer}->{raw_data}) {
277		$outf = IO::File->new('FSL_VERTEX_BUFFER.bin', 'w');
278		$self->fsl_vertex_buffer::export($outf);
279		$outf->close();
280	}
281	if (defined $self->{fsl_index_buffer}->{raw_data}) {
282		$outf = IO::File->new('FSL_INDEX_BUFFER.bin', 'w');
283		$self->fsl_index_buffer::export($outf);
284		$outf->close();
285	}
286	if (defined $self->{fsl_swis}->{raw_data}) {
287		$outf = IO::File->new('FSL_SWIS.bin', 'w');
288		$self->fsl_swis::export($outf);
289		$outf->close();
290	}
291	if (defined $self->{compressed}) {
292		$self->compressed::export();
293	}
294}
295###########################################################
296package fsl_header;
297use strict;
298use stkutils::data_packet;
299use stkutils::chunked_file;
300sub read {
301	my $self = shift;
302	my ($data) = @_;
303	my $packet = stkutils::data_packet->new($data);
304	($self->{fsl_header}->{xrlc_version}, $self->{fsl_header}->{xrlc_quality}) = $packet->unpack('vv');
305	if ($self->{fsl_header}->{xrlc_version} < 11) {
306		@{$self->{fsl_header}->{name}} = $packet->unpack('Z*');
307	} else {
308		die unless $packet->length() == 0;
309	}
310}
311sub write {
312	my $self = shift;
313	my ($cf) = @_;
314	if ($self->{fsl_header}->{xrlc_version} > 10) {
315		$cf->w_chunk(1, pack('vv', $self->{fsl_header}->{xrlc_version}, $self->{fsl_header}->{xrlc_quality}));
316	} else {
317		my $l = length($self->{fsl_header}->{name});
318		my $zc = 123 - $l;
319		$cf->w_chunk_open(1);
320		$cf->w_chunk_data(pack('vvZ*', $self->{fsl_header}->{xrlc_version}, $self->{fsl_header}->{xrlc_quality}, $self->{fsl_header}->{name}));
321		for (my $i = 0; $i < $zc; $i++) {
322			$cf->w_chunk_data(pack('C', 0));
323		}
324		$cf->w_chunk_close();
325	}
326}
327sub export {
328	my $self = shift;
329	my ($cf) = @_;
330
331	print $cf "[header]\n";
332	print $cf "xrLC version = $self->{fsl_header}->{xrlc_version}\n";
333	print $cf "xrLC quality = $self->{fsl_header}->{xrlc_quality}\n";
334	print $cf "name = $self->{fsl_header}->{name}[0]\n" if $self->{fsl_header}->{xrlc_version} < 11;
335}
336sub import {
337	my $self = shift;
338	my ($cf) = @_;
339	$self->{fsl_header}->{xrlc_version} = $cf->value('header', 'xrLC version');
340	$self->{fsl_header}->{xrlc_quality} = $cf->value('header', 'xrLC quality');
341	$self->{fsl_header}->{name} = $cf->value('header', 'name') if $self->{fsl_header}->{xrlc_version} < 11;
342}
343#########################################################
344package fsl_shaders;
345use strict;
346use stkutils::data_packet;
347sub read {
348	my $self = shift;
349	my ($data) = @_;
350	my $packet = stkutils::data_packet->new($data);
351	($self->{fsl_shaders}->{count}) = $packet->unpack('V');
352	@{$self->{fsl_shaders}->{shaders}} = $packet->unpack("(Z*)$self->{fsl_shaders}->{count}");
353	die unless $packet->length() == 0;
354}
355sub write {
356	my $self = shift;
357	my ($cf) = @_;
358	my $index = chunks::get_index('FSL_SHADERS', $self->{fsl_header}->{xrlc_version});
359	$cf->w_chunk_open($index);
360	$cf->w_chunk_data(pack('V', $self->{fsl_shaders}->{count}));
361	for (my $i = 0; $i < $self->{fsl_shaders}->{count}; $i++) {
362		$cf->w_chunk_data(pack('Z*', $self->{fsl_shaders}->{shaders}[$i]));
363	}
364	$cf->w_chunk_close();
365}
366sub export {
367	my $self = shift;
368	my ($cf) = @_;
369
370	for (my $i = 0; $i < $self->{fsl_shaders}->{count}; $i++) {
371		print $cf "[$i]\n";
372		if ($self->{fsl_header}->{xrlc_version} > 11) {
373			if (!(@{$self->{fsl_shaders}->{shaders}}[$i] eq '')) {
374				my ($shader, $texture) = split '/', @{$self->{fsl_shaders}->{shaders}}[$i];
375				print $cf "shader = $shader\n";
376				print $cf "texture = $texture\n\n";
377			} else {
378				print $cf "shader = @{$self->{fsl_shaders}->{shaders}}[$i]\n";
379				print $cf "texture = @{$self->{fsl_shaders}->{shaders}}[$i]\n\n";
380			}
381		} else {
382			print $cf "shader = @{$self->{fsl_shaders}->{shaders}}[$i]\n";
383		}
384	}
385}
386sub import {
387	my $self = shift;
388	my ($cf) = @_;
389	for (my $i = 0; $i < $#{$cf->{sections_list}} + 1; $i++) {
390		my $shader = $cf->value($i, 'shader');
391		if ($self->{fsl_header}->{xrlc_version} > 11) {
392			my $texture = $cf->value($i, 'texture');
393			if (!($shader eq $texture)) {
394				$self->{fsl_shaders}->{shaders}[$i] = join('/', $shader, $texture);
395			} else {
396				$self->{fsl_shaders}->{shaders}[$i] = $shader;
397			}
398		} else {
399			$self->{fsl_shaders}->{shaders}[$i] = $shader;
400		}
401	}
402	$self->{fsl_shaders}->{count} = $#{$cf->{sections_list}} + 1;
403}
404###########################################################
405package fsl_textures;
406use strict;
407use IO::File;
408sub read {
409	my $self = shift;
410	my ($data) = @_;
411	my $packet = stkutils::data_packet->new($data);
412	($self->{fsl_textures}->{count}) = $packet->unpack('V');
413	@{$self->{fsl_textures}->{textures}} = $packet->unpack("(Z*)$self->{fsl_textures}->{count}");
414	die unless $packet->length() == 0;
415}
416sub write {
417	my $self = shift;
418	my ($cf) = @_;
419
420	$cf->w_chunk_open(2);
421	$cf->w_chunk_data(pack('V', $self->{fsl_textures}->{count}));
422	for (my $i = 0; $i < $self->{fsl_textures}->{count}; $i++) {
423		$cf->w_chunk_data(pack('Z*', $self->{fsl_textures}->{textures}[$i]));
424	}
425	$cf->w_chunk_close();
426}
427sub export {
428	my $self = shift;
429	my ($cf) = @_;
430	for (my $i = 0; $i < $self->{fsl_textures}->{count}; $i++) {
431		print $cf "[$i]\n";
432		print $cf "texture = @{$self->{fsl_textures}->{textures}}[$i]\n";
433	}
434}
435sub import {
436	my $self = shift;
437	my ($cf) = @_;
438	for (my $i = 0; $i < $#{$cf->{sections_list}} + 1; $i++) {
439		$self->{fsl_textures}->{textures}[$i] = $cf->value($i, 'texture');
440	}
441	$self->{fsl_textures}->{count} = $#{$cf->{sections_list}} + 1;
442}
443###########################################################
444package fsl_cform;
445use strict;
446use IO::File;
447sub read {
448	my $self = shift;
449	my ($data) = @_;
450	$self->{fsl_cform}->{raw_data} = $data;
451}
452sub write {
453	my $self = shift;
454	my ($cf) = @_;
455	my $index = chunks::get_index('FSL_CFORM', $self->{fsl_header}->{xrlc_version});
456	$cf->w_chunk($index, $self->{fsl_cform}->{raw_data});
457}
458sub export {
459	my $self = shift;
460	my ($cf) = @_;
461	binmode $cf;
462	$cf->write($self->{fsl_cform}->{raw_data}, length($self->{fsl_cform}->{raw_data}));
463}
464sub import {
465	my $self = shift;
466	my ($cf) = @_;
467	$cf->read($self->{fsl_cform}->{raw_data}, ($cf->stat())[7]);
468}
469###########################################################
470package fsl_light_key_frames;
471use strict;
472use IO::File;
473sub read {
474	my $self = shift;
475	my ($data) = @_;
476	$self->{fsl_light_key_frames}->{raw_data} = $data;
477}
478sub write {
479	my $self = shift;
480	my ($cf) = @_;
481	$cf->w_chunk(9, $self->{fsl_light_key_frames}->{raw_data});
482}
483sub export {
484	my $self = shift;
485	my ($cf) = @_;
486	binmode $cf;
487	$cf->write($self->{fsl_light_key_frames}->{raw_data}, length($self->{fsl_light_key_frames}->{raw_data}));
488}
489sub import {
490	my $self = shift;
491	my ($cf) = @_;
492	$cf->read($self->{fsl_light_key_frames}->{raw_data}, ($cf->stat())[7]);
493}
494###########################################################
495package fsl_shader_constant;
496use strict;
497use IO::File;
498sub read {
499	my $self = shift;
500	my ($data) = @_;
501	$self->{fsl_shader_constant}->{raw_data} = $data;
502}
503sub write {
504	my $self = shift;
505	my ($cf) = @_;
506	$cf->w_chunk(7, $self->{fsl_shader_constant}->{raw_data});
507}
508sub export {
509	my $self = shift;
510	my ($cf) = @_;
511	binmode $cf;
512	$cf->write($self->{fsl_shader_constant}->{raw_data}, length($self->{fsl_shader_constant}->{raw_data}));
513}
514sub import {
515	my $self = shift;
516	my ($cf) = @_;
517	$cf->read($self->{fsl_shader_constant}->{raw_data}, ($cf->stat())[7]);
518}
519###########################################################
520package fsl_visuals;
521use strict;
522use IO::File;
523sub read {
524	my $self = shift;
525	my ($data) = @_;
526	$self->{fsl_visuals}->{raw_data} = $data;
527}
528sub write {
529	my $self = shift;
530	my ($cf) = @_;
531	my $index = chunks::get_index('FSL_VISUALS', $self->{fsl_header}->{xrlc_version});
532	$cf->w_chunk($index, $self->{fsl_visuals}->{raw_data});
533}
534sub export {
535	my $self = shift;
536	my ($cf) = @_;
537	binmode $cf;
538	$cf->write($self->{fsl_visuals}->{raw_data}, length($self->{fsl_visuals}->{raw_data}));
539}
540sub import {
541	my $self = shift;
542	my ($cf) = @_;
543	$cf->read($self->{fsl_visuals}->{raw_data}, ($cf->stat())[7]);
544}
545############################################################
546package fsl_portals;
547use strict;
548use IO::File;
549sub read {
550	my $self = shift;
551	my ($data) = @_;
552	$self->{fsl_portals}->{raw_data} = $data;
553}
554sub write {
555	my $self = shift;
556	my ($cf) = @_;
557	my $index = chunks::get_index('FSL_PORTALS', $self->{fsl_header}->{xrlc_version});
558	$cf->w_chunk($index, $self->{fsl_portals}->{raw_data});
559}
560sub export {
561	my $self = shift;
562	my ($cf) = @_;
563	binmode $cf;
564	$cf->write($self->{fsl_portals}->{raw_data}, length($self->{fsl_portals}->{raw_data}));
565}
566sub import {
567	my $self = shift;
568	my ($cf) = @_;
569	$cf->read($self->{fsl_portals}->{raw_data}, ($cf->stat())[7]);
570}
571###########################################################
572package fsl_light_dynamic;
573use strict;
574use IO::File;
575use constant lt_names => {
576	1	=> 'point',
577	2	=> 'spot',
578	3	=> 'directional',
579};
580use constant reverse_lt_names => {
581	'point'	=> 1,
582	'spot'	=> 2,
583	'directional'	=> 3,
584};
585sub read {
586	my $self = shift;
587	my ($data) = @_;
588	my $packet = stkutils::data_packet->new($data);
589	if ($self->{fsl_header}->{xrlc_version} > 8) {
590		$self->{fsl_light_dynamic}->{count} = $packet->length() / 0x6c;
591		die unless $packet->length() % 0x6c == 0;
592		for (my $i = 0; $i < $self->{fsl_light_dynamic}->{count}; $i++) {
593			($self->{fsl_light_dynamic}->{$i}{controller_id},
594			$self->{fsl_light_dynamic}->{$i}{type}) = $packet->unpack('VV');
595			@{$self->{fsl_light_dynamic}->{$i}{diffuse}} = $packet->unpack('f4');
596			@{$self->{fsl_light_dynamic}->{$i}{specular}} = $packet->unpack('f4');
597			@{$self->{fsl_light_dynamic}->{$i}{ambient}} = $packet->unpack('f4');
598			@{$self->{fsl_light_dynamic}->{$i}{position}} = $packet->unpack('f3');
599			@{$self->{fsl_light_dynamic}->{$i}{direction}} = $packet->unpack('f3');
600			@{$self->{fsl_light_dynamic}->{$i}{other}} = $packet->unpack('f7');
601		}
602		die unless $packet->length() == 0;
603	} elsif ($self->{fsl_header}->{xrlc_version} > 5) {
604		$self->{fsl_light_dynamic}->{count} = $packet->length() / 0xB0;
605		die unless $packet->length() % 0xB0 == 0;
606		for (my $i = 0; $i < $self->{fsl_light_dynamic}->{count}; $i++) {
607			$self->{fsl_light_dynamic}->{$i}{type} = $packet->unpack('V');
608			@{$self->{fsl_light_dynamic}->{$i}{diffuse}} = $packet->unpack('f4');
609			@{$self->{fsl_light_dynamic}->{$i}{specular}} = $packet->unpack('f4');
610			@{$self->{fsl_light_dynamic}->{$i}{ambient}} = $packet->unpack('f4');
611			@{$self->{fsl_light_dynamic}->{$i}{position}} = $packet->unpack('f3');
612			@{$self->{fsl_light_dynamic}->{$i}{direction}} = $packet->unpack('f3');
613			@{$self->{fsl_light_dynamic}->{$i}{other}} = $packet->unpack('f7');
614			($self->{fsl_light_dynamic}->{$i}{unk1},
615			$self->{fsl_light_dynamic}->{$i}{unk2},
616			$self->{fsl_light_dynamic}->{$i}{name}) = $packet->unpack('VVZ*');
617			my $l = 63 - length($self->{fsl_light_dynamic}->{$i}{name});
618			$self->{fsl_light_dynamic}->{$i}{garb} = $packet->unpack("C$l");
619		}
620		die unless $packet->length() == 0;
621	} else {
622		$self->{fsl_light_dynamic}->{count} = $packet->length() / 0x7c;
623		die unless $packet->length() % 0x7c == 0;
624		for (my $i = 0; $i < $self->{fsl_light_dynamic}->{count}; $i++) {
625			$self->{fsl_light_dynamic}->{$i}{type} = $packet->unpack('V');
626			@{$self->{fsl_light_dynamic}->{$i}{diffuse}} = $packet->unpack('f4');
627			@{$self->{fsl_light_dynamic}->{$i}{specular}} = $packet->unpack('f4');
628			@{$self->{fsl_light_dynamic}->{$i}{ambient}} = $packet->unpack('f4');
629			@{$self->{fsl_light_dynamic}->{$i}{position}} = $packet->unpack('f3');
630			@{$self->{fsl_light_dynamic}->{$i}{direction}} = $packet->unpack('f3');
631			@{$self->{fsl_light_dynamic}->{$i}{other}} = $packet->unpack('f7');
632			@{$self->{fsl_light_dynamic}->{$i}{unk}} = $packet->unpack('V5');
633		}
634		die unless $packet->length() == 0;
635	}
636}
637sub write {
638	my $self = shift;
639	my ($cf) = @_;
640	my $index = chunks::get_index('FSL_LIGHT_DYNAMIC', $self->{fsl_header}->{xrlc_version});
641	$cf->w_chunk_open($index);
642	if ($self->{fsl_header}->{xrlc_version} > 8) {
643		for (my $i = 0; $i < $self->{fsl_light_dynamic}->{count}; $i++) {
644			$cf->w_chunk_data(pack('VVf4f4f4f3f3f7', $self->{fsl_light_dynamic}->{$i}{controller_id}, $self->{fsl_light_dynamic}->{$i}{type}, @{$self->{fsl_light_dynamic}->{$i}{diffuse}}, @{$self->{fsl_light_dynamic}->{$i}{specular}}, @{$self->{fsl_light_dynamic}->{$i}{ambient}}, @{$self->{fsl_light_dynamic}->{$i}{position}}, @{$self->{fsl_light_dynamic}->{$i}{direction}}, @{$self->{fsl_light_dynamic}->{$i}{other}}));
645		}
646	} elsif ($self->{fsl_header}->{xrlc_version} > 5) {
647		for (my $i = 0; $i < $self->{fsl_light_dynamic}->{count}; $i++) {
648			$cf->w_chunk_data(pack('Vf4f4f4f3f3f7VVZ*', $self->{fsl_light_dynamic}->{$i}{type}, @{$self->{fsl_light_dynamic}->{$i}{diffuse}}, @{$self->{fsl_light_dynamic}->{$i}{specular}}, @{$self->{fsl_light_dynamic}->{$i}{ambient}}, @{$self->{fsl_light_dynamic}->{$i}{position}}, @{$self->{fsl_light_dynamic}->{$i}{direction}}, @{$self->{fsl_light_dynamic}->{$i}{other}}, $self->{fsl_light_dynamic}->{$i}{unk1}, $self->{fsl_light_dynamic}->{$i}{unk2}, $self->{fsl_light_dynamic}->{$i}{name}));
649			for (my $i = 0; $i < (64 - length($self->{fsl_light_dynamic}->{$i}{name})); $i++) {
650				$cf->w_chunk_data(pack('C', 0xED));
651			}
652		}
653	} else {
654		for (my $i = 0; $i < $self->{fsl_light_dynamic}->{count}; $i++) {
655			$cf->w_chunk_data(pack('Vf4f4f4f3f3f7V5', $self->{fsl_light_dynamic}->{$i}{type}, @{$self->{fsl_light_dynamic}->{$i}{diffuse}}, @{$self->{fsl_light_dynamic}->{$i}{specular}}, @{$self->{fsl_light_dynamic}->{$i}{ambient}}, @{$self->{fsl_light_dynamic}->{$i}{position}}, @{$self->{fsl_light_dynamic}->{$i}{direction}}, @{$self->{fsl_light_dynamic}->{$i}{other}}, @{$self->{fsl_light_dynamic}->{$i}{unk}}));
656		}
657	}
658	$cf->w_chunk_close();
659}
660sub export {
661	my $self = shift;
662	my ($cf) = @_;
663	for (my $i = 0; $i < $self->{fsl_light_dynamic}->{count}; $i++) {
664		print $cf "[$i]\n";
665		print $cf "controller_id = $self->{fsl_light_dynamic}->{$i}{controller_id}\n" if defined $self->{fsl_light_dynamic}->{$i}{controller_id};
666		printf $cf "type = %s\n", lt_names->{$self->{fsl_light_dynamic}->{$i}{type}};
667		printf $cf "diffuse = %f, %f, %f, %f\n", @{$self->{fsl_light_dynamic}->{$i}{diffuse}};
668		printf $cf "specular = %f, %f, %f, %f\n", @{$self->{fsl_light_dynamic}->{$i}{specular}};
669		printf $cf "ambient = %f, %f, %f, %f\n", @{$self->{fsl_light_dynamic}->{$i}{ambient}};
670		printf $cf "position = %f, %f, %f\n", @{$self->{fsl_light_dynamic}->{$i}{position}};
671		printf $cf "direction = %f, %f, %f\n", @{$self->{fsl_light_dynamic}->{$i}{direction}};
672		printf $cf "range = %f\n", @{$self->{fsl_light_dynamic}->{$i}{other}}[0];
673		printf $cf "falloff = %f\n", @{$self->{fsl_light_dynamic}->{$i}{other}}[1];
674		printf $cf "attenuation0 = %f\n", @{$self->{fsl_light_dynamic}->{$i}{other}}[2];
675		printf $cf "attenuation1 = %f\n", @{$self->{fsl_light_dynamic}->{$i}{other}}[3];
676		printf $cf "attenuation2 = %f\n", @{$self->{fsl_light_dynamic}->{$i}{other}}[4];
677		printf $cf "theta = %f\n", @{$self->{fsl_light_dynamic}->{$i}{other}}[5];
678		printf $cf "phi = %f\n", @{$self->{fsl_light_dynamic}->{$i}{other}}[6];
679		print $cf "unk1 = $self->{fsl_light_dynamic}->{$i}{unk1}\n" if defined $self->{fsl_light_dynamic}->{$i}{unk1};
680		print $cf "unk2 = $self->{fsl_light_dynamic}->{$i}{unk2}\n" if defined $self->{fsl_light_dynamic}->{$i}{unk2};
681		print $cf "name = $self->{fsl_light_dynamic}->{$i}{name}\n" if defined $self->{fsl_light_dynamic}->{$i}{name};
682		if (defined @{$self->{fsl_light_dynamic}->{$i}{unk}}) {
683			printf $cf "unk_0 = %s\n", @{$self->{fsl_light_dynamic}->{$i}{unk}}[0];
684			printf $cf "unk_1 = %s\n", @{$self->{fsl_light_dynamic}->{$i}{unk}}[1];
685			printf $cf "unk_2 = %s\n", @{$self->{fsl_light_dynamic}->{$i}{unk}}[2];
686			printf $cf "unk_3 = %s\n", @{$self->{fsl_light_dynamic}->{$i}{unk}}[3];
687			printf $cf "unk_4 = %s\n", @{$self->{fsl_light_dynamic}->{$i}{unk}}[4];
688		}
689		print "\n";
690	}
691}
692sub import {
693	my $self = shift;
694	my ($cf) = @_;
695	for (my $i = 0; $i < $#{$cf->{sections_list}} + 1; $i++) {
696		$self->{fsl_light_dynamic}->{$i}{controller_id} = $cf->value($i, 'controller_id');
697		$self->{fsl_light_dynamic}->{$i}{type} = reverse_lt_names->{$cf->value($i, 'type')};
698		@{$self->{fsl_light_dynamic}->{$i}{diffuse}} = split(',', $cf->value($i, 'diffuse'));
699		@{$self->{fsl_light_dynamic}->{$i}{specular}} = split(',', $cf->value($i, 'specular'));
700		@{$self->{fsl_light_dynamic}->{$i}{ambient}} = split(',', $cf->value($i, 'ambient'));
701		@{$self->{fsl_light_dynamic}->{$i}{position}} = split(',', $cf->value($i, 'position'));
702		@{$self->{fsl_light_dynamic}->{$i}{direction}} = split(',', $cf->value($i, 'direction'));
703		$self->{fsl_light_dynamic}->{$i}{other}[0] = $cf->value($i, 'range');
704		$self->{fsl_light_dynamic}->{$i}{other}[1] = $cf->value($i, 'falloff');
705		$self->{fsl_light_dynamic}->{$i}{other}[2] = $cf->value($i, 'attenuation0');
706		$self->{fsl_light_dynamic}->{$i}{other}[3] = $cf->value($i, 'attenuation1');
707		$self->{fsl_light_dynamic}->{$i}{other}[4] = $cf->value($i, 'attenuation2');
708		$self->{fsl_light_dynamic}->{$i}{other}[5] = $cf->value($i, 'theta');
709		$self->{fsl_light_dynamic}->{$i}{other}[6] = $cf->value($i, 'phi');
710		$self->{fsl_light_dynamic}->{$i}{unk1} = $cf->value($i, 'unk1');
711		$self->{fsl_light_dynamic}->{$i}{unk2} = $cf->value($i, 'unk2');
712		$self->{fsl_light_dynamic}->{$i}{name} = $cf->value($i, 'name');
713		$self->{fsl_light_dynamic}->{$i}{unk}[0] = $cf->value($i, 'unk_0');
714		$self->{fsl_light_dynamic}->{$i}{unk}[1] = $cf->value($i, 'unk_1');
715		$self->{fsl_light_dynamic}->{$i}{unk}[2] = $cf->value($i, 'unk_2');
716		$self->{fsl_light_dynamic}->{$i}{unk}[3] = $cf->value($i, 'unk_3');
717		$self->{fsl_light_dynamic}->{$i}{unk}[4] = $cf->value($i, 'unk_4');
718	}
719	$self->{fsl_light_dynamic}->{count} = $#{$cf->{sections_list}} + 1;
720}
721###########################################################
722package fsl_glows;
723use strict;
724use IO::File;
725sub read {
726	my $self = shift;
727	my ($data) = @_;
728	my $packet = stkutils::data_packet->new($data);
729	if ($self->{fsl_header}->{xrlc_version} > 11) {
730		$self->{fsl_glows}->{count} = $packet->length() / 0x12;
731		die unless $packet->length() % 0x12 == 0;
732	} else {
733		$self->{fsl_glows}->{count} = $packet->length() / 0x18;
734		die unless $packet->length() % 0x18 == 0;
735	}
736	for (my $i = 0; $i < $self->{fsl_glows}->{count}; $i++) {
737		(${$self->{fsl_glows}->{$i}{position}}[0],
738		${$self->{fsl_glows}->{$i}{position}}[1],
739		${$self->{fsl_glows}->{$i}{position}}[2],
740		$self->{fsl_glows}->{$i}{radius}) = $packet->unpack('ffff');
741		if ($self->{fsl_header}->{xrlc_version} <= 11) {
742			($self->{fsl_glows}->{$i}{texture_index},
743			$self->{fsl_glows}->{$i}{shader_index})	= $packet->unpack('VV');
744		} else {
745			$self->{fsl_glows}->{$i}{shader_index} = $packet->unpack('v');
746		}
747	}
748	die unless $packet->length() == 0;
749}
750sub write {
751	my $self = shift;
752	my ($cf) = @_;
753	my $index = chunks::get_index('FSL_GLOWS', $self->{fsl_header}->{xrlc_version});
754	$cf->w_chunk_open($index);
755	if ($self->{fsl_header}->{xrlc_version} <= 11) {
756		for (my $i = 0; $i < $self->{fsl_glows}->{count}; $i++) {
757			$cf->w_chunk_data(pack('f3fVV', @{$self->{fsl_glows}->{$i}{position}}, $self->{fsl_glows}->{$i}{radius}, $self->{fsl_glows}->{$i}{texture_index}, $self->{fsl_glows}->{$i}{shader_index}));
758		}
759	} else {
760		for (my $i = 0; $i < $self->{fsl_glows}->{count}; $i++) {
761			$cf->w_chunk_data(pack('f3fv', @{$self->{fsl_glows}->{$i}{position}}, $self->{fsl_glows}->{$i}{radius}, $self->{fsl_glows}->{$i}{shader_index}));
762		}
763
764	}
765	$cf->w_chunk_close();
766}
767sub export {
768	my $self = shift;
769	my ($cf) = @_;
770
771	for (my $i = 0; $i < $self->{fsl_glows}->{count}; $i++) {
772		print $cf "[$i]\n";
773		printf $cf "position = %f,%f,%f\n", @{$self->{fsl_glows}->{$i}{position}}[0..2];
774		printf $cf "radius = %f\n", $self->{fsl_glows}->{$i}{radius};
775		print $cf "texture_index = $self->{fsl_glows}->{$i}{texture_index}\n" if $self->{fsl_header}->{xrlc_version} <= 11;
776		print $cf "shader_index = $self->{fsl_glows}->{$i}{shader_index}\n\n";
777	}
778}
779sub import {
780	my $self = shift;
781	my ($cf) = @_;
782	for (my $i = 0; $i < $#{$cf->{sections_list}} + 1; $i++) {
783		@{$self->{fsl_glows}->{$i}{position}} = split(',', $cf->value($i, 'position'));
784		$self->{fsl_glows}->{$i}{radius} = $cf->value($i, 'radius');
785		$self->{fsl_glows}->{$i}{texture_index} = $cf->value($i, 'texture_index') if $self->{fsl_header}->{xrlc_version} <= 11;
786		$self->{fsl_glows}->{$i}{shader_index} = $cf->value($i, 'shader_index');
787	}
788	$self->{fsl_glows}->{count} = $#{$cf->{sections_list}} + 1;
789}
790###########################################################
791package fsl_sectors;
792use strict;
793use IO::File;
794sub read {
795	my $self = shift;
796	my ($data) = @_;
797	$self->{fsl_sectors}->{raw_data} = $data;
798}
799sub write {
800	my $self = shift;
801	my ($cf) = @_;
802	my $index = chunks::get_index('FSL_SECTORS', $self->{fsl_header}->{xrlc_version});
803	$cf->w_chunk($index, $self->{fsl_sectors}->{raw_data});
804}
805sub export {
806	my $self = shift;
807	my ($cf) = @_;
808	binmode $cf;
809	$cf->write($self->{fsl_sectors}->{raw_data}, length($self->{fsl_sectors}->{raw_data}));
810}
811sub import {
812	my $self = shift;
813	my ($cf) = @_;
814	$cf->read($self->{fsl_sectors}->{raw_data}, ($cf->stat())[7]);
815}
816###########################################################
817package fsl_vertex_buffer;
818use strict;
819use IO::File;
820sub read {
821	my $self = shift;
822	my ($data) = @_;
823	$self->{fsl_vertex_buffer}->{raw_data} = $data;
824}
825sub write {
826	my $self = shift;
827	my ($cf) = @_;
828	my $index = chunks::get_index('FSL_VB', $self->{fsl_header}->{xrlc_version});
829	$cf->w_chunk($index, $self->{fsl_vertex_buffer}->{raw_data});
830}
831sub export {
832	my $self = shift;
833	my ($cf) = @_;
834	binmode $cf;
835	$cf->write($self->{fsl_vertex_buffer}->{raw_data}, length($self->{fsl_vertex_buffer}->{raw_data}));
836}
837sub import {
838	my $self = shift;
839	my ($cf) = @_;
840	$cf->read($self->{fsl_vertex_buffer}->{raw_data}, ($cf->stat())[7]);
841}
842###########################################################
843package fsl_index_buffer;
844use strict;
845use IO::File;
846sub read {
847	my $self = shift;
848	my ($data) = @_;
849	$self->{fsl_index_buffer}->{raw_data} = $data;
850}
851sub write {
852	my $self = shift;
853	my ($cf) = @_;
854	my $index = chunks::get_index('FSL_IB', $self->{fsl_header}->{xrlc_version});
855	$cf->w_chunk($index, $self->{fsl_index_buffer}->{raw_data});
856}
857sub export {
858	my $self = shift;
859	my ($cf) = @_;
860	binmode $cf;
861	$cf->write($self->{fsl_index_buffer}->{raw_data}, length($self->{fsl_index_buffer}->{raw_data}));
862}
863sub import {
864	my $self = shift;
865	my ($cf) = @_;
866	$cf->read($self->{fsl_index_buffer}->{raw_data}, ($cf->stat())[7]);
867}
868###########################################################
869package fsl_swis;
870use strict;
871use IO::File;
872sub read {
873	my $self = shift;
874	my ($data) = @_;
875	$self->{fsl_swis}->{raw_data} = $data;
876}
877sub write {
878	my $self = shift;
879	my ($cf) = @_;
880	$cf->w_chunk(11, $self->{fsl_swis}->{raw_data});
881}
882sub export {
883	my $self = shift;
884	my ($cf) = @_;
885	binmode $cf;
886	$cf->write($self->{fsl_swis}->{raw_data}, length($self->{fsl_swis}->{raw_data}));
887}
888sub import {
889	my $self = shift;
890	my ($cf) = @_;
891	$cf->read($self->{fsl_swis}->{raw_data}, ($cf->stat())[7]);
892}
893###########################################################
894package compressed;
895use strict;
896use IO::File;
897sub read {
898	my $self = shift;
899	my ($index, $data) = @_;
900	$self->{compressed}->{$index} = $data;
901}
902sub write {
903	my $self = shift;
904	my ($index, $cf) = @_;
905	my $ind = 0x80000000 + $index;
906	$cf->w_chunk($ind, $self->{compressed}->{$index});
907}
908sub export {
909	my $self = shift;
910	for (my $i = 0; $i < 13; $i++) {
911		if (defined $self->{compressed}->{$i}) {
912			my $outf = IO::File->new('COMPRESSED_'.chunks::get_name($i, $self->{fsl_header}->{xrlc_version}).'.bin', 'w');
913			binmode $outf;
914			$outf->write($self->{compressed}->{$i}, length($self->{compressed}->{$i}));
915			$outf->close();
916		}
917	}
918}
919sub import {
920	my $self = shift;
921	my ($cf, $name) = @_;
922	$cf->read($self->{compressed}->{chunks::get_index($name, $self->{fsl_header}->{xrlc_version})}, ($cf->stat())[7]);
923}
924###########################################################
925package chunks;
926use constant chunk_table => (
927	{name => 'FSL_HEADER', version => 0, chunk_index => 0x1},
928	{name => 'FSL_TEXTURES', version => 0, chunk_index => 0x2},
929	{name => 'FSL_SHADERS', version => 5, chunk_index => 0x2},
930	{name => 'FSL_SHADERS', version => 0, chunk_index => 0x3},
931	{name => 'FSL_VISUALS', version => 5, chunk_index => 0x3},
932	{name => 'FSL_VISUALS', version => 0, chunk_index => 0x4},
933	{name => 'FSL_PORTALS', version => 9, chunk_index => 0x4},
934	{name => 'FSL_PORTALS', version => 5, chunk_index => 0x6},
935	{name => 'FSL_PORTALS', version => 0, chunk_index => 0x7},
936	{name => 'FSL_CFORM', version => 5, chunk_index => 0x5},
937	{name => 'FSL_CFORM', version => 0, chunk_index => 0x6},
938	{name => 'FSL_SHADER_CONSTANT', version => 8, chunk_index => 0x7},
939	{name => 'FSL_LIGHT_KEY_FRAMES', version => 0, chunk_index => 0x9},
940	{name => 'FSL_LIGHT_DYNAMIC', version => 9, chunk_index => 0x6},
941	{name => 'FSL_LIGHT_DYNAMIC', version => 8, chunk_index => 0x8},
942	{name => 'FSL_LIGHT_DYNAMIC', version => 5, chunk_index => 0x7},
943	{name => 'FSL_LIGHT_DYNAMIC', version => 0, chunk_index => 0x8},
944	{name => 'FSL_GLOWS', version => 9, chunk_index => 0x7},
945	{name => 'FSL_GLOWS', version => 5, chunk_index => 0x9},
946	{name => 'FSL_GLOWS', version => 0, chunk_index => 0xa},
947	{name => 'FSL_SECTORS', version => 9, chunk_index => 0x8},
948	{name => 'FSL_SECTORS', version => 5, chunk_index => 0xa},
949	{name => 'FSL_SECTORS', version => 0, chunk_index => 0xb},
950	{name => 'FSL_VB', version => 12, chunk_index => 0x9},
951	{name => 'FSL_VB', version => 9, chunk_index => 0xa},
952	{name => 'FSL_VB', version => 8, chunk_index => 0xc},
953	{name => 'FSL_VB_OLD', version => 5, chunk_index => 0x4},
954	{name => 'FSL_VB_OLD', version => 0, chunk_index => 0x5},
955	{name => 'FSL_IB', version => 12, chunk_index => 0xa},
956	{name => 'FSL_IB', version => 9, chunk_index => 0x9},
957	{name => 'FSL_IB', version => 8, chunk_index => 0xb},
958	{name => 'FSL_SWIS', version => 9, chunk_index => 0xb},
959);
960use constant reverse_chunk_table => (
961	{name => 'FSL_HEADER', version => 0, chunk_index => 0x1},
962	{name => 'FSL_SHADERS', version => 5, chunk_index => 0x2},
963	{name => 'FSL_TEXTURES', version => 0, chunk_index => 0x2},
964	{name => 'FSL_VISUALS', version => 5, chunk_index => 0x3},
965	{name => 'FSL_SHADERS', version => 0, chunk_index => 0x3},
966	{name => 'FSL_PORTALS', version => 9, chunk_index => 0x4},
967	{name => 'FSL_VB_OLD', version => 5, chunk_index => 0x4},
968	{name => 'FSL_VISUALS', version => 0, chunk_index => 0x4},
969	{name => 'FSL_CFORM', version => 5, chunk_index => 0x5},
970	{name => 'FSL_VB_OLD', version => 0, chunk_index => 0x5},
971	{name => 'FSL_LIGHT_DYNAMIC', version => 9, chunk_index => 0x6},
972	{name => 'FSL_PORTALS', version => 5, chunk_index => 0x6},
973	{name => 'FSL_CFORM', version => 0, chunk_index => 0x6},
974	{name => 'FSL_GLOWS', version => 9, chunk_index => 0x7},
975	{name => 'FSL_SHADER_CONSTANT', version => 8, chunk_index => 0x7},
976	{name => 'FSL_LIGHT_DYNAMIC', version => 5, chunk_index => 0x7},
977	{name => 'FSL_PORTALS', version => 0, chunk_index => 0x7},
978	{name => 'FSL_SECTORS', version => 9, chunk_index => 0x8},
979	{name => 'FSL_LIGHT_DYNAMIC', version => 8, chunk_index => 0x8},
980	{name => 'FSL_LIGHT_DYNAMIC', version => 0, chunk_index => 0x8},
981	{name => 'FSL_VB', version => 12, chunk_index => 0x9},
982	{name => 'FSL_IB', version => 9, chunk_index => 0x9},
983	{name => 'FSL_GLOWS', version => 5, chunk_index => 0x9},
984	{name => 'FSL_LIGHT_KEY_FRAMES', version => 0, chunk_index => 0x9},
985	{name => 'FSL_IB', version => 12, chunk_index => 0xa},
986	{name => 'FSL_VB', version => 9, chunk_index => 0xa},
987	{name => 'FSL_SECTORS', version => 5, chunk_index => 0xa},
988	{name => 'FSL_GLOWS', version => 0, chunk_index => 0xa},
989	{name => 'FSL_SWIS', version => 9, chunk_index => 0xb},
990	{name => 'FSL_IB', version => 5, chunk_index => 0xb},
991	{name => 'FSL_SECTORS', version => 0, chunk_index => 0xb},
992	{name => 'FSL_VB', version => 8, chunk_index => 0xc},
993);
994sub get_index {
995	foreach my $chunk (chunk_table) {
996		if (($_[0] eq $chunk->{name}) && ($_[1] > $chunk->{version})) {
997			return $chunk->{chunk_index};
998		}
999	}
1000	return undef;
1001}
1002sub get_name {
1003	if ($_[0] & 0x80000000) {
1004		return 'none';
1005	}
1006	foreach my $chunk (reverse_chunk_table) {
1007		if (($_[0] == $chunk->{chunk_index}) && ($_[1] > $chunk->{version})) {
1008			return $chunk->{name};
1009		}
1010	}
1011	return undef;
1012}
1013#########################################################
1014package main;
1015
1016use strict;
1017use Getopt::Long;
1018use IO::File;
1019use File::Path;
1020use Cwd;
1021
1022sub usage {
1023	return <<END
1024level compiiler/decompiler
1025Usage: lcdc [-d input_file] [-o outdir]
1026       lcdc [-c input_dir] [-o outfile]
1027END
1028}
1029
1030my $input_file;
1031my $src_dir;
1032my $out;
1033
1034GetOptions(
1035	'd=s' => \$input_file,
1036	'c=s' => \$src_dir,
1037	'o=s' => \$out,
1038) or die usage();
1039
1040my $work_dir = getcwd();
1041
1042if (defined $input_file) {
1043	die "bad params\n" if (defined $src_dir);
1044	my $file = level->new();
1045	print "reading level...\n";
1046	$file->read($input_file);
1047	defined $out && do {
1048		File::Path::mkpath($out, 0);
1049		chdir $out or die "cannot change path to $out\n";
1050	};
1051	print "exporting level...\n";
1052	$file->export();
1053	print "done!\n";
1054} else {
1055	die "bad params\n" if (defined $input_file);
1056	if (defined $src_dir) {
1057		chdir $src_dir or die "cannot change dir to $src_dir\n";
1058	}
1059	my $file = level->new();
1060	$out = 'level.new' unless defined $out;
1061	print "importing $src_dir...\n";
1062	$file->import();
1063	chdir $work_dir;
1064	print "writing $out...\n";
1065	$file->write($out);
1066	print "done!\n";
1067}
1068###########################################################