diff --git a/data/blueprints/library/test/ecosystem/golden/gui_quantum-build.csv b/data/blueprints/library/test/ecosystem/golden/gui_quantum-2-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/gui_quantum-build.csv rename to data/blueprints/library/test/ecosystem/golden/gui_quantum-2-build.csv diff --git a/data/blueprints/library/test/ecosystem/golden/gui_quantum-place.csv b/data/blueprints/library/test/ecosystem/golden/gui_quantum-3-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/gui_quantum-place.csv rename to data/blueprints/library/test/ecosystem/golden/gui_quantum-3-place.csv diff --git a/data/blueprints/library/test/ecosystem/golden/gui_quantum-4-query.csv b/data/blueprints/library/test/ecosystem/golden/gui_quantum-4-query.csv new file mode 100644 index 000000000..811f7b3b0 --- /dev/null +++ b/data/blueprints/library/test/ecosystem/golden/gui_quantum-4-query.csv @@ -0,0 +1,6 @@ +#query label(query) + + + +,,"{givename name=""foo dumper""}" +,,"{givename name=""foo""}" diff --git a/data/blueprints/library/test/ecosystem/golden/meta-dig.csv b/data/blueprints/library/test/ecosystem/golden/meta-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/meta-dig.csv rename to data/blueprints/library/test/ecosystem/golden/meta-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/golden/tracks-carve.csv b/data/blueprints/library/test/ecosystem/golden/tracks-2-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/tracks-carve.csv rename to data/blueprints/library/test/ecosystem/golden/tracks-2-carve.csv diff --git a/data/blueprints/library/test/ecosystem/golden/transform-dig.csv b/data/blueprints/library/test/ecosystem/golden/transform-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/transform-dig.csv rename to data/blueprints/library/test/ecosystem/golden/transform-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/golden/transform-construct.csv b/data/blueprints/library/test/ecosystem/golden/transform-2-construct.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/transform-construct.csv rename to data/blueprints/library/test/ecosystem/golden/transform-2-construct.csv diff --git a/data/blueprints/library/test/ecosystem/golden/transform-build.csv b/data/blueprints/library/test/ecosystem/golden/transform-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/transform-build.csv rename to data/blueprints/library/test/ecosystem/golden/transform-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-dig.csv b/data/blueprints/library/test/ecosystem/in/basic-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-dig.csv rename to data/blueprints/library/test/ecosystem/in/basic-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-carve.csv b/data/blueprints/library/test/ecosystem/in/basic-2-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-carve.csv rename to data/blueprints/library/test/ecosystem/in/basic-2-carve.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-build.csv b/data/blueprints/library/test/ecosystem/in/basic-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-build.csv rename to data/blueprints/library/test/ecosystem/in/basic-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-place.csv b/data/blueprints/library/test/ecosystem/in/basic-4-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-place.csv rename to data/blueprints/library/test/ecosystem/in/basic-4-place.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-zone.csv b/data/blueprints/library/test/ecosystem/in/basic-5-zone.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-zone.csv rename to data/blueprints/library/test/ecosystem/in/basic-5-zone.csv diff --git a/data/blueprints/library/test/ecosystem/in/buildings-dig.csv b/data/blueprints/library/test/ecosystem/in/buildings-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/buildings-dig.csv rename to data/blueprints/library/test/ecosystem/in/buildings-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/buildings-construct.csv b/data/blueprints/library/test/ecosystem/in/buildings-2-construct.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/buildings-construct.csv rename to data/blueprints/library/test/ecosystem/in/buildings-2-construct.csv diff --git a/data/blueprints/library/test/ecosystem/in/buildings-build.csv b/data/blueprints/library/test/ecosystem/in/buildings-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/buildings-build.csv rename to data/blueprints/library/test/ecosystem/in/buildings-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/fortifications-dig.csv b/data/blueprints/library/test/ecosystem/in/fortifications-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/fortifications-dig.csv rename to data/blueprints/library/test/ecosystem/in/fortifications-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/fortifications-smooth.csv b/data/blueprints/library/test/ecosystem/in/fortifications-2-smooth.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/fortifications-smooth.csv rename to data/blueprints/library/test/ecosystem/in/fortifications-2-smooth.csv diff --git a/data/blueprints/library/test/ecosystem/in/fortifications-carve.csv b/data/blueprints/library/test/ecosystem/in/fortifications-3-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/fortifications-carve.csv rename to data/blueprints/library/test/ecosystem/in/fortifications-3-carve.csv diff --git a/data/blueprints/library/test/ecosystem/in/gui_quantum-build.csv b/data/blueprints/library/test/ecosystem/in/gui_quantum-2-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/gui_quantum-build.csv rename to data/blueprints/library/test/ecosystem/in/gui_quantum-2-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/gui_quantum-place.csv b/data/blueprints/library/test/ecosystem/in/gui_quantum-3-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/gui_quantum-place.csv rename to data/blueprints/library/test/ecosystem/in/gui_quantum-3-place.csv diff --git a/data/blueprints/library/test/ecosystem/in/gui_quantum-4-query.csv b/data/blueprints/library/test/ecosystem/in/gui_quantum-4-query.csv new file mode 100644 index 000000000..eb287fc8a --- /dev/null +++ b/data/blueprints/library/test/ecosystem/in/gui_quantum-4-query.csv @@ -0,0 +1 @@ +#query diff --git a/data/blueprints/library/test/ecosystem/in/meta-dig.csv b/data/blueprints/library/test/ecosystem/in/meta-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/meta-dig.csv rename to data/blueprints/library/test/ecosystem/in/meta-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/stockpiles-place.csv b/data/blueprints/library/test/ecosystem/in/stockpiles-2-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/stockpiles-place.csv rename to data/blueprints/library/test/ecosystem/in/stockpiles-2-place.csv diff --git a/data/blueprints/library/test/ecosystem/in/tracks-dig.csv b/data/blueprints/library/test/ecosystem/in/tracks-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/tracks-dig.csv rename to data/blueprints/library/test/ecosystem/in/tracks-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/tracks-carve.csv b/data/blueprints/library/test/ecosystem/in/tracks-2-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/tracks-carve.csv rename to data/blueprints/library/test/ecosystem/in/tracks-2-carve.csv diff --git a/data/blueprints/library/test/ecosystem/in/tracks-build.csv b/data/blueprints/library/test/ecosystem/in/tracks-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/tracks-build.csv rename to data/blueprints/library/test/ecosystem/in/tracks-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/transform-dig.csv b/data/blueprints/library/test/ecosystem/in/transform-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/transform-dig.csv rename to data/blueprints/library/test/ecosystem/in/transform-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/transform-construct.csv b/data/blueprints/library/test/ecosystem/in/transform-2-construct.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/transform-construct.csv rename to data/blueprints/library/test/ecosystem/in/transform-2-construct.csv diff --git a/data/blueprints/library/test/ecosystem/in/transform-build.csv b/data/blueprints/library/test/ecosystem/in/transform-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/transform-build.csv rename to data/blueprints/library/test/ecosystem/in/transform-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/zones-zone.csv b/data/blueprints/library/test/ecosystem/in/zones-2-zone.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/zones-zone.csv rename to data/blueprints/library/test/ecosystem/in/zones-2-zone.csv diff --git a/docs/changelog.txt b/docs/changelog.txt index a28ae5f9f..c7c36ac8b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,6 +43,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `blueprint`: record stockpile/building/zone names in blueprints - `blueprint`: record room sizes in blueprints - `blueprint`: generate meta blueprints to reduce the number of blueprints you have to apply +- `blueprint`: support splitting the output file into phases grouped by when they can be applied +- `blueprint`: when splitting output files, number them so they sort into the order you should apply them in - `ls`: indent tag listings and wrap them in the right column for better readability - `ls`: new ``--exclude`` option for hiding matched scripts from the output. this can be especially useful for modders who don't want their mod scripts to be included in ``ls`` output. - `digtype`: new ``-z`` option for digtype to restrict designations to the current z-level and down diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 3a5e5a0c5..6c38a6152 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -146,5 +146,10 @@ The ``--splitby`` flag can take any of the following values: ``none`` Writes all blueprints into a single file. This is the standard format for quickfort fortress blueprint bundles and is the default. +``group`` + Creates one file per group of blueprints that can be played back at the same + time (without have to unpause the game and let dwarves fulfill jobs between + blueprint runs). ``phase`` - Creates a separate file for each phase. + Creates a separate file for each phase. Implies ``--nometa`` since meta + blueprints can't combine blueprints that are in separate files. diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index 361aaa1ca..509b1e6bc 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -5,8 +5,6 @@ * Written by cdombroski. */ -#include -#include #include #include @@ -1011,11 +1009,23 @@ static const char * get_tile_zone(const df::coord &pos, return add_expansion_syntax(zone, get_zone_keys(zone)); } -static string csv_sanitize(const string &str) { - static const std::regex pattern("\""); - static const string replacement("\"\""); +// surrounds the given string in quotes and replaces internal double quotes (") +// with double double quotes ("") (as per the csv spec) +static string csv_quote(const string &str) { + std::ostringstream outstr; + outstr << "\""; + + size_t start = 0; + auto end = str.find('"'); + while (end != std::string::npos) { + outstr << str.substr(start, end - start); + outstr << "\"\""; + start = end + 1; + end = str.find('"', start); + } + outstr << str.substr(start, end) << "\""; - return std::regex_replace(str, pattern, replacement); + return outstr.str(); } static const char * get_tile_query(const df::coord &pos, @@ -1042,11 +1052,11 @@ static const char * get_tile_query(const df::coord &pos, std::ostringstream str; if (bld_name.size()) - str << "{givename name=\"" << csv_sanitize(bld_name) << "\"}"; + str << "{givename name=" + csv_quote(bld_name) + "}"; if (zone_name.size()) - str << "{namezone name=\"" << csv_sanitize(zone_name) << "\"}"; + str << "{namezone name=" + csv_quote(zone_name) + "}"; - return cache(str); + return cache(csv_quote(str.str())); } static const char * get_tile_rooms(const df::coord &, const tile_context &ctx) { @@ -1095,11 +1105,12 @@ static bool create_output_dir(color_ostream &out, static bool get_filename(string &fname, color_ostream &out, blueprint_options opts, // copy because we can't const - const string &phase) { + const string &phase, + int32_t ordinal) { auto L = Lua::Core::State; Lua::StackUnwinder top(L); - if (!lua_checkstack(L, 3) || + if (!lua_checkstack(L, 4) || !Lua::PushModulePublic( out, L, "plugins.blueprint", "get_filename")) { out.printerr("Failed to load blueprint Lua code\n"); @@ -1108,8 +1119,9 @@ static bool get_filename(string &fname, Lua::Push(L, &opts); Lua::Push(L, phase); + Lua::Push(L, ordinal); - if (!Lua::SafeCall(out, L, 2, 1)) { + if (!Lua::SafeCall(out, L, 3, 1)) { out.printerr("Failed Lua call to get_filename\n"); return false; } @@ -1228,9 +1240,9 @@ static bool write_blueprint(color_ostream &out, std::map &output_files, const blueprint_options &opts, const blueprint_processor &processor, - bool pretty) { + bool pretty, int32_t ordinal) { string fname; - if (!get_filename(fname, out, opts, processor.phase)) + if (!get_filename(fname, out, opts, processor.phase, ordinal)) return false; if (!output_files.count(fname)) output_files[fname] = new ofstream(fname, ofstream::trunc); @@ -1249,9 +1261,10 @@ static bool write_blueprint(color_ostream &out, static void write_meta_blueprint(color_ostream &out, std::map &output_files, const blueprint_options &opts, - const std::vector & meta_phases) { + const std::vector & meta_phases, + int32_t ordinal) { string fname; - get_filename(fname, out, opts, meta_phases.front()); + get_filename(fname, out, opts, meta_phases.front(), ordinal); ofstream &ofile = *output_files[fname]; ofile << "#meta label("; @@ -1285,7 +1298,7 @@ static void add_processor(vector &processors, static bool do_transform(color_ostream &out, const df::coord &start, const df::coord &end, - blueprint_options &opts, + blueprint_options opts, // copy so we can munge it vector &filenames) { // empty map instances to pass to emplace() below static const bp_area EMPTY_AREA; @@ -1368,16 +1381,26 @@ static bool do_transform(color_ostream &out, if (meta_phases.size() <= 1) opts.nometa = true; + bool in_meta = false; + int32_t ordinal = 0; std::map output_files; for (blueprint_processor &processor : processors) { if (processor.mapdata.empty() && !processor.force_create) continue; - if (!write_blueprint(out, output_files, opts, processor, pretty)) + bool meta_phase = is_meta_phase(out, opts, processor.phase); + if (!in_meta) + ++ordinal; + if (in_meta && !meta_phase) { + write_meta_blueprint(out, output_files, opts, meta_phases, ordinal); + ++ordinal; + } + in_meta = meta_phase; + if (!write_blueprint(out, output_files, opts, processor, pretty, + ordinal)) break; } - if (!opts.nometa) { - write_meta_blueprint(out, output_files, opts, meta_phases); - } + if (in_meta) + write_meta_blueprint(out, output_files, opts, meta_phases, ordinal); for (auto &it : output_files) { filenames.push_back(it.first); diff --git a/plugins/lua/blueprint.lua b/plugins/lua/blueprint.lua index e3b36d9fe..718b211e8 100644 --- a/plugins/lua/blueprint.lua +++ b/plugins/lua/blueprint.lua @@ -31,6 +31,7 @@ valid_formats = utils.invert(valid_formats_list) local valid_split_strategies_list = { 'none', + 'group', 'phase', } valid_split_strategies = utils.invert(valid_split_strategies_list) @@ -143,6 +144,10 @@ local function process_args(opts, args) return end + if opts.split_strategy == 'phase' then + opts.nometa = true + end + return positionals end @@ -200,7 +205,7 @@ function is_meta_phase(opts, phase) end -- returns the name of the output file for the given context -function get_filename(opts, phase) +function get_filename(opts, phase, ordinal) local fullname = 'blueprints/' .. opts.name local _,_,basename = fullname:find('/([^/]+)/?$') if not basename then @@ -210,14 +215,13 @@ function get_filename(opts, phase) if fullname:endswith('/') then fullname = fullname .. basename end - if opts.split_strategy == 'phase' then - if is_meta_phase(opts, phase) then - phase = 'meta' - end - return ('%s-%s.csv'):format(fullname, phase) + if opts.split_strategy == 'none' then + return ('%s.csv'):format(fullname) + end + if is_meta_phase(opts, phase) then + phase = 'meta' end - -- no splitting - return ('%s.csv'):format(fullname) + return ('%s-%d-%s.csv'):format(fullname, ordinal, phase) end -- compatibility with old exported API. diff --git a/scripts b/scripts index 6f0d7ea22..0be5cc0ea 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 6f0d7ea22edcd68a1716d843e3ff439a75f23c35 +Subproject commit 0be5cc0eaf625029ef26392224db5c3d2822a63d diff --git a/test/plugins/blueprint.lua b/test/plugins/blueprint.lua index 73ec33eda..6146aea3f 100644 --- a/test/plugins/blueprint.lua +++ b/test/plugins/blueprint.lua @@ -101,7 +101,7 @@ function test.parse_gui_commandline() opts = {} b.parse_gui_commandline(opts, {'--splitby', 'phase'}) expect.table_eq({auto_phase=true, format='minimal', split_strategy='phase', - name='blueprint'}, + name='blueprint', nometa=true}, opts) expect.error_match('unknown split_strategy', @@ -241,16 +241,16 @@ end function test.get_filename() local opts = {name='a', split_strategy='none'} - expect.eq('blueprints/a.csv', b.get_filename(opts, 'dig')) + expect.eq('blueprints/a.csv', b.get_filename(opts, 'dig', 1)) opts = {name='a/', split_strategy='none'} - expect.eq('blueprints/a/a.csv', b.get_filename(opts, 'dig')) + expect.eq('blueprints/a/a.csv', b.get_filename(opts, 'dig', 1)) opts = {name='a', split_strategy='phase'} - expect.eq('blueprints/a-dig.csv', b.get_filename(opts, 'dig')) + expect.eq('blueprints/a-1-dig.csv', b.get_filename(opts, 'dig', 1)) opts = {name='a/', split_strategy='phase'} - expect.eq('blueprints/a/a-dig.csv', b.get_filename(opts, 'dig')) + expect.eq('blueprints/a/a-5-dig.csv', b.get_filename(opts, 'dig', 5)) expect.error_match('could not parse basename', function() b.get_filename({name='', split_strategy='none'}) diff --git a/test/quickfort/ecosystem.lua b/test/quickfort/ecosystem.lua index 05f531e2e..27dddc5d5 100644 --- a/test/quickfort/ecosystem.lua +++ b/test/quickfort/ecosystem.lua @@ -244,7 +244,7 @@ end local function run_blueprint(basename, spec, pos) local args = {tostring(spec.width), tostring(spec.height), tostring(-spec.depth), output_dir..basename, - get_cursor_arg(pos), '-tphase', '--nometa'} + get_cursor_arg(pos), '-tphase'} local playback_start_arg = get_playback_start_arg(spec.start) if playback_start_arg then table.insert(args, playback_start_arg)