LDD TIX Builder Perl script
Note: there is an earlier version of this script at LDD TIX Builder Perl script (obsolete) which uses the format of inventories stored elsewhere in this wiki (LEGO Factory My Plane bag inventory for example). However it does require significant hand editing and may contain other errors. It is recommended that you use this one if you have the necessary input files available. (one such input file is available here: LEGO Factory My House bags .fbc file... as more are created a subcategory will be set up for them)
The following Perl script can be used to take an official .tix file and a file of the format
[SET1] [BAG1] part_id,material_id ... [BAGn] part_id,material_id
and produce a set of n .tix files with parts divided by bag. The resulting files will be named sssbbbx...xBagY, where:
Field | Value |
---|---|
sss | 3 digit, left zero padded set id taken from source .fcb |
bbb | 3 digit, left zero padded bag id taken from source .fcb |
x...x | multi character base set name taken from source .tix |
Bag | literal |
Y | One letter bag code calculated based on bag id in source .fcb |
and should be installed as Custom LDD part sets
The script should be invoked as follows:
perl tix_builder.pl base_set_file.tix bag_file.txt
NOTE: This script has been lightly tested and so should still be considered beta. It does create and write to files which it calculates the names of dynamically. While this hasn't caused problems for the author yet, you should exercise your own best judgement in whether or not to run it based on your understanding of the code.
$xml_file = shift @ARGV; $bag_file = shift @ARGV; %localized_bag = ( 'Eng' => 'Bag', 'Deu' => 'Sack' ); sub print_opening_xml; sub print_item_xml; sub print_identifier_xml; sub print_closing_xml; # ########################################## # SCAN IN BASE XML TIX # no doubt better ways to do this with XML::DOM # ########################################## open TIX_INFILE, "< $xml_file" or die "Can't open base .tix file $xml_file"; $scanning_item = 0; $attribute_name = ""; $attribute_value = ""; %doc_attr_hash = (); %item_hash = (); %item_attr_hash = (); while (<TIX_INFILE>) { chomp; if (/\s*<SetItem>\s*/) { $scanning_item = 1; %item_attr_hash = (); } elsif (/\s*<\/SetItem>\s*/) { $scanning_item = 0; $item_id = $item_attr_hash{'DesignID'}; $material_id = $item_attr_hash{'MaterialID'}; $item_hash{$item_id}{$material_id}{'quantity'} = $quantity; $item_hash{$item_id}{$material_id}{'__scanned'} = 'N'; } if (/^\s*<Origin>\s*(.*)\s*<\/Origin>\s*$/) { $attribute_name = $1; } if (/^\s*<Name>\s*(.*)\s*<\/Name>\s*$/) { $attribute_value = $1; } if (/^\s*<Quantity>\s*(\d+)\s*<\/Quantity>\s*$/) { $quantity = $1; } if (($attribute_name ne "") and ($attribute_value ne "")) { if ($scanning_item != 0) { $item_attr_hash{$attribute_name} = $attribute_value; } else { $doc_attr_hash{$attribute_name} = $attribute_value; } $attribute_name = ""; $attribute_value = ""; } } # PULL OUT SOME IMPORTANT INFO $ldd_set_name = $doc_attr_hash{'LddSetName'}; $ldd_set_name =~ /\d+(.*)/; $base_set_name = $1; # ########################################## # SCAN IN BAG DESCRIPTION # ########################################## open BAG_INFILE, "< $bag_file" or die "Can't open bag descriptor file $bag_file"; %bag_hash = (); $line = 0; $set_id = "_"; while (<BAG_INFILE>) { $line++; chomp; if (/\[SET(\d+)\]/) { $set_id = $1; } elsif (/\[BAG(\d+)\]/) { $bag_id = $1; } elsif (/(\d+),(\d+).*/) { $item_id = $1; $material_id = $2; if (! exists $item_hash{$item_id}) { print "[Warning] line: $line file: $bag_file - part $item_id does not appear in $xml_file\n"; } elsif (!exists $item_hash{$item_id}{$material_id}) { print "[Warning] line: $line file: $bag_file - part $item_id not available in material $material_id in $xml_file\n"; } else { $scanned = $item_hash{$item_id}{$material_id}{'__scanned'}; if ($scanned ne "N") { print "[Warning] line: $line file: $bag_file - duplicate entry, part $item_id in material $material_id\n"; } else { $item_hash{$item_id}{$material_id}{'__scanned'} = 'Y'; } $bag_hash{$bag_id}{$item_id}{$material_id} = $item_hash{$item_id}{$material_id}{'quantity'}; } } } if ($set_id eq "_") { print "[Warning] file: $bag_file does not contain a [SETx] set_id statement, defaulting to 1\n"; $set_id = 1; } $padded_set_id = sprintf("%03d", $set_id); # CHECK IF THE BAG SET MISSED ANY PARTS IN THE .tix for $item_id ( sort keys %item_hash ) { for $material_id (sort keys %{$item_hash{$item_id}}) { $scanned = $item_hash{$item_id}{$material_id}{'__scanned'}; $quantity = $item_hash{$item_id}{$material_id}{'quantity'}; if ($scanned eq "N") { print "[Warning] file: $bag_file does not contain any entry for part $item_id in material $material_id ($quantity) in $xml_file\n"; } } } # OVERRIDES FOR ALL GENERATED .tix FILES $doc_attr_hash{'Starter'} = '0'; # OUTPUT OUR NEW .tix FILES for $bag_id ( sort { $a <=> $b } keys %bag_hash) { $padded_bag_num = sprintf("%03d", $bag_id); $bag_letter = chr(ord('A') + $bag_id - 1); $ldd_set_name = "99" . $padded_set_id . $padded_bag_num . $base_set_name . 'Bag' . $bag_letter; $ldd_file_name = $ldd_set_name . ".tix"; open BAG_OUTFILE, "> $ldd_file_name" or die "Can't open output bag descriptor file $ldd_file_name"; print_opening_xml(*BAG_OUTFILE); for $item_id (sort keys %{$bag_hash{$bag_id}}) { for $material_id (sort keys %{$bag_hash{$bag_id}{$item_id}}) { $quantity = $bag_hash{$bag_id}{$item_id}{$material_id}; print_item_xml(*BAG_OUTFILE, $material_id, $item_id, $quantity); } } $doc_attr_hash{'LddSetName'} = $ldd_set_name; for $doc_attr (sort keys %doc_attr_hash) { $attr_value = $doc_attr_hash{$doc_attr}; if ($doc_attr =~ /SetDescription(\S*)/) { if (exists $localized_bag{$1}) { $attr_value .= " $localized_bag{$1} $bag_id"; } else { $attr_value .= " $localized_bag{'Eng'} $bag_id"; } } print_identifier_xml(*BAG_OUTFILE, $doc_attr, $attr_value); } print_closing_xml(*BAG_OUTFILE); close $BAG_OUTFILE; } sub print_opening_xml { local (*BAG_OUTFILE) = @_[0]; print BAG_OUTFILE "<?xml version=\"1.0\"?>\n"; print BAG_OUTFILE "<TabulaXML>\n"; print BAG_OUTFILE " <Collection>\n\n"; } sub print_item_xml { local (*BAG_OUTFILE) = @_[0]; my @params = @_; print BAG_OUTFILE " <SetItem>\n"; print BAG_OUTFILE " <Primitive>\n"; print BAG_OUTFILE " <Material>\n"; print BAG_OUTFILE " <Identifier>\n"; print BAG_OUTFILE " <Origin>MaterialID</Origin>\n"; print BAG_OUTFILE " <Name>$params[1]</Name>\n"; print BAG_OUTFILE " </Identifier>\n"; print BAG_OUTFILE " </Material>\n"; print BAG_OUTFILE " <Identifier>\n"; print BAG_OUTFILE " <Origin>DesignID</Origin>\n"; print BAG_OUTFILE " <Name>$params[2]</Name>\n"; print BAG_OUTFILE " </Identifier>\n"; print BAG_OUTFILE " </Primitive>\n"; print BAG_OUTFILE " <Quantity>$params[3]</Quantity>\n"; print BAG_OUTFILE " </SetItem>\n\n"; } sub print_identifier_xml { local (*BAG_OUTFILE) = @_[0]; my @params = @_; print BAG_OUTFILE " <Identifier>\n"; print BAG_OUTFILE " <Origin>$params[1]</Origin>\n"; if ($params[1] eq "") { print BAG_OUTFILE " <Name/>\n"; } else { print BAG_OUTFILE " <Name>$params[2]</Name>\n"; } print BAG_OUTFILE " </Identifier>\n"; } sub print_closing_xml { local (*BAG_OUTFILE) = @_[0]; print BAG_OUTFILE " </Collection>\n"; print BAG_OUTFILE "</TabulaXML>\n"; }