summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/HPMHookGen/HPMDataCheckGen.pl68
-rwxr-xr-xtools/HPMHookGen/HPMHookGen.pl580
-rw-r--r--tools/HPMHookGen/Makefile.in80
-rw-r--r--tools/HPMHookGen/doxygen.conf216
-rw-r--r--tools/Script-Checker.applescript151
-rwxr-xr-xtools/check-doc8
-rwxr-xr-xtools/item_db.pl116
-rw-r--r--tools/item_merge.lua619
-rwxr-xr-xtools/itemdbconverter.pl195
-rw-r--r--tools/mapreg-converter.php2
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
+?>