diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/HPMHookGen/HPMDataCheckGen.pl | 68 | ||||
-rwxr-xr-x | tools/HPMHookGen/HPMHookGen.pl | 580 | ||||
-rw-r--r-- | tools/HPMHookGen/Makefile.in | 80 | ||||
-rw-r--r-- | tools/HPMHookGen/doxygen.conf | 216 | ||||
-rw-r--r-- | tools/Script-Checker.applescript | 151 | ||||
-rwxr-xr-x | tools/check-doc | 8 | ||||
-rwxr-xr-x | tools/item_db.pl | 116 | ||||
-rw-r--r-- | tools/item_merge.lua | 619 | ||||
-rwxr-xr-x | tools/itemdbconverter.pl | 195 | ||||
-rw-r--r-- | tools/mapreg-converter.php | 2 |
10 files changed, 1914 insertions, 121 deletions
diff --git a/tools/HPMHookGen/HPMDataCheckGen.pl b/tools/HPMHookGen/HPMDataCheckGen.pl new file mode 100644 index 000000000..7ec1ba7e4 --- /dev/null +++ b/tools/HPMHookGen/HPMDataCheckGen.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +# Copyright (c) Hercules Dev Team, licensed under GNU GPL. +# See the LICENSE file + +use strict; +use warnings; +use XML::Simple; + +# XML Parser hint (some are faster than others) +#local $ENV{XML_SIMPLE_PREFERRED_PARSER} = ''; # 0m14.181s +local $ENV{XML_SIMPLE_PREFERRED_PARSER} = 'XML::Parser'; # 0m4.256s +#local $ENV{XML_SIMPLE_PREFERRED_PARSER} = 'XML::SAX::Expat'; # 0m14.186s +#local $ENV{XML_SIMPLE_PREFERRED_PARSER} = 'XML::LibXML::SAX'; # 0m7.055s + +my @files = grep { -f } grep { /[^h]\.xml/ } glob 'doxyoutput/xml/struct*.xml'; +my %out; + +foreach my $file (@files) { + my $xml = new XML::Simple; + my $data = $xml->XMLin($file); + next unless $data->{compounddef}->{includes}; # means its a struct from a .c file, plugins cant access those so we don't care. + next if $data->{compounddef}->{compoundname} =~ /::/; # its a duplicate with a :: name e.g. struct script_state {<...>} ay; + my @filepath = split(/[\/\\]/, $data->{compounddef}->{location}->{file}); + my $foldername = uc($filepath[-2]); + my $filename = uc($filepath[-1]); $filename =~ s/-/_/g; $filename =~ s/\.[^.]*$//; + my $name = "${foldername}_${filename}_H"; + push @{ $out{$name} }, $data->{compounddef}->{compoundname}; +} + +my $fname = '../../src/common/HPMDataCheck.h'; +open(FH, '>', $fname); + +print FH <<"EOF"; +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// +// NOTE: This file was auto-generated and should never be manually edited, +// as it will get overwritten. +#ifndef HPM_DATA_CHECK_H +#define HPM_DATA_CHECK_H + + +HPExport const struct s_HPMDataCheck HPMDataCheck[] = { +EOF + +foreach my $key (sort keys %out) { + print FH <<"EOF"; + #ifdef $key +EOF + foreach my $entry (@{ $out{$key} }) { + print FH <<"EOF" + { "$entry", sizeof(struct $entry) }, +EOF + } + print FH <<"EOF" + #else + #define $key + #endif // $key +EOF +} +print FH <<"EOF"; +}; +HPExport unsigned int HPMDataCheckLen = ARRAYLENGTH(HPMDataCheck); + +#endif /* HPM_DATA_CHECK_H */ +EOF +close(FH); diff --git a/tools/HPMHookGen/HPMHookGen.pl b/tools/HPMHookGen/HPMHookGen.pl new file mode 100755 index 000000000..3e2a11e5d --- /dev/null +++ b/tools/HPMHookGen/HPMHookGen.pl @@ -0,0 +1,580 @@ +#!/usr/bin/perl + +# Copyright (c) Hercules Dev Team, licensed under GNU GPL. +# See the LICENSE file + +use strict; +use warnings; +use XML::Simple; + +# XML Parser hint (some are faster than others) +#local $ENV{XML_SIMPLE_PREFERRED_PARSER} = ''; # 0m7.138s +local $ENV{XML_SIMPLE_PREFERRED_PARSER} = 'XML::Parser'; # 0m2.674s +#local $ENV{XML_SIMPLE_PREFERRED_PARSER} = 'XML::SAX::Expat'; # 0m7.026s +#local $ENV{XML_SIMPLE_PREFERRED_PARSER} = 'XML::LibXML::SAX'; # 0m4.152s + +sub trim($) { + my $s = $_[0]; + $s =~ s/^\s+//; $s =~ s/\s+$//; + return $s; +} + +sub parse($$) { + my ($p, $d) = @_; + $p =~ s/^.*?\)\((.*)\).*$/$1/; # Clean up extra parentheses )(around the arglist) + + # Retrieve return type + unless ($d =~ /^(.+)\(\*\s*[a-zA-Z0-9_]+_interface::([^\)]+)\s*\)\(.*\)$/) { + print "Error: unable to parse '$d'\n"; + return {}; + } + my $rt = trim($1); #return type + my $name = trim($2); #function name + + my @args; + my ($anonvars, $variadic) = (0, 0); + my ($lastvar, $notes) = ('', ''); + + $p = ' ' unless $p; # ensure there's at least one character (we don't want a do{} block) + while ($p) { # Scan the string for variables + my $current = ''; + my ($paren, $needspace) = (0, 0); + + while ($p) { # Parse tokens + $p =~ s|^(?:\s*(?:/\*.*?\*/)?)*||; # strip heading whitespace and c-style comments + last unless $p; + + if ($p =~ s/^([a-zA-Z0-9_]+)//) { # Word (variable, type) + $current .= ' ' if $needspace; + $current .= $1; + $needspace = 1; + next; + } + if ($p =~ s/^(,)//) { # Comma + last unless $paren; # Argument terminator unless inside parentheses + $current .= $1; + $needspace = 1; + next; + } + if ($p =~ s/^(\*)//) { # Pointer + $current .= ' ' if $needspace; + $current .= $1; + $needspace = 0; + next; + } + if ($p =~ s/^([\[\].])//) { # Array subscript + $current .= $1; + $needspace = 0; + next; + } + if ($p =~ s/^(\()//) { # Open parenthesis + $current .= ' ' if $needspace; + $current .= $1; + $needspace = 0; + $paren++; + next; + } + if ($p =~ s/^(\))//) { # Closed parenthesis + $current .= $1; + $needspace = 1; + if (!$paren) { + $notes .= "\n/* Error: unexpected ')' */"; + print "Error: unexpected ')' at '$p'\n"; + } else { + $paren--; + } + next; + } + $p =~ s/^(.)//; # Any other symbol + $notes .= "\n/* Error: Unexpected character '$1' */"; + print "Error: Unexpected character '$1' at '$p'\n"; + $current .= $1; + $needspace = 0; + } + + $current =~ s/^\s+//; $current =~ s/\s+$//g; # trim + + next if (!$current or $current =~ /^void$/); # Skip if empty + + my ($array, $type1, $var, $type2, $indir) = ('', '', '', '', ''); + if ($current =~ qr/^ + ([\w\s\[\]*]+\() # Capture words, spaces, array subscripts, up to the first '(' (return type) + \s* # Skip spaces + (\*) # Capture the '*' from the function name + \s* # Skip spaces + ([\w]*) # Capture words (function name) + \s* # Skip spaces + (\)\s*\([\w\s\[\]*,]*\)) # Capture first ')' followed by a '( )' block containing arguments + \s* # Skip spaces + $/x + ) { # Match a function pointer + $type1 = trim($1); + $indir = $2; + $var = trim($3 // ''); + $type2 = trim($4); + } elsif ($current eq '...') { # Match a '...' variadic argument + $type1 = '...'; + $indir = ''; + $var = ''; + $type2 = ''; + } else { # Match a "regular" variable + $type1 = ''; + while(1) { + if ($current =~ /^(const)\s+(.*)$/) { # const modifier + $type1 .= "$1 "; + $current = $2 // ''; + next; + } + if ($current =~ /^((?:un)?signed)\s+((?:char|int|long|short)[*\s]+.*)$/ + or $current =~ /^(long|short)\s+((?:int|long)[*\s]+.*)$/ + ) { # signed/unsigned/long/short modifiers + $current = $2; + $type1 .= "$1 "; + next; + } + if ($current =~ /^(struct|enum)\s+(.*)$/) { # enum and struct names + $current = $2 // ''; + $type1 .= "$1 "; + } + last; # No other modifiers + } + if ($current =~ /^\s*(\w+)((?:const|[*\s])*)(\w*)\s*((?:\[\])?)$/) { # Variable type and name + $type1 .= trim($1); + $indir = trim($2 // ''); + $var = trim($3 // ''); + $array = trim($4 // ''); + $type2 = ''; + } else { # Unsupported + $notes .= "\n/* Error: Unhandled var type '$current' */"; + print "Error: Unhandled var type '$current'\n"; + push(@args, { var => $current }); + next; + } + } + unless ($var) { + $anonvars++; + $var = "p$anonvars"; + } + my ($callvar, $pre_code, $post_code, $dereference, $addressof) = ($var, '', '', '', ''); + my $indirectionlvl = () = $indir =~ /\*/; + if ($type1 eq 'va_list') { # Special handling for argument-list variables + $callvar = "${var}___copy"; + $pre_code = "va_list ${callvar}; va_copy(${callvar}, ${var});"; + $post_code = "va_end(${callvar});"; + } elsif ($type1 eq '...') { # Special handling for variadic arguments + unless ($lastvar) { + $notes .= "\n/* Error: Variadic function with no fixed arguments */"; + print "Error: Variadic function with no fixed arguments\n"; + next; + } + $pre_code = "va_list ${callvar}; va_start(${callvar}, ${lastvar});"; + $post_code = "va_end(${callvar});"; + $var = ''; + $variadic = 1; + } elsif (!$indirectionlvl) { # Increase indirection level when necessary + $dereference = '*'; + $addressof = '&'; + } + $indirectionlvl++ if ($array); # Arrays are pointer, no matter how cute you write them + + push(@args, { + var => $var, + callvar => $callvar, + type => $type1.$array.$type2, + orig => $type1 eq '...' ? '...' : trim("$type1 $indir$var$array $type2"), + indir => $indirectionlvl, + hookf => $type1 eq '...' ? "va_list ${var}" : trim("$type1 $dereference$indir$var$array $type2"), + hookc => trim("$addressof$callvar"), + origc => trim($callvar), + pre => $pre_code, + post => $post_code, + }); + $lastvar = $var; + } + + my $rtmemset = 0; + my $rtinit = ''; + foreach ($rt) { # Decide initialization for the return value + my $x = $_; + if ($x =~ /^const\s+(.+)$/) { # Strip const modifier + $x = $1; + } + if ($x =~ /\*/g) { # Pointer + $rtinit = ' = NULL'; + } elsif ($x eq 'void') { # void + $rtinit = ''; + } elsif ($x eq 'bool') { # bool + $rtinit = ' = false'; + } elsif ($x =~ /^(?:enum\s+)?damage_lv$/) { # Known enum damage_lv + $rtinit = ' = ATK_NONE'; + } elsif ($x =~ /^(?:enum\s+)?sc_type$/) { # Known enum sc_type + $rtinit = ' = SC_NONE'; + } elsif ($x =~/^(?:enum\s+)?c_op$/) { # Known enum c_op + $rtinit = ' = C_NOP'; + } elsif ($x =~ /^enum\s+BATTLEGROUNDS_QUEUE_ACK$/) { # Known enum BATTLEGROUNDS_QUEUE_ACK + $rtinit = ' = BGQA_SUCCESS'; + } elsif ($x =~ /^enum\s+bl_type$/) { # Known enum bl_type + $rtinit = ' = BL_NUL'; + } elsif ($x =~ /^enum\s+homun_type$/) { # Known enum homun_type + $rtinit = ' = HT_INVALID'; + } elsif ($x =~ /^enum\s+bg_queue_types$/) { # Known enum bg_queue_types + $rtinit = ' = BGQT_INVALID'; + } elsif ($x =~ /^struct\s+.*$/ or $x eq 'DBData') { # Structs + $rtinit = ''; + $rtmemset = 1; + } elsif ($x =~ /^(?:(?:un)?signed\s+)?(?:char|int|long|short)$/ + or $x =~ /^(?:long|short)\s+(?:int|long)$/ + or $x =~ /^u?int(?:8|16|32|64)$/ + or $x eq 'defType' + ) { # Numeric variables + $rtinit = ' = 0'; + } else { # Anything else + $notes .= "\n/* Unknown return type '$rt'. Initializing to '0'. */"; + print "Unknown return type '$rt'. Initializing to '0'.\n"; + $rtinit = ' = 0'; + } + } + + return { + name => $name, + vname => $variadic ? "v$name" : $name, + type => $rt, + typeinit => $rtinit, + memset => $rtmemset, + variadic => $variadic, + args => \@args, + notes => $notes, + }; +} + +my %key2original; +my @files = grep { -f } glob 'doxyoutput/xml/*interface*.xml'; +my %ifs; +my @keys; +foreach my $file (@files) { # Loop through the xml files + + my $xml = new XML::Simple; + my $data = $xml->XMLin($file); + + my $loc = $data->{compounddef}->{location}; + next unless $loc->{file} =~ /src\/map\//; # We only handle mapserver for the time being + + my $key = $data->{compounddef}->{compoundname}; + my $original = $key; + + # Some known interfaces with different names + if ($key =~ /battleground/) { + $key = "bg"; + } elsif ($key =~ /guild_storage/) { + $key = "gstorage"; + } elsif ($key =~ /homunculus/) { + $key = "homun"; + } elsif ($key =~ /irc_bot/) { + $key = "ircbot"; + } elsif ($key =~ /log_interface/) { + $key = "logs"; + } elsif ($key =~ /pc_groups_interface/) { + $key = "pcg"; + } else { + $key =~ s/_interface//; + } + + foreach my $v ($data->{compounddef}->{sectiondef}) { # Loop through the sections + my $memberdef = $v->{memberdef}; + foreach my $fk (sort { # Sort the members in declaration order according to what the xml says + my $astart = $memberdef->{$a}->{location}->{bodystart} || $memberdef->{$a}->{location}->{line}; + my $bstart = $memberdef->{$b}->{location}->{bodystart} || $memberdef->{$b}->{location}->{line}; + $astart <=> $bstart + } keys %$memberdef) { # Loop through the members + my $f = $memberdef->{$fk}; + + my $t = $f->{argsstring}; + next unless ref $t ne 'HASH' and $t =~ /^[^\[]/; # If it's not a string, or if it starts with an array subscript, we can skip it + + my $if = parse($t, $f->{definition}); + next unless scalar keys %$if; # If it returns an empty hash reference, an error must've occurred + + # Skip variadic functions, we only allow hooks on their arglist equivalents. + # i.e. you can't hook on map->foreachinmap, but you hook on map->vforeachinmap + # (foreachinmap is guaranteed to do nothing other than call vforeachinmap) + next if ($if->{variadic}); + + # Some preprocessing + $if->{hname} = "HP_${key}_$if->{name}"; + $if->{hvname} = "HP_${key}_$if->{vname}"; + + $if->{handlerdef} = "$if->{type} $if->{hname}("; + $if->{predef} = "$if->{type} (*preHookFunc) ("; + $if->{postdef} = "$if->{type} (*postHookFunc) ("; + if ($if->{type} eq 'void') { + $if->{precall} = ''; + $if->{postcall} = ''; + $if->{origcall} = ''; + } else { + $if->{precall} = "retVal___ = "; + $if->{postcall} = "retVal___ = "; + $if->{origcall} = "retVal___ = "; + } + $if->{precall} .= "preHookFunc("; + $if->{postcall} .= "postHookFunc("; + $if->{origcall} .= "HPMHooks.source.$key.$if->{vname}("; + $if->{before} = []; $if->{after} = []; + + my ($i, $j) = (0, 0); + + if ($if->{type} ne 'void') { + $j++; + $if->{postdef} .= "$if->{type} retVal___"; + $if->{postcall} .= "retVal___"; + } + + foreach my $arg (@{ $if->{args} }) { + push(@{ $if->{before} }, $arg->{pre}) if ($arg->{pre}); + push(@{ $if->{after} }, $arg->{post}) if ($arg->{post}); + if ($i) { + $if->{handlerdef} .= ', '; + $if->{predef} .= ', '; + $if->{precall} .= ', '; + $if->{origcall} .= ', '; + } + if ($j) { + $if->{postdef} .= ', '; + $if->{postcall} .= ', '; + } + $if->{handlerdef} .= $arg->{orig}; + $if->{predef} .= $arg->{hookf}; + $if->{precall} .= $arg->{hookc}; + $if->{postdef} .= $arg->{hookf}; + $if->{postcall} .= $arg->{hookc}; + $if->{origcall} .= $arg->{origc}; + $i++; $j++; + } + + if (!$i) { + $if->{predef} .= 'void'; + $if->{handlerdef} .= 'void'; + } + if (!$j) { + $if->{postdef} .= 'void'; + } + + $if->{handlerdef} .= ')'; + $if->{predef} .= ");"; + $if->{precall} .= ");"; + $if->{postdef} .= ");"; + $if->{postcall} .= ");"; + $if->{origcall} .= ");"; + + $key2original{$key} = $original; + $ifs{$key} = [] unless $ifs{$key}; + push(@{ $ifs{$key} }, $if); + } + } + push(@keys, $key) if $key2original{$key}; +} + +# Some interfaces use different names +my %exportsymbols = map { + $_ => &{ sub ($) { + return 'battlegrounds' if $_ =~ /^bg$/; + return 'pc_groups' if $_ =~ /^pcg$/; + return $_; + }}($_); +} @keys; + +my ($maxlen, $idx) = (0, 0); +my $fname; +$fname = "../../src/plugins/HPMHooking/HPMHooking.HookingPoints.inc"; +open(FH, ">", $fname) + or die "cannot open > $fname: $!"; + +print FH <<"EOF"; +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// +// NOTE: This file was auto-generated and should never be manually edited, +// as it will get overwritten. + +struct HookingPointData HookingPoints[] = { +EOF + +foreach my $key (@keys) { + print FH "/* ".$key." */\n"; + foreach my $if (@{ $ifs{$key} }) { + + print FH <<"EOF"; + { HP_POP($key\->$if->{name}, $if->{hname}) }, +EOF + + $idx += 2; + $maxlen = length($key."->".$if->{name}) if( length($key."->".$if->{name}) > $maxlen ) + } +} +print FH <<"EOF"; +}; + +int HookingPointsLenMax = $maxlen; +EOF +close FH; + +$fname = "../../src/plugins/HPMHooking/HPMHooking.sources.inc"; +open(FH, ">", $fname) + or die "cannot open > $fname: $!"; + +print FH <<"EOF"; +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// +// NOTE: This file was auto-generated and should never be manually edited, +// as it will get overwritten. + +EOF +foreach my $key (@keys) { + + print FH <<"EOF"; +memcpy(&HPMHooks.source.$key, $key, sizeof(struct $key2original{$key})); +EOF +} +close FH; + +$fname = "../../src/plugins/HPMHooking/HPMHooking.GetSymbol.inc"; +open(FH, ">", $fname) + or die "cannot open > $fname: $!"; + +print FH <<"EOF"; +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// +// NOTE: This file was auto-generated and should never be manually edited, +// as it will get overwritten. + +EOF +foreach my $key (@keys) { + + print FH <<"EOF"; +if( !($key = GET_SYMBOL("$exportsymbols{$key}") ) ) return false; +EOF +} +close FH; + +$fname = "../../src/plugins/HPMHooking/HPMHooking.HPMHooksCore.inc"; +open(FH, ">", $fname) + or die "cannot open > $fname: $!"; + +print FH <<"EOF"; +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// +// NOTE: This file was auto-generated and should never be manually edited, +// as it will get overwritten. + +struct { +EOF + +foreach my $key (@keys) { + foreach my $if (@{ $ifs{$key} }) { + + print FH <<"EOF"; + struct HPMHookPoint *$if->{hname}_pre; + struct HPMHookPoint *$if->{hname}_post; +EOF + } +} +print FH <<"EOF"; +} list; + +struct { +EOF + +foreach my $key (@keys) { + foreach my $if (@{ $ifs{$key} }) { + + print FH <<"EOF"; + int $if->{hname}_pre; + int $if->{hname}_post; +EOF + } +} +print FH <<"EOF"; +} count; + +struct { +EOF + +foreach my $key (@keys) { + + print FH <<"EOF"; + struct $key2original{$key} $key; +EOF +} + +print FH <<"EOF"; +} source; +EOF +close FH; + +$fname = "../../src/plugins/HPMHooking/HPMHooking.Hooks.inc"; +open(FH, ">", $fname) + or die "cannot open > $fname: $!"; + +print FH <<"EOF"; +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// +// NOTE: This file was auto-generated and should never be manually edited, +// as it will get overwritten. + +EOF +foreach my $key (@keys) { + + print FH <<"EOF"; +/* $key */ +EOF + + foreach my $if (@{ $ifs{$key} }) { + my ($initialization, $beforeblock3, $beforeblock2, $afterblock3, $afterblock2, $retval) = ('', '', '', '', '', ''); + + unless ($if->{type} eq 'void') { + $initialization = "\n\t$if->{type} retVal___$if->{typeinit};"; + $initialization .= "\n\tmemset(&retVal___, '\\0', sizeof($if->{type}));" if $if->{memset}; + } + + $beforeblock3 .= "\n\t\t\t$_" foreach (@{ $if->{before} }); + $afterblock3 .= "\n\t\t\t$_" foreach (@{ $if->{after} }); + $beforeblock2 .= "\n\t\t$_" foreach (@{ $if->{before} }); + $afterblock2 .= "\n\t\t$_" foreach (@{ $if->{after} }); + $retval = ' retVal___' unless $if->{type} eq 'void'; + + print FH <<"EOF"; +$if->{handlerdef} {$if->{notes} + int hIndex = 0;${initialization} + if( HPMHooks.count.$if->{hname}_pre ) { + $if->{predef} + *HPMforce_return = false; + for(hIndex = 0; hIndex < HPMHooks.count.$if->{hname}_pre; hIndex++ ) {$beforeblock3 + preHookFunc = HPMHooks.list.$if->{hname}_pre[hIndex].func; + $if->{precall}$afterblock3 + } + if( *HPMforce_return ) { + *HPMforce_return = false; + return$retval; + } + } + {$beforeblock2 + $if->{origcall}$afterblock2 + } + if( HPMHooks.count.$if->{hname}_post ) { + $if->{postdef} + for(hIndex = 0; hIndex < HPMHooks.count.$if->{hname}_post; hIndex++ ) {$beforeblock3 + postHookFunc = HPMHooks.list.$if->{hname}_post[hIndex].func; + $if->{postcall}$afterblock3 + } + } + return$retval; +} +EOF + } +} + +close FH; + diff --git a/tools/HPMHookGen/Makefile.in b/tools/HPMHookGen/Makefile.in new file mode 100644 index 000000000..fefe0eef4 --- /dev/null +++ b/tools/HPMHookGen/Makefile.in @@ -0,0 +1,80 @@ +# Copyright (c) Hercules Dev Team, licensed under GNU GPL. +# See the LICENSE file + +# @configure_input@ + +@SET_MAKE@ + +COMMON_D = ../../src/common +MAP_D = ../../src/map +CHAR_D = ../../src/char +LOGIN_D = ../../src/login +PLUGIN_D = ../../src/plugins +COMMON_C = $(wildcard $(COMMON_D)/*.c) +COMMON_H = $(filter-out $(COMMON_D)/HPMDataCheck.%,$(wildcard $(COMMON_D)/*.h)) +MAP_C = $(wildcard $(MAP_D)/*.c) +MAP_H = $(wildcard $(MAP_D)/*.h) +CHAR_C = $(wildcard $(CHAR_D)/*.c) +CHAR_H = $(wildcard $(CHAR_D)/*.h) +LOGIN_C = $(wildcard $(LOGIN_D)/*.c) +LOGIN_H = $(wildcard $(LOGIN_D)/*.h) +ALL_C = $(COMMON_C) $(MAP_C) $(CHAR_C) $(LOGIN_C) +ALL_H = $(COMMON_H) $(MAP_H) $(CHAR_H) $(LOGIN_H) + +HOOK_INC = $(addprefix $(PLUGIN_D)/HPMHooking., \ + $(addsuffix .inc, HookingPoints sources GetSymbol HPMHooksCore Hooks)) \ + $(COMMON_D)/HPMDataCheck.h + +HAVE_DOXYGEN=@HAVE_DOXYGEN@ +HAVE_PERL=@HAVE_PERL@ +ifeq ($(HAVE_DOXYGEN)$(HAVE_PERL),yesyes) + ALL_DEPENDS = hooks +else + ifeq ($(HAVE_PERL),no) + ALL_DEPENDS = needs_perl + else + ifeq ($(HAVE_DOXYGEN),no) + ALL_DEPENDS = needs_doxygen + endif + endif +endif + +##################################################################### +.PHONY: hooks clean buildclean generate + +all: $(ALL_DEPENDS) + +buildclean: + +clean: buildclean + @echo " CLEAN HPMHookGen" + @rm -rf doxyoutput + +Makefile: Makefile.in + @$(MAKE) -C ../.. tools/HPMHookGen/Makefile + +hooks: $(HOOK_INC) + +##################################################################### + +$(HOOK_INC): generate + +generate: doxyoutput + @echo " Regenerating HPM Hook definitions..." + @perl HPMHookGen.pl + @echo " Regenerating HPM Data Check definitions..." + @perl HPMDataCheckGen.pl + +doxyoutput: $(ALL_C) $(ALL_H) doxygen.conf + @echo " Extracting functions information..." + @doxygen doxygen.conf + +##################################################################### + +needs_doxygen: + @echo "doxygen not found or disabled by the configure script" + @exit 1 + +needs_perl: + @echo "perl not found or disabled by the configure script" + @exit 1 diff --git a/tools/HPMHookGen/doxygen.conf b/tools/HPMHookGen/doxygen.conf new file mode 100644 index 000000000..b6dc7444b --- /dev/null +++ b/tools/HPMHookGen/doxygen.conf @@ -0,0 +1,216 @@ +# Doxyfile 1.8.4 + +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "Hercules HPMHookGen" +PROJECT_NUMBER = +OUTPUT_DIRECTORY = doxyoutput +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = NO +REPEAT_BRIEF = NO +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = NO +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = h=C +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = NO +TYPEDEF_HIDES_STRUCT = NO +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = NO +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = YES +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = NO +GENERATE_TESTLIST = NO +GENERATE_BUGLIST = NO +GENERATE_DEPRECATEDLIST= NO +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 0 +SHOW_USED_FILES = NO +SHOW_FILES = NO +SHOW_NAMESPACES = NO +FILE_VERSION_FILTER = +LAYOUT_FILE = +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = NO +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +INPUT = ../../src/map ../../src/common ../../src/char ../../src/login +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c *.h +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = NO +USE_HTAGS = NO +VERBATIM_HEADERS = NO +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +GENERATE_HTML = NO +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +GENERATE_XML = YES +XML_OUTPUT = xml +XML_PROGRAMLISTING = NO +GENERATE_AUTOGEN_DEF = NO +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = NO +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = NO +PERL_PATH = /usr/bin/perl +CLASS_DIAGRAMS = NO +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = NO +COLLABORATION_GRAPH = NO +GROUP_GRAPHS = NO +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = NO +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = NO +DOT_CLEANUP = YES diff --git a/tools/Script-Checker.applescript b/tools/Script-Checker.applescript new file mode 100644 index 000000000..db1983f4b --- /dev/null +++ b/tools/Script-Checker.applescript @@ -0,0 +1,151 @@ +(* + Copyright (c) Hercules Dev Team, licensed under GNU GPL. + See the LICENSE file + Base Author: Haru @ http://hercules.ws +*) + +(* + ************************************************************* + ************************************************************* + ******** ************** + ******** NOTE: This script must be saved as app!!! ************** + ******** ************** + ************************************************************* + ************************************************************* +*) + +property allowed_extensions : {"txt", "c", "ath", "herc"} +property allowed_types : {"TEXT"} + +on run + try + set the resource_path to path to resource "Scripts" + tell application "Finder" to set the resource_path to the container of the resource_path + on error + display alert "Error" message "You need to save this script as an app bundle." buttons {"Cancel"} cancel button 1 as warning + end try + set the dialog_result to display dialog "You can drag files to this app to have them checked." with title "Hercules Script Checker" buttons {"Cancel", "(Re)build Hercules", "Check Script"} default button 3 cancel button 1 + if the button returned of the dialog_result is "(Re)build Hercules" then + rebuild() + else + set these_items to choose file of type allowed_extensions with prompt "Choose the script(s) you want to check:" with multiple selections allowed + process_items(these_items) + end if +end run + +on open these_items + process_items(these_items) +end open + +on build_hercules(hercules_repo) + try + set the resource_path to path to resource "Scripts" + tell application "Finder" to set the resource_path to the container of the resource_path + on error + display alert "Error" message "You need to save this script as an app bundle." buttons {"Cancel"} cancel button 1 as warning + end try + set the configuration_flags to display dialog "Do you want to use any configuration flags? (i.e. --disable-renewal)" with title "Configuration" default answer "" buttons {"Cancel", "Ok"} default button 2 cancel button 1 + try + set the command_output to do shell script "cd " & (the quoted form of the POSIX path of the hercules_repo) & " && echo './configure " & the text returned of the configuration_flags & " 2>&1' | bash -i" + on error + display dialog "Configuration failed" with title "Configuration result" buttons {"Abort"} cancel button 1 + end try + tell application "TextEdit" + activate + set the new_document to make new document + set the text of new_document to the command_output + end tell + display dialog "Configuration successfully completed. Please check the log file for details." with title "Configuration result" buttons {"Abort", "Continue"} default button 2 cancel button 1 + try + set the command_output to do shell script "cd " & (the quoted form of the POSIX path of the hercules_repo) & " && echo 'make clean 2>&1 && make sql -j8 2>&1' | bash -i" + on error + display dialog "Build failed." with title "Build result" buttons {"Abort"} cancel button 1 + end try + tell application "TextEdit" + activate + set the new_document to make new document + set the text of new_document to the command_output + end tell + display dialog "Build successfully completed. Please check the log file for details." with title "Build result" buttons {"Abort", "Continue"} default button 2 cancel button 1 + set the files_to_copy to {"map-server", "script-checker"} + set the conf_files_to_copy to {"inter-server.conf", "import", "packet.conf", "script.conf"} + set the db_files_to_copy to {"map_index.txt", "item_db2.txt", "const.txt", "mob_db2.txt"} + set the db2_files_to_copy to {"map_cache.dat", "item_db.txt", "skill_db.txt", "mob_db.txt"} + try + set the hercules_path to path to resource "Hercules" + do shell script "rm -r " & the quoted form of ((the POSIX path of hercules_path) & "/") + end try + set the hercules_path to the resource_path + tell application "Finder" to make new folder at hercules_path with properties {name:"Hercules"} + delay 3 + set the hercules_path to path to resource "Hercules" + repeat with each_file in files_to_copy + copy_file(each_file, hercules_repo, hercules_path, ".") + end repeat + do shell script "mkdir " & the quoted form of ((POSIX path of the hercules_path) & "/conf") + repeat with each_file in conf_files_to_copy + copy_file(each_file, hercules_repo, hercules_path, "conf") + end repeat + do shell script "mkdir " & the quoted form of ((POSIX path of the hercules_path) & "/db") + repeat with each_file in db_files_to_copy + copy_file(each_file, hercules_repo, hercules_path, "db") + end repeat + do shell script "mkdir " & the quoted form of ((POSIX path of the hercules_path) & "/db/pre-re") + repeat with each_file in db2_files_to_copy + copy_file(each_file, hercules_repo, hercules_path, "db/pre-re") + end repeat + do shell script "mkdir " & the quoted form of ((POSIX path of the hercules_path) & "/db/re") + repeat with each_file in db2_files_to_copy + copy_file(each_file, hercules_repo, hercules_path, "db/re") + end repeat + display dialog "Build complete" with title "Done" buttons {"Ok"} default button "Ok" +end build_hercules + +on rebuild() + set the repo_path to choose folder with prompt "Choose the folder where your Hercules repository is located:" + build_hercules(repo_path) +end rebuild + +on copy_file(filename, source, destination, subpath) + do shell script "cp -rp " & the quoted form of ((the POSIX path of source) & "/" & subpath & "/" & filename) & " " & the quoted form of ((the POSIX path of destination) & "/" & subpath & "/") +end copy_file + +on process_items(these_items) + repeat + try + set the scriptchecker to the path to resource "script-checker" in directory "Hercules" + set the mapserver to the path to resource "map-server" in directory "Hercules" + on error + display alert "Missing Hercules binaries" message "You need to build Hercules and place it within this app bundle before running the script checker." & linefeed & linefeed & "I can try to build it for you, but only if you have the Xcode command line tools installed." & linefeed & "Do you want to continue?" buttons {"Cancel", "Build"} default button "Build" cancel button "Cancel" as warning + rebuild() + return false + end try + exit repeat + end repeat + repeat with i from 1 to the count of these_items + set this_item to item i of these_items + set the item_info to info for this_item + set this_name to the name of the item_info + try + set this_extension to the name extension of item_info + on error + set this_extension to "" + end try + try + set this_filetype to the file type of item_info + on error + set this_filetype to "" + end try + if (folder of the item_info is false) and (alias of the item_info is false) and ((this_filetype is in the allowed_types) or (this_extension is in the allowed_extensions)) then + process_item(scriptchecker, this_item) + end if + end repeat +end process_items + +on process_item(checkerpath, this_item) + set the_result to do shell script (the quoted form of the POSIX path of checkerpath & " " & the quoted form of the POSIX path of this_item) & " 2>&1" + if the_result is "" then + set the_result to "Check passed." + end if + display dialog ("File: " & POSIX path of this_item) & linefeed & "Result: " & linefeed & the_result with title "Output" buttons {"Abort", "Next"} default button "Next" cancel button "Abort" +end process_item diff --git a/tools/check-doc b/tools/check-doc index 4a5dccc88..4b283fcea 100755 --- a/tools/check-doc +++ b/tools/check-doc @@ -3,26 +3,26 @@ # modified by lighta case $1 in - 'script') + 'script') #find which script commands are missing from doc/script_commands.txt echo "Missing script documentation for function :" awk '/BUILDIN_DEF\(.*\),/ {b=match($0,"BUILDIN_DEF(.*),");c=match($0,",");print substr($0,b+12,c-b-12);}' ../src/map/script.c | xargs -I{} sh -c '! grep -Lq {} ../doc/script_commands.txt && echo {}' awk '/BUILDIN_DEF2\(.*\),/ {b=match($0,"BUILDIN_DEF2(.*),");c=match($0,",");d=match($0 ,"\",\"");print substr($0,c+2,d-c-2);}' ../src/map/script.c | xargs -I{} sh -c '! grep -Lq {} ../doc/script_commands.txt && echo {}' ;; - 'atc') + 'atc') #find which atcommands are missing from doc/atcommands.txt echo "Missing atcommand documentation for function :" awk '/ACMD_DEF\(.*\),/ {b=match($0,"ACMD_DEF(.*),");c=match($0,",");print substr($0,b+9,c-b-10);}' ../src/map/atcommand.c | xargs -I{} sh -c '! grep -Lq {} ../doc/atcommands.txt && echo {}' awk '/ACMD_DEF2\(.*\),/ {b=match($0,"ACMD_DEF2(.*),");c=match($0,",");print substr($0,b+10,c-b-10);}' ../src/map/atcommand.c | xargs -I{} sh -c '! grep -Lq {} ../doc/atcommands.txt && echo {}' ;; - 'both') + 'both') $0 script $0 atc ;; *) - echo "Usage: check-doc { script | atc | both }" + echo "Usage: check-doc { script | atc | both }" ;; esac diff --git a/tools/item_db.pl b/tools/item_db.pl deleted file mode 100755 index a2b443de9..000000000 --- a/tools/item_db.pl +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/perl -$db = "item_db"; -$nb_columns = 22; -@str_col = (1,2,7,19,20,21); -$line_format = "([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),(\{.*\}),(\{.*\}),(\{.*\})"; -#$line_format = ; -$create_table = "# -# Table structure for table `item_db` -# - -DROP TABLE IF EXISTS `item_db`; -CREATE TABLE `item_db` ( - `id` smallint(5) unsigned NOT NULL default '0', - `name_english` varchar(50) NOT NULL default '', - `name_japanese` varchar(50) NOT NULL default '', - `type` tinyint(2) unsigned NOT NULL default '0', - `price_buy` mediumint(10) unsigned default NULL, - `price_sell` mediumint(10) unsigned default NULL, - `weight` smallint(5) unsigned NOT NULL default '0', - `attack` smallint(3) unsigned default NULL, - `defence` tinyint(3) unsigned default NULL, - `range` tinyint(2) unsigned default NULL, - `slots` tinyint(2) unsigned default NULL, - `equip_jobs` int(12) unsigned default NULL, - `equip_upper` tinyint(8) unsigned default NULL, - `equip_genders` tinyint(2) unsigned default NULL, - `equip_locations` smallint(4) unsigned default NULL, - `weapon_level` tinyint(2) unsigned default NULL, - `equip_level` tinyint(3) unsigned default NULL, - `refineable` tinyint(1) unsigned default NULL, - `view` smallint(3) unsigned default NULL, - `script` text, - `equip_script` text, - `unequip_script` text, - PRIMARY KEY (`id`) -) ENGINE=MyISAM; -"; -printf("%s\n",$create_table); -while ($ligne=<STDIN>) -{ - if ($ligne =~ /[^\r\n]+/) - { - $ligne = $&; - if ($ligne =~ /^\/\//) - { - printf("# "); - $ligne = substr($ligne, 2); - } - if ($ligne =~ $line_format) { - @champ = ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22); - } else { - @champ = (); - } - if ($#champ != $nb_columns - 1) - { - # Can't parse, it's a real comment - printf ("%s\n", $ligne); - } else { - printf("REPLACE INTO `%s` VALUES (", $db); - for ($i=0; $i<$#champ; $i++) - { - printField($champ[$i],",",$i); - } - printField($champ[$#champ],");\n",$#champ); - } - } -} -print("\n"); - - -sub printField { - my ($str, $suffix, $idCol) = @_; - # Remove first { and last } - if ($str =~ /{.*}/) - { - $str = substr($&,1,-1); - } - # If nothing, put NULL - if ($str eq "") { - printf("NULL%s", $suffix); - } else { - my $flag = 0; - # Search if it's a string column ? - foreach $col (@str_col) - { - if ($col == $idCol) - { - $flag = 1; - break; - } - } - if ($flag == 1) - { - # String column, so escape and add '' - printf("'%s'%s", escape($str), $suffix); - } else { - # Not a string column - printf("%s%s", $str,$suffix); - } - } -} - -sub escape { - my ($str) = @_; - my @str_splitted = split("'", $str); - my $result = ""; - for (my $i=0; $i<=$#str_splitted; $i++) - { - if ($i == 0) { - $result = @str_splitted[0]; - } else { - $result = $result."\\'".@str_splitted[$i]; - } - } - return $result -} diff --git a/tools/item_merge.lua b/tools/item_merge.lua new file mode 100644 index 000000000..f6f3a4a75 --- /dev/null +++ b/tools/item_merge.lua @@ -0,0 +1,619 @@ +-- Copyright (c) Hercules Dev Team, licensed under GNU GPL. +-- See the LICENSE file +-- Base Author: Dastgir @ http://hercules.ws +-- +-- This script requires lua 5.1 to run. + +getmetatable('').__call = string.sub +require('math') + +--Bit Operators +local tab = { -- tab[i][j] = xor(i-1, j-1) + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }, + {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, }, + {2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13, }, + {3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, }, + {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11, }, + {5, 4, 7, 6, 1, 0, 3, 2, 13, 12, 15, 14, 9, 8, 11, 10, }, + {6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9, }, + {7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, }, + {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, }, + {9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6, }, + {10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 5, }, + {11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4, }, + {12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3, }, + {13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2, }, + {14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, }, + {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, }, +} +function bxor (a,b) + local res, c = 0, 1 + while a > 0 and b > 0 do + local a2, b2 = math.fmod(a, 16), math.fmod(b, 16) + res = res + tab[a2+1][b2+1]*c + a = (a-a2)/16 + b = (b-b2)/16 + c = c*16 + end + res = res + a*c + b*c + return res +end +local ff = 2^32 - 1 +function bnot (a) return (ff - a) end +function band (a,b) return ((a+b) - bxor(a,b))/2 end +function bor (a,b) return (ff - band(ff - a, ff - b)) end + +function default_value(input,default) + if (input==nil) then + return default + else + return input + end +end + +print("-----------------------------------------------------------------") +print("----------------- item_*.txt to ItemDB.conf --------------------") +print("------------------- By Dastgir[Hercules] ------------------------") +print("-----------------------------------------------------------------") +if (arg[1]==nil) then --If any argument is missing, error + print("Invalid Command:") + print('Usage: item_merge.lua' + ..' {item_db.conf}' + ..' {item_trade.txt}' + ..' {item_buyingstore.txt}' + ..' {item_delay.txt}' + ..' {item_nouse.txt}' + ..' {item_stack.txt}' + ..' {item_avail.txt}' + ..' {output.conf}' + --..' {sql_update_file.sql}' + ) + print("","If you have default names of *.txt files, then just run this tool") + print("","","","\"item_merge.lua item_db.conf\"") + os.exit() +end +arg[2] = default_value(arg[2],"item_trade.txt") +arg[3] = default_value(arg[3],"item_buyingstore.txt") +arg[4] = default_value(arg[4],"item_delay.txt") +arg[5] = default_value(arg[5],"item_nouse.txt") +arg[6] = default_value(arg[6],"item_stack.txt") +arg[7] = default_value(arg[7],"item_avail.txt") +arg[8] = default_value(arg[8],string.sub(arg[1],1,-6).."_converted.conf") + +function replace_char(pos, str, r) + return str:sub(1, pos-1) .. r .. str:sub(pos+1) +end +function ltrim(trb) + return (trb:gsub("^%s*", "")) +end + +function file_exists(name) + local f=io.open(name,"r") + if f~=nil then io.close(f) return true else return false end +end + +function d_t_s_quotes(s) --Double Quotes to Single Quotes and Comment Replacer + s = s:gsub('\\\\',"\\") + --Replace Lua Comments to C Style + s = s:gsub('%-%-%[%[', '/%*') + s = s:gsub("%-%-%]%]", '%*/') + local newline = ParseCSVLine(s,"\n") + if (#newline==0) then + newline[1] = s + end + for i=1,#newline do + if(string.sub(ltrim(newline[i]),1,2)=="--") then + newline[i] = newline[i]:gsub("%-%-", '//', 1) + end + end + s = MergeCSVLine(newline) + return s +end + +function st_fill_zero(s) --Fill remaining zero in Job Field of item_db + zeros = "" + if (s:len()>=8) then + return s + else + for i=1,(8-s:len()) do + zeros = zeros.."0" + end + zeros = zeros..""..s + end + --print(s:len(),zeros) + return zeros +end + +function newline(s,a) --Add Newline to Scripts Field + if ((#(ParseCSVLine(s,"\n"))) > 1) then + if(a==1) then + return "\n" + else + return "\n\t" + end + else + return "" + end +end + +function MergeCSVLine(linetbl) + local merged_tbl = "" + for j=1,#linetbl do + if (j==#linetbl) then + merged_tbl = merged_tbl..linetbl[j] + else + merged_tbl = merged_tbl..linetbl[j].."\n" + end + end + return merged_tbl +end + +function ParseCSVLine (line,sep) --Parse CSV for itemdb_*.txt fields + local res = {} + local pos = 1 + sep = sep or ',' + while true do + local c = string.sub(line,pos,pos) + if (c == "") then break end + if (c == '"') then + local txt = "" + repeat + local startp,endp = string.find(line,'^%b""',pos) + txt = txt..string.sub(line,startp+1,endp-1) + pos = endp + 1 + c = string.sub(line,pos,pos) + if (c == '"') then txt = txt..'"' end + until (c ~= '"') + table.insert(res,txt) + assert(c == sep or c == "") + pos = pos + 1 + else + local startp,endp = string.find(line,sep,pos) + if (startp) then + table.insert(res,string.sub(line,pos,startp-1)) + pos = endp + 1 + else + table.insert(res,string.sub(line,pos)) + break + end + end + end + return res +end + +function deepcopy(orig) --Copy one table to other + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key)] = deepcopy(orig_value) + end + setmetatable(copy, deepcopy(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy +end + +function parse_line(line2,typl) --1=trade + line_number = line_number+1 + csv = ParseCSVLine(line2,",") + csv1 = ParseCSVLine(csv[1],"\t") + csv2 = ParseCSVLine(csv[1]," ") + csv4 = tonumber(csv1[1]) + if (csv4==nil) then + csv4 = csv2[1] + if (csv4 == nil) then + csv4 = csv[1] + end + end + k=tonumber(csv4) + if (typl==3) then + if (item_db2[k] ~= nil) then + item_db2[k].BuyingStore = true + --sql_update = sql_update.."UPDATE `item_db` SET `buyingstore` = 1 WHERE `id`="..csv4..";\n" + end + return + end + if (#csv<1) then + return + end + k=tonumber(csv4) + if (item_db2[k]~=nil) then + if(typl>1 and typl~=3 and typl~=4 and typl~=7) then + m=3 + else + m=2 + end + csv1 = ParseCSVLine(csv[m],"\t") + csv2 = ParseCSVLine(csv[m]," ") + csv3 = tonumber(csv1[1]) + if (csv3==nil) then + csv3 = csv2[1] + if (csv3 == nil) then + csv3 = csv[m] + end + end + if (csv3==nil) then + print("Invalid format on "..arg[m]..", line:"..line_number..", Skipping.") + return + end + if (typl==2 and #csv==3) then + item_db2[k].trade_flag = tonumber(csv[2]) + item_db2[k].trade_override = tonumber(csv3) + --sql_update = sql_update.."UPDATE `item_db` SET `trade_flag` = "..tonumber(csv[2]).." AND `trade_group` = "..tonumber(csv3).." WHERE `id`="..k..";\n" + --No 3, since its parsed differently. + elseif (typl==4 and #csv>=2) then + if (tonumber(csv3)~=nil) then + item_db2[k].Delay = tonumber(csv3) + --sql_update = sql_update.."UPDATE `item_db` SET `delay` = "..tonumber(csv3).." WHERE `id`="..k..";\n" + end + elseif (typl==5 and #csv==3) then + item_db2[k].nouse_flag = tonumber(csv[2]) + item_db2[k].nouse_override = tonumber(csv3) + --sql_update = sql_update.."UPDATE `item_db` SET `nouse_flag` = "..tonumber(csv[2]).." and `nouse_group` = "..tonumber(csv3).." WHERE `id`="..k..";\n" + elseif (typl==6 and #csv==3) then + item_db2[k].Stack1 = tonumber(csv[2]) + item_db2[k].Stack2 = tonumber(csv3) + --sql_update = sql_update.."UPDATE `item_db` SET `stack_amount` = "..tonumber(csv[2]).." and `stack_flag` = "..tonumber(csv3).." WHERE `id`="..k..";\n" + elseif (typl==7 and #csv==2) then + item_db2[k].Sprite = tonumber(csv3) + --sql_update = sql_update.."UPDATE `item_db` SET `avail` = "..tonumber(csv3).." WHERE `id`="..k..";\n" + else + print("Invalid format on "..arg[typl]..", line:"..line_number..", Skipping.") + --[[ + --Debug Lines + print("Invalid CSV",#csv,typl) + for mo=1,#csv do + print("csv["..mo.."]",csv[mo]) + end + os.exit() + --]] + end + end + + return +end + +function check_val(field) --Check if field null + if (field == nil) then + return("") + end + return(field) +end + +function check_val_bool(field) --Checks boolean value + if (field == false or field == "false") then + return "false" + elseif (field == true or field == "true") then + return "true" + end + return "" +end + +function check_val_two(field) --Check [a, b] types of field + local field1,field2 + field1 = "" + field2 = "" + if (field~=nil) then + if (#field==2) then + if (field[1][1] ~= nil and field[2] ~= nil) then + field1 = field[1][1] + field2 = field[2] + elseif (field[1][1] ~= nil and field[2] == nil) then + field1 = field[1][1] + else + field2 = field[2] + end + elseif(#field==1) then + field1 = field[1] + end + end + return field1, field2 +end + + +function item_db_write(s) --Write into item_db + for i=1,65535 do ----Last ID till now + if (s[i]~=nil) then + s[i].Atk = check_val(s[i].Atk) + s[i].Matk = check_val(s[i].Matk) + s[i].EquipLv = check_val(s[i].EquipLv) + + if ((s[i].Atk == "" or s[i].Atk == 0) and (s[i].Matk == "" or s[i].Matk == 0)) then --Both Nil + s[i].at = "" + elseif (s[i].Atk ~= "" and (s[i].Matk == "" or s[i].Matk == 0)) then + s[i].at = s[i].Atk + + elseif ((s[i].Atk == "" or s[i].Atk == 0) and s[i].Matk ~= "") then + s[i].at = "0:"..s[i].Matk + else + s[i].at = s[i].Atk..":"..s[i].Matk + end + + s[i].Refine = check_val_bool(s[i].Refine) + s[i].BindOnEquip = check_val_bool(s[i].BindOnEquip) + s[i].BuyingStore = check_val_bool(s[i].BuyingStore) + s[i].Script = check_val(s[i].Script) + s[i].OnEquipScript = check_val(s[i].OnEquipScript) + s[i].OnUnequipScript = check_val(s[i].OnUnequipScript) + s[i].ELv1, s[i].ELv2 = check_val_two(s[i].EquipLv) + s[i].EquipMin = s[i].EquipLv[1] --Equip Min + s[i].EquipMax = s[i].EquipLv[2] --Equip Max + s[i].Script = d_t_s_quotes(s[i].Script) + s[i].OnEquipScript = d_t_s_quotes(s[i].OnEquipScript) + s[i].OnUnequipScript = d_t_s_quotes(s[i].OnUnequipScript) + file3:write("{\n") + --four Must fields to write. + file3:write("\tId: "..(s[i].Id).."\n") + file3:write("\tAegisName: \""..(s[i].AegisName).."\"\n") + file3:write("\tName: \""..(s[i].Name).."\"\n") + file3:write("\tType: "..(s[i].Type).."\n") + if (check_val(s[i].Buy)~="") then + file3:write("\tBuy: "..(s[i].Buy).."\n") + end + if (check_val(s[i].Sell)~="") then + file3:write("\tSell: "..(s[i].Sell).."\n") + end + if (check_val(s[i].Weight)~="") then + file3:write("\tWeight: "..(s[i].Weight).."\n") + end + if (s[i].Atk~="") then + file3:write("\tAtk: "..(s[i].Atk).."\n") + end + if (s[i].Matk~="") then + file3:write("\tMatk: "..(s[i].Matk).."\n") + end + if (check_val(s[i].Def)~="") then + file3:write("\tDef: "..(s[i].Def).."\n") + end + if (check_val(s[i].Range)~="") then + file3:write("\tRange: "..(s[i].Range).."\n") + end + if (check_val(s[i].Slots)~="") then + file3:write("\tSlots: "..(s[i].Slots).."\n") + end + if (check_val(s[i].Job)~="") then + file3:write("\tJob: 0x"..st_fill_zero(string.upper(string.format("%x",s[i].Job))).."\n") --Convert to Hex,Make it to UpperCase, Fill with zero's + end + if (check_val(s[i].Upper)~="") then + file3:write("\tUpper: "..(s[i].Upper).."\n") + end + if (check_val(s[i].Gender)~="") then + file3:write("\tGender: "..(s[i].Gender).."\n") + end + if (check_val(s[i].Loc)~="") then + file3:write("\tLoc: "..(s[i].Loc).."\n") + end + if (check_val(s[i].WeaponLv)~="") then + file3:write("\tWeaponLv: "..(s[i].WeaponLv).."\n") + end + if (s[i].ELv1~="" and s[i].ELv2~="" ) then --Both Not Empty + file3:write("\tEquipLv: ["..(s[i].ELv1)..", "..(s[i].ELv2).."]\n") + else + if(s[i].ELv1~="") then --First not empty + file3:write("\tEquipLv: "..(s[i].ELv1).."\n") + end + end + if (check_val(s[i].Refine)~="") then + file3:write("\tRefine: "..(s[i].Refine).."\n") + end + if (check_val(s[i].View)~="") then + file3:write("\tView: "..(s[i].View).."\n") + end + if (s[i].BindOnEquip~="") then + file3:write("\tBindOnEquip: "..(s[i].BindOnEquip).."\n") + end + if (s[i].BuyingStore~="") then + file3:write("\tBuyingStore: true\n") + end + if (check_val(s[i].Delay) ~= "") then + file3:write("\tDelay: "..(s[i].Delay).."\n") + end + if (check_val(s[i].trade_flag)~="" and check_val(s[i].trade_override)~="" ) then + file3:write("\tTrade: {\n") + if (s[i].trade_override > 0 and s[i].trade_override < 100) then + file3:write("\t\toverride: ".. s[i].trade_override .."\n") + end + if (band(s[i].trade_flag,1)>0) then + file3:write("\t\tnodrop: true\n") + end + if (band(s[i].trade_flag,2)>0) then + file3:write("\t\tnotrade: true\n") + end + if (band(s[i].trade_flag,4)>0) then + file3:write("\t\tpartneroverride: true\n") + end + if (band(s[i].trade_flag,8)>0) then + file3:write("\t\tnoselltonpc: true\n") + end + if (band(s[i].trade_flag,16)>0) then + file3:write("\t\tnocart: true\n") + end + if (band(s[i].trade_flag,32)>0) then + file3:write("\t\tnostorage: true\n") + end + if (band(s[i].trade_flag,64)>0) then + file3:write("\t\tnogstorage: true\n") + end + if (band(s[i].trade_flag,128)>0) then + file3:write("\t\tnomail: true\n") + end + if (band(s[i].trade_flag,256)>0) then + file3:write("\t\tnoauction: true\n") + end + file3:write("\t}\n") + end + if (check_val(s[i].nouse_flag)~="" and check_val(s[i].nouse_override)~="" ) then + file3:write("\tNouse: {\n") + if (tonumber(s[i].nouse_override) > 0 and tonumber(s[i].nouse_override) < 100) then + file3:write("\t\toverride: ".. s[i].nouse_override .."\n") + end + if (band(s[i].nouse_flag,1)>0) then + file3:write("\t\tsitting: true\n") + end + file3:write("\t}\n") + end + if check_val((s[i].Stack1)~="" and check_val(s[i].Stack2)~="" ) then + file3:write("\tStack: ["..(s[i].Stack1)..", "..(s[i].Stack2).."]\n") + end + if (check_val(s[i].Sprite)~="" ) then + file3:write("\tSprite: "..(s[i].Sprite).."\n") + end + if (s[i].Script ~= "") then + file3:write("\tScript: <\""..newline(s[i].Script,1)..""..s[i].Script.."\">\n") + end + if (s[i].OnEquipScript ~= "") then + file3:write("\tOnEquipScript: <\""..newline(s[i].OnEquipScript,1)..""..s[i].OnEquipScript.."\">\n") + end + if (s[i].OnUnequipScript ~= "") then + file3:write("\tOnUnequipScript: <\""..newline(s[i].OnUnequipScript,1)..""..s[i].OnUnequipScript.."\">\n") + end + file3:write("},\n") + end + end +end + +for i=1,7 do + if (file_exists(arg[i])==false) then + print("Error: ",arg[i]," Not Found") + os.exit() + end +end + +file = assert(io.open(arg[1])) --Open ItemDB And Read its contents +contents = file:read'*a' +file:close() + +--Replacing Contents +find = { '\\', '\'', '//', '/%*', '%*/', '<\"', '\">', 'Id([^:]?):([^\n]+)', 'AegisName([^:]?):([^\n]+)', 'Name([^:]?):([^\n]+)', 'Type([^:]?):([^\n]+)', 'Buy([^:]?):([^\n]+)', 'Sell([^:]?):([^\n]+)', 'Weight([^:]?):([^\n]+)', 'Atk([^:]?):([^\n]+)', 'Matk([^:]?):([^\n]+)', 'Def([^:]?):([^\n]+)', 'Range([^:]?):([^\n]+)', 'Slots([^:]?):([^\n]+)', 'Job([^:]?):([^\n]+)', 'Upper([^:]?):([^\n]+)', 'Gender([^:]?):([^\n]+)', 'Loc([^:]?):([^\n]+)', 'WeaponLv([^:]?):([^\n]+)', 'EquipLv([^:]?):([^\n]+)', 'Refine([^:]?):([^\n]+)', 'View([^:]?):([^\n]+)', 'BindOnEquip([^:]?):([^\n]+)', "BuyingStore([^:]?):([^\n]+)", "Delay([^:]?):([^\n]+)", "Stack([^:]?): %[([^,]+), ([^]]+)%]", "Sprite([^:]?):([^\n]+)", 'override([^:]?):([^\n]+)', 'nodrop([^:]?):([^\n]+)', 'notrade([^:]?):([^\n]+)', 'partneroverride([^:]?):([^\n]+)', 'noselltonpc([^:]?):([^\n]+)', 'nocart([^:]?):([^\n]+)', 'nostorage([^:]?):([^\n]+)', 'nogstorage([^:]?):([^\n]+)', 'nomail([^:]?):([^\n]+)', 'noauction([^:]?):([^\n]+)', 'sitting([^:]?):([^\n]+)', 'Script([^:]?):', 'OnEquipScript([^:]?):', 'OnUnequipScript([^:]?):', 'EquipLv([^=]?)= %[([^,]+), ([^]]+)%],', 'EquipLv([^=]?)= ([^,]+),', 'item_db: %(', '= ""', '= \n', ',,'} +replace = {'\\\\', '\\\'', '--', '--[[', '--]]', '[==[', ']==],', 'Id%1=%2,', 'AegisName%1=%2,', 'Name%1=%2,', 'Type%1=%2,', 'Buy%1=%2,', 'Sell%1=%2,', 'Weight%1=%2,', 'Atk%1=%2,', 'Matk%1=%2,', 'Def%1=%2,', 'Range%1=%2,', 'Slots%1=%2,', 'Job%1=%2,', 'Upper%1=%2,', 'Gender%1=%2,', 'Loc%1=%2,', 'WeaponLv%1=%2,', 'EquipLv%1=%2,', 'Refine%1=%2,', 'View%1=%2,', 'BindOnEquip%1=%2,', "BuyingStore%1=%2,", "Delay%1=%2,", "Stack%1= {%2, %3},", "Sprite%1=%2,", 'override%1=%2,', 'nodrop%1=%2,', 'notrade%1=%2,', 'partneroverride%1=%2,', 'noselltonpc%1=%2,', 'nocart%1=%2,', 'nostorage%1=%2,', 'nogstorage%1=%2,', 'nomail%1=%2,', 'noauction%1=%2,', 'sitting%1=%2,', 'Script%1=', 'OnEquipScript%1=', 'OnUnequipScript%1=', 'EquipLv%1= {%2, %3},', 'EquipLv%1= {%2},', 'item_db = {', '= "",', '= "",\n', ',' } +findk=0 + +if #find==#replace then --Check if both table matches + start_time = os.time() + print("-- Putting ",arg[1]," Items into Memory") + --Replace Every Strings + contents = contents:gsub("Nouse: {([^}]+)}","Nouse= {%1},") --It needs to be done first. + contents = contents:gsub("Trade: {([^}]+)}","Trade= {%1},") --It needs to be done first. + for i=1,#find do + contents = contents:gsub(find[i],replace[i]) + end + + --Check for last ) in item_db (it auto breaks if found, can have many empty lines on end) + for i=1,1000000 do + if (string.sub(contents,-i,-i) == ")") then + findk = 1 + contents = replace_char(contents:len()-i+1,contents,'}') + end + if (findk==1) then break end + end +else + print('Error, Cannot Convert to Lua(Code:1['..#find..' '..#replace..'])') + os.exit() +end +item_db = {} --Initialize Empty item_db table +a = assert(loadstring(contents)) --Load the string as lua code +a() --Execute it so it gets loaded into item_db table +e_time = os.difftime((os.time())-start_time) --Time Taken +print("---- Total Items Found"," = ",#item_db,"(".. e_time .."s)") +csv = {} +item_db2 = {} + +for i=1,#item_db do + item_db2[item_db[i].Id] = deepcopy(item_db[i]) +end + + +--Initializing Global Tables +csv = {} +csv1 = {} +csv2 = {} +csv3 = "" +csv4 = 0 +--sql_update = "" +k=0 +line = "" + +--file4 = io.open(arg[9],"w") +for m=2,7 do --arg[2] to arg[7] = item_*.txt + line_number = 0 + sp_file = assert(io.open(arg[m])) + for line in sp_file:lines() do + if ( line ~= "" and line:sub(1,2)~="//") then --If Line is comment or empty, don't parse it + parse_line(line,m) + end + end + print(arg[m],"Done") + sp_file:close() + --os.exit() +end +--file4:write(sql_update) +--file4:close() +print("-- Writing Item_DB") +file3 = io.open(arg[8],"w") --Open Output File +file3:write('item_db: (\n' + ..'// Items Database\n' + ..'//\n' + ..'/******************************************************************************\n' + ..' ************* Entry structure ************************************************\n' + ..' ******************************************************************************\n' + ..'{\n' + ..' // =================== Mandatory fields ===============================\n' + ..' Id: ID (int)\n' + ..' AegisName: "Aegis_Name" (string)\n' + ..' Name: "Item Name" (string)\n' + ..' // =================== Optional fields ================================\n' + ..' Type: Item Type (int, defaults to 3 = etc item)\n' + ..' Buy: Buy Price (int, defaults to Sell * 2)\n' + ..' Sell: Sell Price (int, defaults to Buy / 2)\n' + ..' Weight: Item Weight (int, defaults to 0)\n' + ..' Atk: Attack (int, defaults to 0)\n' + ..' Matk: Magical Attack (int, defaults to 0, ignored in pre-re)\n' + ..' Def: Defense (int, defaults to 0)\n' + ..' Range: Attack Range (int, defaults to 0)\n' + ..' Slots: Slots (int, defaults to 0)\n' + ..' Job: Job mask (int, defaults to all jobs = 0xFFFFFFFF)\n' + ..' Upper: Upper mask (int, defaults to any = 0x3f)\n' + ..' Gender: Gender (int, defaults to both = 2)\n' + ..' Loc: Equip location (int, required value for equipment)\n' + ..' WeaponLv: Weapon Level (int, defaults to 0)\n' + ..' EquipLv: Equip required level (int, defaults to 0)\n' + ..' EquipLv: [min, max] (alternative syntax with min / max level)\n' + ..' Refine: Refineable (boolean, defaults to true)\n' + ..' View: View ID (int, defaults to 0)\n' + ..' BindOnEquip: true/false (boolean, defaults to false)\n' + ..' BuyingStore: true/false (boolean, defaults to false)\n' + ..' Delay: Delay to use item (int, defaults to 0)\n' + ..' Trade: { (defaults to no restrictions)\n' + ..' override: GroupID (int, defaults to 100)\n' + ..' nodrop: true/false (boolean, defaults to false)\n' + ..' notrade: true/false (boolean, defaults to false)\n' + ..' partneroverride: true/false (boolean, defaults to false)\n' + ..' noselltonpc: true/false (boolean, defaults to false)\n' + ..' nocart: true/false (boolean, defaults to false)\n' + ..' nostorage: true/false (boolean, defaults to false)\n' + ..' nogstorage: true/false (boolean, defaults to false)\n' + ..' nomail: true/false (boolean, defaults to false)\n' + ..' noauction: true/false (boolean, defaults to false)\n' + ..' }\n' + ..' Nouse: { (defaults to no restrictions)\n' + ..' override: GroupID (int, defaults to 100)\n' + ..' sitting: true/false (boolean, defaults to false)\n' + ..' }\n' + ..' Stack: [amount, type] (int, defaults to 0)\n' + ..' Sprite: SpriteID (int, defaults to 0)\n' + ..' Script: <"\n' + ..' Script\n' + ..' (it can be multi-line)\n' + ..' ">\n' + ..' OnEquipScript: <" OnEquip Script (can also be multi-line) ">\n' + ..' OnUnequipScript: <" OnUnequip Script (can also be multi-line) ">\n' + ..'},\n' + ..'******************************************************************************/\n') + +item_db_write(item_db2) +file3:write('\n)\n') +file3:close() --Close Output File +os.exit() diff --git a/tools/itemdbconverter.pl b/tools/itemdbconverter.pl new file mode 100755 index 000000000..13805c09e --- /dev/null +++ b/tools/itemdbconverter.pl @@ -0,0 +1,195 @@ +#!/usr/bin/perl +# +# Copyright (c) Hercules Dev Team, licensed under GNU GPL. +# See the LICENSE file +# Base Author: Haru @ http://hercules.ws +# +# This script converts an item_db(2).txt to the new item_db(2).conf format. +# usage example: perl tools/itemdbconverter.pl < db/item_db2.txt > db/item_db2.conf +# +use strict; +use warnings; + +sub prettifyscript ($) { + my ($orig) = @_; + $orig =~ s/^[\s\t]*//; $orig =~ s/[\s\t]*$//; + return '' unless $orig =~ /[^\s\t]/; + my ($p, $script) = ($orig, ''); + my ($curly, $lines, $comment) = (2, 0, 0); + my ($linebreak, $needindent) = (0, 0); + while ($p =~ /[^\s\t]/) { + $linebreak = 0; + if ($comment && $p =~ s|^\s*\*/\s*||) { + $comment = 0; + next; + } elsif ($p =~ s/^\s*({)\s*//) { + $curly++ unless $comment; + $comment++ if $comment; + $script .= " "; + $linebreak = 1; + $lines++; + } elsif ($p =~ s/^\s*(})\s*//) { + $curly-- unless $comment; + $comment-- if $comment - 1 > 0; + $linebreak = 1; + $lines++; + } elsif ($p =~ s/^\s*(;)\s*//) { + if ($p && (!$comment || $p !~ m|^[\s\t]*(?:\*/)[\s\t]*$|)) { + $linebreak = 1; + $lines++ + } + } elsif ($p =~ s/^("[^"]*")//) { + } elsif ($p =~ s|^\s*/\*\s*||) { + $comment = 1; + next; + } elsif ($p !~ s/^(.)//) { + last; + } + $script .= "\t" x $curly if $needindent; + $script .= "//" . ("\t" x ($comment-1)) if ($comment && ($needindent || $script eq '')); + $script .= "$1"; + if ($linebreak) { + $script .= "\n"; + $needindent = 1; + } else { + $needindent = 0; + } + } + if ($curly != 2) { + printf STDERR "Parse error, curly braces count ". ($curly-2) .". returning unmodified script:\n$orig\n\n"; + return $orig; + } + if ($lines) { + $script = "\n\t\t$script\n\t"; + } else { + $script = " $script "; + } + return $script; +} + +sub parsedb (@) { + my @input = @_; + foreach (@input) { + chomp $_; +# ID,AegisName,Name,Type,Buy,Sell,Weight,ATK,DEF,Range,Slots,Job,Upper,Gender,Loc,wLV,eLV,Refineable,View,{ Script },{ OnEquip_Script },{ OnUnequip_Script } + if( $_ =~ qr/^ + (?<prefix>(?:\/\/[^0-9]*)?) + (?<ID>[0-9]+)[^,]*, + (?<AegisName>[^,]+), + (?<Name>[^,]+),[\s\t]* + (?<Type>[0-9]+)[^,]*,[\s\t]* + (?<Buy>[0-9]*)[^,]*,[\s\t]* + (?<Sell>[0-9]*)[^,]*,[\s\t]* + (?<Weight>[0-9]*)[^,]*,[\s\t]* + (?<ATK>[0-9-]*)[^,:]*(?<hasmatk>:[\s\t]*(?<MATK>[0-9-]*))?[^,]*,[\s\t]* + (?<DEF>[0-9-]*)[^,]*,[\s\t]* + (?<Range>[0-9]*)[^,]*,[\s\t]* + (?<Slots>[0-9]*)[^,]*,[\s\t]* + (?<Job>[x0-9A-Fa-f]*)[^,]*,[\s\t]* + (?<Upper>[0-9]*)[^,]*,[\s\t]* + (?<Gender>[0-9]*)[^,]*,[\s\t]* + (?<Loc>[0-9]*)[^,]*,[\s\t]* + (?<wLV>[0-9]*)[^,]*,[\s\t]* + (?<eLV>[0-9]*)[^,:]*(?<hasmaxlv>:[\s\t]*(?<eLVmax>[0-9]*))?[^,]*,[\s\t]* + (?<Refineable>[0-9]*)[^,]*,[\s\t]* + (?<View>[0-9]*)[^,]*,[\s\t]* + {(?<Script>.*)}, + {(?<OnEquip>.*)}, + {(?<OnUnequip>.*)} + /x ) { + my %cols = map { $_ => $+{$_} } keys %+; + print "/*\n" if $cols{prefix}; + print "$cols{prefix}\n" if $cols{prefix} and $cols{prefix} !~ m|^//[\s\t]*$|; + print "{\n"; + print "\tId: $cols{ID}\n"; + print "\tAegisName: \"$cols{AegisName}\"\n"; + print "\tName: \"$cols{Name}\"\n"; + print "\tType: $cols{Type}\n"; + print "\tBuy: $cols{Buy}\n" if $cols{Buy} || $cols{Buy} eq '0'; + print "\tSell: $cols{Sell}\n" if $cols{Sell} || $cols{Sell} eq '0'; + print "\tWeight: $cols{Weight}\n" if $cols{Weight}; + print "\tAtk: $cols{ATK}\n" if $cols{ATK}; + print "\tMatk: $cols{MATK}\n" if $cols{MATK}; + print "\tDef: $cols{DEF}\n" if $cols{DEF}; + print "\tRange: $cols{Range}\n" if $cols{Range}; + print "\tSlots: $cols{Slots}\n" if $cols{Slots}; + $cols{Job} = '0xFFFFFFFF' unless $cols{Job}; + print "\tJob: $cols{Job}\n" unless $cols{Job} =~ /0xFFFFFFFF/i; + print "\tUpper: $cols{Upper}\n" if $cols{Upper} && (($cols{hasmatk} && $cols{Upper} != 0x3f) || (!$cols{hasmatk} && $cols{Upper} != 7)); + $cols{Gender} = '2' unless $cols{Gender}; + print "\tGender: $cols{Gender}\n" unless $cols{Gender} eq '2'; + print "\tLoc: $cols{Loc}\n" if $cols{Loc}; + print "\tWeaponLv: $cols{wLV}\n" if $cols{wLV}; + if ($cols{hasmaxlv} and $cols{eLVmax}) { + $cols{eLV} = 0 unless $cols{eLV}; + print "\tEquipLv: [$cols{eLV}, $cols{eLVmax}]\n"; + } else { + print "\tEquipLv: $cols{eLV}\n" if $cols{eLV}; + } + print "\tRefine: false\n" if !$cols{Refineable} and ($cols{Type} == 4 or $cols{Type} == 5); + print "\tView: $cols{View}\n" if $cols{View}; + $cols{Script} = prettifyscript($cols{Script}); + print "\tScript: <\"$cols{Script}\">\n" if $cols{Script}; + $cols{OnEquip} = prettifyscript($cols{OnEquip}); + print "\tOnEquipScript: <\"$cols{OnEquip}\">\n" if $cols{OnEquip}; + $cols{OnUnequip} = prettifyscript($cols{OnUnequip}); + print "\tOnUnequipScript: <\"$cols{OnUnequip}\">\n" if $cols{OnUnequip}; + print "},\n"; + print "*/\n" if $cols{prefix}; + } elsif( $_ =~ /^\/\/(.*)$/ ) { + my $s = $1; + print "// $s\n" unless $s =~ /^[\s\t]*$/; + } elsif( $_ !~ /^\s*$/ ) { + print "// Error parsing: $_\n"; + } + + } +} +print <<"EOF"; +item_db: ( +/****************************************************************************** + ************* Entry structure ************************************************ + ****************************************************************************** +{ + // =================== Mandatory fields =============================== + Id: ID (int) + AegisName: "Aegis_Name" (string, optional if Inherit: true) + Name: "Item Name" (string, optional if Inherit: true) + // =================== Optional fields ================================ + Type: Item Type (int, defaults to 3 = etc item) + Buy: Buy Price (int, defaults to Sell * 2) + Sell: Sell Price (int, defaults to Buy / 2) + Weight: Item Weight (int, defaults to 0) + Atk: Attack (int, defaults to 0) + Matk: Magical Attack (int, defaults to 0, ignored in pre-re) + Def: Defense (int, defaults to 0) + Range: Attack Range (int, defaults to 0) + Slots: Slots (int, defaults to 0) + Job: Job mask (int, defaults to all jobs = 0xFFFFFFFF) + Upper: Upper mask (int, defaults to any = 0x3f) + Gender: Gender (int, defaults to both = 2) + Loc: Equip location (int, required value for equipment) + WeaponLv: Weapon Level (int, defaults to 0) + EquipLv: Equip required level (int, defaults to 0) + EquipLv: [min, max] (alternative syntax with min / max level) + Refine: Refineable (boolean, defaults to true) + View: View ID (int, defaults to 0) + BindOnEquip: true/false (boolean, defaults to false) + Script: <" + Script + (it can be multi-line) + "> + OnEquipScript: <" OnEquip Script (can also be multi-line) "> + OnUnequipScript: <" OnUnequip Script (can also be multi-line) "> + // =================== Optional fields (item_db2 only) ================ + Inherit: true/false (boolean, if true, inherit the values + that weren't specified, from item_db.conf, + else override it and use default values) +}, +******************************************************************************/ + +EOF + +parsedb(<>); + +print ")\n"; diff --git a/tools/mapreg-converter.php b/tools/mapreg-converter.php index 3d548554a..ef5cc791c 100644 --- a/tools/mapreg-converter.php +++ b/tools/mapreg-converter.php @@ -35,4 +35,4 @@ } fprintf(STDERR, "done.".PHP_EOL); -?>
\ No newline at end of file +?> |