#!/usr/bin/perl # -*- perl -*- ###################################################################### # charedit.pl -- Edit NWN character files # Copyright (c) 2005 Tero Kivinen # All Rights Reserved. ###################################################################### # Program: coinlet-cumulate.pl # $Source: /u/kivinen/portalify/RCS/coinlet-cumulate.pl,v $ # Author : $Author: kivinen $ # # (C) Tero Kivinen 2005 # # Creation : 17:26 Sep 29 2005 kivinen # Last Modification : 20:43 Oct 2 2005 kivinen # Last check in : $Date: 2002/03/06 23:56:31 $ # Revision number : $Revision: 1.1 $ # State : $State: Exp $ # Version : 1.636 # Edit time : 309 min # # Description : Edit NWN character files # # $Log: coinlet-cumulate.pl,v $ # $EndLog$ # # # ###################################################################### # initialization require 5.6.0; package CharEdit; use strict; use locale; use File::Glob ':glob'; use CGI; use POSIX qw(locale_h); #push(@INC, "/u/kivinen/nwn/perllib"); eval "use Gff;"; eval "use GffRead;"; eval "use GffWrite;"; eval "use Twoda;"; if (!defined($CharEdit::initialized)) { $CharEdit::initialized = 1; $CharEdit::dir = "/home/nwn/nwn/servervault/"; $CharEdit::nwn_2da = "/home/nwn/nwn/2da/"; $CharEdit::portraits = "/var/www/html/cerea/po/"; $CharEdit::sounds = "/var/www/html/cerea/wav/"; %CharEdit::popup_menu = ('Wings' => { '0' => 'None', '1' => 'Demon', '2' => 'Angel', '3' => 'Bat', '4' => 'Dragon', '5' => 'Butterfly', '6' => 'Bird' }, 'Tail' => { '0' => 'None', '1' => 'Lizard', '2' => 'Bone', '3' => 'Devil' }, 'Phenotype' => { '0' => 'Thin', '2' => 'Large' } ); %CharEdit::head = ('Color_Hair+' => 'Hair and Skin', 'Color_Tattoo1+' => 'Tattoo'); $CharEdit::soundset = Twoda::read($CharEdit::nwn_2da . "soundset.2da"); my($i, $tmp); for($i = 0; $i < 55; $i++) { $CharEdit::popup_menu{'Color_Hair'}{$i} = [ $i, "/pal/hair_$i.jpg", 32, 32 ]; $CharEdit::popup_menu{'Color_Tattoo1'}{$i} = [ $i, "/pal/tattoo_$i.jpg", 32, 32 ]; $CharEdit::popup_menu{'Color_Tattoo2'}{$i} = [ $i, "/pal/tattoo_$i.jpg", 32, 32 ]; if ($i < 32) { $CharEdit::popup_menu{'Color_Skin'}{$i} = [ $i, "/pal/skin_$i.jpg", 32, 32 ]; } } for($i = 0; $i < $#$CharEdit::soundset; $i++) { if ($$CharEdit::soundset[$i]{'RESREF'} ne '****') { $CharEdit::popup_menu{'SoundSetFile'}{$i} = [ lc($$CharEdit::soundset[$i]{'LABEL'}), lc($$CharEdit::soundset[$i]{'RESREF'}) . ".wav" ]; } } @CharEdit::portraits = map { s/.*\///g; s/h\.jpg//g; $_; } bsd_glob($CharEdit::portraits . "/*.jpg"); foreach $i (@CharEdit::portraits) { $tmp = $i; $tmp =~ s/^po_//g; $tmp =~ s/_/ /g; $CharEdit::popup_menu{'Portrait'}{$i} = [ $tmp, "/po/" . $i . "h.jpg" ]; } } setlocale(LC_CTYPE, "fi_FI"); print start(new CGI); ###################################################################### # ($header, $start, $html, $end) = start($query) sub start { my($query) = @_; my(@data); @data = charedit($query); undef $CharEdit::gff; undef $CharEdit::login; undef $CharEdit::char; return @data; } ###################################################################### # ($header, $start, $html, $end) = charedit($query) sub charedit { my($query) = @_; if (check_login($query)) { return login($query); } if (defined($query->param('Save'))) { return char_save($query); } elsif (defined($query->param('Delete'))) { return char_delete($query); } else { return char_form($query); } } ###################################################################### # $login_fail = check_login($query) sub check_login { my($query) = @_; my($gff, $i, $passwd); $CharEdit::login = $query->param('login'); $CharEdit::char = $query->param('char'); $CharEdit::passwd = $query->param('passwd'); # $CharEdit::login =~ tr/a-zA-Z0-9 -//cd if (defined($CharEdit::login)); # $CharEdit::char =~ tr/a-zA-Z0-9 -//cd if (defined($CharEdit::char)); $CharEdit::login =~ tr/a-zA-Z0-9_\',.\241-\377 -//cd if (defined($CharEdit::login)); $CharEdit::char =~ tr/a-zA-Z0-9_\'\241-\377 -//cd if (defined($CharEdit::char)); $CharEdit::error = ''; if (!defined($CharEdit::login)) { return 1; } if (! -d ($CharEdit::dir . $CharEdit::login)) { $CharEdit::error = "Invalid login"; return 1; } if (!defined($CharEdit::char) || ! -f ($CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic")) { if (defined($CharEdit::char) && $CharEdit::char ne '') { $CharEdit::error = "Invalid character"; } return 1; } if (!defined($CharEdit::passwd) || $CharEdit::passwd eq '') { return 1; } $CharEdit::gff = GffRead::read(filename => $CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic", return_errors => 1); $CharEdit::name_change_allowd = 0; foreach $i (@{$$CharEdit::gff{ItemList}}) { if ($$i{Tag} eq 'CharEditToken' && $$i{VarTable}[0]{Name} eq 'Password' && $$i{VarTable}[0]{Type} == 3) { $passwd = $$i{VarTable}[0]{Value}; } if ($$i{Tag} eq 'CharNameToken') { $CharEdit::name_change_allowd = 1; } } # Check passwd if ($CharEdit::passwd eq $passwd) { return 0; } else { $CharEdit::error = "Invalid passwd"; return 1; } } ###################################################################### # ($header, $start, $html, $end) = login($query) sub login { my($query) = @_; my($header, $start, $html, $end, $i, $gff); $header = $query->header(-type => 'text/html', -nph => 0); $start = $query->start_html(-title => 'Login', -style => { 'src' => '/styles/charedit.css'}); $end = $query->end_html; $html = $query->h1("Login") . "

