FileParsers.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:26k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - Config File Parsers
  3. #
  4. #  This software is open source, licensed under the GNU General Public
  5. #  License, version 2.
  6. #  Basically, this means that you're allowed to modify and distribute
  7. #  this software. However, if you distribute modified versions, you MUST
  8. #  also distribute the source code.
  9. #  See http://www.gnu.org/licenses/gpl.html for the full license.
  10. #
  11. #  $Revision: 6638 $
  12. #  $Id: FileParsers.pm 6638 2008-12-19 17:33:44Z technologyguild $
  13. #
  14. #########################################################################
  15. ##
  16. # MODULE DESCRIPTION: Configuration file parsers
  17. #
  18. # This module contains functions for parsing an writing config/* and
  19. # tables/* files.
  20. package FileParsers;
  21. use strict;
  22. use File::Spec;
  23. use Exporter;
  24. use base qw(Exporter);
  25. use encoding 'utf8';
  26. use Carp;
  27. use Utils;
  28. use Utils::TextReader;
  29. use Plugins;
  30. use Log qw(warning error);
  31. our @EXPORT = qw(
  32. parseArrayFile
  33. parseAvoidControl
  34. parseChatResp
  35. parseCommandsDescription
  36. parseConfigFile
  37. parseDataFile
  38. parseDataFile_lc
  39. parseDataFile2
  40. parseEmotionsFile
  41. parseItemsControl
  42. parseList
  43. parseNPCs
  44. parseMonControl
  45. parsePortals
  46. parsePortalsLOS
  47. parsePriority
  48. parseResponses
  49. parseROLUT
  50. parseRODescLUT
  51. parseROSlotsLUT
  52. parseSectionedFile
  53. parseShopControl
  54. parseSkillsSPLUT
  55. parseTimeouts
  56. parseWaypoint
  57. processUltimate
  58. writeDataFile
  59. writeDataFileIntact
  60. writeDataFileIntact2
  61. writePortalsLOS
  62. writeSectionedFileIntact
  63. updateMonsterLUT
  64. updatePortalLUT
  65. updateNPCLUT
  66. );
  67. sub parseArrayFile {
  68. my $file = shift;
  69. my $r_array = shift;
  70. undef @{$r_array};
  71. my @lines;
  72. my $reader = new Utils::TextReader($file);
  73. while (!$reader->eof()) {
  74. my $line = $reader->readLine();
  75. $line =~ s/[rnx{FEFF}]//g;
  76. push @lines, $line;
  77. }
  78. @{$r_array} = scalar(@lines) + 1;
  79. my $i = 1;
  80. foreach (@lines) {
  81. $r_array->[$i] = $_;
  82. $i++;
  83. }
  84. return 1;
  85. }
  86. sub parseAvoidControl {
  87. my $file = shift;
  88. my $r_hash = shift;
  89. undef %{$r_hash};
  90. my ($key,@args,$args);
  91. my $reader = new Utils::TextReader($file);
  92. my $section = "";
  93. while (!$reader->eof()) {
  94. my $line = $reader->readLine();
  95. $line =~ s/x{FEFF}//g;
  96. next if ($line =~ /^#/);
  97. $line =~ s/[rn]//g;
  98. $line =~ s/s+$//g;
  99. next if ($line eq "");
  100. if ($line =~ /^[(.*)]$/) {
  101. $section = $1;
  102. next;
  103. } else {
  104. ($key, $args) = lc($line) =~ /([sS]+?)[s]+(d+[sS]*)/;
  105. @args = split / /,$args;
  106. if ($key ne "") {
  107. $r_hash->{$section}{$key}{disconnect_on_sight} = $args[0];
  108. $r_hash->{$section}{$key}{teleport_on_sight} = $args[1];
  109. $r_hash->{$section}{$key}{disconnect_on_chat} = $args[2];
  110. }
  111. }
  112. }
  113. return 1;
  114. }
  115. sub parseChatResp {
  116. my $file = shift;
  117. my $r_array = shift;
  118. my $reader = new Utils::TextReader($file);
  119. while (!$reader->eof()) {
  120. my $line = $reader->readLine();
  121. $line =~ s/[rnx{FEFF}]//g;
  122. next if ($line eq "" || $line =~ /^#/);
  123. if ($line =~ /^first_resp_/) {
  124. Log::error(Translation::T("The chat_resp.txt format has changed. Please read News.txt and upgrade to the new format.n"));
  125. return;
  126. }
  127. my ($key, $value) = split /t+/, lc($line), 2;
  128. my @input = split /,+/, $key;
  129. my @responses = split /,+/, $value;
  130. foreach my $word (@input) {
  131. my %args = (
  132. word => $word,
  133. responses => @responses
  134. );
  135. push @{$r_array}, %args;
  136. }
  137. }
  138. return 1;
  139. }
  140. sub parseCommandsDescription {
  141. my $file = shift;
  142. my $r_hash = shift;
  143. my $no_undef = shift;
  144. undef %{$r_hash} unless $no_undef;
  145. my ($key, $commentBlock, $description);
  146. my $reader = new Utils::TextReader($file);
  147. while (!$reader->eof()) {
  148. my $line = $reader->readLine();
  149. $line =~ s/x{FEFF}//g;
  150. next if ($line =~ /^[st]*#/);
  151. $line =~ s/[rn]//g; # Remove line endings
  152. $line =~ s/^[ts]*//; # Remove leading tabs and whitespace
  153. $line =~ s/s+$//g; # Remove trailing whitespace
  154. next if ($line eq "");
  155. if (!defined $commentBlock && $line =~ /^/*/) {
  156. $commentBlock = 1;
  157. next;
  158. } elsif ($line =~ m/*/$/) {
  159. undef $commentBlock;
  160. next;
  161. } elsif (defined $commentBlock) {
  162. next;
  163. } elsif ($description) {
  164. $description = 0;
  165. push @{$r_hash->{$key}}, $line;
  166. } elsif ($line =~ /^[(w+)]$/) {
  167. $key = $1;
  168. $description = 1;
  169. $r_hash->{$key} = [];
  170. } elsif ($line =~ /^(.*?)t+(.*)$/) {
  171. push @{$r_hash->{$key}}, [$1, $2];
  172. }
  173. }
  174. return 1;
  175. }
  176. sub parseConfigFile {
  177. my $file = shift;
  178. my $r_hash = shift;
  179. my $no_undef = shift;
  180. undef %{$r_hash} unless $no_undef;
  181. my ($key, $value, $inBlock, $commentBlock, %blocks);
  182. my $reader = new Utils::TextReader($file);
  183. while (!$reader->eof()) {
  184. my $line = $reader->readLine();
  185. $line =~ s/x{FEFF}//g;
  186. next if ($line =~ /^[st]*#/);
  187. $line =~ s/[rn]//g; # Remove line endings
  188. $line =~ s/^[ts]*//; # Remove leading tabs and whitespace
  189. $line =~ s/s+$//g; # Remove trailing whitespace
  190. next if ($line eq "");
  191. if (!defined $commentBlock && $line =~ /^/*/) {
  192. $commentBlock = 1;
  193. next;
  194. } elsif (defined $commentBlock && $line =~ m/*/$/) {
  195. undef $commentBlock;
  196. next;
  197. } elsif (defined $commentBlock) {
  198. next;
  199. } elsif (!defined $inBlock && $line =~ /{$/) {
  200. # Begin of block
  201. $line =~ s/ *{$//;
  202. ($key, $value) = $line =~ /^(.*?) (.*)/;
  203. $key = $line if ($key eq '');
  204. if (!exists $blocks{$key}) {
  205. $blocks{$key} = 0;
  206. } else {
  207. $blocks{$key}++;
  208. }
  209. if ($key ne 'teleportAuto'){
  210. $inBlock = "${key}_$blocks{$key}";
  211. } else {
  212. $inBlock = "${key}";
  213. }
  214. $r_hash->{$inBlock} = $value;
  215. } elsif (defined $inBlock && $line eq "}") {
  216. # End of block
  217. undef $inBlock;
  218. } else {
  219. # Option
  220. ($key, $value) = $line =~ /^(.*?) (.*)/;
  221. if ($key eq "") {
  222. $key = $line;
  223. $key =~ s/ *$//;
  224. }
  225. #$value = $r_hash->{$1} if ($value =~ /^constant.(.*)/);
  226. $key = "${inBlock}_${key}" if (defined $inBlock);
  227. if ($key eq "!include") {
  228. # Process special !include directives
  229. # The filename can be relative to the current file
  230. my $f = $value;
  231. if (!File::Spec->file_name_is_absolute($value) && $value !~ /^//) {
  232. if ($file =~ /[/\]/) {
  233. $f = $file;
  234. $f =~ s/(.*)[/\].*/$1/;
  235. $f = File::Spec->catfile($f, $value);
  236. } else {
  237. $f = $value;
  238. }
  239. }
  240. if (-f $f) {
  241. my $ret = parseConfigFile($f, $r_hash, 1);
  242. return $ret unless $ret;
  243. } else {
  244. error Translation::TF("%s: Include file not found: %sn", $file, $f);
  245. return 0;
  246. }
  247. } else {
  248. $r_hash->{$key} = $value;
  249. }
  250. }
  251. }
  252. if ($inBlock) {
  253. error Translation::TF("%s: Unclosed { at EOFn", $file);
  254. return 0;
  255. }
  256. return 1;
  257. }
  258. sub parseEmotionsFile {
  259. my $file = shift;
  260. my $r_hash = shift;
  261. undef %{$r_hash};
  262. my ($key, $word, $name);
  263. my $reader = new Utils::TextReader($file);
  264. while (!$reader->eof()) {
  265. my $line = $reader->readLine();
  266. $line =~ s/x{FEFF}//g;
  267. next if ($line =~ /^#/);
  268. $line =~ s/[rn]//g;
  269. $line =~ s/s+$//g;
  270. ($key, $word, $name) = $line =~ /^(d+) (S+) (.*)$/;
  271. if ($key ne "") {
  272. $$r_hash{$key}{command} = $word;
  273. $$r_hash{$key}{display} = $name;
  274. }
  275. }
  276. return 1;
  277. }
  278. sub parseDataFile {
  279. my $file = shift;
  280. my $r_hash = shift;
  281. undef %{$r_hash};
  282. my ($key,$value);
  283. my $reader = new Utils::TextReader($file);
  284. while (!$reader->eof()) {
  285. my $line = $reader->readLine();
  286. $line =~ s/x{FEFF}//g;
  287. next if ($line =~ /^#/);
  288. $line =~ s/[rn]//g;
  289. $line =~ s/s+$//g;
  290. ($key, $value) = $line =~ /([sS]*) ([sS]*?)$/;
  291. if ($key ne "" && $value ne "") {
  292. $$r_hash{$key} = $value;
  293. }
  294. }
  295. return 1;
  296. }
  297. sub parseDataFile_lc {
  298. my $file = shift;
  299. my $r_hash = shift;
  300. undef %{$r_hash};
  301. my ($key,$value);
  302. my $reader = new Utils::TextReader($file);
  303. while (!$reader->eof()) {
  304. my $line = $reader->readLine();
  305. $line =~ s/x{FEFF}//g;
  306. next if ($line =~ /^#/);
  307. $line =~ s/[rn]//g;
  308. $line =~ s/s+$//g;
  309. ($key, $value) = $line =~ /([sS]*) ([sS]*?)$/;
  310. if ($key ne "" && $value ne "") {
  311. $$r_hash{lc($key)} = $value;
  312. }
  313. }
  314. return 1;
  315. }
  316. sub parseDataFile2 {
  317. my ($file, $r_hash) = @_;
  318. %{$r_hash} = ();
  319. my $reader = new Utils::TextReader($file);
  320. while (!$reader->eof()) {
  321. my $line = $reader->readLine();
  322. $line =~ s/x{FEFF}//g;
  323. next if ($line =~ /^#/);
  324. $line =~ s/[rn]//g;
  325. next if (length($line) == 0);
  326. my ($key, $value) = split / /, $line, 2;
  327. $r_hash->{$key} = $value;
  328. }
  329. close FILE;
  330. return 1;
  331. }
  332. sub parseList {
  333. my $file = shift;
  334. my $r_hash = shift;
  335. undef %{$r_hash};
  336. my $reader = new Utils::TextReader($file);
  337. while (!$reader->eof()) {
  338. my $line = $reader->readLine();
  339. chomp($line);
  340. $r_hash->{$line} = 1;
  341. }
  342. return 1;
  343. }
  344. ##
  345. # parseShopControl(file, shop)
  346. # file: Filename to parse
  347. # shop: Return hash
  348. #
  349. # Parses a shop control file. The shop control file should have the shop title
  350. # on its first line, followed by "$itemt$pricet$quantity" on subsequent
  351. # lines. Blank lines, or lines starting with "#" are ignored. If $price
  352. # contains commas, then they are checked for syntactical validity (e.g.
  353. # "1,000,000" instead of "1,000,00") to protect the user from entering the
  354. # wrong price. If $quantity is missing, all available will be sold.
  355. #
  356. # Example:
  357. # My Shop!
  358. # Poring Card 1,000
  359. # Andre Card 1,000,000 3
  360. # +8 Chain [3] 500,000
  361. sub parseShopControl {
  362. my ($file, $shop) = @_;
  363. %{$shop} = ();
  364. my $reader = new Utils::TextReader($file);
  365. # Read shop items
  366. $shop->{items} = [];
  367. my $linenum = 0;
  368. my @errors = ();
  369. my $line;
  370. while (!$reader->eof()) {
  371. $line = $reader->readLine();
  372. $linenum++;
  373. chomp;
  374. $line =~ s/[rnx{FEFF}]//g;
  375. next if $line =~ /^$/ || $line =~ /^#/;
  376. if (!$shop->{title_line}) { 
  377. $shop->{title_line} = $line;
  378. next;
  379. }
  380. my ($name, $price, $amount) = split(/t+/, $line);
  381. $price =~ s/^s+//g;
  382. $amount =~ s/^s+//g;
  383. my $real_price = $price;
  384. $real_price =~ s/,//g;
  385. my $loc = Translation::TF("Line %s: Item '%s'", $linenum, $name);
  386. if ($real_price !~ /^d+$/) {
  387. push(@errors, Translation::TF("%s has non-integer price: %s", $loc, $price));
  388. } elsif ($price ne $real_price && formatNumber($real_price) ne $price) {
  389. push(@errors, Translation::TF("%s has incorrect comma placement in price: %s", $loc, $price));
  390. } elsif ($real_price < 0) {
  391. push(@errors, Translation::TF("%s has non-positive price: %s", $loc, $price));
  392. } elsif ($real_price > 1000000000) {
  393. push(@errors, Translation::TF("%s has price over 1,000,000,000: %s", $loc, $price));
  394. }
  395. if ($amount > 30000) {
  396. push(@errors, Translation::TF("%s has amount over 30,000: %s", $loc, $amount));
  397. }
  398. push(@{$shop->{items}}, {name => $name, price => $real_price, amount => $amount});
  399. }
  400. if (@errors) {
  401. error Translation::TF("Errors were found in %s:n", $file);
  402. foreach (@errors) { error("$linen"); }
  403. error Translation::TF("Please correct the above errors and type 'reload %s'.n", $file);
  404. return 0;
  405. }
  406. return 1;
  407. }
  408. sub parseItemsControl {
  409. my $file = shift;
  410. my $r_hash = shift;
  411. undef %{$r_hash};
  412. my ($key, $args_text, %cache);
  413. my $reader = new Utils::TextReader($file);
  414. while (!$reader->eof()) {
  415. my $line = $reader->readLine();
  416. $line =~ s/x{FEFF}//g;
  417. next if ($line =~ /^#/);
  418. $line =~ s/[rn]//g;
  419. $line =~ s/s+$//g;
  420. ($key, $args_text) = lc($line) =~ /([sS]+?) (d+[sS]*)/;
  421. next if ($key eq "");
  422. if ($cache{$args_text}) {
  423. $r_hash->{$key} = $cache{$args_text};
  424. } else {
  425. my @args = split / /, $args_text;
  426. my %item = (
  427. keep => $args[0],
  428. storage => $args[1],
  429. sell => $args[2],
  430. cart_add => $args[3],
  431. cart_get => $args[4]
  432. );
  433. # Cache similar entries to save memory.
  434. $r_hash->{$key} = $cache{$args_text} = %item;
  435. }
  436. }
  437. return 1;
  438. }
  439. sub parseNPCs {
  440. my $file = shift;
  441. my $r_hash = shift;
  442. my ($i, $string);
  443. undef %{$r_hash};
  444. my ($key, $value, @args);
  445. my $reader = new Utils::TextReader($file);
  446. while (!$reader->eof()) {
  447. my $line = $reader->readLine();
  448. $line =~ s/cM|cJ//g;
  449. $line =~ s/^s+|s+$//g;
  450. next if $line =~ /^#/ || $line eq '';
  451. #izlude 135 78 Charfri
  452. my ($map,$x,$y,$name) = split /s+/, $line,4;
  453. next unless $name;
  454. $$r_hash{"$map $x $y"} = $name;
  455. }
  456. return 1;
  457. }
  458. sub parseMonControl {
  459. my $file = shift;
  460. my $r_hash = shift;
  461. undef %{$r_hash};
  462. my ($key,@args,$args);
  463. my $reader = new Utils::TextReader($file);
  464. while (!$reader->eof()) {
  465. my $line = $reader->readLine();
  466. $line =~ s/x{FEFF}//g;
  467. next if ($line =~ /^#/);
  468. $line =~ s/[rn]//g;
  469. $line =~ s/s+$//g;
  470. if ($line =~ /t/) {
  471. ($key, $args) = split /t+/, lc($line);
  472. } else {
  473. ($key, $args) = lc($line) =~ /([sS]+?) ([-d.]+[sS]*)/;
  474. }
  475. @args = split / /, $args;
  476. if ($key ne "") {
  477. $r_hash->{$key}{attack_auto} = $args[0];
  478. $r_hash->{$key}{teleport_auto} = $args[1];
  479. $r_hash->{$key}{teleport_search} = $args[2];
  480. $r_hash->{$key}{skillcancel_auto} = $args[3];
  481. $r_hash->{$key}{attack_lvl} = $args[4];
  482. $r_hash->{$key}{attack_jlvl} = $args[5];
  483. $r_hash->{$key}{attack_hp} = $args[6];
  484. $r_hash->{$key}{attack_sp} = $args[7];
  485. $r_hash->{$key}{weight} = $args[8];
  486. }
  487. }
  488. return 1;
  489. }
  490. sub parsePortals {
  491. my $file = shift;
  492. my $r_hash = shift;
  493. undef %{$r_hash};
  494. open FILE, "<", $file;
  495. while (my $line = <FILE>) {
  496. next if $line =~ /^#/;
  497. $line =~ s/cM|cJ//g;
  498. $line =~ s/s+/ /g;
  499. $line =~ s/^s+|s+$//g;
  500. my @args = split /s/, $line, 8;
  501. if (@args > 5) {
  502. my $portal = "$args[0] $args[1] $args[2]";
  503. my $dest = "$args[3] $args[4] $args[5]";
  504. $$r_hash{$portal}{'source'}{'map'} = $args[0];
  505. $$r_hash{$portal}{'source'}{'x'} = $args[1];
  506. $$r_hash{$portal}{'source'}{'y'} = $args[2];
  507. $$r_hash{$portal}{'dest'}{$dest}{'map'} = $args[3];
  508. $$r_hash{$portal}{'dest'}{$dest}{'x'} = $args[4];
  509. $$r_hash{$portal}{'dest'}{$dest}{'y'} = $args[5];
  510. $$r_hash{$portal}{'dest'}{$dest}{'cost'} = $args[6];
  511. $$r_hash{$portal}{'dest'}{$dest}{'steps'} = $args[7];
  512. }
  513. }
  514. close FILE;
  515. return 1;
  516. }
  517. sub parsePortalsLOS {
  518. my $file = shift;
  519. my $r_hash = shift;
  520. undef %{$r_hash};
  521. my $key;
  522. open FILE, "<", $file;
  523. foreach (<FILE>) {
  524. s/x{FEFF}//g;
  525. next if (/^#/);
  526. s/[rn]//g;
  527. s/s+/ /g;
  528. s/s+$//g;
  529. my @args = split /s/, $_;
  530. if (@args) {
  531. my $map = shift @args;
  532. my $x = shift @args;
  533. my $y = shift @args;
  534. for (my $i = 0; $i < @args; $i += 4) {
  535. $$r_hash{"$map $x $y"}{"$args[$i] $args[$i+1] $args[$i+2]"} = $args[$i+3];
  536. }
  537. }
  538. }
  539. close FILE;
  540. return 1;
  541. }
  542. sub parsePriority {
  543. my $file = shift;
  544. my $r_hash = shift;
  545. return unless my $reader = new Utils::TextReader($file);
  546. my @lines;
  547. while (!$reader->eof()) {
  548. push @lines, $reader->readLine();
  549. }
  550. my $pri = $#lines;
  551. foreach (@lines) {
  552. s/x{FEFF}//g;
  553. next if (/^#/);
  554. s/[rn]//g;
  555. $$r_hash{lc($_)} = $pri + 1;
  556. $pri--;
  557. }
  558. return 1;
  559. }
  560. sub parseResponses {
  561. my $file = shift;
  562. my $r_hash = shift;
  563. undef %{$r_hash};
  564. my ($i, $key,$value);
  565. my $reader = new Utils::TextReader($file);
  566. while (!$reader->eof()) {
  567. my $line = $reader->readLine();
  568. $line =~ s/x{FEFF}//g;
  569. next if ($line =~ /^#/);
  570. $line =~ s/[rn]//g;
  571. ($key, $value) = $line =~ /([sS]*?) ([sS]*)$/;
  572. if ($key ne "" && $value ne "") {
  573. $i = 0;
  574. while ($$r_hash{"$key_$i"} ne "") {
  575. $i++;
  576. }
  577. $$r_hash{"$key_$i"} = $value;
  578. }
  579. }
  580. return 1;
  581. }
  582. sub parseROLUT {
  583. my ($file, $r_hash) = @_;
  584. my %ret = (
  585. file => $file,
  586. hash => $r_hash
  587.     );
  588. Plugins::callHook("FileParsers::ROLUT", %ret);
  589. return if ($ret{return});
  590. undef %{$r_hash};
  591. my $reader = new Utils::TextReader($file);
  592. while (!$reader->eof()) {
  593. my $line = $reader->readLine();
  594. $line =~ s/[rnx{FEFF}]//g;
  595. next if (length($line) == 0 || $line =~ /^///);
  596. my ($id, $name) = split /#/, $line, 3;
  597. if ($id ne "" && $name ne "") {
  598. $name =~ s/_/ /g;
  599. $r_hash->{$id} = $name;
  600. }
  601. }
  602. return 1;
  603. }
  604. sub parseRODescLUT {
  605. my ($file, $r_hash) = @_;
  606. my %ret = (
  607. file => $file,
  608. hash => $r_hash
  609.     );
  610. Plugins::callHook("FileParsers::RODescLUT", %ret);
  611. return if ($ret{return});
  612. undef %{$r_hash};
  613. my $ID;
  614. my $IDdesc;
  615. open FILE, "< $file";
  616. foreach (<FILE>) {
  617. s/r//g;
  618. if (/^#/) {
  619. $$r_hash{$ID} = $IDdesc;
  620. undef $ID;
  621. undef $IDdesc;
  622. } elsif (!$ID) {
  623. ($ID) = /([sS]+)#/;
  624. } else {
  625. $IDdesc .= $_;
  626. $IDdesc =~ s/^......//g;
  627. $IDdesc =~ s/_/--------------/g;
  628. }
  629. }
  630. close FILE;
  631. return 1;
  632. }
  633. sub parseROSlotsLUT {
  634. my $file = shift;
  635. my $r_hash = shift;
  636. undef %{$r_hash};
  637. my $ID;
  638. open FILE, $file;
  639. foreach (<FILE>) {
  640. if (!$ID) {
  641. ($ID) = /(d+)#/;
  642. } else {
  643. ($$r_hash{$ID}) = /(d+)#/;
  644. undef $ID;
  645. }
  646. }
  647. close FILE;
  648. return 1;
  649. }
  650. sub parseSectionedFile {
  651. my $file = shift;
  652. my $r_hash = shift;
  653. undef %{$r_hash};
  654. my $reader = new Utils::TextReader($file);
  655. my $section = "";
  656. while (!$reader->eof()) {
  657. my $line = $reader->readLine();
  658. $line =~ s/x{FEFF}//g;
  659. next if ($line =~ /^#/);
  660. $line =~ s/[rn]//g;
  661. $line =~ s/s+$//g;
  662. next if ($line eq "");
  663. if ($line =~ /^[(.*)]$/i) {
  664. $section = $1;
  665. next;
  666. } else {
  667. my ($key, $value);
  668. if ($line =~ / /) {
  669. ($key, $value) = $line =~ /^(.*?) (.*)/;
  670. } else {
  671. $key = $line;
  672. }
  673. $r_hash->{$section}{$key} = $value;
  674. }
  675. }
  676. return 1;
  677. }
  678. sub parseSkillsSPLUT {
  679. my $file = shift;
  680. my $r_hash = shift;
  681. undef %{$r_hash};
  682. my $ID;
  683. my $i;
  684. $i = 1;
  685. open(FILE, "< $file");
  686. foreach (<FILE>) {
  687. if (/^@/) {
  688. undef $ID;
  689. $i = 1;
  690. } elsif (!$ID) {
  691. ($ID) = /([sS]+)#/;
  692. } else {
  693. ($$r_hash{$ID}{$i++}) = /(d+)#/;
  694. }
  695. }
  696. close FILE;
  697. return 1;
  698. }
  699. sub parseTimeouts {
  700. my $file = shift;
  701. my $r_hash = shift;
  702. my $reader = new Utils::TextReader($file);
  703. while (!$reader->eof()) {
  704. my $line = $reader->readLine();
  705. $line =~ s/x{FEFF}//g;
  706. next if ($line =~ /^#/);
  707. $line =~ s/[rn]//g;
  708. my ($key, $value) = $line =~ /([sS]+?) ([sS]*?)$/;
  709. my @args = split (/ /, $value);
  710. if ($key ne "") {
  711. $$r_hash{$key}{'timeout'} = $args[0];
  712. }
  713. }
  714. return 1;
  715. }
  716. sub parseWaypoint {
  717. my $file = shift;
  718. my $r_array = shift;
  719. @{$r_array} = ();
  720. open FILE, "< $file";
  721. foreach (<FILE>) {
  722. s/x{FEFF}//g;
  723. next if (/^#/ || /^$/);
  724. s/[rn]//g;
  725. my @items = split / +/, $_;
  726. my %point = (
  727. map => $items[0],
  728. x => $items[1],
  729. y => $items[2]
  730. );
  731. push @{$r_array}, %point;
  732. }
  733. close FILE;
  734. return 1;
  735. }
  736. # The ultimate config file format. This function is a parser and writer in one.
  737. # The config file can be divided in section, example:
  738. #
  739. #   foo 1
  740. #   bar 2
  741. #
  742. #   [Options]
  743. #   username me
  744. #   password p
  745. #
  746. #   [Names]
  747. #   joe
  748. #   mike
  749. #
  750. # Sections can be treated as hashes or arrays. It's defined by $rules.
  751. # If you want [Names] to be an array:
  752. # %rule = (Names => 'list');
  753. #
  754. # processUltimate("file", %hash, %rule) returns:
  755. # {
  756. #   foo => 1,
  757. #   bar => 2,
  758. #   Options => {
  759. #       username => 'me',
  760. #       password => 'p';
  761. #   },
  762. #   Names => [
  763. #       "joe",
  764. #       "mike"
  765. #   ]
  766. # }
  767. #
  768. # When in write mode, this function will automatically add new keys and lines,
  769. # while preserving comments.
  770. sub processUltimate {
  771. my ($file, $hash, $rules, $writeMode) = @_;
  772. my $f;
  773. my $secname = '';
  774. my ($section, $rule, @lines, %written, %sectionsWritten);
  775. undef %{$hash} if (!$writeMode);
  776. if (-f $file) {
  777. my $reader = new Utils::TextReader($file);
  778. while (!$reader->eof()) {
  779. my $line = $reader->readLine();
  780. $line =~ s/x{FEFF}//g;
  781. $line =~ s/[rn]//g;
  782. if ($line eq '' || $line =~ /^[ t]*#/) {
  783. push @lines, $line if ($writeMode);
  784. next;
  785. }
  786. if ($line =~ /^[(.+)]$/) {
  787. # New section
  788. if ($writeMode) {
  789. # First, finish writing everything in the previous section
  790. my $h = (defined $section) ? $section : $hash;
  791. my @add;
  792. if ($rule ne 'list') {
  793. foreach my $key (keys %{$h}) {
  794. if (!$written{$key} && !ref($h->{$key})) {
  795. push @add, "$key $h->{$key}";
  796. }
  797. }
  798. } else {
  799. foreach (@{$h}) {
  800. push @add, $_ if (!$written{$_});
  801. }
  802. }
  803. # Add after the first non-empty line from the end
  804. my $linesFromEnd;
  805. for (my $i = @lines - 1; $i >= 0; $i--) {
  806. if ($lines[$i] ne '') {
  807. $linesFromEnd = @lines - $i - 1;
  808. for (my $j = $i + 1; $j < @lines; $j++) {
  809. delete $lines[$j];
  810. }
  811. push @lines, @add;
  812. for (my $j = 0; $j < $linesFromEnd; $j++) {
  813. push @lines, '';
  814. }
  815. last;
  816. }
  817. }
  818. undef %written;
  819. }
  820. # Parse the new section
  821. $secname = $1;
  822. $rule = $rules->{$secname};
  823. if ($writeMode) {
  824. $section = $hash->{$secname};
  825. push @lines, $line;
  826. $sectionsWritten{$secname} = 1;
  827. } else {
  828. if ($rule ne 'list') {
  829. $section = {};
  830. } else {
  831. $section = [];
  832. }
  833. $hash->{$secname} = $section;
  834. }
  835. } elsif ($rule ne 'list') {
  836. # Line is a key-value pair
  837. my ($key, $val) = split / /, $line, 2;
  838. my $h = (defined $section) ? $section : $hash;
  839. if ($writeMode) {
  840. # Delete line if value doesn't exist
  841. if (exists $h->{$key}) {
  842. if (!defined $h->{$key}) {
  843. push @lines, $key;
  844. } else {
  845. push @lines, "$key $h->{$key}";
  846. }
  847. $written{$key} = 1;
  848. }
  849. } else {
  850. $h->{$key} = $val;
  851. }
  852. } else {
  853. # Line is part of a list
  854. if ($writeMode) {
  855. # Add line only if it exists in the hash
  856. push @lines, $line if (defined(binFind($section, $line)));
  857. $written{$line} = 1;
  858. } else {
  859. push @{$section}, $line;
  860. }
  861. }
  862. }
  863. }
  864. if ($writeMode) {
  865. # Add stuff that haven't already been added
  866. my $h = (defined $section) ? $section : $hash;
  867. if ($rule ne 'list') {
  868. foreach my $key (keys %{$h}) {
  869. if (!$written{$key} && !ref($h->{$key})) {
  870. push @lines, "$key $h->{$key}";
  871. }
  872. }
  873. } else {
  874. foreach my $line (@{$h}) {
  875. push @lines, $line if (!$written{$line});
  876. }
  877. }
  878. # Write sections that aren't already in the file
  879. foreach my $section (keys %{$hash}) {
  880. next if (!ref($hash->{$section}) || $sectionsWritten{$section});
  881. push @lines, "", "[$section]";
  882. if ($rules->{$section} eq 'list') {
  883. push @lines, @{$hash->{$section}};
  884. } else {
  885. foreach my $key (keys %{$hash->{$section}}) {
  886. push @lines, "$key $hash->{$section}{$key}";
  887. }
  888. }
  889. }
  890. open($f, ">:utf8", $file);
  891. print $f join("n", @lines) . "n";
  892. close $f;
  893. }
  894. return 1;
  895. }
  896. sub writeDataFile {
  897. my $file = shift;
  898. my $r_hash = shift;
  899. my ($key,$value);
  900. open(FILE, ">>:utf8", $file);
  901. foreach (keys %{$r_hash}) {
  902. if ($_ ne "") {
  903. print FILE $_;
  904. print FILE " $$r_hash{$_}" if $$r_hash{$_} ne '';
  905. print FILE "n";
  906. }
  907. }
  908. close FILE;
  909. }
  910. sub writeDataFileIntact {
  911. my $file = shift;
  912. my $r_hash = shift;
  913. my $no_undef = shift;
  914. my (@lines, $key, $value, $inBlock, $commentBlock, %blocks);
  915. my $reader = new Utils::TextReader($file);
  916. while (!$reader->eof()) {
  917. my $lines = $reader->readLine();
  918. $lines =~ s/x{FEFF}//g;
  919. $lines =~ s/[rn]//g; # Remove line endings
  920. if ($lines =~ /^[st]*#/ || $lines =~ /^[st]*$/ || $lines =~ /^!include( |$)/) {
  921. push @lines, $lines;
  922. next;
  923. }
  924. $lines =~ s/^[ts]*//; # Remove leading tabs and whitespace
  925. $lines =~ s/s+$//g; # Remove trailing whitespace
  926. if (!defined $commentBlock && $lines =~ /^/*/) {
  927. push @lines, "$lines";
  928. $commentBlock = 1;
  929. next;
  930. } elsif ($lines =~ m/*/$/) {
  931. push @lines, "$lines";
  932. undef $commentBlock;
  933. next;
  934. } elsif ($commentBlock) {
  935. push @lines, "$lines";
  936. next;
  937. } elsif (!defined $inBlock && $lines =~ /{$/) {
  938. # Begin of block
  939. $lines =~ s/ *{$//;
  940. ($key, $value) = $lines =~ /^(.*?) (.*)/;
  941. $key = $lines if ($key eq '');
  942. if (!exists $blocks{$key}) {
  943. $blocks{$key} = 0;
  944. } else {
  945. $blocks{$key}++;
  946. }
  947. if ($key ne 'teleportAuto'){
  948. $inBlock = "${key}_$blocks{$key}";
  949. } else {
  950. $inBlock = "${key}";
  951. }
  952. my $line = $key;
  953. $line .= " $r_hash->{$inBlock}" if ($r_hash->{$inBlock} ne '');
  954. push @lines, "$line {";
  955. } elsif (defined $inBlock && $lines eq "}") {
  956. # End of block
  957. undef $inBlock;
  958. push @lines, "}";
  959. } else {
  960. # Option
  961. ($key, $value) = $lines =~ /^(.*?) (.*)/;
  962. if ($key eq "") {
  963. $key = $lines;
  964. $key =~ s/ *$//;
  965. }
  966. if (defined $inBlock) {
  967. my $realKey = "${inBlock}_${key}";
  968. my $line = "t$key";
  969. $line .= " $r_hash->{$realKey}" if ($r_hash->{$realKey} ne '');
  970. push @lines, $line;
  971. } else {
  972. my $line = $key;
  973. $line .= " $r_hash->{$key}" if ($r_hash->{$key} ne '');
  974. push @lines, $line;
  975. }
  976. }
  977. }
  978. close FILE;
  979. open FILE, ">:utf8", $file;
  980. print FILE join("n", @lines) . "n";
  981. close FILE;
  982. }
  983. sub writeDataFileIntact2 {
  984. my $file = shift;
  985. my $r_hash = shift;
  986. my $data;
  987. my $key;
  988. my $reader = new Utils::TextReader($file);
  989. while (!$reader->eof()) {
  990. my $line = $reader->readLine();
  991. $line =~ s/x{FEFF}//g;
  992. if ($line =~ /^#/ || $line =~ /^n/ || $line =~ /^r/) {
  993. $data .= $line;
  994. next;
  995. }
  996. ($key) = $line =~ /^(w+)/;
  997. $data .= $key;
  998. $data .= " $$r_hash{$key}{'timeout'}" if $$r_hash{$key}{'timeout'} ne '';
  999. $data .= "n";
  1000. }
  1001. close FILE;
  1002. open(FILE, ">:utf8", $file);
  1003. print FILE $data;
  1004. close FILE;
  1005. }
  1006. sub writePortalsLOS {
  1007. my $file = shift;
  1008. my $r_hash = shift;
  1009. open(FILE, "+> $file");
  1010. foreach my $key (sort keys %{$r_hash}) {
  1011. next if (!$$r_hash{$key} || !(keys %{$$r_hash{$key}}));
  1012. print FILE $key;
  1013. foreach (keys %{$$r_hash{$key}}) {
  1014. print FILE " $_ $$r_hash{$key}{$_}";
  1015. }
  1016. print FILE "n";
  1017. }
  1018. close FILE;
  1019. }
  1020. sub writeSectionedFileIntact {
  1021. my $file = shift;
  1022. my $r_hash = shift;
  1023. my $section = "";
  1024. my @lines;
  1025. my $reader = new Utils::TextReader($file);
  1026. while (!$reader->eof()) {
  1027. my $line = $reader->readLine();
  1028. $line =~ s/[rn]//g;
  1029. if ($line =~ /^#/ || $line =~ /^ *$/) {
  1030. push @lines, $line;
  1031. next;
  1032. }
  1033. if ($line =~ /^[(.*)]$/) {
  1034. $section = $1;
  1035. push @lines, $line;
  1036. } else {
  1037. my ($key, $value);
  1038. if ($line =~ / /) {
  1039. ($key) = $line =~ /^(.*?) /;
  1040. } else {
  1041. $key = $line;
  1042. }
  1043. $value = $r_hash->{$section}{$key};
  1044. push @lines, "$key $value";
  1045. }
  1046. }
  1047. close FILE;
  1048. open(FILE, ">:utf8", $file);
  1049. print FILE join("n", @lines) . "n";
  1050. close FILE;
  1051. }
  1052. sub updateMonsterLUT {
  1053. my $file = shift;
  1054. my $ID = shift;
  1055. my $name = shift;
  1056. open FILE, ">>:utf8", $file;
  1057. print FILE "$ID $namen";
  1058. close FILE;
  1059. }
  1060. sub updatePortalLUT {
  1061. my ($file, $src, $x1, $y1, $dest, $x2, $y2) = @_;
  1062. open FILE, ">> $file";
  1063. print FILE "$src $x1 $y1 $dest $x2 $y2n";
  1064. close FILE;
  1065. }
  1066. sub updateNPCLUT {
  1067. my ($file, $location, $name) = @_;
  1068. return unless $name;
  1069. open FILE, ">>:utf8", $file;
  1070. print FILE "$location $namen";
  1071. close FILE;
  1072. }
  1073. 1;