diff -r fe49e33862e2 -r 04b7640f6fb5 widgettools/wrtwidgetpreinstaller/src/installwidgets.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgettools/wrtwidgetpreinstaller/src/installwidgets.pl Wed Sep 01 12:32:13 2010 +0100 @@ -0,0 +1,2156 @@ +# +# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# This component and the accompanying materials are made available +# under the terms of "Eclipse Public License v1.0" +# which accompanies this distribution, and is available +# at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Nokia Corporation - initial contribution. +# +# Contributors: +# +# Description: +# + +use strict; +use warnings; +use File::Spec::Functions; +use Getopt::Long; +use Pod::Usage; + +# Version of the script - just use the date +$main::VERSION = '12-July-2010'; + +# New getopt::long provides this - but old version doesn't? +sub version + { + print sprintf("$0: $main::VERSION\n$^X: %vd\nos: $^O", $^V); + exit; + } + +# Get arguments +my ( $help, $version, $verbose, $debug, $epocroot, $l10n ); +GetOptions( "help" => \$help, "version|ver" => \$version, + "verbose|v" => \$verbose, "debug" => \$debug, + "epocroot|e=s" => \$epocroot, "localization|l=s" => \$l10n ) + or pod2usage('Invalid parameters'); + +# Handle help and version +pod2usage({ verbose => 1, exitval => 0}) if $help; +version() if $version; + +# Interpret arguments +$verbose = 1 if $debug; +$verbose = $verbose ? '-v' : ''; +$epocroot = $ENV{EPOCROOT} if !$epocroot; + +# Interpret localization l10n argument + +# There are two modes. In one mode you specify the location of a +# lproj.xml file and the code computes l10n files for all languages so +# that runtime locale selection should work. In the other mode you +# specify a language code (example, fr for French) and the code +# computes l10n just for that language. +$l10n = '' if !$l10n; +my $lproj = ''; +if ($l10n =~ /lproj\.xml$/i) + { + $lproj = $l10n; + $l10n = ''; + } + +my ($configini, @unnecessary) = @ARGV; + +if ($configini) + { + # There shouldn't be any need for any other arguments + pod2usage('Unnecessary arguments supplied.') if @unnecessary; + + # Now do the installation + my $installer = WidgetInstaller->new('verbose' => $verbose, 'debug' => $debug, 'epocroot' => $epocroot, 'lproj' => $lproj, 'l10n' => $l10n); + $installer->installConfig($configini); + } +else + { + pod2usage('Missing config.ini argument.'); + } + +# *** +# Package for installing Widgets +# + +package WidgetInstaller; + +use File::Spec::Functions; +use File::Path; +use File::Basename; +use Unicode::String qw(utf8 utf16); +use File::Temp qw/tempdir/; +use File::Copy; +use XML::Parser; +use Data::Dumper; +use XML::Simple; + +# CONSTANTS +use constant ICON_SIZES => "88,32,24"; # Size of icons to generate in the MBM file +#use constant INTERNAL_UID_LOWER => 0x2000DAD2; # Lower UID bound for midlets installed to internal drives and rom +#use constant EXTERNAL_UID_LOWER => 0x2000DCC6; # Lower UID bound for midlets installed to removable cards +use constant INTERNAL_UID_LOWER_WRT => 0x2000DAD2; # Lower UID bound for WRT widgets installed to internal drives and rom +use constant INTERNAL_UID_LOWER_CWRT => 0x2000DBCC; # Lower UID bound for CWRT widgets installed to internal drives and rom +use constant EXTERNAL_UID_LOWER_WRT => 0x2000DCC6; # Lower UID bound for WRT widgets installed to removable cards +use constant EXTERNAL_UID_LOWER_CWRT => 0x2000DDC0; # Lower UID bound for CWRT widgets installed to removable cards +use constant WIDGET_UI_UID => 0x10282822; # UID of the widget app +use constant CWRT_WIDGET_UI_UID => 0x200267C0; # UID of the cwrt widget app +use constant SWIDGET_UI_UID => 0x102829A0; # UID of the securewidget app +use constant CWRT_WIDGET_UI_NON_NOKIA_UID => 0x200267D6; # UID of the cwrt widget app + +# Folder paths +use constant DESTINATION => 'private/10003a3f/import/apps/NonNative/Resource'; +use constant ROM_DEST => 'epoc32/release/winscw/udeb/Z/'; +use constant DRIVE_DEST => 'epoc32/winscw/%s'; +use constant WIDGET_REGISTRY => 'private/10282f06/WidgetEntryStore.xml'; +use constant CWRT_WIDGET_REGISTRY => 'private/10282f06/CWRTWidgetEntryStore.xml'; +use constant CWRT_WEBAPP_REGISTRY => 'private/200267D9/webapp_registry.db'; +use constant CWRT_ACCESS_POLICY_PATH => 'browser_access_policy.xml'; +use constant WIDGET_NAME => 'en-gb/widget/name'; +use constant WIDGET_PROCESSUID => 'processUid'; +use constant WIDGET_ID => 'widget/id'; +use constant WIDGET_VERSION => 'widget/version'; +use constant WIDGET_CONTENT => 'widget/content'; +use constant WIDGET_FEATURE => 'widget/feature/'; +use constant WIDGET_FEATURE_PARAM => '/param/'; +use constant WIDGET_ACCESS => 'widget/access/'; +use constant WIDGET_HEIGHT => 'widget/height'; +use constant WIDGET_WIDTH => 'widget/width'; +use constant WIDGET_NOKIA_SHAREDLIB => 'widget/NOKIA:sharedlibrary'; +use constant WIDGET_NOKIA_SHAREDLIB_FOLDER => 'widget/NOKIA:sharedlibrary/NOKIA:folder'; +use constant WIDGET_NOKIA_SHAREDLIB_WIDGET => 'widget/NOKIA:sharedlibrary/NOKIA:widget'; +use constant WIDGET_NOKIA_SHAREDLIB_ICON => 'widget/NOKIA:sharedlibrary/NOKIA:icon'; +use constant KEY_VALUE_SEPERATOR => ','; +use constant KEY_VALUE_PAIR_SEPERATOR => ';'; +use constant KEY_ATTR_SEPERATOR => ':'; + +our $w3c_widget = 0 ; +our $hashval; +our $non_nokia_widget = 0; +our ($isSharedLibrary, $sharedFolderName ,$isSharedWidget, $isSharedIcon)= (0,'',0,0); +our (@staticUidWrtIntList,@staticUidCwrtIntList,@staticUidWrtExtList,@staticUidCwrtExtList) = ({},{},{},{}); +our $isSecureWidget = 0; + +# Create a new object +sub new + { + my $invocant = shift; + my $self = bless({}, ref $invocant || $invocant); + + my %args = @_; + $self->{'args'} = \%args; + $self->{'args'}->{'verbose'} = $self->{'args'}->{'verbose'} ? '-v' : ''; + + $self->{'installedFiles'} = (); + $self->{'freeuidwrtint'} = INTERNAL_UID_LOWER_WRT; + $self->{'freeuidwrtext'} = EXTERNAL_UID_LOWER_WRT; + $self->{'freeuidcwrtint'} = INTERNAL_UID_LOWER_CWRT; + $self->{'freeuidcwrtext'} = EXTERNAL_UID_LOWER_CWRT; + $self->{'langmapping'} = $self->getLangMapping($self->{'args'}->{'lproj'}) unless $self->{'args'}->{'l10n'}; + + #Check if localisation argument is land number ID; If true, get the lang Name using langmapping file - Widget_lproj.xml + if ($l10n =~ /\d+$/) + { + print "\nLANG ID given as localisation argument :: $l10n \n"; + $self->{'langmapping'} = $self->getLangMapping(); + + if($self->{'langmapping'}) + { + # Iterate through all the languages we know about + for(my $i = 0; $i < scalar(@{ $self->{'langmapping'}->{LangID} }); $i++) + { + my ( $langid, $langname ) = ( $self->{'langmapping'}->{LangID}->[$i], $self->{'langmapping'}->{LangDir}->[$i] ); + if($l10n == $langid) + { + print "Setting argument to LangName - $langname \n"; + $self->{'args'}->{'l10n'} = lc($langname); + last; + } + } + } + } + + return $self; + } + +# Gets the language mapping from the Widget_lproj.xml file +sub getLangMapping + { + my ( $self, $lproj ) = @_; + + # Get the LPROJ file which specifies lang id mappings + if (!$lproj) + { + (my $EPOCROOT = $ENV{EPOCROOT}) =~ s/[\/\\]+$//; + $lproj = WidgetInstaller::fixFilename("${epocroot}epoc32/winscw/c/private/10282f06/Widget_lproj.xml"); + $lproj = WidgetInstaller::fixFilename("$EPOCROOT/epoc32/winscw/c/private/10282f06/Widget_lproj.xml") if !-e $lproj; + $lproj = WidgetInstaller::fixFilename("${epocroot}epoc32/data/Z/private/10282f06/Widget_lproj.xml") if !-e $lproj; + $lproj = WidgetInstaller::fixFilename("$EPOCROOT/epoc32/data/Z/private/10282f06/Widget_lproj.xml") if !-e $lproj; + $lproj = WidgetInstaller::fixFilename("${epocroot}S60/mw/web/WebEngine/WidgetRegistry/Data/Widget_lproj.xml") if !-e $lproj; + $lproj = WidgetInstaller::fixFilename("$EPOCROOT/S60/mw/web/WebEngine/WidgetRegistry/Data/Widget_lproj.xml") if !-e $lproj; + $lproj = WidgetInstaller::findCmd('Widget_lproj.xml') if !-e $lproj; + $lproj = WidgetInstaller::findCmd('internal/Widget_lproj.xml') if !-e $lproj; + undef $lproj if !-e $lproj; + + # Display a warning if localisation can't be performed + warn "WARNING: Can't find Widget_lproj.xml - localisation is disabled.\n" if !$lproj; + } + + # Load the mapping file + if ($lproj) + { + die "Can't find $lproj file." if !-e $lproj; + print "Localisation support enabled using config: $lproj\n" if $verbose; + my $mapping = XMLin($lproj); + print "Found ", scalar(@{ $mapping->{'LangID'} }), " language mappings.\n" if $verbose; + return $mapping; + } + } + +# Install Widgets listed in config file +# Format is as follows where "drive-z" specifies widgets for drive z: etc... +# Comments are okay - they start with a # character +# If the file doesn't exist as given then EPOCROOT is prepended to the filename to try and find it +# +# You can specify whether a widget is intended for the homescreen by adding the text [HomeScreen] after the filename +# This will set the BlanketPermissionGranted attribute in the registry. +# Widgets intended for the homescreen must have the MiniViewEnabled attribute set in its PLIST file otherwise an error is generated. +# +# You can specify commands to run before exit after a [run-commands] or [homescreen-processor] +# +# [drive-z] +# \path\widget1.wgz +# widget2.wdgt.zip [HomeScreen] +# +# [drive-e] +# widget3.wgz +# +# [run-commands] +# dostuff.pl +# + +sub installConfig + { + my ( $self, $file ) = @_; + $self->{'installedFiles'} = (); + + my ( %installData, $sectionName ); + open CONFIG, $file or die "Failed to open config file $file: $!"; + + # Loop for gathering STATIC UIDs allocated for widgets in config INI file + while(my $line = ) + { + # Ignore comments + $line =~ s/\#.*$//; + + if ($line =~ /^\[([^\]]+)\]/) + { + $sectionName = lc $1; + next; + } + + # Process sections after this point + next if !$sectionName; + chomp $line; + + # Have we found a list of widgets? + if ($sectionName =~ /^drive-([a-z])/) + { + my $drive = uc($1); + #Assuming Static UID given in ini are within int/ext/wgt/wgz specified ranges + # Add Static UID to the respective list + if ($line =~ /^(.+?\.(?:wdgt\.zip|wgz|wgt))\s*(\[HomeScreen\])?\s*(0x[a-fA-F0-9]{8})?\s*(\[Operator\])?\s*$/i) + { + if ($3) + { + my $uid = hex($3); + + if ($drive =~ /^[zcZC]/) + { + if(($uid >= INTERNAL_UID_LOWER_WRT) && ($uid < INTERNAL_UID_LOWER_CWRT)) + { + push(@staticUidWrtIntList,$uid) ; + } + else + { + push(@staticUidCwrtIntList,$uid); + } + } + else + { + if(($uid >= EXTERNAL_UID_LOWER_WRT) && ($uid < EXTERNAL_UID_LOWER_CWRT)) + { + push(@staticUidWrtExtList,$uid) ; + } + else + { + push(@staticUidCwrtExtList,$uid) ; + } + } + } + } + } + } + close CONFIG; + @staticUidWrtIntList = sort(@staticUidWrtIntList); + print "SORTED WRT INTERNAL STATIC UID @staticUidWrtIntList \n"; + @staticUidCwrtIntList = sort(@staticUidCwrtIntList); + print "SORTED CWRT INTERNAL STATIC UID @staticUidCwrtIntList \n"; + @staticUidWrtExtList = sort(@staticUidWrtExtList); + print "SORTED WRT EXTERNAL STATIC UID @staticUidWrtExtList \n"; + @staticUidCwrtExtList = sort(@staticUidCwrtExtList); + print "SORTED CWRT EXTERNAL STATIC UID @staticUidCwrtExtList \n"; + + open CONFIG, $file or die "Failed to open config file $file: $!"; + # Main loop which reads the config INI line by line and installs widgets[wgz/wgt] in respective drives + while(my $line = ) + { + # Ignore comments + $line =~ s/\#.*$//; + + if ($line =~ /^\[([^\]]+)\]/) + { + $sectionName = lc $1; + + # Remember destination if any specified + if ($sectionName =~ /^drive-([a-z])=(.+)$/) + { + $self->{'destination'}->{uc($1)} = $2; + } + next; + } + + # Process sections after this point + next if !$sectionName; + chomp $line; + + # Have we found a list of widgets? + if ($sectionName =~ /^drive-([a-z])/) + { + my $drive = uc($1); + + # Add to the list of Widget files + if ($line =~ /^(.+?\.(?:wdgt\.zip|wgz|wgt))\s*(\[HomeScreen\])?\s*(0x[a-fA-F0-9]{8})?\s*(\[Operator\])?\s*$/i) + { + my $widget = $1; + $widget = fixFilename(catfile($self->{'args'}->{'epocroot'}, $1)) if !-e $widget; + die "Can't find widget $widget" if !-e $widget; + $self->{'homescreen'}{lc $widget} = 1 if $2; # Intended for the homescreen? + $self->{'staticUID'}{lc $widget} = hex($3) if $3; + $self->{'secure'}{lc $widget} = 0; + $self->{'operator'}{lc $widget} = 1 if $4; + print "Widget for drive $drive: $widget\n" if $self->{'args'}->{'verbose'}; + push @{ $installData{"$drive:"} }, $widget; + } + elsif ($line =~ /^(.+?\.(?:wdgt\.zip|wgz))\s*(\[Secure\])?\s*(0x[a-fA-F0-9]{8})?\s*$/i) + { + my $widget = $1; + $widget = fixFilename(catfile($self->{'args'}->{'epocroot'}, $1)) if !-e $widget; + die "Can't find widget $widget" if !-e $widget; + $self->{'secure'}{lc $widget} = 1 if $2; # Intended for a secure widget handling? + $self->{'staticUID'}{lc $widget} = hex($3) if $3; + print "Widget for drive $drive: $widget\n" if $self->{'args'}->{'verbose'}; + push @{ $installData{"$drive:"} }, $widget; + } + else + { + my $widget = $1; + $self->{'secure'}{lc $widget} = 0; + } + } + # Retrieve the command to execute before exit + elsif ($sectionName =~ /^(run-commands|homescreen-processor)$/i && $line) + { + push @{ $self->{'exitcmds'} }, $line; + } + } + close CONFIG; + + # Now intall the widgets for each drive specified in the config file + foreach my $drive ( keys %installData ) + { + $self->installFiles($drive, $installData{$drive} ); + $self->makeIBY($drive); + $self->makeIBY($drive, 'localised') unless $self->{'args'}->{'l10n'}; + } + + # Exit commands at the end of the process + if ($self->{'exitcmds'}) + { + foreach my $cmd ( @{ $self->{'exitcmds'} } ) + { + print "Executing: $cmd\n" if $self->{'args'}->{'verbose'}; + warn "WARNING: error running $cmd" if system($cmd) != 0; + } + } + } + +# *** +# Installs files to a drive +# +sub installFiles + { + my ( $self, $drive, $fileList ) = @_; + my $startsrc ; + my $iconsrc ; + + $isSharedLibrary = 0; + $isSharedWidget = 0; + $isSharedIcon = 0; + $sharedFolderName =''; + $isSecureWidget = 0; + + print "Installing files for drive $drive\n" if $self->{'args'}->{'verbose'}; + + # Unregister any existing widgets as otherwise when the registry is rewritten their icons will appear in the emulator but they won't work + $self->unregisterWidgets($drive); + + print "\n INSTALLING FILES FOR DRIVE $drive \n"; + # Process each widget in turn + my ( @installedProps ); + my ( @installedCWRTProps ); + foreach my $filename ( @$fileList ) + { + # Check the file exists + die "Can't find $filename" if !-e $filename; + + # Create a temporary folder + print "\nInstalling $filename\n"; + my $tempdir = tempdir ( DIR => '.', CLEANUP => !$self->{args}->{'debug'}); + + # Prefer to use 7zip rather than unzip because it's better behaved wrt Unicode + if (findCmd('7z.exe')) + { + $self->un7zipWidget($filename, $tempdir); + } + else + { + $self->unzipWidget($filename, $tempdir); + } + my $widgetdata ; + + $isSharedLibrary = 0; + $isSharedWidget = 0; + $isSharedIcon = 0; + $sharedFolderName =''; + + my ( $root, $extracted, $size ) = $self->getFiles($tempdir); + die "No files extracted from $filename" if !@$extracted; + + if ($self->{'operator'}{lc $filename}) + { + print "\n NON NOKIA WGT \n"; + $non_nokia_widget = 1; + } + else + { + print "\n NOKIA WGT \n"; + $non_nokia_widget = 0; + } + + + if (($filename =~ /.wgz/ )&& (my $plist = catfile($tempdir, $root, 'Info.plist'))) + { + print ("S60 widget \n"); + + $w3c_widget = 0; + # Parse the XML file into a hash + $widgetdata = parsePList($plist); + } + elsif (($filename =~ /.wgt/ ) && ( my $confxml = catfile($tempdir, $root, 'config.xml'))) + { + print (" W3C widget \n"); + + # Parse the XML file into a hash + $widgetdata = parseConfXml($confxml,$self->{'args'}->{'l10n'}); + $w3c_widget = 1; + } + else + { + die "Can't find $root/Info.plist , $root/config.xml file"; + } + + if ($self->{'secure'}{lc $filename}){ + $isSecureWidget = 1; + } else { + $isSecureWidget = 0; + } + + if( $w3c_widget ) + { + if( $widgetdata->{'MainHTML'} ) + { + $startsrc = $widgetdata->{'MainHTML'}; + } + else + { + $startsrc = 'index.html'; + } + if( $widgetdata->{'IconPath'} ) + { + $iconsrc = $widgetdata->{'IconPath'}; + } + else + { + $iconsrc = 'icon.png'; + } + + if($non_nokia_widget) + { + $widgetdata->{'ProcessUid'} = CWRT_WIDGET_UI_NON_NOKIA_UID; + } + else + { + $widgetdata->{'ProcessUid'} = CWRT_WIDGET_UI_UID; + } + + $widgetdata->{'MimeType'} = "application/widget"; + } + + print "Identifier: $widgetdata->{'BundleIdentifier'}\n" if $self->{args}->{'verbose'}; + + # Set widget package properties + $widgetdata->{'FileName'} = $filename; + $widgetdata->{'FileSize'} = $size; + + # Load the language translations for BundleDisplayName + if ($self->{'args'}->{'l10n'}) + { + my $localised = $self->getLocalisedStringByLangCode(catfile($tempdir, $root), 'DisplayName', $self->{'args'}->{'l10n'}); + $widgetdata->{'BundleDisplayName'} = $localised if $localised; + } + else + { + $widgetdata->{'LocBundleDisplayName'} = $self->getLocalisedStrings(catfile($tempdir, $root), 'DisplayName'); + } + + # Fix up some of the fields + $widgetdata->{'BundleName'} = $widgetdata->{'BundleDisplayName'} if !$widgetdata->{'BundleName'}; + $widgetdata->{'AllowNetworkAccess'} = $widgetdata->{'AllowFullAccess'} if !$widgetdata->{'AllowNetworkAccess'}; + $widgetdata->{'DriveName'} = $drive; + + if( $w3c_widget ) + { + $widgetdata->{'PropertyListVersion'} = 4; + my $widget_uid; + + if($non_nokia_widget) + { + $widget_uid = CWRT_WIDGET_UI_NON_NOKIA_UID; + } + else + { + $widget_uid = CWRT_WIDGET_UI_UID; + } + + if($isSharedLibrary) + { + #In case of NOKIA:sharedlibrary, the extracted widget contents and data source are stored under private\\..\lib\\ + $widgetdata->{'BasePath'} = sprintf('%s\\private\\%08X\\%s\\%s\\%s\\', $widgetdata->{'DriveName'}, $widget_uid, "widgets_21D_4C7","lib",$sharedFolderName); + $widgetdata->{'IconPath'} = sprintf('%s\\private\\%08X\\%s\\%s\\%s\\',$widgetdata->{'DriveName'}, $widget_uid, "data" ,"lib" ,$sharedFolderName); + } + else + { + $widgetdata->{'BasePath'} = sprintf('%s\\private\\%08X\\%s\\%s\\', $widgetdata->{'DriveName'}, $widget_uid, "widgets_21D_4C7", hashpjw($widgetdata->{'BundleIdentifier'})); + $widgetdata->{'IconPath'} = sprintf('%s\\private\\%08X\\%s\\%s\\',$widgetdata->{'DriveName'}, $widget_uid, "data" , hashpjw($widgetdata->{'BundleIdentifier'})); + } + $widgetdata->{'MainHTML'} = "$widgetdata->{'BasePath'}$startsrc"; + $widgetdata->{'AllowNetworkAccess'} = 1; + + } + else + { + $widgetdata->{'PropertyListVersion'} = 1; + $widgetdata->{'BasePath'} = sprintf('%s\\private\\%08x\\%s\\', $widgetdata->{'DriveName'}, WIDGET_UI_UID, $widgetdata->{'BundleIdentifier'}); + $widgetdata->{'MainHTML'} = "$widgetdata->{'BasePath'}$root\\$widgetdata->{'MainHTML'}"; + $widgetdata->{'IconPath'} = "$widgetdata->{'BasePath'}$root\\"; + } + + $widgetdata->{'BlanketPermissionGranted'} = 0 if( ! $widgetdata->{'BlanketPermissionGranted'} ); + $widgetdata->{'MiniViewEnabled'} = 0 if( ! $widgetdata->{'MiniViewEnabled'} ); + + # Indicate that the widget was pre-installed + $widgetdata->{'PreInstalled'} = 1; + + # Set BlanketPermissionGranted flag if Widget is listed as a homescreen widget in the INI file + # Error if MiniViewEnabled isn't set + if ($self->{'homescreen'}{lc $filename}) + { + $widgetdata->{'BlanketPermissionGranted'} = 1; + die "ERROR: $filename - MiniViewEnabled not set for homescreen widget" if !$widgetdata->{'MiniViewEnabled'}; + } + + # Defining UID + if($self->{'staticUID'}{lc $filename}) + { + print "Using Static UID from INI file\n"; + $widgetdata->{'Uid'} = $self->{'staticUID'}{lc $filename}; + } + else + { + # Find the next free UID to use + $widgetdata->{'Uid'} = $self->findfreeUid($widgetdata); + } + print sprintf("Using UID for midlet: 0x%08X\n", $widgetdata->{'Uid'}) if $self->{args}->{'verbose'}; + + # Make sure the destination exists + my $dest = $self->regFileName($drive); + mkpath $dest; + + my $icon; + if($w3c_widget) + { + my $locate = $self->{'args'}->{'l10n'}; + my $seperator ='-'; + my $position = -1; + my $found = 0 ; + if ($self->{'args'}->{'l10n'}) + { + do + { + my $searchdir = '.\\'.$tempdir.'\\locales\\'.$locate; + if( opendir(DIR, $searchdir)) + { + my @files = grep(/$iconsrc$/,readdir(DIR)); + closedir(DIR); + if(@files) + { + $iconsrc = 'locales\\'.$locate.'\\'.$iconsrc ; + $found = 1; + } + } + if(($position = rindex($locate,$seperator)) > 0 ) + { + $locate = substr($locate,0,$position); + } + } while (!$found && $position > 0); + } + if ($iconsrc) + { + $icon = catfile($tempdir, $root, $iconsrc); + } + + if(-e $icon) + { + $widgetdata->{'DBIconPath'}= "$widgetdata->{'BasePath'}$iconsrc"; + print "Icon found -- $widgetdata->{'DBIconPath'} \n"; + } + else + { + $icon = findCmd('default_widget_icon.png'); + print "default_widget_icon.png used \n"; + $widgetdata->{'DBIconPath'}= ":/resource/default_widget_icon.png"; + } + } + else + { + $icon = catfile($tempdir, $root, 'Icon.png'); + die "ERROR: Widget bundle must include an Icon.png file in $root directory.\n" unless -e $icon; + } + + # Create the MBM file icon - in case of sharedlibrary, create only if NOKIA:widget is true + my $mbm; + + print("\nIs shared library - $isSharedLibrary \nIs sharedlib widget - $isSharedWidget \nIs Secure WGZ - $self->{'secure'}{lc $filename}\n"); + + if( ((!$isSharedLibrary) || ($isSharedLibrary && $isSharedWidget)) && (!$self->{'secure'}{lc $filename})) + { + print "Creating mbm !!!\n"; + die "ERROR: Can't find PNG2MBM command in PATH." if !(my $cmd = findCmd('png2mbm.exe')); + $mbm = $self->regFileName($drive, sprintf("[%08x].mbm", $widgetdata->{'Uid'})); + print "Generating: $mbm\n"; + die "Failed to create MBM $mbm " if system("$cmd $self->{args}->{'verbose'} -in \"$icon\" -out $mbm -sizes ".ICON_SIZES) != 0; + + # Add the mbm to the list of files + $self->addToRomList($drive, $mbm); + + # Create the INI file which defines the registry info + my $ini = catfile($tempdir, 'reg.ini'); + $self->makeIni($widgetdata, $ini); + unlink 'debug.ini'; copy($ini, 'debug.ini') if $debug; + print "Generated INI file: $ini\n" if $self->{args}->{'verbose'}; + + # Generate the registry files + die "ERROR: Can't find WidgetRegFiles.exe command in PATH." if !($cmd = findCmd('WidgetRegFiles.exe')); + die "Failed to generate registry files" if system("$cmd $ini") != 0; + my ( $reg, $loc ) = ( catfile($dest, sprintf("%08x_reg.rsc", $widgetdata->{'Uid'})), catfile($dest, sprintf("%08x_loc.rsc", $widgetdata->{'Uid'})) ); + die "Failed to generate REG file: $!" if !-e $reg; + $self->addToRomList($drive, $reg); + die "Failed to generate LOC file: $!" if !-e $loc; + if ($self->{'args'}->{'l10n'}) + { + $self->addToRomList($drive, $loc); + } + else + { + $self->addToRomList($drive, $loc, 'localised'); + } + } + + # Create install folder + my $dir = $self->installDir($drive, $widgetdata->{'BundleIdentifier'}); + mkpath $dir; + + # Now copy the widget files to the right place + print "Install Directory: $dir\n"; + foreach my $widgetfile ( @$extracted ) + { + my ( $sourceFile, $destFile ) = ( catfile($tempdir, $widgetfile), catfile($dir, $widgetfile) ); + print "Copying $sourceFile to $destFile\n" if $self->{args}->{'debug'}; + + # Create the directory if it doesn't exist already + my $dir = dirname($destFile); + if (!-d $dir) + { + mkpath ($dir) or die "Failed to create $dir $!"; + } + unlink $destFile; + if (!copy($sourceFile, $destFile)) + { + warn "WARNING: Failed to copy $sourceFile to $destFile: $!"; + } + #else + { + $self->addToRomList($drive, $destFile); + } + } + + # Copy the MBM file into the widget install directory + # because native installation does and uses it after + # installation to manage UID consistency. + if( (!$isSharedLibrary) || ($isSharedLibrary && $isSharedWidget)) + { + my $mbmName = sprintf("[%08x].mbm", $widgetdata->{'Uid'}); + my $destFile; + if($w3c_widget) + { + my $dataDir; + + my $widget_uid; + + if($non_nokia_widget) + { + $widget_uid = CWRT_WIDGET_UI_NON_NOKIA_UID; + } + else + { + $widget_uid = CWRT_WIDGET_UI_UID; + } + + # NOKIA:sharedlibrary check + if($isSharedWidget) + { + $dataDir = fixFilename(catfile($self->destLocation($drive), 'private', sprintf("%08X", $widget_uid),"data","lib",$sharedFolderName)); + } + else + { + #in case of WGT copy .mbm to \epoc32\winscw\C\private\200267c0\data\hash\ + $dataDir = fixFilename(catfile($self->destLocation($drive), 'private', sprintf("%08X", $widget_uid),"data", $hashval)); + } + mkpath $dataDir; + $destFile = catfile($dataDir,$mbmName); + } + else + { + $destFile = catfile($dir, $root, $mbmName); + } + # Exclude securewidget from making a mbm file + if (!$self->{'secure'}{lc $filename}){ + print "Copying $mbm to $destFile\n" if $self->{args}->{'debug'}; + copy $mbm, $destFile or die "Failed to copy $mbm to $destFile: $!"; + $self->addToRomList($drive, $destFile); + } + } + + # Remember the data for the registry + if( $w3c_widget ) + { + push @installedCWRTProps, $widgetdata; + } + else + { + if (!$self->{'secure'}{lc $filename}){ + push @installedProps, $widgetdata; + } + } + + if (!$debug) + { + # Perl v5.6 can't delete files with unicode in their name on Windows + if ($] < 5.008 && $^O =~ /MSWin32/) + { + system("rmdir /s /q $tempdir"); + } + else + { + rmtree $tempdir; + } + } + #Generate the secsession for .wgt widgets + if( $w3c_widget ) + { + $self->addToRomList($drive, $self->makeSecSession($drive)); + } + } + + my $cwrtdbPath = catfile($self->destLocation($drive), CWRT_WEBAPP_REGISTRY); + $self->addToRomList($drive, $cwrtdbPath); + + # Generate the registry and IBY file + $self->addToRomList($drive, $self->makeRegistry($drive, \@installedProps, 0)); + $self->addToRomList($drive, $self->makeRegistry($drive, \@installedCWRTProps, 1)); + + + print "\n\n %%%%% WIDGET PRE-INSTALLATION completed for Drive $drive %%%%% \n\n"; + } + +sub un7zipWidget + { + my ( $self, $filename, $tempdir ) = @_; + + # Unzip the file + die "Can't find 7z.exe tool on PATH." if !findCmd('7z.exe'); + die "Failed to extract widget contents using 7zip: $!" if (system("7z x -aoa -o$tempdir \"$filename\" >nul 2>&1") != 0); + } + +sub unzipWidget + { + my ( $self, $filename, $tempdir ) = @_; + + # Unzip the file + die "Can't find unzip.exe tool on PATH." if !findCmd('unzip.exe'); + die "Failed to extract widget contents using zip: $!" if (system("unzip.exe \"$filename\" -d $tempdir >nul 2>&1") != 0); + } + +# Recursively get list of files in a folder +sub getFiles + { + my ( $self, $tempdir ) = @_; + + my $root = '.'; + my @extracted; + my $size =0; + + my @dirs = '.'; + foreach my $dir ( @dirs ) + { + opendir DIR, catfile($tempdir, $dir) or die "Failed to opendir $dir: $!"; + foreach ( grep !/^\.{1,2}$/, readdir DIR ) + { + my $name = catfile($dir, $_); + my $fullname = catfile($tempdir, $name); + if (-d $fullname) + { + push @dirs, $name; + } + else + { + # Remember total size for later + if (-e $fullname) + { + push @extracted, $name; + print "Extracted: $name\n" if $self->{args}->{'debug'}; + $size += -s $fullname; + + # Get root + if ($name =~ /info.plist$/i && $name =~ /^([^\/\\]+)[\/\\]/) + { + $root = $1; + } + } + else + { + warn "WARNING: Failed to find extracted file $fullname"; + } + } + } + closedir DIR; + } + + return ( $root, \@extracted, $size ); + } + +## see http://www.loc.gov/standards/iso639-2/php/code_list.php +## see http://www.loc.gov/standards/iso639-2/faq.html#2 +sub getLocalisedStringByLangCode + { + my ( $self, $dir, $strName, $langCode ) = @_; + my $localised; + + # Generate the name of the file containing localised strings for this language + my $locfile = catfile($dir, "$langCode.lproj", "InfoPlist.strings"); + if (-e $locfile) + { + # Open the file + print "Found $langCode language translations in $locfile\n" if $verbose; + open LOC, $locfile or die "Failed to open $locfile: $!"; + + # Get the byte order mark from the start of the file + my $bom; + $bom = unpack("S", $bom) if (read(LOC, $bom, 2) == 2); + if ($bom) + { + if ($bom == 0xEFBB) + { + # Skip utf8 bom + seek LOC, 3, 0; + } + elsif ($bom != 0xFFFE && $bom != 0xFEFF) + { + seek LOC, 0, 0; + undef $bom; + } + } + else + { + # go back to start of file if no bom + seek LOC, 0, 0; + undef $bom; + } + + while(my $line = ) + { + # Do unicode conversion + my $ustr; + if ($bom) + { + $ustr = utf16($line); + $ustr->byteswap if $bom != 0xFFFE; + } + else + { + $ustr = utf8($line); + } + + # Find the string we're looking for + if ($ustr->utf8 =~ /(?:^|\s)$strName\s*=\s*\"([^\"]*)\"/) + { + print "\t...$strName => $1\n" if $debug; + $localised = utf8($1); + } + } + close LOC; + } + return $localised; + } + +# Get localised version of strings if they exist +sub getLocalisedStrings + { + my ( $self, $dir, $strName ) = @_; + return if (!$self->{'langmapping'}); + + # Iterate through all the languages we know about + my %result; + for(my $i = 0; $i < scalar(@{ $self->{'langmapping'}->{LangID} }); $i++) + { + my ( $langid, $langname ) = ( $self->{'langmapping'}->{LangID}->[$i], $self->{'langmapping'}->{LangDir}->[$i] ); + + my $localised = $self->getLocalisedStringByLangCode($dir, $strName, $langname); + $result{$langid} = $localised if $localised; + } + + return \%result if keys %result; + } + +# Find and execute a command +sub findCmd + { + my $cmd = shift; + return fixFilename("./$cmd") if -e $cmd; + + # PATH separator differs on Windows and Linux + my $sep = $^O =~ /MSWin32/ ? ';' : ':'; + + # Search each entry in the PATH + my @paths = split /$sep/, $ENV{PATH}; + push @paths, dirname($0); + foreach my $path ( @paths ) + { + my $fullcmd = fixFilename(catfile($path, $cmd)); + return $fullcmd if -e $fullcmd; + } + } + +# Make INI file describing widget - this is passed to widgetregfiles.exe +sub makeIni + { + my ( $self, $data, $file ) = @_; + open INI, ">$file" or die "Failed to open $file for writing: $!"; + binmode INI, ":utf8" if $] >= 5.008; + + # Get directory where mbm should go + my $dir = $self->regFileName($data->{'DriveName'}); + + print INI "[app_registration_info]\n"; + print INI sprintf("uid=%08x\n", $data->{'Uid'}); + print INI "app_file=$data->{'MainHTML'}\n"; + print INI "caption=$data->{'BundleDisplayName'}\n"; + print INI "drive_name=$data->{'DriveName'}\n"; + print INI "results_dir=$dir\n"; + + if( $w3c_widget ) + { + print INI "app_type=200267DC\n"; + print " WGT Widget:: app_type added to ini of widgetregfiles.exe \n" if $debug; + } + + + # Add language stuff if we have the mapping + if ($data->{'LocBundleDisplayName'}) + { + my @langList; + foreach my $langid ( sort { $a <=> $b } keys %{ $data->{'LocBundleDisplayName'} } ) + { + my $symid = sprintf("%02d", $langid); + push @langList, $symid; + print INI "caption$symid=", $data->{'LocBundleDisplayName'}->{$langid}->utf8, "\n"; + } + print INI "languages=", join(' ', @langList), "\n"; + } + + close INI; + convert2Unicode($file); + } + + sub findfreeUid + { + my ( $self, $data ) = @_; + my $appfile = lc $data->{'MainHTML'}; + my $uid; + my $size; + if($w3c_widget) #CWRT Widget + { + # pick the next free CWRT Internal UID + if(isInternal($data->{'DriveName'})) + { + print "\nInternal CWRT UID"; + $size = scalar @staticUidCwrtIntList; + print " Static UID :: @staticUidCwrtIntList Length -- $size \n" if($size); + for(my $i=0; $i<$size; $i++) + { + #skip looping if freeUid found + if( $self->{'freeuidcwrtint'} lt $staticUidCwrtIntList[$i] ) {last}; + + if($self->{'freeuidcwrtint'} == $staticUidCwrtIntList[$i]) + { + $self->{'freeuidcwrtint'}++; + } + } + $uid = $self->{'freeuidcwrtint'}++; # Assign and then set next free UID + } + else + { + # pick the next free CWRT External UID + print "\nExternal CWRT UID "; + $size = scalar @staticUidCwrtExtList; + print "Static UID :: @staticUidCwrtExtList Length -- $size \n"; + + for(my $i=0; $i<$size; $i++) + { + if( $self->{'freeuidcwrtext'} lt $staticUidCwrtExtList[$i] ) {last}; + + if($self->{'freeuidcwrtext'} == $staticUidCwrtExtList[$i]) + { + $self->{'freeuidcwrtext'}++; + } + } + $uid = $self->{'freeuidcwrtext'}++; # Assign and then set next free UID + } + } + else #WRT widget + { + # pick the next free WRT Internal UID + if(isInternal($data->{'DriveName'})) + { + # pick the next free CWRT Internal UID + if(isInternal($data->{'DriveName'})) + { + print "\nInternal WRT UID "; + $size = scalar @staticUidWrtIntList; + print " STATIC UID :: @staticUidWrtIntList Length -- $size\n"; + + for(my $i=0; $i<$size; $i++) + { + if( $self->{'freeuidwrtint'} lt $staticUidWrtIntList[$i] ) {last}; + + if($self->{'freeuidwrtint'} == $staticUidWrtIntList[$i]) + { + $self->{'freeuidwrtint'}++; + } + } + $uid = $self->{'freeuidwrtint'}++; # Assign and then set next free UID + } + } + else + { + # pick the next free WRT External UID + print "\nExternal WRT UID "; + $size = scalar @staticUidWrtExtList; + print "STATIC UID :: @staticUidWrtExtList Length -- $size\n"; + + for(my $i=0; $i<$size; $i++) + { + if( $self->{'freeuidwrtext'} lt $staticUidWrtExtList[$i] ) {last}; + + if($self->{'freeuidwrtext'} == $staticUidWrtExtList[$i]) + { + $self->{'freeuidwrtext'}++; + } + } + $uid = $self->{'freeuidwrtext'}++; # Assign and then set next free UID + } + } + print sprintf("Generated UID: hex->0x%08X dec->$uid\n \n",$uid); + return $uid; + } + +# Fix slash problems in a filename +sub fixFilename + { + my $filename = shift; + $filename =~ s/([\\\/])[\\\/]/$1/g; + return catfile(split(/[\\\/]/, $filename)); + } + +# Get install location +sub destLocation + { + my ( $self, $drive ) = @_; + my $letter = uc(substr($drive, 0, 1)); + + # Was any destination location specified in the config file? + if ($self->{destination} && $self->{destination}->{$letter}) + { + return catfile($self->{'args'}->{'epocroot'}, $self->{destination}->{$letter}); + } + + # No destination specified - use emulator location + return catfile($self->{'args'}->{'epocroot'}, $letter eq 'Z' ? ROM_DEST : sprintf(DRIVE_DEST, $letter)); + } + +# Get the destination for REG/LOC/MBM file +sub regFileName + { + my ( $self, $drive, $filename ) = @_; + my $result = catfile($self->destLocation($drive), DESTINATION, $filename); + return fixFilename($result); + } + +# Widget install directory +sub installDir + { + my ( $self, $drive, $id ) = @_; + my $result ; + $hashval = hashpjw($id); + if( $w3c_widget ) + { + my $widget_uid; + if($non_nokia_widget) + { + $widget_uid = CWRT_WIDGET_UI_NON_NOKIA_UID; + } + else + { + $widget_uid = CWRT_WIDGET_UI_UID; + } + if($isSharedLibrary) + { + $result = catfile($self->destLocation($drive), 'private', sprintf("%08X", $widget_uid),"widgets_21D_4C7", "lib",$sharedFolderName); + } + else + { + $result = catfile($self->destLocation($drive), 'private', sprintf("%08X", $widget_uid),"widgets_21D_4C7", $hashval); + } + } + else + { + if ($isSecureWidget){ + print "Installing secure WGZ Widget\n"; + $result = catfile($self->destLocation($drive), 'private', sprintf("%08x", SWIDGET_UI_UID), $id); + } else { + print "Installing WGZ Widget\n"; + $result = catfile($self->destLocation($drive), 'private', sprintf("%08x", WIDGET_UI_UID), $id); + } + } + return fixFilename($result); + } + +# Determines whether a drive should be considered internal or not +sub isInternal + { + my $drive = shift; + die "Invalid drive format: $drive" if $drive !~ /^[a-zA-Z]:/; + return 1 if $drive =~ /^[zcZC]/; + } + +# Parse these awful PLIST files +sub parsePList + { + my $file = shift; + + # Create parser object + our ($key, $val, $plisthash ) = ('', '', {}); + my $parser = new XML::Parser; + $parser->setHandlers('Doctype' => \&docT, 'Start' => \&startH, 'End' => \&endH, 'Char' => \&dataH); + + # Parse the file + open XML, $file or die "Couldn't open $file"; + + # Skip the UTF8 BOM - perl 5.6 can't handle it + my $bom; + read XML, $bom, 3; + $bom = join('', map(sprintf('%X', $_), unpack("CCC", $bom))); + print "Testing the following for BOM: $bom\n" if $debug; + seek(XML, 0, 0) if $bom ne 'EFBBBF'; + + $parser->parse(*XML); + close XML; + + # Check required fields exist + die "Widget MainHTML unknown" if !$plisthash->{'MainHTML'}; + die "Widget BundleIdentifier unknown" if !$plisthash->{'BundleIdentifier'}; + die "Widget BundleDisplayName unknown" if !$plisthash->{'BundleDisplayName'}; + + # Return result + return $plisthash; + + # Called on a start tag + sub startH + { + my ($p, $el, %atts) = @_; + undef $key if ($el =~ /^key$/i); + $val = ''; + } + + # Receives document type + sub docT + { + my ($expat, $name, $sysid, $pubid, $internal ) = @_; + die "PLIST format looks wrong!" if lc($name) ne 'plist'; + $plisthash->{'NokiaWidget'} = ( $pubid =~ m[^-//Nokia//DTD PLIST]i ) ? 1 : 0; + } + + # Receives character data + sub dataH + { + my ($p, $s) = @_; + $val .= $s; + } + + # Called on an end tag + sub endH + { + my ($p, $el) = @_; + if ($el =~ /^key$/i) + { + $key = $val; + } + elsif ($key) + { + $val = 1 if $el =~ /^true$/i; + $val = 0 if $el =~ /^false$/i; + + # Fix stuff so it's in the correct format + $key =~ s/^CF//; + $key = 'BundleIdentifier' if $key =~ /^Identifier$/i; + $key = 'BundleDisplayName' if $key =~ /^DisplayName$/i; + $key = 'BundleVersion' if $key =~ /^Version$/i; + + $plisthash->{$key} = $val; + undef $key; + } + $val = '' + } + } + + +sub parseConfXml + { + my ( $file , $language ) = @_; + + our ($key, $val, $plisthash, $localizationValue,$localizationIconValue, $lang,$langIcon) = ('', '', {}, '','', {},{}); + our ($featureCount, $paramCount, $accessCount) = (1,1,1); + our ($attributeMap, $featureMap, $featureParamMap, $accessMap)=('','','',''); + + # Create parser object + my $parser = new XML::Parser; + $parser->setHandlers( 'Start' => \&startC, 'End' => \&endC, 'Char' => \&dataC); + + # Parse the file + open XML,$file or die "Couldn't open $file"; + # Skip the UTF8 BOM - perl 5.6 can't handle it + my $bom; + read XML, $bom, 3; + $bom = join('', map(sprintf('%X', $_), unpack("CCC", $bom))); + print "Testing the following for BOM: $bom\n" if $debug; + seek(XML, 0, 0) if $bom ne 'EFBBBF'; + $plisthash->{'NokiaWidget'} = 2 ; + $plisthash->{'WidgetPackagingFormat'} = "w3c-partial-v1" ; + + $parser->parse(*XML); + close XML; + + if( $language ) + { + if($lang->{$language}) + { + $plisthash->{'BundleDisplayName'} = $lang->{$language}; + } + if($langIcon->{$language}) + { + $plisthash->{'IconPath'} = $langIcon->{$language}; + } + } + die "Widget BundleIdentifier unknown" if !$plisthash->{'BundleIdentifier'}; + die "Widget BundleDisplayName unknown" if !$plisthash->{'BundleDisplayName'}; + + $attributeMap = $attributeMap.WIDGET_NAME.KEY_VALUE_SEPERATOR.$plisthash->{'BundleDisplayName'}.KEY_VALUE_PAIR_SEPERATOR; + + if($non_nokia_widget) + { + $attributeMap = $attributeMap.WIDGET_PROCESSUID.KEY_VALUE_SEPERATOR."200267D6".KEY_VALUE_PAIR_SEPERATOR; + } + else + { + $attributeMap = $attributeMap.WIDGET_PROCESSUID.KEY_VALUE_SEPERATOR."200267C0".KEY_VALUE_PAIR_SEPERATOR; + } + + if($attributeMap) + { + $plisthash->{'AttributeList'} = $attributeMap; + print "\nAttributeList::$plisthash->{'AttributeList'}\n" + } + + # Return result + return $plisthash; + + # Called on a start tag + sub startC + { + my ($p, $el, %atts) = @_; + + if($el eq "widget") + { + if ( $atts{"id"} ) + { + $plisthash->{'BundleIdentifier'} = $atts{"id"}; + $attributeMap = $attributeMap.WIDGET_ID.KEY_VALUE_SEPERATOR.$atts{"id"}.KEY_VALUE_PAIR_SEPERATOR; + + } + if ( $atts{"version"} ) + { + $plisthash->{'BundleVersion'} = $atts{"version"}; + $attributeMap = $attributeMap.WIDGET_VERSION.KEY_VALUE_SEPERATOR.$atts{"version"}.KEY_VALUE_PAIR_SEPERATOR; + } + if ( $atts{"viewmodes"} && $atts{"viewmodes"} eq "all" ) + { + $plisthash->{'MiniViewEnabled'} = 1; + } + $attributeMap = $attributeMap.WIDGET_HEIGHT.KEY_VALUE_SEPERATOR.$atts{"height"}.KEY_VALUE_PAIR_SEPERATOR; + $attributeMap = $attributeMap.WIDGET_WIDTH.KEY_VALUE_SEPERATOR.$atts{"width"}.KEY_VALUE_PAIR_SEPERATOR; + } + elsif ($el eq "icon") + { + if ( $atts{"xml:lang"} ) + { + $localizationIconValue = lc($atts{"xml:lang"}) ; + } + if ( $atts{"src"} ) + { + $plisthash->{'IconPath'} = $atts{"src"}; + } + } + elsif ($el eq "content") + { + $attributeMap = $attributeMap.WIDGET_CONTENT.KEY_VALUE_SEPERATOR.KEY_VALUE_PAIR_SEPERATOR; + if ( $atts{"src"} ) + { + $plisthash->{'MainHTML'} = $atts{"src"}; + $attributeMap = $attributeMap.WIDGET_CONTENT.KEY_ATTR_SEPERATOR."src".KEY_VALUE_SEPERATOR.$atts{"src"}.KEY_VALUE_PAIR_SEPERATOR; + } + } + elsif ($el eq "name") + { + if ( $atts{"xml:lang"} ) + { + $localizationValue = lc($atts{"xml:lang"}) ; + } + } + elsif ($el eq "feature") + { + $featureMap = WIDGET_FEATURE . $featureCount; + $attributeMap = $attributeMap.$featureMap.KEY_VALUE_SEPERATOR.KEY_VALUE_PAIR_SEPERATOR; + + if ( $atts{"name"} ) + { + $attributeMap = $attributeMap.$featureMap.KEY_ATTR_SEPERATOR."name".KEY_VALUE_SEPERATOR.$atts{"name"}.KEY_VALUE_PAIR_SEPERATOR; + my $jilFeature = "http://jil.org/jil"; + if($atts{"name"}=~ m/^$jilFeature/i) + { + $plisthash->{'WidgetPackagingFormat'} = "jil"; + } + } + if ( $atts{"required"} ) + { + $attributeMap = $attributeMap.$featureMap.KEY_ATTR_SEPERATOR."required".KEY_VALUE_SEPERATOR.$atts{"required"}.KEY_VALUE_PAIR_SEPERATOR; + } + } + elsif($el eq "param") + { + $featureParamMap = $featureMap. WIDGET_FEATURE_PARAM . $paramCount; + $attributeMap = $attributeMap.$featureParamMap.KEY_VALUE_SEPERATOR.KEY_VALUE_PAIR_SEPERATOR; + if ($atts{"name"}) + { + $attributeMap = $attributeMap.$featureParamMap.KEY_ATTR_SEPERATOR."name".KEY_VALUE_SEPERATOR.$atts{"name"}.KEY_VALUE_PAIR_SEPERATOR; + } + if ($atts{"value"}) + { + $attributeMap = $attributeMap.$featureParamMap.KEY_ATTR_SEPERATOR."value".KEY_VALUE_SEPERATOR.$atts{"value"}.KEY_VALUE_PAIR_SEPERATOR; + } + } + elsif ($el eq "access" || $el eq "jil:access") + { + $accessMap = WIDGET_ACCESS . $accessCount; + $attributeMap = $attributeMap.$accessMap.KEY_VALUE_SEPERATOR.KEY_VALUE_PAIR_SEPERATOR; + + if ( $atts{"network"} ) + { + $attributeMap = $attributeMap.$accessMap.KEY_ATTR_SEPERATOR."network".KEY_VALUE_SEPERATOR.$atts{"network"}.KEY_VALUE_PAIR_SEPERATOR; + } + if ( $atts{"remotescripts"} ) + { + $attributeMap = $attributeMap.$accessMap.KEY_ATTR_SEPERATOR."remotescripts".KEY_VALUE_SEPERATOR.$atts{"remotescripts"}.KEY_VALUE_PAIR_SEPERATOR; + } + if ( $atts{"localfs"} ) + { + $attributeMap = $attributeMap.$accessMap.KEY_ATTR_SEPERATOR."localfs".KEY_VALUE_SEPERATOR.$atts{"localfs"}.KEY_VALUE_PAIR_SEPERATOR; + } + if ( $atts{"origin"} ) + { + $attributeMap = $attributeMap.$accessMap.KEY_ATTR_SEPERATOR."origin".KEY_VALUE_SEPERATOR.$atts{"origin"}.KEY_VALUE_PAIR_SEPERATOR; + } + if ( $atts{"subdomains"} ) + { + $attributeMap = $attributeMap.$accessMap. KEY_ATTR_SEPERATOR."subdomains".KEY_VALUE_SEPERATOR.$atts{"subdomains"}.KEY_VALUE_PAIR_SEPERATOR; + } + } + elsif ($el eq "NOKIA:sharedlibrary") + { + print " \n\n ^^^^^^^^^^^^^^^^^^^^^^^NOKIA:sharedlibrary ^^^^^^^^^^^^^^^^^^^^ \n\n"; + $isSharedLibrary = 1; + $plisthash->{'WidgetPackagingFormat'} = "shared-library"; + $attributeMap = $attributeMap.WIDGET_NOKIA_SHAREDLIB.KEY_VALUE_SEPERATOR.KEY_VALUE_PAIR_SEPERATOR; + } + $val = ''; + } + +sub docC{} +# Receives character data + sub dataC + { + my ($p, $s) = @_; + $val .= $s; + } + + # Called on an end tag + sub endC + { + my ($p, $el) = @_; + if ($el eq "name") + { + if( $localizationValue ) + { + chomp($val); + $lang->{$localizationValue} = $val; + $localizationValue = ''; + } + else + { + chomp($val); + $plisthash->{'BundleDisplayName'} = $val; + } + } + elsif ($el eq "icon") + { + if( $localizationIconValue ) + { + $langIcon->{$localizationIconValue} = $plisthash->{'IconPath'}; + print "\n %%%%%%%%%%% $localizationIconValue :: $plisthash->{'IconPath'}%%%%%%%%%%%%%\n"; + $localizationIconValue = ''; + } + } + elsif($el eq "feature") + { + $featureCount++; + $paramCount = 0; + } + elsif($el eq "param") + { + $paramCount++; + } + elsif($el eq "access" || $el eq "jil:access") + { + $accessCount++; + } + elsif ($el eq "NOKIA:folder") + { + chomp($val); + $sharedFolderName = $val; + $attributeMap = $attributeMap.WIDGET_NOKIA_SHAREDLIB_FOLDER.KEY_VALUE_SEPERATOR.$sharedFolderName.KEY_VALUE_PAIR_SEPERATOR; + } + elsif ($el eq "NOKIA:widget") + { + chomp($val); + if( lc($val) eq "true" ) + { + $isSharedWidget = 1; + $plisthash->{'WidgetPackagingFormat'} = "w3c-partial-v1"; + } + $attributeMap = $attributeMap.WIDGET_NOKIA_SHAREDLIB_WIDGET.KEY_VALUE_SEPERATOR.$val.KEY_VALUE_PAIR_SEPERATOR; + } + elsif ($el eq "NOKIA:icon") + { + chomp($val); + if( lc($val) eq "true" ) + { + $isSharedIcon = 1; + } + $attributeMap = $attributeMap.WIDGET_NOKIA_SHAREDLIB_ICON.KEY_VALUE_SEPERATOR.$val.KEY_VALUE_PAIR_SEPERATOR; + } + $val = '' + } + + } + + +# Stores the details of files to be added to "rom" +sub addToRomList + { + my ( $self, $drive, $file, $localised ) = @_; + $file = fixFilename($file); + + # All files should be under epoc32 somewhere - need to drop a bit of the path for the rom destination + my $localpath = $self->destLocation($drive); + + my $dest = fixFilename($file); + $dest =~ s/^\Q$localpath\E//i; + + # Add the file to the list for the rom + # It may be localised - in which it'll be put in a different IBY file + $localised = $localised ? '_rsc' : ''; + $self->{"installedFiles${localised}"}->{$drive}->{$file} = $dest; + } + +# Make the IBY file +sub makeIBY + { + my ( $self, $drive, $localised ) = @_; + + # Generate the file name for the IBY file + $localised = $localised ? '_rsc' : ''; + my $name = $drive =~ /^[zZ]/ ? "preinstalledwidgets${localised}.iby" : sprintf("preinstalledwidgets_drive%s${localised}.iby", substr($drive, 0, 1)); + my $iby = fixFilename(catfile($self->{'args'}->{'epocroot'}, 'epoc32', 'rom', 'include', $name)); + print "Generating: $iby\n"; + + mkpath dirname($iby); + open IBY, ">$iby" or die "Failed to open $iby for writing: $!"; + $name =~ s/\./_/g; $name = uc($name); + print IBY "// GENERATED FILE: EDIT WITH CARE\n\#ifndef __${name}__\n\#define __${name}__\n\n"; + foreach my $file ( sort keys %{ $self->{"installedFiles${localised}"}->{$drive} } ) + { + my $dest = $self->{"installedFiles${localised}"}->{$drive}->{$file}; + + # Quote filenames as they may contain spaces! + print IBY "data=\"$file\"\t\"$dest\"\n"; + } + print IBY "\#endif\n"; + close IBY; + } + +# Unregister (with Apparc) existing Widgets +sub unregisterWidgets +{ + my ( $self, $drive ) = @_; + + my $registry = fixFilename(catfile($self->destLocation($drive), WIDGET_REGISTRY)); + # If the WRT registry already exists, remove apparc registry info for those widgets + # This should avoid problems with unregistered widget icons in the emulator? + if (-e $registry) + { + print("\n UNREGISTERING WGZ WIDGETS \n"); + + my $ref = XMLin($registry, 'forcearray' => [ 'entry' ], 'keyattr' => { 'prop' => 'content' } ); + foreach my $entry ( @{ $ref->{entry} } ) + { + my $uid = $entry->{prop}->{Uid}->{val}->{content}; + + print "Unregistering existing Widget: $entry->{prop}->{BundleIdentifier}->{val}->{content}\n" if $verbose; + my $dest = $self->regFileName($drive); + my $mbm = catfile($dest, sprintf("[%08x].mbm", $uid)); + my ( $reg, $loc ) = ( catfile($dest, sprintf("%08x_reg.rsc", $uid)), catfile($dest, sprintf("%08x_loc.rsc", $uid)) ); + unlink $mbm, $reg, $loc; + + # We also have to delete the widget directory otherwise it'll be re-registered + my $id = $entry->{prop}->{BundleIdentifier}->{val}->{content}; + $w3c_widget = 0; + my $dir = $self->installDir($drive, $id); + rmtree $dir; + } + } + + $registry = fixFilename(catfile($self->destLocation($drive), CWRT_WIDGET_REGISTRY)); + + # If the CWRT registry already exists, remove apparc registry info for those widgets + # This should avoid problems with unregistered widget icons in the emulator? + if (-e $registry) + { + print("\n UNREGISTERING WGT WIDGETS \n"); + + my $ref = XMLin($registry, 'forcearray' => [ 'entry' ], 'keyattr' => { 'prop' => 'content' } ); + foreach my $entry ( @{ $ref->{entry} } ) + { + my $uid = $entry->{prop}->{Uid}->{val}->{content}; + + print "Unregistering existing Widget: $entry->{prop}->{BundleIdentifier}->{val}->{content}\n" if $verbose; + my $dest = $self->regFileName($drive); + my $mbm = catfile($dest, sprintf("[%08x].mbm", $uid)); + my ( $reg, $loc ) = ( catfile($dest, sprintf("%08x_reg.rsc", $uid)), catfile($dest, sprintf("%08x_loc.rsc", $uid)) ); + unlink $mbm, $reg, $loc; + + # We also have to delete the widget directory otherwise it'll be re-registered + my $id = $entry->{prop}->{BundleIdentifier}->{val}->{content}; + print " Unregistering $id "; + my $basepath = $entry->{prop}->{BasePath}->{val}->{content}; + $w3c_widget = 1; + my $sharedLib = "lib"; #BasePath will have lib only if the widget is shared Library + + if($basepath =~ m/$sharedLib/) + { + $isSharedLibrary = 1; + } + + if($basepath =~ m/\\200267d6\\/i) + { + $non_nokia_widget = 1; + } + #sharedFolderName TBD + my $dir = $self->installDir($drive, $id); + rmtree $dir; + + print("BasePath:$basepath \nIs Non-Nokia? $non_nokia_widget Is SharedLibrary? $isSharedLibrary \nDirectory:$dir \n"); + + $dir =~ s/widgets_21D_4C7/data/; + rmtree $dir; + } + } + #delete CWRT webapp DB if exists + my $cwrtdbPath = catfile($self->destLocation($drive), CWRT_WEBAPP_REGISTRY); + if($cwrtdbPath) + { + print "Deleting CWRT_WEBAPP_REGISTRY \n"; + rmtree $cwrtdbPath; + } + } + +# Make the registry +sub makeRegistry + { + my ($self, $drive, $installed, $w3c) = @_; + + my ($registry); + if($w3c) + { + $registry = fixFilename(catfile($self->destLocation($drive), CWRT_WIDGET_REGISTRY)); + } + else + { + $registry = fixFilename(catfile($self->destLocation($drive), WIDGET_REGISTRY)); + } + + print "\nGenerating: $registry\n"; + + # Write the file + mkpath dirname($registry); + open OUT, ">$registry" or die "Failed to open WidgetEntryStore.xml: $!"; + + print OUT "\n\n"; + binmode OUT, ":utf8" if $] >= 5.008; + foreach my $pList ( @$installed ) + { + $self->dumpPList(\*OUT, $pList, $w3c); + } + print OUT "\n"; + close OUT; + + # Convert the file to unicode + convert2Unicode($registry); + + # Return the generated file + return $registry; + } + +# Converts a file to Unicode +sub convert2Unicode + { + my $file = shift; + + my @lines; + open IN, $file or die "Failed to open $file: $!"; + binmode IN; + while() + { + my $u = utf8($_); + $u->byteswap; + push @lines, $u->utf16; + } + close IN; + + open OUT, ">$file" or die "Failed to open $file for writing: $!"; + binmode OUT; + print OUT pack("CC", 0xff, 0xfe); + print OUT @lines; + close OUT; + } + +sub hashpjw { + my $s = shift; + my $hashval = 0; + my $g; + + for ( split //, $s ) { + $hashval = ( $hashval << 4 ) + ord $_; + + if ($g = $hashval & 0xf0000000) { + $hashval ^= $g >> 23; + } + $hashval &= ~$g; + } + #print "$hashval \n"; + return $hashval; +} + +# Dumps a single PList hash object +sub dumpPList + { + my ($self, $fh, $data, $w3c) = @_; + my @regProperties = ( + [ 'PropertyListVersion', 'int' ], + [ 'BundleIdentifier', 'string' ], + [ 'BundleName', 'string' ], + [ 'BundleDisplayName', 'string' ], + [ 'MainHTML', 'string' ], + [ 'BundleVersion', 'string' ], + # [ 'Height', 'int' ], + # [ 'Width', 'int' ], + [ 'AllowNetworkAccess', 'int' ], + [ 'DriveName', 'string' ], + [ 'BasePath', 'string' ], + [ 'IconPath', 'string' ], + [ 'FileSize', 'int' ], + [ 'Uid', 'int' ], + [ 'NokiaWidget', 'int' ], + [ 'MiniViewEnabled', 'int' ], + [ 'ProcessUid', 'int' ], + [ 'MimeType', 'string'], + [ 'WidgetPackagingFormat', 'string'], + [ 'DBIconPath', 'string'], + [ 'AttributeList', 'string'], + [ 'BlanketPermissionGranted', 'int' ], + [ 'PreInstalled', 'int' ], + ); + + print $fh "\n"; + my $encodeddata = {}; + foreach my $prop ( @regProperties ) + { + my ( $key, $type ) = @$prop; + if($key ne "DBIconPath" && $key ne "WidgetPackagingFormat" && $key ne "AttributeList" && $key ne "BlanketPermissionGranted" && $key ne "PreInstalled") + { + print $fh "$key$data->{$key}$type\n" if defined $data->{$key}; + } + + if( $w3c ) + { + $encodeddata->{$key} = encodeChar($data->{$key}) if defined $data->{$key}; + } + else + { + if($key eq "BlanketPermissionGranted" || $key eq "PreInstalled") + { + print $fh "$key$data->{$key}$type\n" if defined $data->{$key}; + } + } + } + print $fh "\n"; + print "\n Is a w3c widget? -- $w3c\n"; + if( $w3c ) + { + my $dbPath = fixFilename(catfile($self->destLocation('C'), CWRT_WEBAPP_REGISTRY)); + mkpath dirname($dbPath); + die "ERROR: Can't find CWRTWebAppRegistry.exe in PATH" if !(my $cmd = findCmd('CWRTWebAppRegistry.exe')); + my $regCmd; + + if($encodeddata->{'AttributeList'}) + { + print "\n AttributeList argument sent to DB\n"; + $regCmd = "$cmd $dbPath $encodeddata->{'BundleIdentifier'} $encodeddata->{'Uid'} $encodeddata->{'BundleDisplayName'} $encodeddata->{'BasePath'} $encodeddata->{'DBIconPath'} $encodeddata->{'WidgetPackagingFormat'} $encodeddata->{'MainHTML'} $encodeddata->{'AttributeList'}"; + } + else + { + print "\n AttributeList argument not sent to DB\n"; + $regCmd = "$cmd $dbPath $encodeddata->{'BundleIdentifier'} $encodeddata->{'Uid'} $encodeddata->{'BundleDisplayName'} $encodeddata->{'BasePath'} $encodeddata->{'DBIconPath'} $encodeddata->{'WidgetPackagingFormat'} $encodeddata->{'MainHTML'}"; + } + print "\n regCmd : $regCmd \n\n"; + system($regCmd); + } + } + +#encode the space character +sub encodeChar + { + my ($encoded) = @_; + + $encoded =~ s/%/%10/g; + $encoded =~ s/\s/%40/g; + $encoded =~ s/\'/%apos/g; + $encoded =~ s/&/%amp/g; + $encoded =~ s//%gt/g; + $encoded =~ s/\"/%quot/g; + return $encoded; + } + + +# Make secssion +sub makeSecSession + { + my ($self, $drive) = @_; + my $secSession; + my $widget_uid; + if($non_nokia_widget) + { + $widget_uid = CWRT_WIDGET_UI_NON_NOKIA_UID; + } + else + { + $widget_uid = CWRT_WIDGET_UI_UID; + } + + if($isSharedLibrary) + { + $secSession = fixFilename(catfile($self->destLocation($drive), 'private', sprintf("%08X", $widget_uid),"data","lib",$sharedFolderName,"secsession")); + } + else + { + $secSession = fixFilename(catfile($self->destLocation($drive), 'private', sprintf("%08X", $widget_uid),"data", $hashval,"secsession")); + } + print "\nGenerating: $secSession\n\n"; + + # Write the file + mkpath dirname($secSession); + open OUT, ">$secSession" or die "Failed to open SecSession.xml: $!"; + + print OUT "\n\n"; + if($non_nokia_widget) + { + print OUT "\n"; + } + else + { + print OUT "\n"; + } + + if(CWRT_ACCESS_POLICY_PATH) { + my $accesspolicypath = findCmd(CWRT_ACCESS_POLICY_PATH); + # Copy Secsession capabilities from browser_access_policy.xml if it exists + if (-e $accesspolicypath) { + print "Browser Access Policy File exists :: ",$accesspolicypath,"\n" ; + my $accesspolicy = XMLin($accesspolicypath, keyattr => {domain => 'name'}, forcearray =>['domain'] ); + my $domainName; + + if($non_nokia_widget) + { + $domainName = $accesspolicy->{domain}->{Operator}; + } + else + { + $domainName = $accesspolicy->{domain}->{TrustedWidgets}; + } + + my $xml = XMLout($domainName, keeproot => 1); + print OUT $xml; + } + #browser_access_policy.xml does not exist in the path + else { + print "\n Browser Access policy file '$accesspolicypath' does not exist in the given path\n"; + if(!$non_nokia_widget) + { + print OUT " \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n"; + } + } + } + #Access policy path not defined + else { + print "\n Browser Access policy path not defined \n"; + if(!$non_nokia_widget) + { + print OUT " \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n"; + } + } + + print OUT "\n"; + print OUT "\n"; + close OUT; + + # Return the generated file + return $secSession; + } +__END__ + +=head1 NAME + +installwidgets.pl - A script for generating all the files needed to install Widgets + +=head1 SYNOPSIS + +installwidgets.pl [-h] [-ver] [-v] [-debug] [-e ] [-l ] config.ini + + Options: + -help|h Show this help + -version|ver Show version number + -verbose|v Show verbose output + -debug Show debug output + -epocroot|e Override value of EPOCROOT + -localization|l lproj_dir + +A script for generating all the files needed to preinstall Widgets. + + Example: + perl installwidgets.pl -l fr config.ini Install widgets listed in config.ini using French localization + + Author: + peter.harper@sosco.com + +=head1 DESCRIPTION + +This tool can be used to pre-generate all the files needed to install Widgets. The tool and its dependencies can be placed anywhere on your PATH. +It generates the results in the epoc32 folder - in the appropriate locations for the emulator. +It finds the epoc32 folder using the EPOCROOT environment variable which can be overridden via the -e command line option. + +=head2 CONFIG FILE + +The preferred way to run the tool is via a configuration INI file. +You can list the widgets to install on each drive. You can specify the exact location of the Widget, otherwise it will try and find the Widget via EPOCROOT. + +You can specify whether a widget is intended for the homescreen by adding the text [HomeScreen] after the filename +This will set the BlanketPermissionGranted attribute in the registry. +Widgets intended for the homescreen must have the MiniViewEnabled attribute set in its PLIST file otherwise an error is generated. + + # Widgets to be pre-installed for the ROM + [drive-z] + \somepath\foo.wgz + \somepath\bar.wgz + + # Widgets for the internal disk + [drive-c] + \somepath\widget1.wdgt.zip [HomeScreen] + + # Widgets for the removable disk + [drive-e] + \somepath\widget2.wdgt.zip + + # Commands to run at the end + [run-commands] + dostuff.pl + domorestuff.exe + +=head2 DEPENDENCIES + +The tool has some dependencies which must exist for it to work. + +=over + +=item 1 + +png2mbm.pl - A script to generate an MBM file from a PNG + +=item 2 + +WidgetRegFiles.exe - an EXE which can generate Symbian REG and LOC files for registering non-native Widget apps. +This tool is built with "SymPort" a native tools port of basic Symbian OS services. + +=item 3 + +7z/unzip - For extracting files from the Widget archive. +7Zip will be used in preference to unzip if it's found on your path because it handles Unicode a better. + +=item 4 + +GD.pm - Perl support for the GD graphics library for PNG support, see http://www.libgd.org . + +=back + +=head3 INSTALLING GD + +You can install GD automatically with a simple command - however the command you need to use differs depending on the version of Perl you have installed. +At the time of writing Symbian requires Perl version 5.6 - although in my experience Perl 5.8 works okay. To find out which version of Perl you have type "perl -v" on the command line. + +To install the GD library: + +=over + +=item * + +For Perl v5.6: "ppm install http://theoryx5.uwinnipeg.ca/ppmpackages/GD.ppd " + +=item * + +For Perl v5.8: "ppm install http://theoryx5.uwinnipeg.ca/ppms/GD.ppd " + +=back + +=head2 WIDGET INSTALL PROCESS + +Here's a detailed breakdown of what the script does. + +=over + +=item 1 + +It gets the lists of Widgets from the config.ini file passed on the command line. +This process is repeated for all Widgets and all drives listed in the config file. + +=item 2 + +Any existing Widgets listed in "private\10282f06\WidgetEntryStore.xml" are deleted from the epoc32 tree. +This ensures that there are no problems when testing Widgets in the emulator. + +=item 3 + +All the compressed files in the Widget are extracted to a temporary folder. + +=item 4 + +The details for the Widget are loaded from its "Info.plist" file. + +=item 5 + +A UID is chosen for the widget. This differs depending on whether installation is for an internal drive (z: or c:) or an external drive (e: etc). + +=item 5 + +A Symbian MBM file is generated from the "Icon.png" file supplied by the Widgets. +Three different sized icons are generated "88x88", "32x32" and "24x24". +The MBM file is placed in "private/10003a3f/import/apps/NonNative/Resource/[].mbm". + +=item 6 + +"WidgetRegFiles.exe" is executed to generate REG and LOC resource files used to register the Widget as an app in Symbian OS. +These files are placed in "private/10003a3f/import/apps/NonNative/Resource". + +=item 7 + +All the widgets files are copied to a folder under "private\10282822". +The Widget's bundle identifier is used to create a unique folder under here for the Widget. + +=item 8 + +The Widget registry is generated in "private\10282f06\WidgetEntryStore.xml" + +=item 9 + +If Widgets are being preinstalled for ROM an IBY file is created in "epoc32\rom\include\preinstalledwidgets.iby". +A separate IBY file is generated for the localised parts of a Widget "epoc32\rom\include\preinstalledwidgets_rsc.iby". +Separate IBY files (per drive) are generated for Widgets preinstalled to UDA, e.g. preinstalledwidgets_driveC.iby and preinstalledwidgets_driveC_rsc.iby. +These IBY files can be used to add all the Widgets to ROM, ROFS or UDA. + +=back + +=head3 INSTALLING ON HARDWARE USING iMaker + +=over + +=item 1 + +Create the following folder structure at the root level. + +X:\variants\content + +=item 2 + +Copy the files specified in the generated ibys (preinstalledwidgets_driveC.iby and preinstalledwidgets_driveC_rsc.iby) to X:\variants\content. Preserve the dir structure. (Note, this step will be automated in the future) + +For example if you want the following file on UDA (User Disk Area, C drive on phone) at the following location C:\private\10282f06\WidgetEntryStore.xml + +Drop the file under X:\variants\content\private\10282f06\WidgetEntryStore.xml + +=item 3 + +Run the foll command to generate UDA + +B +X:\epoc32\tools>imaker -f /epoc32/rom/s60_makefiles/image_conf_sp_rnd_gadget.mk VARIANT_DIR=/variants variantuda + +B +Y:\epoc32\tools>imaker -f /epoc32/rom/config/ncp52/tube/image_conf_tube_ui.mk VARIANT_DIR=/variants variantuda + +=item 4 + +Flash the fpsx file generated under X:\epoc32\rombuild\gadget\uda for Gadget and Y:\epoc32\rombuild\tube\uda for Tube to your device. + +Note: More info on iMaker tool at: L + +=back + +=head3 LOCALISATION + +Widget handles localization by providing localized resources in various language project directories(lproj_dir), one level deep than the root directory. In order to specify a language variant for pre-installing widget, you need to provide the language project directory name, e.g. 'en' for english, 'fr' for French. + +A list of Nokia supported languages can be found in Widget_lproj.xml or at L. If the widget does not provide the localized resource for the variant you specified, the default resources in widget's home directory will be used instead. + +=head3 NOTES + +=over + +=item 1 + +The location of the private folder is in the appropriate place for the files to appear in the emulator. +This is different depending on the intended destination drive (see -dd command line option) for the Widget. +e.g. "epoc32/release/winscw/udeb/z/", "epoc32/winscw/c" or "epoc32/winscw/e" + +=item 2 + +Files are extracted to epoc32 on the current drive relative to the EPOCROOT environment variable or the value given for -epocroot (-e) on the command line. + +=item 3 + +A different IBY file is generated for each drive. + +=over + +=item * + +Z: - \epoc32\rom\include\preinstalledwidgets.iby + +=item * + +C: - \epoc32\rom\include\preinstalledwidgets_driveC.iby + +=item * + +E: - \epoc32\rom\include\preinstalledwidgets_driveE.iby + +=back + +There are separate resource files for localised resources e.g. preinstalledwidgets_rsc.iby. + +=cut