803 lines
22 KiB
C++
803 lines
22 KiB
C++
#include "global.h"
|
|
#include "ActorUtil.h"
|
|
#include "IniFile.h"
|
|
#include "RageFile.h"
|
|
#include "RageFileManager.h"
|
|
#include "RageLog.h"
|
|
#include "RageTypes.h"
|
|
#include "LuaManager.h"
|
|
#include "LuaBinding.h"
|
|
#include "XmlFile.h"
|
|
#include "XmlFileUtil.h"
|
|
|
|
#include <cstddef>
|
|
#include <map>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#define TWEEN_QUEUE_MAX 50
|
|
|
|
RString unique_name();
|
|
void convert_xmls_in_dir(RString const& dirname);
|
|
void convert_xml_file(RString const& fname, RString const& dirname);
|
|
RString maybe_conv_pos(RString pos, RString (*conv_func)(float p));
|
|
RString add_extension_to_relative_path_from_found_file(
|
|
RString const& relative_path, RString const& found_file);
|
|
|
|
RString unique_name(RString const& type)
|
|
{
|
|
static char const* name_chars= "abcdefghijklmnopqrstuvwxyz";
|
|
static int name_count= 0;
|
|
int curr_name= name_count;
|
|
RString ret= "xtl_" + type + "_"; // Minimize the chance of a name collision.
|
|
ret= ret + name_chars[curr_name%26];
|
|
while(curr_name / 26 > 0)
|
|
{
|
|
curr_name= curr_name / 26;
|
|
ret= ret + name_chars[curr_name%26];
|
|
}
|
|
++name_count;
|
|
return ret;
|
|
}
|
|
|
|
void convert_xmls_in_dir(RString const& dirname)
|
|
{
|
|
std::vector<RString> listing;
|
|
FILEMAN->GetDirListing(dirname, listing, false, true);
|
|
for(std::vector<RString>::iterator curr_file= listing.begin();
|
|
curr_file != listing.end(); ++curr_file)
|
|
{
|
|
switch(ActorUtil::GetFileType(*curr_file))
|
|
{
|
|
case FT_Xml:
|
|
convert_xml_file(*curr_file, dirname);
|
|
break;
|
|
case FT_Directory:
|
|
convert_xmls_in_dir((*curr_file) + "/");
|
|
break;
|
|
default: // Ignore anything not xml or directory.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RString convert_xpos(float x)
|
|
{
|
|
return "SCREEN_CENTER_X + " + std::to_string(x - 320.0f);
|
|
}
|
|
|
|
RString convert_ypos(float y)
|
|
{
|
|
return "SCREEN_CENTER_Y + " + std::to_string(y - 240.0f);
|
|
}
|
|
|
|
RString maybe_conv_pos(RString pos, RString (*conv_func)(float p))
|
|
{
|
|
float f;
|
|
if(pos >> f)
|
|
{
|
|
return conv_func(f);
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
std::size_t after_slash_or_zero(RString const& s)
|
|
{
|
|
std::size_t ret= s.rfind('/');
|
|
if(ret != std::string::npos)
|
|
{
|
|
return ret+1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
RString add_extension_to_relative_path_from_found_file(
|
|
RString const& relative_path, RString const& found_file)
|
|
{
|
|
std::size_t rel_last_slash= after_slash_or_zero(relative_path);
|
|
std::size_t found_last_slash= after_slash_or_zero(found_file);
|
|
return relative_path.Left(rel_last_slash) +
|
|
found_file.substr(found_last_slash, std::string::npos);
|
|
}
|
|
|
|
bool verify_arg_count(RString cmd, std::vector<RString>& args, std::size_t req)
|
|
{
|
|
if(args.size() < req)
|
|
{
|
|
LuaHelpers::ReportScriptError("Not enough args to " + cmd + " command.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
typedef void (*arg_converter_t)(std::vector<RString>& args);
|
|
|
|
std::map<RString, arg_converter_t> arg_converters;
|
|
std::map<RString, std::size_t> tween_counters;
|
|
std::set<RString> fields_that_are_strings;
|
|
std::map<RString, RString> chunks_to_replace;
|
|
|
|
#define COMMON_ARG_VERIFY(count) if(!verify_arg_count(args[0], args, count)) return;
|
|
void x_conv(std::vector<RString>& args)
|
|
{
|
|
COMMON_ARG_VERIFY(2);
|
|
float pos;
|
|
if(args[1] >> pos)
|
|
{
|
|
args[1]= convert_xpos(pos);
|
|
}
|
|
}
|
|
void y_conv(std::vector<RString>& args)
|
|
{
|
|
COMMON_ARG_VERIFY(2);
|
|
float pos;
|
|
if(args[1] >> pos)
|
|
{
|
|
args[1]= convert_ypos(pos);
|
|
}
|
|
}
|
|
void string_arg_conv(std::vector<RString>& args)
|
|
{
|
|
COMMON_ARG_VERIFY(2);
|
|
args[1]= "\"" + args[1] + "\"";
|
|
}
|
|
void lower_string_conv(std::vector<RString>& args)
|
|
{
|
|
args[0].MakeLower();
|
|
}
|
|
void hidden_conv(std::vector<RString>& args)
|
|
{
|
|
COMMON_ARG_VERIFY(2);
|
|
args[0]= "visible";
|
|
if(args[1] == "1")
|
|
{
|
|
args[1]= "false";
|
|
}
|
|
else
|
|
{
|
|
args[1]= "true";
|
|
}
|
|
}
|
|
void diffuse_conv(std::vector<RString>& args)
|
|
{
|
|
COMMON_ARG_VERIFY(2);
|
|
RString retarg;
|
|
for(std::size_t i= 1; i < args.size(); ++i)
|
|
{
|
|
retarg+= args[i];
|
|
if(i < args.size()-1)
|
|
{
|
|
retarg+= ",";
|
|
}
|
|
}
|
|
args[1]= "color('" + retarg + "')";
|
|
args.resize(2);
|
|
}
|
|
|
|
// Prototype for a function that is created by a macro in another translation unit and has no visible prototype, don't do this unless you have a good reason.
|
|
const RString& BlendModeToString(BlendMode);
|
|
const RString& CullModeToString(CullMode);
|
|
void blend_conv(std::vector<RString>& args)
|
|
{
|
|
COMMON_ARG_VERIFY(2);
|
|
for(int i= 0; i < NUM_BlendMode; ++i)
|
|
{
|
|
RString blend_str= BlendModeToString(static_cast<BlendMode>(i));
|
|
blend_str.MakeLower();
|
|
if(args[1] == blend_str)
|
|
{
|
|
args[1]= "\"BlendMode_" + BlendModeToString(static_cast<BlendMode>(i)) + "\"";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void cull_conv(std::vector<RString>& args)
|
|
{
|
|
COMMON_ARG_VERIFY(2);
|
|
for(int i= 0; i < NUM_CullMode; ++i)
|
|
{
|
|
RString cull_str= CullModeToString(static_cast<CullMode>(i));
|
|
cull_str.MakeLower();
|
|
if(args[1] == cull_str)
|
|
{
|
|
args[1]= "\"CullMode_" + CullModeToString(static_cast<CullMode>(i)) + "\"";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#undef COMMON_ARG_VERIFY
|
|
|
|
void init_parser_helpers()
|
|
{
|
|
arg_converters["x"]= &x_conv;
|
|
arg_converters["y"]= &y_conv;
|
|
arg_converters["queuecommand"]= &string_arg_conv;
|
|
arg_converters["playcommand"]= &string_arg_conv;
|
|
arg_converters["effectclock"]= &string_arg_conv;
|
|
arg_converters["EffectMagnitude"]= &lower_string_conv;
|
|
arg_converters["ZoomToWidth"]= &lower_string_conv;
|
|
arg_converters["ZoomToHeight"]= &lower_string_conv;
|
|
arg_converters["blend"]= &blend_conv;
|
|
arg_converters["cullmode"]= &cull_conv;
|
|
arg_converters["hidden"]= &hidden_conv;
|
|
arg_converters["diffuse"]= &diffuse_conv;
|
|
arg_converters["effectcolor1"]= &diffuse_conv;
|
|
arg_converters["effectcolor2"]= &diffuse_conv;
|
|
tween_counters["sleep"]= 2;
|
|
tween_counters["linear"]= 1;
|
|
tween_counters["accelerate"]= 1;
|
|
tween_counters["decelerate"]= 1;
|
|
tween_counters["spring"]= 1;
|
|
tween_counters["tween"]= 1;
|
|
tween_counters["queuecommand"]= 1;
|
|
fields_that_are_strings.insert("Texture");
|
|
fields_that_are_strings.insert("Text");
|
|
fields_that_are_strings.insert("AltText");
|
|
fields_that_are_strings.insert("File");
|
|
fields_that_are_strings.insert("Font");
|
|
fields_that_are_strings.insert("Meshes");
|
|
fields_that_are_strings.insert("Materials");
|
|
fields_that_are_strings.insert("Bones");
|
|
chunks_to_replace["hidden(0)"]= "visible(true)";
|
|
chunks_to_replace["hidden(1)"]= "visible(false)";
|
|
chunks_to_replace["effectdelay"]= "effect_hold_at_full";
|
|
chunks_to_replace["IsPlayerEnabled(0)"]= "IsPlayerEnabled(PLAYER_1)";
|
|
chunks_to_replace["IsPlayerEnabled(1)"]= "IsPlayerEnabled(PLAYER_2)";
|
|
}
|
|
|
|
void convert_lua_chunk(RString& chunk_text)
|
|
{
|
|
for(std::map<RString, RString>::iterator chunk= chunks_to_replace.begin();
|
|
chunk != chunks_to_replace.end(); ++chunk)
|
|
{
|
|
chunk_text.Replace(chunk->first, chunk->second);
|
|
}
|
|
}
|
|
|
|
// Conditions are mapped by condition string.
|
|
// So condition_set_t::iterator->first is the lua to execute for the
|
|
// condition, and condition_set_t::iterator->second is the name of the
|
|
// condition.
|
|
typedef std::map<RString, RString> condition_set_t;
|
|
typedef std::map<RString, RString> field_cont_t;
|
|
struct frame_t
|
|
{
|
|
int frame;
|
|
float delay;
|
|
frame_t() :frame(0), delay(0.0f) {}
|
|
};
|
|
|
|
struct actor_template_t
|
|
{
|
|
RString type;
|
|
field_cont_t fields;
|
|
RString condition;
|
|
RString name;
|
|
std::vector<frame_t> frames;
|
|
std::vector<actor_template_t> children;
|
|
RString x;
|
|
RString y;
|
|
void make_space_for_frame(int id);
|
|
void store_cmd(RString const& cmd_name, RString const& full_cmd);
|
|
void store_field(RString const& field_name, RString const& value, bool cmd_convert, RString const& pref= "", RString const& suf= "");
|
|
void store_field(RString const& field_name, XNodeValue const* value, bool cmd_convert, RString const& pref= "", RString const& suf= "");
|
|
void rename_field(RString const& old_name, RString const& new_name);
|
|
RString get_field(RString const& field_name);
|
|
void load_frames_from_file(RString const& fname, RString const& rel_path);
|
|
void load_model_from_file(RString const& fname, RString const& rel_path);
|
|
void load_node(XNode const& node, RString const& dirname, condition_set_t& conditions);
|
|
void output_to_file(RageFile* file, RString const& indent);
|
|
};
|
|
|
|
void actor_template_t::make_space_for_frame(int id)
|
|
{
|
|
if(id >= static_cast<int>(frames.size()))
|
|
{
|
|
frames.resize(id+1);
|
|
}
|
|
}
|
|
|
|
void actor_template_t::store_cmd(RString const& cmd_name, RString const& full_cmd)
|
|
{
|
|
if(full_cmd.Left(1) == "%")
|
|
{
|
|
RString cmd_text= full_cmd.Right(full_cmd.size()-1);
|
|
convert_lua_chunk(cmd_text);
|
|
fields[cmd_name]= cmd_text;
|
|
return;
|
|
}
|
|
std::vector<RString> cmds;
|
|
split(full_cmd, ";", cmds, true);
|
|
std::size_t queue_size= 0;
|
|
// If someone has a simfile that uses a playcommand that pushes tween
|
|
// states onto the queue, queue size counting will have to be made much
|
|
// more complex to prevent that from causing an overflow.
|
|
for(std::vector<RString>::iterator cmd= cmds.begin(); cmd != cmds.end(); ++cmd)
|
|
{
|
|
std::vector<RString> args;
|
|
split(*cmd, ",", args, true);
|
|
if(!args.empty())
|
|
{
|
|
for(std::vector<RString>::iterator arg= args.begin(); arg != args.end(); ++arg)
|
|
{
|
|
std::size_t first_nonspace= 0;
|
|
std::size_t last_nonspace= arg->size();
|
|
while((*arg)[first_nonspace] == ' ')
|
|
{
|
|
++first_nonspace;
|
|
}
|
|
while((*arg)[last_nonspace] == ' ')
|
|
{
|
|
--last_nonspace;
|
|
}
|
|
*arg= arg->substr(first_nonspace, last_nonspace - first_nonspace);
|
|
}
|
|
std::map<RString, arg_converter_t>::iterator conv= arg_converters.find(args[0]);
|
|
if(conv != arg_converters.end())
|
|
{
|
|
conv->second(args);
|
|
}
|
|
std::map<RString, std::size_t>::iterator counter= tween_counters.find(args[0]);
|
|
if(counter != tween_counters.end())
|
|
{
|
|
queue_size+= counter->second;
|
|
}
|
|
}
|
|
*cmd= join(",", args);
|
|
}
|
|
// This code is probably actually useless, OITG has the same tween queue size
|
|
// and the real reason I saw overflows in converted files was a bug in
|
|
// foreground loading that ran InitCommand twice. -Kyz
|
|
if(queue_size >= TWEEN_QUEUE_MAX)
|
|
{
|
|
std::size_t num_to_make= (queue_size / TWEEN_QUEUE_MAX) + 1;
|
|
std::size_t states_per= (queue_size / num_to_make) + 1;
|
|
std::size_t states_in_curr= 0;
|
|
RString this_name= cmd_name;
|
|
std::vector<RString> curr_cmd;
|
|
for(std::vector<RString>::iterator cmd= cmds.begin(); cmd != cmds.end(); ++cmd)
|
|
{
|
|
curr_cmd.push_back(*cmd);
|
|
std::vector<RString> args;
|
|
split(*cmd, ",", args, true);
|
|
if(!args.empty())
|
|
{
|
|
std::map<RString, std::size_t>::iterator counter= tween_counters.find(args[0]);
|
|
if(counter != tween_counters.end())
|
|
{
|
|
states_in_curr+= counter->second;
|
|
if(states_in_curr >= states_per - 1)
|
|
{
|
|
RString next_name= unique_name("cmd");
|
|
curr_cmd.push_back("queuecommand,\"" + next_name + "\"");
|
|
fields[this_name]= "cmd(" + join(";", curr_cmd) + ")";
|
|
curr_cmd.clear();
|
|
this_name= next_name;
|
|
states_in_curr= 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!curr_cmd.empty())
|
|
{
|
|
fields[this_name]= "cmd(" + join(";", curr_cmd) + ")";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fields[cmd_name]= "cmd(" + join(";", cmds) + ")";
|
|
}
|
|
}
|
|
|
|
void actor_template_t::store_field(RString const& field_name, RString const& value, bool cmd_convert, RString const& pref, RString const& suf)
|
|
{
|
|
// OITG apparently allowed "Oncommand" as valid.
|
|
if(field_name.Right(7).MakeLower() != "command")
|
|
{
|
|
cmd_convert= false;
|
|
}
|
|
if(cmd_convert)
|
|
{
|
|
RString real_field_name= field_name.Left(field_name.size()-7) + "Command";
|
|
store_cmd(real_field_name, value);
|
|
}
|
|
else
|
|
{
|
|
fields[field_name]= pref + value + suf;
|
|
}
|
|
|
|
}
|
|
void actor_template_t::store_field(RString const& field_name, XNodeValue const* value, bool cmd_convert, RString const& pref, RString const& suf)
|
|
{
|
|
RString val;
|
|
value->GetValue(val);
|
|
store_field(field_name, val, cmd_convert, pref, suf);
|
|
}
|
|
|
|
void actor_template_t::rename_field(RString const& old_name, RString const& new_name)
|
|
{
|
|
field_cont_t::iterator old_field= fields.find(old_name);
|
|
if(old_field == fields.end())
|
|
{
|
|
return;
|
|
}
|
|
fields[new_name]= old_field->second;
|
|
fields.erase(old_field);
|
|
}
|
|
|
|
RString actor_template_t::get_field(RString const& field_name)
|
|
{
|
|
field_cont_t::iterator field= fields.find(field_name);
|
|
if(field == fields.end())
|
|
{
|
|
return "";
|
|
}
|
|
return field->second;
|
|
}
|
|
|
|
void actor_template_t::load_frames_from_file(RString const& fname, RString const& rel_path)
|
|
{
|
|
IniFile ini;
|
|
if(!ini.ReadFile(fname))
|
|
{
|
|
LOG->Trace("Failed to read sprite file %s: %s", fname.c_str(), ini.GetError().c_str());
|
|
return;
|
|
}
|
|
XNode const* sprite_node= ini.GetChild("Sprite");
|
|
if(sprite_node != nullptr)
|
|
{
|
|
FOREACH_CONST_Attr(sprite_node, attr)
|
|
{
|
|
// Frame and Delay fields have names of the form "Frame0000" where the
|
|
// "0000" part is the id of the frame.
|
|
RString field_type= attr->first.Left(5);
|
|
if(field_type == "Frame")
|
|
{
|
|
int id= StringToInt(attr->first.Right(attr->first.size()-5));
|
|
make_space_for_frame(id);
|
|
attr->second->GetValue(frames[id].frame);
|
|
}
|
|
else if(field_type == "Delay")
|
|
{
|
|
int id= StringToInt(attr->first.Right(attr->first.size()-5));
|
|
make_space_for_frame(id);
|
|
attr->second->GetValue(frames[id].delay);
|
|
}
|
|
else if(field_type == "Textu")
|
|
{
|
|
store_field("Texture", attr->second, false, rel_path, "");
|
|
}
|
|
else
|
|
{
|
|
// Unrecognized, ignore.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void actor_template_t::load_model_from_file(RString const& fname, RString const& rel_path)
|
|
{
|
|
IniFile ini;
|
|
if(!ini.ReadFile(fname))
|
|
{
|
|
LOG->Trace("Failed to read model file %s: %s", fname.c_str(), ini.GetError().c_str());
|
|
return;
|
|
}
|
|
XNode const* model_node= ini.GetChild("Model");
|
|
if(model_node != nullptr)
|
|
{
|
|
FOREACH_CONST_Attr(model_node, attr)
|
|
{
|
|
store_field(attr->first, attr->second, false, rel_path, "");
|
|
}
|
|
}
|
|
}
|
|
|
|
void actor_template_t::load_node(XNode const& node, RString const& dirname, condition_set_t& conditions)
|
|
{
|
|
type= node.GetName();
|
|
bool type_set_by_automagic= false;
|
|
#define set_type(auto_type) type_set_by_automagic= true; type= auto_type;
|
|
FOREACH_CONST_Attr(&node, attr)
|
|
{
|
|
if(attr->first == "Name")
|
|
{
|
|
attr->second->GetValue(name);
|
|
}
|
|
else if(attr->first == "Condition")
|
|
{
|
|
RString cond_str;
|
|
attr->second->GetValue(cond_str);
|
|
condition_set_t::iterator cond= conditions.find(cond_str);
|
|
if(cond == conditions.end())
|
|
{
|
|
condition= unique_name("cond");
|
|
conditions[cond_str]= condition;
|
|
}
|
|
else
|
|
{
|
|
condition= cond->second;
|
|
}
|
|
}
|
|
else if(attr->first == "Type" || attr->first == "Class")
|
|
{
|
|
if(!type_set_by_automagic)
|
|
{
|
|
attr->second->GetValue(type);
|
|
}
|
|
}
|
|
else if(attr->first == "__TEXT__")
|
|
{
|
|
// Ignore. This attribute seems to be put in by the xml parser, and
|
|
// not part of the actual xml code.
|
|
}
|
|
else if(attr->first == "Text")
|
|
{
|
|
set_type("BitmapText");
|
|
store_field(attr->first, attr->second, true);
|
|
}
|
|
else if(attr->first == "File")
|
|
{
|
|
RString relative_path;
|
|
attr->second->GetValue(relative_path);
|
|
RString sfname= dirname + relative_path;
|
|
if(FILEMAN->IsADirectory(sfname))
|
|
{
|
|
set_type("LoadActor");
|
|
store_field("File", attr->second, false);
|
|
}
|
|
else
|
|
{
|
|
std::vector<RString> files_in_dir;
|
|
FILEMAN->GetDirListing(sfname + "*", files_in_dir, false, true);
|
|
int handled_level= 0;
|
|
RString found_file= "";
|
|
for(std::vector<RString>::iterator file= files_in_dir.begin();
|
|
file != files_in_dir.end() && handled_level < 2; ++file)
|
|
{
|
|
RString extension= GetExtension(*file);
|
|
FileType file_type= ActorUtil::GetFileType(*file);
|
|
RString this_relative=
|
|
add_extension_to_relative_path_from_found_file(relative_path, *file);
|
|
switch(file_type)
|
|
{
|
|
case FT_Xml:
|
|
this_relative= SetExtension(this_relative, "lua");
|
|
[[fallthrough]];
|
|
case FT_Lua:
|
|
set_type("LoadActor");
|
|
store_field("File", this_relative, false);
|
|
handled_level= 2;
|
|
break;
|
|
case FT_Ini:
|
|
break;
|
|
case FT_Bitmap:
|
|
case FT_Movie:
|
|
if(handled_level < 2)
|
|
{
|
|
set_type("Sprite");
|
|
store_field("File", this_relative, false);
|
|
handled_level= 1;
|
|
}
|
|
break;
|
|
case FT_Sound:
|
|
set_type("ActorSound");
|
|
store_field("File", this_relative, false);
|
|
handled_level= 1;
|
|
break;
|
|
case FT_Sprite:
|
|
set_type("Sprite");
|
|
load_frames_from_file(dirname + this_relative, Dirname(relative_path));
|
|
handled_level= 2;
|
|
break;
|
|
case FT_Model:
|
|
set_type("Model");
|
|
store_field("Meshes", this_relative, false);
|
|
store_field("Materials", this_relative, false);
|
|
store_field("Bones", this_relative, false);
|
|
handled_level= 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if(!handled_level)
|
|
{
|
|
if(extension == "model")
|
|
{
|
|
set_type("Model");
|
|
load_model_from_file(dirname + this_relative, Dirname(relative_path));
|
|
handled_level= 2;
|
|
}
|
|
}
|
|
}
|
|
if(!handled_level)
|
|
{
|
|
if(!files_in_dir.empty())
|
|
{
|
|
RString this_relative=
|
|
add_extension_to_relative_path_from_found_file(relative_path, files_in_dir[0]);
|
|
store_field("File", this_relative, false);
|
|
}
|
|
else
|
|
{
|
|
store_field("File", attr->second, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
store_field(attr->first, attr->second, true);
|
|
}
|
|
}
|
|
if(type == "BitmapText")
|
|
{
|
|
rename_field("Texture", "Font");
|
|
rename_field("File", "Font");
|
|
}
|
|
if(type == "Sprite")
|
|
{
|
|
rename_field("File", "Texture");
|
|
}
|
|
XNode const* xren= node.GetChild("children");
|
|
if(xren != nullptr)
|
|
{
|
|
FOREACH_CONST_Child(xren, child)
|
|
{
|
|
actor_template_t chill_plate;
|
|
chill_plate.load_node(*child, dirname, conditions);
|
|
children.push_back(chill_plate);
|
|
}
|
|
}
|
|
if(!x.empty() || !y.empty())
|
|
{
|
|
RString pos_init= "xy," + x + "," + y;
|
|
field_cont_t::iterator init= fields.find("InitCommand");
|
|
if(init != fields.end())
|
|
{
|
|
pos_init= pos_init + ";queuecommand,xtl_passed_initCommand";
|
|
fields["xtl_passed_initCommand"]= init->second;
|
|
}
|
|
fields["InitCommand"]= "cmd(" + pos_init + ")";
|
|
}
|
|
}
|
|
|
|
void actor_template_t::output_to_file(RageFile* file, RString const& indent)
|
|
{
|
|
if(!condition.empty())
|
|
{
|
|
file->Write(indent + "optional_actor(" + condition + "_result,\n");
|
|
}
|
|
if(type == "LoadActor")
|
|
{
|
|
file->Write(indent + "LoadActor('" + get_field("File") + "') .. {\n");
|
|
}
|
|
else
|
|
{
|
|
file->Write(indent + "Def." + type + "{\n");
|
|
}
|
|
RString subindent= indent + " ";
|
|
if(name.empty())
|
|
{
|
|
name= unique_name("actor");
|
|
}
|
|
file->Write(subindent + "Name= \"" + name + "\",\n");
|
|
if(!frames.empty())
|
|
{
|
|
file->Write(subindent + "Frames= {\n");
|
|
RString frameindent= subindent + " ";
|
|
for(std::vector<frame_t>::iterator frame= frames.begin();
|
|
frame != frames.end(); ++frame)
|
|
{
|
|
file->Write(frameindent + "{Frame= " + std::to_string(frame->frame) +
|
|
", Delay= " + std::to_string(frame->delay) + "},\n");
|
|
}
|
|
file->Write(indent + "},\n");
|
|
}
|
|
for(field_cont_t::iterator field= fields.begin();
|
|
field != fields.end(); ++field)
|
|
{
|
|
std::set<RString>::iterator is_string= fields_that_are_strings.find(field->first);
|
|
if(is_string != fields_that_are_strings.end())
|
|
{
|
|
file->Write(subindent + field->first + "= \"" + field->second + "\",\n");
|
|
}
|
|
else
|
|
{
|
|
file->Write(subindent + field->first + "= " + field->second + ",\n");
|
|
}
|
|
}
|
|
for(std::vector<actor_template_t>::iterator child= children.begin();
|
|
child != children.end(); ++child)
|
|
{
|
|
child->output_to_file(file, subindent);
|
|
file->Write(",\n");
|
|
}
|
|
file->Write(indent + "}");
|
|
if(!condition.empty())
|
|
{
|
|
file->Write(")");
|
|
}
|
|
}
|
|
|
|
void convert_xml_file(RString const& fname, RString const& dirname)
|
|
{
|
|
if(arg_converters.empty())
|
|
{
|
|
init_parser_helpers();
|
|
}
|
|
LOG->Trace("Beginning conversion of entry: %s", fname.c_str());
|
|
XNode xml;
|
|
if(!XmlFileUtil::LoadFromFileShowErrors(xml, fname))
|
|
{
|
|
LOG->Trace("Error when loading xml.");
|
|
return;
|
|
}
|
|
actor_template_t plate;
|
|
condition_set_t conditions;
|
|
plate.load_node(xml, dirname, conditions);
|
|
RageFile* file= new RageFile;
|
|
RString out_name= fname.Left(fname.size()-4) + ".lua";
|
|
if(!file->Open(out_name, RageFile::WRITE))
|
|
{
|
|
LOG->Trace("Could not open %s: %s", out_name.c_str(), file->GetError().c_str());
|
|
return;
|
|
}
|
|
LOG->Trace("Saving conversion to: %s", out_name.c_str());
|
|
for(condition_set_t::iterator cond= conditions.begin();
|
|
cond != conditions.end(); ++cond)
|
|
{
|
|
RString cond_text= cond->first;
|
|
convert_lua_chunk(cond_text);
|
|
file->Write("local " + cond->second + "_result= " + cond_text + "\n\n");
|
|
}
|
|
if(!conditions.empty())
|
|
{
|
|
file->Write("local function optional_actor(cond, actor)\n"
|
|
" if cond then return actor end\n"
|
|
" return Def.Actor{}\n"
|
|
"end\n\n");
|
|
}
|
|
file->Write("return ");
|
|
plate.output_to_file(file, "");
|
|
file->Write("\n");
|
|
file->Close();
|
|
delete file;
|
|
}
|
|
|
|
int LuaFunc_convert_xml_bgs(lua_State* L);
|
|
int LuaFunc_convert_xml_bgs(lua_State* L)
|
|
{
|
|
RString dir= SArg(1);
|
|
std::vector<RString> xml_list;
|
|
convert_xmls_in_dir(dir + "/");
|
|
return 0;
|
|
}
|
|
|
|
LUAFUNC_REGISTER_COMMON(convert_xml_bgs);
|
|
|
|
/*
|
|
* (c) 2014 Eric Reese
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
* whom the Software is furnished to do so, provided that the above
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
* the Software and that both the above copyright notice(s) and this
|
|
* permission notice appear in supporting documentation.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|