Merge branch 'master' of https://Governor-Tarkin@bitbucket.org/Governor-Tarkin/swg...
[swg-src.git] / tools / fixupClientBakedWearableData.pl
blob12a8819712209dd75bfae325145eaee82c14b89b
1 # =====================================================================
2 # fixupClientBakedWearableData.pl
3 # Copyright 2003, Sony Online Entertainment, Inc.
4 # All rights reserved.
5 # =====================================================================
7 # =====================================================================
9 # This program is based on buildAssetCustomizationManagerData.pl. It
10 # is intended to be a one-shot program used to convert the
11 # client-baked wearable data in the client data files to not specify
12 # full variable declaration information. The program does the
13 # following:
15 # Accept a list of directories to recursively scan for CDF mif files.
17 # For each mif source file, do the following:
19 # Remove any UsePaletteCustomization or UseRangedIntCustomization
20 # declaration, taking not of the variable name and default value
21 # given.
23 # For the variable name, lookup if the variable is provided by any of
24 # the MeshGenerator assets specified in the wearable, and if so, find
25 # the art asset's default value.
27 # If the variable is not used by the LMG or any dependencies, nothing
28 # replaces the Use* declaration. It was bogus and any intended
29 # default is pointless to preserve since it can't affect anything.
31 # If the variable is used and the value specified as the default in the
32 # Use* declaration matches the default provided by the artist, nothing
33 # replaces the use declaration since the object will get the default
34 # without specifying anything.
36 # If the variable is used and the value specified within the Use*
37 # declaration specifies an out-of-range value, nothing replaces the
38 # use declaration since the intent of the value can't be determined
39 # and we might as well do the thing that reduced data size in its
40 # place.
42 # If the variable is used and the value specified within the Use*
43 # declaration is within range but is different than the artist
44 # default, emit a WearableCustomizationSetInt(variableName, intValue).
45 # This declaration will override the default value of the
46 # customization variable.
48 # =====================================================================
50 use strict;
52 use Crc;
53 use Cwd;
54 use File::Find;
55 use File::Spec;
56 use File::Temp;
57 use TreeFile;
59 # =====================================================================
61 my $debug = 0;
62 my $debugLinesPerOutputTick = 100;
63 my $examineProcessedData = 0;
65 my $assetLinkLineCount = 0;
66 my $basicRangedIntVariableLineCount = 0;
67 my $paletteColorVariableLineCount = 0;
69 my @directoriesToScan;
70 my $rawInputFileName;
71 my $treeFileLookupFileName;
73 my %assetIdByNameMap;
74 my %assetNameByCrcMap;
75 my %defaultIdByValueMap;
76 my %intRangeByIntRangeIdMap;
77 my %rangeIdByKeyMap;
78 my %rangeTypeByIdMap;
79 my %usedAssetIdByUserMap;
80 my %variableIdByNameMap;
81 my %variableUsageIdByAssetIdMap;
82 my %variableUsageIdByKeyMap;
84 my $maxAssignedAssetId = 0;
85 my $maxAssignedDefaultId = 0;
86 my $maxAssignedIntRangeId = 0;
87 my $maxAssignedPaletteId = 0;
88 my $maxAssignedRangeId = 0;
89 my $maxAssignedVariableId = 0;
90 my $maxAssignedVariableUsageId = 0;
92 # Data used specifically for CDF fixup process.
93 my $cdfDecreasedByteCount = 0;
94 my $cdfTotalByteCount = 0;
95 my $checkedCdfCount = 0;
96 my $fixedCdfCount = 0;
98 my $ignoreMissingPalettes = 0;
99 my $p4EditRetryCount = 10;
101 my %assetNameByIdMap;
102 my %defaultValueByIdMap;
103 my %paletteEntryCountByName;
104 my %rangeKeyByIdMap;
105 my %variableNameByIdMap;
106 my %variableUsageKeyByIdMap;
108 my $currentCdfFileName;
110 # =====================================================================
112 sub printUsage
114 print "Usage: \n";
115 print " perl fixupClientBakedWearableData.pl [-h]\n";
116 print " perl fixupClientBakedWearableData.pl [-d] -i <rawInfoFile.dat> \n";
117 print " [-e] -t <treefile-xlat-file.dat> directory [directory ...]\n";
118 print "\n";
119 print "Options:\n";
120 print " -d: enable verbose debugging information\n";
121 print " -e: examine (dump) processed data on STDOUT\n";
122 print " -h: print this help\n";
123 print " -i: the raw data output file from collectAssetCustomizationData.pl\n";
124 print " -p: ignore missing palettes\n";
125 print " -t: the treefile lookup data file\n";
126 print "\n";
127 print " Directories given will be scanned recursively for client data files\n";
128 print " ending in the extension .mif. CDF source files will be p4 edited,\n";
129 print " cleaned up and compiled if any modifications are needed.\n";
132 # ----------------------------------------------------------------------
134 sub processCommandLine
136 # Process command line options.
137 my $requestHelp = 0;
138 my $printHelp = 0;
140 for (; (@_ > 0) && ($_[0] =~ m/^-(.*)$/); shift)
142 if ($1 eq 'd')
144 $debug = 1;
146 elsif ($1 eq 'e')
148 $examineProcessedData = 1;
149 print "examineProcessedData=$examineProcessedData\n" if $debug;
151 elsif ($1 eq 'i')
153 shift;
154 $rawInputFileName = $_[0];
155 print "rawInputFileName=$rawInputFileName\n" if $debug;
157 elsif ($1 eq 'h')
159 $requestHelp = 1;
160 $printHelp = 1;
162 elsif ($1 eq 'p')
164 $ignoreMissingPalettes = 1;
165 print "ignoreMissingPalettes=1\n" if $debug;
167 elsif ($1 eq 't')
169 shift;
170 $treeFileLookupFileName = $_[0];
171 print "treeFileLookupFileName=$treeFileLookupFileName\n" if $debug;
175 # Take remainder as directories to scan.
176 foreach (@_)
178 # Record directory's absolute path.
179 push @directoriesToScan, Cwd::abs_path($_);
182 if (!$requestHelp)
184 #-- Validate required parameters.
186 # Make sure we have a raw input file specified.
187 if (!defined($rawInputFileName) || (length($rawInputFileName) < 1))
189 print "No raw input filename specified, specify with -i switch.\n";
190 $printHelp = 1;
193 if (!defined($treeFileLookupFileName) || (length($treeFileLookupFileName) < 1))
195 print "No tree file lookup filename specified, specify with -t switch.\n";
196 $printHelp = 1;
200 # Make sure we have at least one directory.
201 if (@directoriesToScan < 1)
203 print "No directories specified for scanning, printing help.\n";
204 $printHelp = 1;
208 if ($printHelp)
210 printUsage();
211 exit -1;
215 # ----------------------------------------------------------------------
217 sub getNewAssetId
219 # For now assign next higher unused number. Once we figure out how
220 # many bits we really want to use, I'll want to come back and look
221 # for holes in id space and fill those up. Maybe find holes at
222 # startup and add to an 'asset id holes' list that we pull from.
223 my $newAssetId = $maxAssignedAssetId + 1;
224 ++$maxAssignedAssetId;
226 return $newAssetId;
229 # ----------------------------------------------------------------------
231 sub getNewVariableId
233 my $newVariableId = $maxAssignedVariableId + 1;
234 ++$maxAssignedVariableId;
236 return $newVariableId;
239 # ----------------------------------------------------------------------
241 sub getNewVariableUsageId
243 my $newVariableUsageId = $maxAssignedVariableUsageId + 1;
244 ++$maxAssignedVariableUsageId;
246 return $newVariableUsageId;
249 # ----------------------------------------------------------------------
251 sub getNewDefaultId
253 my $newDefaultId = $maxAssignedDefaultId + 1;
254 ++$maxAssignedDefaultId;
256 return $newDefaultId;
259 # ----------------------------------------------------------------------
261 sub getNewRangeId
263 my $newRangeId = $maxAssignedRangeId + 1;
264 ++$maxAssignedRangeId;
266 return $newRangeId;
269 # ----------------------------------------------------------------------
271 sub getAssetId
273 # Process args.
274 my $assetName = shift;
275 die "Bad asset name arg" if !defined($assetName);
277 # Check if an asset id already has been assigned to this name.
278 my $assetId = $assetIdByNameMap{$assetName};
279 if (!defined($assetId))
281 # Check for a Crc dupe. We'll have a table mapping crc to internal asset it.
282 my $crc = Crc::calculate($assetName);
283 my $clashingAssetName = $assetNameByCrcMap{$crc};
284 die "asset crc clash: same crc=$crc for asset name $assetName and $clashingAssetName" if defined($clashingAssetName);
286 # Add crc entry.
287 $assetNameByCrcMap{$crc} = $assetName;
289 # Assign a new id.
290 $assetId = getNewAssetId();
291 $assetIdByNameMap{$assetName} = $assetId;
294 return $assetId;
297 # ----------------------------------------------------------------------
299 sub getVariableId
301 # Process args.
302 my $variableName = shift;
303 die "Bad variable name arg" if !defined($variableName);
305 # Check if a variable id already has been assigned to this name.
306 my $variableId = $variableIdByNameMap{$variableName};
307 if (!defined($variableId))
309 # Assign a new id.
310 $variableId = getNewVariableId();
311 $variableIdByNameMap{$variableName} = $variableId;
314 return $variableId;
317 # ----------------------------------------------------------------------
319 sub getDefaultId
321 # Process args.
322 my $defaultValue = shift;
323 die "Bad default value arg" if !defined($defaultValue);
325 # Check if a default id already has been assigned to this name.
326 my $defaultId = $defaultIdByValueMap{$defaultValue};
327 if (!defined($defaultId))
329 # Assign a new id.
330 $defaultId = getNewDefaultId();
331 $defaultIdByValueMap{$defaultValue} = $defaultId;
334 return $defaultId;
337 # ----------------------------------------------------------------------
339 sub getBasicRangedIntRangeId
341 die "Bad number of args" if (@_ != 2);
342 my $rangeKey = join(':', @_);
344 # Both types of ranges operate off of the same rangeIdByKeyMap.
345 my $rangeId = $rangeIdByKeyMap{$rangeKey};
346 if (!defined($rangeId))
348 # Assign a new id.
349 $rangeId = getNewRangeId();
350 $rangeIdByKeyMap{$rangeKey} = $rangeId;
353 return $rangeId;
356 # ----------------------------------------------------------------------
358 sub getPaletteRangeId
360 die "Bad number of args" if (@_ != 1);
361 my $rangeKey = shift;
363 # Both types of ranges operate off of the same rangeIdByKeyMap.
364 my $rangeId = $rangeIdByKeyMap{$rangeKey};
365 if (!defined($rangeId))
367 # Assign a new id.
368 $rangeId = getNewRangeId();
369 $rangeIdByKeyMap{$rangeKey} = $rangeId;
372 return $rangeId;
375 # ----------------------------------------------------------------------
377 sub getVariableUsageId
379 die "Bad number of args" if (@_ != 3);
380 my $key = join(':', @_);
382 my $variableUsageId = $variableUsageIdByKeyMap{$key};
383 if (!defined($variableUsageId))
385 # Assign a new id.
386 $variableUsageId = getNewVariableUsageId();
387 $variableUsageIdByKeyMap{$key} = $variableUsageId;
390 return $variableUsageId;
393 # ----------------------------------------------------------------------
395 sub processAssetLink
397 # Validate arg count.
398 die("Wrong argument count: ", join(':', @_)) if (@_ != 2);
400 # Convert asset names to asset ids.
401 my $userAssetId = getAssetId(shift);
402 my $usedAssetId = getAssetId(shift);
404 # Save asset usage info.
405 if (exists $usedAssetIdByUserMap{$userAssetId})
407 # Add to ':'-separated list of used asset ids.
408 $usedAssetIdByUserMap{$userAssetId} .= ':' . $usedAssetId;
410 else
412 # Initialize used asset id.
413 $usedAssetIdByUserMap{$userAssetId} = $usedAssetId;
417 # ----------------------------------------------------------------------
419 sub processBasicRangedIntVariable
421 # Validate arg count.
422 die("Wrong argument count: ", join(':', @_)) if (@_ != 5);
424 # Get args.
425 my $assetId = getAssetId(shift);
426 my $variableId = getVariableId(shift);
427 my $minValueInclusive = shift;
428 my $maxValueExclusive = shift;
429 my $defaultId = getDefaultId(shift);
431 my $rangeId = getBasicRangedIntRangeId($minValueInclusive, $maxValueExclusive);
433 # Add variableUsageId to the usage map for the asset.
434 my $variableUsageId = getVariableUsageId($variableId, $rangeId, $defaultId);
435 if (exists $variableUsageIdByAssetIdMap{$assetId})
437 # Append variable usage id to the map.
438 $variableUsageIdByAssetIdMap{$assetId} .= ':' . $variableUsageId;
440 else
442 # Add new entry.
443 $variableUsageIdByAssetIdMap{$assetId} = $variableUsageId;
447 # ----------------------------------------------------------------------
449 sub processPaletteColorVariable
451 # Validate arg count.
452 die("Wrong argument count: ", join(':', @_)) if (@_ != 4);
454 # Get args.
455 my $assetId = getAssetId(shift);
456 my $variableId = getVariableId(shift);
457 my $rangeId = getPaletteRangeId(shift);
458 my $defaultId = getDefaultId(shift);
460 # Add variableUsageId to the usage map for the asset.
461 my $variableUsageId = getVariableUsageId($variableId, $rangeId, $defaultId);
462 if (exists $variableUsageIdByAssetIdMap{$assetId})
464 # Append variable usage id to the map.
465 $variableUsageIdByAssetIdMap{$assetId} .= ':' . $variableUsageId;
467 else
469 # Add new entry.
470 $variableUsageIdByAssetIdMap{$assetId} = $variableUsageId;
474 # ----------------------------------------------------------------------
476 sub processRawInputData
478 die "No raw input file specified, specify with -i switch" if !defined($rawInputFileName) || (length($rawInputFileName) < 1);
480 print "Processing raw input file [$rawInputFileName].\n";
482 my $skippedLineCount = 0;
483 my $debugTickLineCount = 0;
485 my $inputFile;
486 open($inputFile, "< " . $rawInputFileName) or die "Failed to open raw input file for reading: $!";
488 while (<$inputFile>)
490 my $originalLine = $_;
492 chomp();
493 if (s/^L\s+//)
495 # Process asset linkage information. Args are the user (main) asset and the used (subordinate,dependency) asset.
496 processAssetLink(split /:/);
497 ++$assetLinkLineCount;
500 elsif (s/^I\s+//)
502 # Process a use case of a basic ranged int variable.
503 processBasicRangedIntVariable(split /:/);
504 ++$basicRangedIntVariableLineCount;
506 elsif (s/^P\s+//)
508 # Process a use case of a palette color variable.
509 processPaletteColorVariable(split /:/);
510 ++$paletteColorVariableLineCount;
512 else
514 ++$skippedLineCount;
517 ++$debugTickLineCount;
518 if ($debug && ($debugTickLineCount >= $debugLinesPerOutputTick))
520 print STDERR ". ";
521 $debugTickLineCount = 0;
525 close($inputFile) or die "Failed to close raw input file: $!";
527 # Print statistics.
528 if ($debug)
530 print "\n";
531 print "Input file processing statistics:\n";
532 print "\tasset links: $assetLinkLineCount\n";
533 print "\tbasic ranged int variables: $basicRangedIntVariableLineCount\n";
534 print "\tpalette color variables: $paletteColorVariableLineCount\n";
535 print "\tskipped lines: $skippedLineCount\n";
539 # ----------------------------------------------------------------------
541 sub examineProcessedData
543 print "max assigned variable counts:\n";
544 print "maxAssignedAssetId=$maxAssignedAssetId\n";
545 print "maxAssignedDefaultId=$maxAssignedDefaultId\n";
546 print "maxAssignedRangeId=$maxAssignedRangeId\n";
547 print "maxAssignedVariableId=$maxAssignedVariableId\n";
548 print "maxAssignedVariableUsageId=$maxAssignedVariableUsageId\n";
549 print "\n";
551 print "assetIdByNameMap:\n";
552 foreach my $key (sort keys %assetIdByNameMap)
554 print "\t$key=[$assetIdByNameMap{$key}]\n";
556 print "\n";
558 print "assetNameByCrcMap:\n";
559 foreach my $key (sort compare_uint32 keys %assetNameByCrcMap)
561 printf "\t%u=[$assetNameByCrcMap{$key}]\n", $key;
563 print "\n";
565 print "defaultIdByValueMap:\n";
566 foreach my $key (sort compare_uint32 keys %defaultIdByValueMap)
568 print "\t$key=[$defaultIdByValueMap{$key}]\n";
570 print "\n";
572 print "rangeIdByKeyMap:\n";
573 foreach my $key (sort keys %rangeIdByKeyMap)
575 print "\t$key=[$rangeIdByKeyMap{$key}]\n";
577 print "\n";
579 print "usedAssetIdByUserMap:\n";
580 foreach my $key (sort compare_uint32 keys %usedAssetIdByUserMap)
582 print "\t$key=[$usedAssetIdByUserMap{$key}]\n";
584 print "\n";
586 print "variableIdByNameMap:\n";
587 foreach my $key (sort keys %variableIdByNameMap)
589 print "\t$key=[$variableIdByNameMap{$key}]\n";
591 print "\n";
593 print "variableUsageIdByAssetIdMap:\n";
594 foreach my $key (sort compare_uint32 keys %variableUsageIdByAssetIdMap)
596 print "\t$key=[$variableUsageIdByAssetIdMap{$key}]\n";
598 print "\n";
600 print "variableUsageIdByKeyMap:\n";
601 foreach my $key (sort keys %variableUsageIdByKeyMap)
603 print "\t$key=[$variableUsageIdByKeyMap{$key}]\n";
605 print "\n";
608 # ---------------------------------------------------------------------
610 sub findFirstVariableUsage
612 my $assetId = shift;
613 my $variableId = shift;
615 print "checking asset [$assetNameByIdMap{$assetId}] for variable [$variableNameByIdMap{$variableId}]\n" if $debug;
617 # Check specified asset id for usage of specified variable.
618 my $variableUsageIdsString = $variableUsageIdByAssetIdMap{$assetId};
619 if (defined $variableUsageIdsString)
621 my @variableUsageIdArray = split /:/, $variableUsageIdsString;
622 foreach my $variableUsageId (@variableUsageIdArray)
624 my $variableUsageKey = $variableUsageKeyByIdMap{$variableUsageId};
625 my @variableUsageData = split /:/, $variableUsageKey;
626 die "Bad variable usage data format, expecting 3 parts" if (@variableUsageData != 3);
628 if ($variableUsageData[0] == $variableId)
630 print "found [$variableNameByIdMap{$variableId}] on [$assetNameByIdMap{$assetId}]\n" if $debug;
631 return \@variableUsageData;
636 # Check linked assets of specified variable
637 my $linkedAssetIdsString = $usedAssetIdByUserMap{$assetId};
638 return undef if !defined($linkedAssetIdsString);
640 my @linkedAssetIdArray = split /:/, $linkedAssetIdsString;
641 foreach my $linkedAssetId (@linkedAssetIdArray)
643 my $result = findFirstVariableUsage($linkedAssetId, $variableId);
644 return $result if defined($result);
647 return undef;
650 # ---------------------------------------------------------------------
652 sub clamp
654 my $min = shift;
655 my $value = shift;
656 my $max = shift;
658 $value = $max if ($value > $max);
659 $value = $min if ($value < $min);
661 return $value;
664 # ---------------------------------------------------------------------
666 sub getPaletteVariableInfo
668 # Get args.
669 my $meshAssetIdArrayRef = shift;
670 my $variableName = shift;
671 my $palettePathName = shift; # this optionally can be undef() in which case palette name match validation doesn't occur.
672 my $defaultIndex = shift;
674 # Variables for return state.
675 my $definesVariable = 0;
676 my $samePalette = 0;
677 my $sameDefault = 0;
678 my $defaultWithinRange = 0;
679 my $clampedDefault = 0;
681 # Lookup variable id for variable name.
682 my $variableId = $variableIdByNameMap{$variableName};
683 if (defined($variableId))
685 # Find the first asset in the list that defines the variable.
686 for (my $i = 0; ($i < @$meshAssetIdArrayRef) && !$definesVariable; ++$i)
688 # Check if this asset id defines the variable.
689 my $assetId = $$meshAssetIdArrayRef[$i];
690 my $variableUsageArrayRef = findFirstVariableUsage($assetId, $variableId);
691 $definesVariable = 1 if defined($variableUsageArrayRef);
693 if ($definesVariable)
695 # Get range info.
696 my $rangeId = $$variableUsageArrayRef[1];
697 my $rangeKey = $rangeKeyByIdMap{$rangeId};
699 # Check if range is a palette.
700 if ($rangeKey =~ m/:/)
702 # This isn't a palcolor variable: it probably is a basic ranged int variable.
703 $definesVariable = 0;
705 else
707 # The range indicates this variable is a palette color variable.
708 # Palette range keys are the palette pathname.
709 my $matchPaletteVariable;
711 if (!defined($palettePathName))
713 # Unknown if the caller-asserted palette name and the real palette name match.
714 $samePalette = '?';
716 # Use this palette color variable since the type is right and the variable name matches.
717 $matchPaletteVariable = 1;
718 print "CBW uses <unspecified palette>, variable uses [$rangeKey]\n" if $debug;
720 else
722 # Caller did assert the palette path name. Check if they match.
723 $samePalette = ($rangeKey eq $palettePathName) ? 1 : 0;
724 $matchPaletteVariable = $samePalette;
725 print "CBW uses palette [$palettePathName], variable uses [$rangeKey]\n" if $debug;
728 if ($matchPaletteVariable)
730 # Check the default info.
731 my $defaultId = $$variableUsageArrayRef[2];
732 $sameDefault = 1 if ($defaultValueByIdMap{$defaultId} == $defaultIndex);
734 # Check if default is within range.
735 my $paletteEntryCount = $paletteEntryCountByName{$rangeKey};
736 if (!defined($paletteEntryCount))
738 emitCdfWarning("art data error: variable [$variableName] references an invalid palette [$rangeKey], can't validate palette range.");
739 $defaultWithinRange = 0;
741 else
743 $defaultWithinRange = 1 if (defined($paletteEntryCount) && ($defaultIndex < $paletteEntryCount) && ($defaultIndex >= 0));
744 emitCdfWarning(sprintf("user specified new variable override for pal color [$variableName] but value $defaultIndex is outside the valid range of [0 .. %d]", $paletteEntryCount - 1)) if !$defaultWithinRange;
745 $clampedDefault = clamp(0, $defaultIndex, $paletteEntryCount - 1);
753 return ($definesVariable, $samePalette, $sameDefault, $defaultWithinRange, $clampedDefault);
756 # ---------------------------------------------------------------------
758 sub getRangedIntVariableInfo
760 my $meshAssetIdArrayRef = shift;
761 my $variableName = shift;
762 my $minRangeInclusive = shift;
763 my $defaultValue = shift;
764 my $maxRangeExclusive = shift;
766 # Variables for return state.
767 my $definesVariable = 0;
768 my $sameRange = 0;
769 my $sameDefault = 0;
770 my $defaultWithinRange = 0;
771 my $clampedDefault = 0;
773 my $callerSpecifiedRange = defined($minRangeInclusive) && defined($maxRangeExclusive);
775 # Lookup variable id for variable name.
776 my $variableId = $variableIdByNameMap{$variableName};
777 if (defined($variableId))
779 # Find the first asset in the list that defines the variable.
780 for (my $i = 0; ($i < @$meshAssetIdArrayRef) && !$definesVariable; ++$i)
782 # Check if this asset id defines the variable.
783 my $assetId = $$meshAssetIdArrayRef[$i];
784 my $variableUsageArrayRef = findFirstVariableUsage($assetId, $variableId);
785 $definesVariable = 1 if defined($variableUsageArrayRef);
787 if ($definesVariable)
789 # Check the range info.
790 my $rangeId = $$variableUsageArrayRef[1];
791 my $rangeKey = $rangeKeyByIdMap{$rangeId};
792 if (!($rangeKey =~ m/:/))
794 # This is not a basic ranged int variable. It probably is a palcolor variable.
795 $definesVariable = 0;
797 else
799 my ($rangeMin, $rangeMax) = split /:/, $rangeKey;
801 if ($callerSpecifiedRange)
803 $sameRange = 1 if (($minRangeInclusive == $rangeMin) && ($maxRangeExclusive == $rangeMax));
805 else
807 $sameRange = '?';
810 # Check the default info.
811 my $defaultId = $$variableUsageArrayRef[2];
812 $sameDefault = 1 if ($defaultValueByIdMap{$defaultId} == $defaultValue);
814 # Check if default is within range.
815 $defaultWithinRange = 1 if (($defaultValue >= $rangeMin) && ($defaultValue < $rangeMax));
816 emitCdfWarning(sprintf("user specified new variable override for ranged int [$variableName] but value $defaultValue is outside the valid range of [$rangeMin .. %d]",$rangeMax - 1)) if !$defaultWithinRange;
818 $clampedDefault = clamp($rangeMin, $defaultValue, $rangeMax);
824 return ($definesVariable, $sameRange, $sameDefault, $defaultWithinRange, $clampedDefault);
827 # ---------------------------------------------------------------------
829 sub emitCdfWarning
831 my $warning = shift;
832 print STDERR "CDF WARNING($currentCdfFileName): $warning\n";
835 # ---------------------------------------------------------------------
837 sub trimString
839 # Get args.
840 my $string = shift;
842 # Remove leading and trailing whitespace.
843 $string =~ s/^\s*//;
844 $string =~ s/\s*$//;
846 # Return to caller.
847 return $string;
850 # ---------------------------------------------------------------------
852 sub processWearableLines
854 my $lineArrayRef = shift;
855 my $emitWarnings = shift;
857 # Pull out mesh asset ids.
858 my @meshAssetIds = ();
859 foreach (@$lineArrayRef)
861 if ( m/UseMeshGenerator\s*\(\s*\"([^\"]+)\"\s*\)/ )
863 # Get asset id for the mesh generator.
864 my $meshName = $1;
865 my $assetId = $assetIdByNameMap{$meshName};
867 # If the mesh doesn't exist in the map, it may just mean that it provides no customizations. I can't warn here
868 # with the data I have. I could warn if I incorporated the tree file data.
869 if (defined($assetId))
871 push(@meshAssetIds, $assetId) if defined($assetId);
872 print "CBW uses customizable asset: assetId=[$assetId],name=[$meshName]\n" if $debug;
874 else
876 print "CBW uses non-customizable asset name=[$meshName]\n" if $debug;
881 my $modificationCount = 0;
882 for (my $i = 0; $i < @$lineArrayRef; )
884 my $keepLine = 1;
885 my $line = $$lineArrayRef[$i];
887 if ($line =~ m/^(\s*).*UsePaletteCustomization\s*\(([^\)]+)\)/)
889 # Any way we slice it, we're definitely modifying this line.
890 ++$modificationCount;
892 # Handle old-style palette declaration.
893 my $leadingWhitespace = $1;
894 my @args = split /,/, $2; # "varname", palettePathName, paletteIndex
895 if (@args != 3)
897 # Trim the reported line.
898 my $reportedLine = trimString($line);
899 emitCdfWarning("removing invalid declaration due to wrong number of args, should be 3: [$reportedLine]") if $emitWarnings;
900 $keepLine = 0;
902 else
904 # Strip quotes from variable name.
905 $args[0] =~ s/[\"\s]//g;
906 $args[1] =~ s/[\"\s]//g;
908 my ($definesVariable, $samePalette, $sameDefault, $defaultWithinRange) = getPaletteVariableInfo(\@meshAssetIds, @args);
910 # Only emit a WearableCustomizationSetInt declaration if the variable is defined
911 # by one of the meshes, the variable makes use of the same palette, the default
912 # is within the valid range but the variable has a different default.
913 if ($definesVariable && $samePalette && $defaultWithinRange && !$sameDefault)
915 # Change line to WearableCustomizationSetInt directive.
916 $$lineArrayRef[$i] = $leadingWhitespace . "WearableCustomizationSetInt(\"$args[0]\",$args[2])";
917 print "keeping [$args[0]] due to ($definesVariable, $samePalette, $sameDefault, $defaultWithinRange)\n" if $debug;
919 else
921 # Remove the line.
922 $keepLine = 0;
923 print "stripping [$args[0]] due to ($definesVariable, $samePalette, $sameDefault, $defaultWithinRange)\n" if $debug;
927 elsif ($line =~ m/^(\s*).*UseRangedIntCustomization\s*\(([^\)]+)\)/)
929 # Any way we slice it, we're definitely modifying this line.
930 ++$modificationCount;
932 # Handle old-style ranged-int declaration.
933 my $leadingWhitespace = $1;
934 my @args = split /,/, $2; # "varname", minInclusive, value, maxExclusive
935 if (@args != 4)
937 my $reportedLine = trimString($line);
938 emitCdfWarning("removing invalid line due to wrong number of args, should be 4: [$reportedLine]") if $emitWarnings;
939 $keepLine = 0;
941 else
943 # Strip quotes from variable name.
944 $args[0] =~ s/\"//g;
946 my ($definesVariable, $sameRange, $sameDefault, $defaultWithinRange) = getRangedIntVariableInfo(\@meshAssetIds, @args);
948 # Only emit a WearableCustomizationSetInt declaration if the variable is defined
949 # by one of the meshes, the variable makes use of the same range, the default
950 # is within the valid range but the variable has a different default.
951 if ($definesVariable && $sameRange && $defaultWithinRange && !$sameDefault)
953 # Change line to WearableCustomizationSetInt directive.
954 $$lineArrayRef[$i] = $leadingWhitespace . "WearableCustomizationSetInt(\"$args[0]\",$args[2])";
955 print "keeping [$args[0]] due to ($definesVariable, $sameRange, $sameDefault, $defaultWithinRange)\n" if $debug;
957 else
959 # Remove the line.
960 $keepLine = 0;
961 print "stripping [$args[0]] due to ($definesVariable, $sameRange, $sameDefault, $defaultWithinRange)\n" if $debug;
965 elsif ($line =~ m/WearableCustomizationSetInt\s*\(([^\)]*)\)/)
967 # Grab the args of the function call.
968 my @args = split /\s*,\s*/, $1;
970 # Validate arg count.
971 if (@args != 2)
973 my $reportedLine = trimString($line);
974 emitCdfWarning("WearableCusotmizationSetInt requires two args, bad line [$reportedLine]");
976 # Throw out the line.
977 $keepLine = 0;
978 ++$modificationCount;
980 else
982 # Strip quotes from variable name.
983 $args[0] =~ s/\"//g;
985 # Check if variable is a palette color variable.
986 my ($definesVariable, $samePalette, $sameDefault, $defaultWithinRange, $clampedDefault) = getPaletteVariableInfo(\@meshAssetIds, $args[0], undef(), $args[1]);
987 if ($definesVariable)
989 if (!$defaultWithinRange)
991 # Warn about default.
992 my $reportedLine = trimString($line);
993 emitCdfWarning("index value $args[1] out of valid range, clamping to $clampedDefault");
995 # Replace the line with clamped value.
996 $$lineArrayRef[$i] = "\t\t\t\tWearableCustomizationSetInt(\"$args[0]\", $clampedDefault)";
997 ++$modificationCount;
1000 if ($sameDefault && $defaultWithinRange)
1002 # No need to write this one, it's the same as the artist default.
1003 ++$modificationCount;
1004 $keepLine = 0;
1006 else
1008 # Keep the line.
1009 $keepLine = 1;
1012 else
1014 # Check if variable is some other kind of ranged int variable.
1015 my ($definesVariable2, $sameRange2, $sameDefault2, $defaultWithinRange2, $clampedValue2) = getRangedIntVariableInfo(\@meshAssetIds, $args[0], undef(), $args[1], undef());
1016 if ($definesVariable2)
1018 if (!$defaultWithinRange2)
1020 # Warn about default.
1021 my $reportedLine = trimString($line);
1022 emitCdfWarning("index value $args[1] out of valid range, clamping to $clampedValue2");
1024 # Replace the line with clamped value.
1025 $$lineArrayRef[$i] = "\t\t\tWearableCustomizationSetInt(\"$args[0]\", $clampedValue2)";
1026 ++$modificationCount;
1029 if ($sameDefault2 && $defaultWithinRange2)
1031 # No need to write this one, it's the same as the artist default.
1032 ++$modificationCount;
1033 $keepLine = 0;
1035 else
1037 # Keep the line.
1038 $keepLine = 1;
1041 else
1043 # This variable isn't even defined by any of the assets.
1044 my $reportedLine = trimString($line);
1045 emitCdfWarning("WearableCustomizationSetInt specified for variable not provided by asset, removing [$reportedLine]");
1046 $keepLine = 0;
1047 ++$modificationCount;
1052 elsif (!($line =~ m/(Begin|End)Wearable/) && !($line =~ m/UseMeshGenerator/) && !($line =~ m/^\s+$/))
1054 # @todo convert this to a warning once code is working.
1055 print STDERR "Unsupported client-baked wearable line [$line], ignoring.\n";
1058 # Determine what to do based on $keepLine status.
1059 if ($keepLine)
1061 # Move on to next line, keep this one.
1062 ++$i;
1064 else
1066 # Move on to next line, delete current line.
1067 splice @$lineArrayRef, $i, 1;
1071 return $modificationCount;
1074 # ---------------------------------------------------------------------
1076 sub updateCdfContents
1078 # Get args.
1079 my $inputFileName = shift;
1080 my $outputFile = (@_ > 0) ? shift : undef;
1082 # Determine if we're writing modified.
1083 my $writeOutput = ref($outputFile);
1085 # Open input file.
1086 my $inputFile;
1087 open($inputFile, '< ' . $inputFileName) or die "Failed to open [$inputFile] for reading: $!";
1089 my $modificationCount = 0;
1090 my $inWearable = 0;
1091 my @wearableLines;
1093 while (<$inputFile>)
1095 chomp();
1097 # Determine if we're in a wearable declaration.
1098 if (!$inWearable)
1100 $inWearable = m/BeginWearable/;
1102 else
1106 # Process the line.
1107 if (!$inWearable)
1109 # We're not processing a wearable, just write the line.
1110 print $outputFile "$_\n" if $writeOutput;
1112 else
1114 # Track the wearable line.
1115 push @wearableLines, $_;
1117 if (m/EndWearable/)
1119 # Process the wearable lines data. Returns the fixed up
1120 # set of lines in @wearableLines.
1121 $modificationCount += processWearableLines(\@wearableLines, !$writeOutput);
1123 # Write fixed up wearable block.
1124 if ($writeOutput)
1126 foreach my $line (@wearableLines)
1128 print $outputFile $line, "\n";
1132 # We're no longer in a wearable.
1133 $inWearable = 0;
1134 @wearableLines = ();
1139 # Return the number of modifications that were/would be made.
1140 return $modificationCount;
1143 # ---------------------------------------------------------------------
1145 sub getCdfFileName
1147 my $iffFileName = shift;
1148 $iffFileName =~ s!/dsrc/!/data/!;
1149 $iffFileName =~ s!\.mif$!.cdf!;
1151 return $iffFileName;
1154 # ----------------------------------------------------------------------
1156 sub p4EditFile
1158 my $fileName = shift;
1159 my $attemptCount = 0;
1161 # Do a p4 edit on the file.
1164 ++$attemptCount;
1165 my $commandResult = `p4 edit $fileName`;
1166 print "p4 edit $fileName: $commandResult\n" if $debug;
1167 } while (($? != 0) && ($attemptCount < $p4EditRetryCount));
1169 die "Failed to run p4 edit on $fileName, tried $attemptCount times: [$?]" if ($? != 0);
1172 # ----------------------------------------------------------------------
1174 sub runMiff
1176 my $inputFileName = shift;
1177 my $outputFileName = shift;
1179 my $output = `miff -i $inputFileName -o $outputFileName 2>&1`;
1180 die "failed to run miff -i $inputFileName -o $outputFileName: $output" if ($? != 0);
1181 print "miff -i $inputFileName -o $outputFileName: $output\n" if $debug;
1184 # ---------------------------------------------------------------------
1186 sub fixCdf
1188 # Get args.
1189 my $inputFileName = shift;
1191 # Open the MIF and CDF for editing.
1192 my $cdfFileName = getCdfFileName($inputFileName);
1193 my @oldCdfStats = stat $cdfFileName;
1195 p4EditFile($inputFileName);
1196 p4EditFile($cdfFileName);
1198 # Create temp file for modification output. Temp file is created in
1199 # same directory as the input file it will replace. This guarantees
1200 # we will be able to rename the temp file when finished processing.
1201 my ($inputFileVolume, $inputFileDir, $unused) = File::Spec->splitpath($inputFileName);
1202 my $tempFileDir = File::Spec->catpath($inputFileVolume, $inputFileDir, "");
1203 my ($outputFile, $outputFileName) = File::Temp::tempfile(DIR => $tempFileDir);
1205 # Process the file contents, writing new version to $outputFile.
1206 updateCdfContents($inputFileName, $outputFile);
1208 # Close the temp output file.
1209 close ($outputFile) or die "Failed to close newly-written CDF mif file [$outputFileName]: $!";
1211 # Replace the CDF mif input file with the newly written version.
1212 rename($outputFileName, $inputFileName) or die "Failed to replace [$inputFileName] with $[outputFileName]: $!";
1214 # Mif the file.
1215 runMiff($inputFileName, $cdfFileName);
1217 # Collect stats.
1218 my @newCdfStats = stat $cdfFileName;
1220 # Track # bytes we shrank the files by and # bytes total for modified files.
1221 if ((@oldCdfStats >= 7) && (@newCdfStats >= 7))
1223 my $cdfDecreasedByteCount += ($oldCdfStats[7] - $newCdfStats[7]);
1224 my $cdfTotalByteCount += $newCdfStats[7];
1226 else
1228 print STDERR "failed to process filesizes for [$cdfFileName], probably doesn't exist on client.\n";
1232 # ---------------------------------------------------------------------
1234 sub doesCdfRequireFixing
1236 # Just run the CDF update process in the mode that does not write output.
1237 my $filename = shift;
1238 my $modificationCount = updateCdfContents($filename);
1240 # We need to update the cdf file if one or more modifications would
1241 # be made.
1242 return ($modificationCount > 0);
1245 # ---------------------------------------------------------------------
1247 sub fixCdfFindFileHandler
1249 # Ensure target is a normal file and is readable.
1250 if (-f && -r && m/\.mif$/i)
1252 # Keep track of the file we're processing.
1253 $currentCdfFileName = $File::Find::name;
1255 print "Processing file [$_]\n" if $debug;
1256 ++$checkedCdfCount;
1258 if (doesCdfRequireFixing($File::Find::name))
1260 fixCdf($File::Find::name);
1261 ++$fixedCdfCount;
1266 # ---------------------------------------------------------------------
1268 sub collectPaletteEntryCounts
1270 my $missingPaletteCount = 0;
1272 foreach my $paletteName (keys %rangeIdByKeyMap)
1274 # Skip ranged int range data.
1275 next if $paletteName =~ m/:/;
1277 # Get the full pathname for the palette.
1278 my $fullPathName = TreeFile::getFullPathName($paletteName);
1279 if (!defined ($fullPathName))
1281 print "Failed to find on-disk location of TreeFile-relative palette name [$paletteName]\n";
1282 ++$missingPaletteCount;
1283 next;
1286 # Open the file.
1287 my $inputFile;
1288 open($inputFile, '< ' . $fullPathName) or die "Failed to open palette file [$fullPathName] for reading: $!";
1289 binmode($inputFile) or die "Failed to set file [$fullPathName] to binary mode: $!";
1291 # Skip the first 22 bytes.
1292 seek($inputFile, 22, 0) or die "Failed to seek to palette entry count position within [$fullPathName], bad palette file: $!";
1294 # Collect the entry count (2 bytes starting 22 bytes in).
1295 my $byteAsChar;
1297 read($inputFile, $byteAsChar, 1) or die "Failed to read entry count byte from [$fullPathName]: $!";
1298 my $entryCount = ord($byteAsChar);
1300 read($inputFile, $byteAsChar, 1) or die "Failed to read entry count byte from [$fullPathName]: $!";
1301 $entryCount += (ord($byteAsChar) << 8);
1303 # Close the file.
1304 close($inputFile) or die "Failed to close palette file [$fullPathName]: $!";
1306 # Enter palette entry count into map.
1307 $paletteEntryCountByName{$paletteName} = $entryCount;
1308 printf("palette entries: %5d; name=[%s]\n", $entryCount, $paletteName) if $debug;
1311 if ($missingPaletteCount > 0)
1313 print "There are $missingPaletteCount missing palettes.\n";
1314 die "Missing palettes must be added before processing can continue." if !$ignoreMissingPalettes;
1318 # ---------------------------------------------------------------------
1320 sub fixDirectories
1322 # Setup data needed by process.
1323 %assetNameByIdMap = reverse %assetIdByNameMap;
1324 %defaultValueByIdMap = reverse %defaultIdByValueMap;
1325 %rangeKeyByIdMap = reverse %rangeIdByKeyMap;
1326 %variableNameByIdMap = reverse %variableIdByNameMap;
1327 %variableUsageKeyByIdMap = reverse %variableUsageIdByKeyMap;
1329 # Call our find file handler on each file in the specified directories.
1330 &File::Find::find(\&fixCdfFindFileHandler, @directoriesToScan);
1332 # Print processing info.
1333 print "Client Data File processing statistics:\n";
1334 print "\tchecked file count: $checkedCdfCount\n";
1335 print "\tfixed file count: $fixedCdfCount\n";
1336 printf "\tmodified cdf size: %.2f KB\n", $cdfTotalByteCount / 1024;
1337 printf "\tshrank cdfs by this: %.2f KB\n", $cdfDecreasedByteCount / 1024;
1340 # ---------------------------------------------------------------------
1342 sub initializeTreeFile
1344 my $treeFile;
1345 open($treeFile, '< ' . $treeFileLookupFileName) or die "Failed to open treefile xlat data [$treeFileLookupFileName]: $!";
1346 TreeFile::loadFileLookupTable($treeFile);
1347 close($treeFile) or die "Failed to close treefile xlat data [$treeFileLookupFileName]: $!";
1350 # =====================================================================
1351 # Main Program
1352 # =====================================================================
1354 # Handle the command line options.
1355 processCommandLine(@ARGV);
1357 # Initialize TreeFile.
1358 initializeTreeFile();
1360 # Process the raw input data.
1361 processRawInputData();
1363 # Collect palette entry count data.
1364 collectPaletteEntryCounts();
1366 # Write out the output data.
1367 fixDirectories();
1369 # Done, success.
1370 exit 0;
1372 # =====================================================================