- Joined
- Nov 7, 2014
- Messages
- 571
FileDump - dump data to disk
Using the
So it turns out the call to the
the
We probably want only
That's why FileDump comes with a simple Perl script (feel free to rewrite it in your favoirite language, except Python )
which searches for the preload calls, extracts only the text we wrote, and saves it into another file.
FileDump.j
jass-fd.pl:
demo
If we run the demo in WE we should have a the file <path-to-war3>\save\hero-stats.jass-dump
with the content:
And if we run the above perl script with:
we should get another file called: hero-stats.txt with the content:
I.e only what we wrote with the .writeln method calls.
PS: Blademaster IMBA!
Using the
Preload*
natives it is possible to generate "preload" scripts (.pld) but we can also use those natives to dump some data to disk.
JASS:
// if we run the following code
//
call PreloadGenClear()
call PreloadGenStart()
call Preload("Hello jassy-world")
call PreloadGenEnd("save\\my-file-name.pld")
// and we open <path-to-war3>\save\my-file-name.pld we should see:
function PreloadFiles takes nothing returns nothing
call Preload( "Hello jassy-world" )
call PreloadEnd( 0.0 )
endfunction
So it turns out the call to the
Preload(...)
native becomes exactly that... a call tothe
call Preload( "Hello jassy-world" )
preload native. But that's kind of inconvenient.We probably want only
Hello jassy-world
, the text that we wrote to appear, and not have the rest.That's why FileDump comes with a simple Perl script (feel free to rewrite it in your favoirite language, except Python )
which searches for the preload calls, extracts only the text we wrote, and saves it into another file.
FileDump.j
JASS:
//! novjass
FileDump has a very simple API:
path is the file path to the file we want to write to
we can have nested directories, eg:
"save\\my-dumps\\dump-1.jass-dump"
and the Preload natives will create them if they do not exist
static method open takes string path returns Fd
calls open with the default path: "save" + PS + "output.jass-dump"
where PS is the PATH_SEPARATOR, which might be different for Mac OS users??
static method open_default takes nothing returns Fd
warns if the line is longer than MY_OS_MAX_PATH_LENGTH in DEBUG_MODE
method writeln takes string line returns nothing
creates/overwrites the file with the lines we passed to writeln
NOTE: if this method is not called nothing will be written to disk
method close takes nothing returns nothing
//! endnovjass
library FileDump
globals
private constant string MY_OS_PATH_SEPARATOR = "\\"
// warn if we try to write more than that many characters per line
private constant integer MY_OS_MAX_PATH_LENGTH = 260
endglobals
struct Fd
static string PS = MY_OS_PATH_SEPARATOR
// we use the save directory by default because it exists (and we don't want to pollute the
// war3 directory with our own directory)
// and it should be less cluttered compared to the other directories, because
// it should only contain other directories and no files, which we might overwrite...
private static string DEFAULT_FILE = "save" + PS + "output.jass-dump"
private static hashtable file_lines = InitHashtable()
private static thistype ef_fd
private integer lines_count = 0
private integer line_index = 0
readonly string path = DEFAULT_FILE
static method open takes string path returns thistype
local thistype this = thistype.allocate()
set this.path = path
return this
endmethod
static method open_default takes nothing returns thistype
return thistype.allocate()
endmethod
method writeln takes string str returns nothing
local integer str_len = StringLength(str)
static if DEBUG_MODE then
if str_len > MY_OS_MAX_PATH_LENGTH then
call BJDebugMsg("|cffFF0000warning: could not write all characters of string \"" + SubString(str, 0, 64) + "\"... with length " + I2S(str_len) + " to file " + this.path + "|r")
endif
endif
call SaveStr(file_lines, this, lines_count, str)
set lines_count = lines_count + 1
endmethod
private static method dump_exec takes nothing returns nothing
local integer MAX_LINES_PER_EXEC = 5000
local thistype fd = ef_fd
local integer i
local string str
local integer str_len
set i = 1
loop
exitwhen i > MAX_LINES_PER_EXEC
set str = LoadStr(file_lines, fd, fd.line_index)
call Preload(str)
set fd.line_index = fd.line_index + 1
if fd.line_index == fd.lines_count then
return
endif
set i = i + 1
endloop
call ExecuteFunc(dump_exec.name)
endmethod
private method dump takes nothing returns nothing
set ef_fd = this
call ExecuteFunc(dump_exec.name)
endmethod
method close takes nothing returns nothing
call PreloadGenClear()
call PreloadGenStart()
call dump()
call PreloadGenEnd(path)
call this.destroy()
endmethod
endstruct
endlibrary
jass-fd.pl:
Code:
use strict;
use warnings;
my $args_count = @ARGV;
my @files;
my $delete_source_files = '';
# parse args
{
my $abort = '';
for my $arg (@ARGV) {
if (substr($arg, 0, 2) eq '--') {
my $option_name = substr($arg, 2);
if ($option_name eq 'delete-source-files') {
$delete_source_files = 1;
}
else {
$abort = 1;
print "error: unknown option: '--$option_name'\n";
}
}
elsif (!-e $arg) {
$abort = 1;
print "error: file does not exist: '$arg'\n";
}
else {
push(@files, $arg);
}
}
if ($abort) {
exit(1);
}
}
# extract the lines from the supplied files
{
my $PATTERN_START = 'call Preload( "';
my $start_offset = length($PATTERN_START);
my $PATTERN_END = '" )';
for my $in_file_name (@files) {
open(my $in_file, '<', $in_file_name) || die($!);
my $out_file_name = substr($in_file_name, 0, rindex($in_file_name, '.'));
$out_file_name .= ".txt";
open(my $out_file, '>', $out_file_name) || die($!);
for (;;) {
my $line = readline($in_file);
last if !defined($line);
# we want to find the lines matching:
# ' call Preload( "<data>" )'
# and extract the data
my $i = index($line, $PATTERN_START);
my $j = rindex($line, $PATTERN_END);
if ($i != -1 && $j != -1) {
my $start_index = $i + $start_offset;
my $chars_count = $j - $start_index;
my $data = substr($line, $start_index, $chars_count);
print {$out_file} $data, "\n";
}
}
}
}
if ($delete_source_files) {
for my $file (@files) {
unlink($file) || die($!);
}
}
demo
JASS:
library demo initializer init requires FileDump
globals
integer array heroes
constant integer MAX_HEROES = 24
integer max_heroes
endglobals
function heroes_init takes nothing returns nothing
set heroes[1] = 'Hamg'
set heroes[2] = 'Hmkg'
set heroes[3] = 'Hpal'
set heroes[4] = 'Hblm'
set heroes[5] = 'Ofar'
set heroes[6] = 'Otch'
set heroes[7] = 'Obla'
set heroes[8] = 'Oshd'
set heroes[9] = 'Udea'
set heroes[10] = 'Ulic'
set heroes[11] = 'Udre'
set heroes[12] = 'Ucrl'
set heroes[13] = 'Edem'
set heroes[14] = 'Ekee'
set heroes[15] = 'Emoo'
set heroes[16] = 'Ewar'
set heroes[17] = 'Nalc'
set heroes[18] = 'Nbst'
set heroes[19] = 'Nbrn'
set heroes[20] = 'Nfir'
set heroes[21] = 'Nngs'
set heroes[22] = 'Npbm'
set heroes[23] = 'Nplh'
set heroes[24] = 'Ntin'
set max_heroes = MAX_HEROES
endfunction
struct MaxHeroStats
integer str = 0
integer str_hero_id
integer agi = 0
integer agi_hero_id
integer int = 0
integer int_hero_id
integer all_stats = 0
integer all_stats_hero_id
endstruct
globals
MaxHeroStats array max_stats[10]
endglobals
function get_hero_name takes integer hero_id returns string
local unit u = CreateUnit(Player(0), hero_id, 0.0, 0.0, 0.0)
local string result = GetUnitName(u)
call RemoveUnit(u)
set u = null
return result
endfunction
function get_heroes_stats takes nothing returns nothing
local Fd fd
local unit u
local MaxHeroStats ms
local integer hero_index
local integer hero_id
local integer level
local integer str
local integer agi
local integer int
local integer stats_sum
local string line
local integer MAX_LEVEL = 10
set level = 1
loop
exitwhen level > MAX_LEVEL
set ms = MaxHeroStats.create()
set max_stats[level] = ms
set hero_index = 1
loop
exitwhen hero_index > max_heroes
set hero_id = heroes[hero_index]
set u = CreateUnit(Player(0), hero_id, 0.0, 0.0, 0.0)
call SetHeroLevelBJ(u, level, false)
set str = GetHeroStr(u, /*include-bonuses*/ false)
set agi = GetHeroAgi(u, /*include-bonuses*/ false)
set int = GetHeroInt(u, /*include-bonuses*/ false)
set stats_sum = str + agi + int
call RemoveUnit(u)
if ms.str < str then
set ms.str = str
set ms.str_hero_id = hero_id
endif
if ms.agi < agi then
set ms.agi = agi
set ms.agi_hero_id = hero_id
endif
if ms.int < int then
set ms.int = int
set ms.int_hero_id = hero_id
endif
if ms.all_stats < stats_sum then
set ms.all_stats = stats_sum
set ms.all_stats_hero_id = hero_id
endif
set hero_index = hero_index + 1
endloop
set level = level + 1
endloop
// Save the stats
set fd = Fd.open("save" + Fd.PS + "hero-stats.jass-dump")
set level = 1
loop
exitwhen level > MAX_LEVEL
set ms = max_stats[level]
set line = "[Lvl " + I2S(level) + "]: "
set line = line + "max-str: " + get_hero_name(ms.str_hero_id) + "(" + I2S(ms.str) + "); "
set line = line + "max-agi: " + get_hero_name(ms.agi_hero_id) + "(" + I2S(ms.agi) + "); "
set line = line + "max-int: " + get_hero_name(ms.int_hero_id) + "(" + I2S(ms.int) + "); "
set line = line + "max-stats: " + get_hero_name(ms.all_stats_hero_id) + "(" + I2S(ms.all_stats) + "); "
call fd.writeln(line)
set level = level + 1
endloop
// We must call close, otherwise nothing will be written to disk
call fd.close()
set u = null
endfunction
function init takes nothing returns nothing
call ExecuteFunc(heroes_init.name)
call ExecuteFunc(get_heroes_stats.name)
endfunction
endlibrary
If we run the demo in WE we should have a the file <path-to-war3>\save\hero-stats.jass-dump
with the content:
JASS:
function PreloadFiles takes nothing returns nothing
call Preload( "[Lvl 1]: max-str: Crypt Lord(26); max-agi: Blademaster(23); max-int: Naga Sea Witch(22); max-stats: Blademaster(57); " )
call Preload( "[Lvl 2]: max-str: Crypt Lord(29); max-agi: Blademaster(24); max-int: Naga Sea Witch(25); max-stats: Blademaster(62); " )
call Preload( "[Lvl 3]: max-str: Crypt Lord(32); max-agi: Blademaster(26); max-int: Naga Sea Witch(28); max-stats: Blademaster(68); " )
call Preload( "[Lvl 4]: max-str: Crypt Lord(35); max-agi: Blademaster(28); max-int: Naga Sea Witch(31); max-stats: Blademaster(74); " )
call Preload( "[Lvl 5]: max-str: Crypt Lord(38); max-agi: Blademaster(30); max-int: Naga Sea Witch(34); max-stats: Blademaster(81); " )
call Preload( "[Lvl 6]: max-str: Crypt Lord(42); max-agi: Blademaster(31); max-int: Lich(37); max-stats: Blademaster(86); " )
call Preload( "[Lvl 7]: max-str: Crypt Lord(45); max-agi: Blademaster(33); max-int: Lich(40); max-stats: Blademaster(92); " )
call Preload( "[Lvl 8]: max-str: Crypt Lord(48); max-agi: Blademaster(35); max-int: Lich(43); max-stats: Blademaster(98); " )
call Preload( "[Lvl 9]: max-str: Crypt Lord(51); max-agi: Blademaster(37); max-int: Lich(47); max-stats: Blademaster(105); " )
call Preload( "[Lvl 10]: max-str: Crypt Lord(54); max-agi: Blademaster(38); max-int: Lich(50); max-stats: Blademaster(110); " )
call PreloadEnd( 0.0 )
endfunction
And if we run the above perl script with:
perl jass-fd.pl hero-stats.jass-dump
we should get another file called: hero-stats.txt with the content:
JASS:
[Lvl 1]: max-str: Crypt Lord(26); max-agi: Blademaster(23); max-int: Naga Sea Witch(22); max-stats: Blademaster(57);
[Lvl 2]: max-str: Crypt Lord(29); max-agi: Blademaster(24); max-int: Naga Sea Witch(25); max-stats: Blademaster(62);
[Lvl 3]: max-str: Crypt Lord(32); max-agi: Blademaster(26); max-int: Naga Sea Witch(28); max-stats: Blademaster(68);
[Lvl 4]: max-str: Crypt Lord(35); max-agi: Blademaster(28); max-int: Naga Sea Witch(31); max-stats: Blademaster(74);
[Lvl 5]: max-str: Crypt Lord(38); max-agi: Blademaster(30); max-int: Naga Sea Witch(34); max-stats: Blademaster(81);
[Lvl 6]: max-str: Crypt Lord(42); max-agi: Blademaster(31); max-int: Lich(37); max-stats: Blademaster(86);
[Lvl 7]: max-str: Crypt Lord(45); max-agi: Blademaster(33); max-int: Lich(40); max-stats: Blademaster(92);
[Lvl 8]: max-str: Crypt Lord(48); max-agi: Blademaster(35); max-int: Lich(43); max-stats: Blademaster(98);
[Lvl 9]: max-str: Crypt Lord(51); max-agi: Blademaster(37); max-int: Lich(47); max-stats: Blademaster(105);
[Lvl 10]: max-str: Crypt Lord(54); max-agi: Blademaster(38); max-int: Lich(50); max-stats: Blademaster(110);
I.e only what we wrote with the .writeln method calls.
PS: Blademaster IMBA!