\n"; if (defined($CharEdit::error) && $CharEdit::error ne '') { $html .= "

" . "Error:" . $CharEdit::error . "
\n"; } $html .= $query->start_form(-method => 'POST', -class => 'Login') . "\n"; $html .= "
" . "Login:\n" . $query->textfield(-name => 'login', -onChange => 'this.form.submit()', -class => 'Login', -size => '20') . "
\n"; if (defined($CharEdit::login)) { if (-d $CharEdit::dir . $CharEdit::login) { my($name, @char_files, %labels, @values); @char_files = bsd_glob($CharEdit::dir . $CharEdit::login . "/*.bic"); foreach $i (@char_files) { $gff = GffRead::read(filename => $i, exclude => '^/(Equip_ItemList|ClassList|CombatInfo|FeatList|ItemList|LvlStatList|QBList|SkillList)', return_errors => 1); $i =~ s/.*\///g; $i =~ s/\..*//g; if (defined($gff)) { $name = $$gff{FirstName}{0} . " " . $$gff{LastName}{0}; } else { $name = $i; } push(@values, $i); $labels{$i} = $name; } $html .= "
" . "Character:\n" . $query->popup_menu(-name => 'char', -onChange => 'this.form.submit()', -class => 'Char', -values => \@values, -labels => \%labels) . "
\n"; } } if (defined($CharEdit::char)) { $html .= "
" . "Password:\n" . $query->password_field(-name => 'passwd', -class => 'passwd', -onChange => 'this.form.submit()') . "
\n"; $html .= $query->submit(-name => 'Edit', -class => 'Edit'); } else { $html .= $query->submit(-name => 'Continue', -class => 'Continue'); } $html .= $query->endform() . "\n"; return ($header, $start, $html, $end); } ###################################################################### # ($header, $start, $html, $end) = form($query) sub char_form { my($query) = @_; my($header, $start, $html, $end, $i, $j, $sidehtml); my($name, @portraits, %portrait_labels); $header = $query->header(-type => 'text/html', -nph => 0); $start = $query->start_html(-title => 'Edit character', -style => { 'src' => '/styles/charedit.css'}); $end = $query->end_html; $name = $$CharEdit::gff{FirstName}{0} . " " . $$CharEdit::gff{LastName}{0}; foreach $i ('FirstName', 'LastName', 'Description') { if (!defined($query->param($i))) { $query->param($i, $$CharEdit::gff{$i}{0}); } } foreach $i ('Age', 'Color_Hair', 'Color_Skin', 'Color_Tattoo1', 'Color_Tattoo2', 'Phenotype', 'Portrait', 'SoundSetFile', 'Wings', 'Tail') { if (!defined($query->param($i))) { $query->param($i, $$CharEdit::gff{$i}); } } $sidehtml = ''; $html = $query->h1("Editing $name") . "\n"; $html .= $query->start_form(-method => 'POST', -class => 'Edit') . "\n"; foreach $i ('login', 'char', 'passwd') { $html .= $query->hidden(-name => $i) . "\n"; } $html .= $query->start_table() . "\n"; $html .= $query->start_Tr() . $query->td({-class => 'FirstNameHeader'}, "FirstName:"); if ($CharEdit::name_change_allowd) { $html .= $query->td({-class => 'FirstName'}, $query->textfield(-name => 'FirstName', -size => '30')) . $query->end_Tr() . "\n"; } else { $html .= $query->td({-class => 'FirstName'}, $query->param('FirstName')); } $html .= $query->start_Tr() . $query->td({-class => 'LastNameHeader'}, "LastName:"); if ($CharEdit::name_change_allowd) { $html .= $query->td({-class => 'LastName'}, $query->textfield(-name => 'LastName', -size => '30')) . $query->end_Tr() . "\n"; } else { $html .= $query->td({-class => 'LastName'}, $query->param('LastName')); } $html .= $query->start_Tr() . $query->td({-class => 'DescriptionHeader'}, "Description:") . $query->td({-class => 'Description'}, $query->textarea(-name => 'Description', -rows => 10, -columns => 32)) . $query->end_Tr() . "\n"; foreach $i ('Age', 'Color_Hair+', '+Color_Skin', 'Color_Tattoo1+', '+Color_Tattoo2', 'Phenotype', 'SoundSetFile', 'Wings', 'Tail', 'Portrait') { my($pre, $post, $ind, $head); $head = $CharEdit::head{$i}; $head = $i if (!defined($head)); $ind = $i; $ind =~ s/\+//g; $pre = $query->start_Tr() . $query->td({-class => $ind . 'Header'}, $head . ":") . $query->start_td({-class => $ind}); $post = $query->end_td() . $query->end_Tr() . "\n"; $ind = $i; if ($ind =~ s/^\+//g) { $pre = ''; } if ($ind =~ s/\+$//g) { $post = ''; } $html .= $pre; if (defined($CharEdit::popup_menu{$ind})) { my($img, $value, @values, %labels); if ($ind eq 'Portrait') { @values = sort { $a cmp $b } keys(%{$CharEdit::popup_menu{$ind}}); } elsif ($ind eq 'SoundSetFile') { @values = sort { $CharEdit::popup_menu{$ind}{$a}[0] cmp $CharEdit::popup_menu{$ind}{$b}[0]; } keys(%{$CharEdit::popup_menu{$ind}}); } else { @values = sort { $a <=> $b } keys(%{$CharEdit::popup_menu{$ind}}); } foreach $j (@values) { $value = $CharEdit::popup_menu{$ind}{$j}; if (ref($value) eq 'ARRAY') { $labels{$j} = $$value[0]; } else { $labels{$j} = $value; } } $value = $CharEdit::popup_menu{$ind}{$query->param($ind)}; if (ref($value) eq 'ARRAY') { if ($$value[1] =~ /\.wav$/) { my(@sounds, $origfile, $file, $name); $origfile = $$value[1]; $origfile =~ s/\.wav$//g; $img = '
Sound examples
'; @sounds = bsd_glob($CharEdit::sounds . $origfile . "*.wav"); @sounds = grep { !/\/$origfile[_]\d+\.wav$/; } @sounds; foreach $j (@sounds) { $file = $j; $file =~ s/^.*\///g; $name = $file; $name =~ s/$origfile//g; $name =~ s/^_//g; $name =~ s/\.wav$//g; $img .= "$name\n"; } $img .= "
\n"; } else { if (defined($$value[2])) { $img = $query->img({src => $$value[1], ALIGN => 'CENTER', CLASS => $ind, WIDTH => $$value[2], HEIGHT => $$value[3]}); } else { $img = $query->img({src => $$value[1], CLASS => $ind, ALIGN => 'CENTER'}); } } } else { $img = ''; } if ($ind eq 'Portrait' || $ind eq 'SoundSetFile') { $html .= $query->popup_menu(-name => $ind, -values => \@values, -onChange => 'this.form.submit()', -labels => \%labels); $sidehtml .= $img; } else { if ($img eq '') { $html .= $query->popup_menu(-name => $ind, -values => \@values, -labels => \%labels); } else { $html .= $query->popup_menu(-name => $ind, -values => \@values, -onChange => 'this.form.submit()', -labels => \%labels) . $img; } } } else { $html .= $query->textfield(-name => $ind, -size => '30'); } $html .= $post; } $html .= $query->end_table(); $html .= $query->submit(-name => 'Update', -class => 'Update'); $html .= $query->submit(-name => 'Save', -class => 'Save'); $html .= $query->submit(-name => 'Delete', -class => 'Delete'); $html .= $query->endform() . "\n"; $html .= $sidehtml; return ($header, $start, $html, $end); } ###################################################################### # ($header, $start, $html, $end) = save($query) sub char_save { my($query) = @_; my($header, $start, $html, $end, $i, $j); my($year, $mon, $day, $hour, $min, $sec); $header = $query->header(-type => 'text/html', -nph => 0); $start = $query->start_html(-title => 'Character saved', -style => { 'src' => '/styles/charedit.css'}); $end = $query->end_html; $html = $query->h1("Edit done") . "\n"; $html .= "
\n"; if ($CharEdit::name_change_allowd) { if (defined($query->param('FirstName')) && $query->param('FirstName') ne $$CharEdit::gff{FirstName}{0}) { $$CharEdit::gff{FirstName}{0} = $query->param('FirstName'); $html .= "First name updated
\n"; } if (defined($query->param('LastName')) && $query->param('LastName') ne $$CharEdit::gff{LastName}{0}) { $$CharEdit::gff{LastName}{0} = $query->param('LastName'); $html .= "Last name updated
\n"; } } if (defined($query->param('Description')) && $query->param('Description') ne $$CharEdit::gff{Description}{0}) { $$CharEdit::gff{Description}{0} = $query->param('Description'); $html .= "Description updated
\n"; } foreach $i ('Age', 'Color_Hair', 'Color_Skin', 'Color_Tattoo1', 'Color_Tattoo2', 'Phenotype', 'Portrait', 'SoundSetFile', 'Wings', 'Tail') { if (defined($query->param($i)) && $query->param($i) ne $$CharEdit::gff{$i}) { if (defined($CharEdit::popup_menu{$i})) { if (defined($CharEdit::popup_menu{$i}{$query->param($i)})) { $$CharEdit::gff{$i} = $query->param($i); $html .= "$i updated
\n"; } else { $html .= "Invalid $i not saved
\n"; } } else { $$CharEdit::gff{$i} = $query->param($i); $html .= "$i updated
\n"; } } } ($sec, $min, $hour, $day, $mon, $year) = localtime(time()); rename($CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic", $CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic" . sprintf("-%04d-%02d-%02d-%02d%02d%02d", $year + 1900, $mon + 1, $day, $hour, $min, $sec)); &GffWrite::write($CharEdit::gff, filename => $CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic"); chmod(0666, $CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic"); $html .= "Character file saved
\n"; $html .= $query->start_form(-method => 'POST') . "\n"; $html .= $query->submit(-name => "Start again", -class => 'Start'); $html .= $query->endform() . "\n"; $html .= "
\n"; return ($header, $start, $html, $end); } ###################################################################### # ($header, $start, $html, $end) = save($query) sub char_delete { my($query) = @_; my($header, $start, $html, $end, $i, $j); my($year, $mon, $day, $hour, $min, $sec, $name); $header = $query->header(-type => 'text/html', -nph => 0); $start = $query->start_html(-title => 'Delete character', -style => { 'src' => '/styles/charedit.css'}); $end = $query->end_html; $name = $$CharEdit::gff{FirstName}{0} . " " . $$CharEdit::gff{LastName}{0}; if (!defined($query->param("Delete it, I am really sure"))) { $html = $query->h1("Deleting $name ") . "\n"; $html .= "
\n"; $html .= "Deleting $name, are you really sure?

" . " This will delete the " . "character file completely, with all items and exp and so on. " . "If you delete your character by accident, there is no asking it ". "back."; $html .= $query->start_form(-method => 'POST') . "\n"; foreach $i ('login', 'char', 'passwd', 'Delete') { $html .= $query->hidden(-name => $i) . "\n"; } $html .= $query->submit(-name => 'Delete it, I am really sure', -class => 'DoDelete'); $html .= $query->endform() . "\n"; $html .= $query->start_form(-method => 'POST') . "\n"; $html .= $query->submit(-name => 'Do not delete it', -class => 'DoNotDelete'); $html .= $query->endform() . "\n"; $html .= "

\n"; return ($header, $start, $html, $end); } $html = $query->h1("Delete of $name done") . "\n"; $html .= "
\nDelete of $name done"; $html .= $query->start_form(-method => 'POST') . "\n"; $html .= $query->submit(-name => "Start again", -class => 'Start'); $html .= $query->endform() . "\n
\n"; ($sec, $min, $hour, $day, $mon, $year) = localtime(time()); rename($CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic", $CharEdit::dir . $CharEdit::login . "/" . $CharEdit::char . ".bic" . sprintf("-%04d-%02d-%02d-%02d%02d%02d", $year + 1900, $mon + 1, $day, $hour, $min, $sec)); return ($header, $start, $html, $end); }