diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 6478bc444b..131b9bdfd3 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -210,6 +210,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index cf2d54ec93..fb257e6a8c 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -121,6 +121,11 @@ save yourself some time, copy this for undocumented things: Tries to connect to the server at sAddress. + + Recursively searches dir for xml files of actors and + converts them to equivalent lua files. See Docs/Themerdocs/XmlToLua.txt + for details. + Creates a CubicSplineN for you to use. Make sure you destroy the CubicSplineN when you're done with it, or you will have a memory leak. diff --git a/Docs/Themerdocs/XmlToLua.txt b/Docs/Themerdocs/XmlToLua.txt new file mode 100644 index 0000000000..1dae2f1d4f --- /dev/null +++ b/Docs/Themerdocs/XmlToLua.txt @@ -0,0 +1,158 @@ +StepMania 5 has an xml converter inside it intended to assist authors who want +to convert the xml scripted background and foreground animations written for +StepMania 3.95 or OpenITG to work with StepMania 5. + +The primary way to invoke the converter is to use the debug menu. +While on the Select Music screen, navigate to the song with xml that needs to +be converted. Hold F3, press F6 to change to the theme section, then press 0 +to convert the xml in the folder of the current song. + +The other way is to use a theme that provides some interface to the +convert_xml_bgs lua function. + + +Since the converter recursively searches for all xml files in the folder +given, if you have foo/default.xml, it will be converted to foo/default.lua. +If foo/bar/default.xml exists, foo/bar/default.lua will be created. + +When the converter finds an xml file, it parses it and attempts to create lua +that results in the same actors. Some things are converted without problems, +others are beyond the scope of the converter and are not handled. + +Do not run the converter on a song that you have already converted. When it +finds the xml files, it will overwrite the lua files with new ones, which +means any fixes applied after conversion will be wiped out. + +The converter does not delete the xml files after conversion because you +probably need them to double check things. + + + +# Known working conversions + +## Naming +Actors and conditions are assigned autogenerated unique names with the prefix +"xtl_actor_" or "xtl_cond_". This is so that if some error message tells you +the name of an actor, you can find it in the code. If an actor has a Name +field, that will be used instead of the autogenerated unique name. + +## Type guessing + +Typically, the Type or Class field is used, unless the File field points to +something that has to be a certain type. + +* ActorFrames with children are handled. + +* File field conditions: + * Images and .sprite files are converted to Sprites. + * .txt and .model files are converted to Models. + * Folders are converted to use LoadActor, which has its own rules for + guessing the actor type. + * Sound files are converted to ActorSound. + +* If there is a Text field, the actor is assumed to be a BitmapText. + + +## Field conversion + +Any field that ends with "Command" is recognized as a command and passes +through command conversion. +Anything else passes through unchanged, unless it's known to be a string +field. String fields will be strings after conversion. +This may cause syntax errors if a field is meant to be a string and it's not +a string after conversion. This happens because in XML, all fields are +stored in strings and the only way to know which ones are supposed to be +strings after conversion is to keep a list of field names. +String fields: +* AltText +* Bones +* File +* Font +* Materials +* Meshes +* Text +* Texture + + +## Command conversion + +Commands that use the normal style are converted to use SM5's cmd syntax. +These commands are passed through a parser that looks for certain things that +require special handling: +* x coords are changed to use (SCREEN_CENTER_X + (x - 320)), to put them in + the same position relative to the center. This is only applied if the + coord was a simple number, expressions are not changed. +* y coords are changed similarly to x coords. +* queuecommand, playcommand, and effectclock all have their arguments changed + to strings. +* EffectMagnitude, ZoomToWidth, and ZoomToHeight are renamed to the lowercase + names SM5 uses. +* blend and cullmode have their args converted to SM5's enum values. +* hidden is converted to visible, and its arg is converted to a bool. +* diffuse, effectcolor1, and effectcolor2 have their args converted to use + the color function. + + +Commands that use lua functions are converted with the lua mostly unchanged. +A very short list of things in lua are recognized and replaced with their +SM5 equivalents. +* "hidden(0)" becomes "visible(true)" +* "hidden(1)" becomes "visible(false)" +* "effectdelay" becomes "effect_hold_at_full" +* "IsPlayerEnabled(0)" becomes "IsPlayerEnabled(PLAYER_1)" +* "IsPlayerEnabled(1)" becomes "IsPlayerEnabled(PLAYER_2)" + +The list is short and the strings must match exactly because symbolically +parsing the lua into tokens to recognize other ways of doing the same thing +is beyond the scope of the converter. + + +## Condition conversion + +Because conditions are lua code to execute, they are passed through the same +converter as lua commands. +It is common practice for many actors to have identical conditions, so all +conditions are collected in a list, so they can be evaluated once when the +file is loaded, and then the actors can just use the result. +So after conversion you might see something like this: +``` +xtl_cond_a_result= GAMESTATE:IsPlayerEnabled(PLAYER_1) +local function optional_actor(cond, actor) + if cond then return actor end + return Def.Actor{} +end +``` +The optional_actor function is a convenient way to insert a blank, unrendered +actor instead of one that actually does something. +You'll see it used like this: +``` + optional_actor(xtl_cond_a_result, + Def.Sprite{ + Name= "xtl_actor_g", + InitCommand= cmd(x,0;y,0), + Texture= "foo 3x1.png", + }), +``` + + + +# Known unhandled things + +Some things like difficulty use strings as enum values in SM5. Lua code that +uses GetDifficulty or other things that now return enum strings must be +edited after conversion to use the enum strings. + +There are probably renamed functions that the converter does not handle. + +GAMESTATE:ApplyGameCommand applies mods instantly, ignoring the tween +speeds. Lua code that changes mods should use the PlayerOptions API instead. +This means fetching a PlayerOptions object and converting mod strings to +function names and args, which isn't hard with an editor with regex find and +replace. + +Actor sizes are not changed, so if an actor is meant to be the size of the +screen, it will probably not cover the screen when Stepmania isn't a 4:3 +aspect ratio. Use SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_CENTER_X, +SCREEN_CENTER_Y, _screen.w, _screen.h, _screen.cx, or _screen.cy for +positioning and sizing. Actor:FullScreen exists to stretch an actor to cover +the screen. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d51514f89..9ed3a1a5af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,7 @@ list(APPEND SMDATA_FILE_TYPES_HPP "IniFile.h" "MsdFile.h" "XmlFile.h" + "XmlToLua.h" "XmlFileUtil.h" ) diff --git a/src/ScreenDebugOverlay.cpp b/src/ScreenDebugOverlay.cpp index f145f654d1..f69abe43f6 100644 --- a/src/ScreenDebugOverlay.cpp +++ b/src/ScreenDebugOverlay.cpp @@ -29,6 +29,7 @@ #include "Song.h" #include "ScreenSyncOverlay.h" #include "ThemeMetric.h" +#include "XmlToLua.h" static bool g_bIsDisplayed = false; static bool g_bIsSlow = false; @@ -1116,8 +1117,7 @@ class DebugLineClearErrors : public IDebugLine } }; -void convert_xmls_in_dir(RString const& dirname); -class DebugLineConvrtXML : public IDebugLine +class DebugLineConvertXML : public IDebugLine { virtual RString GetDisplayTitle() { return CONVERT_XML.GetValue(); } virtual RString GetDisplayValue() { return RString(); } @@ -1319,7 +1319,7 @@ DECLARE_ONE( DebugLineReloadTheme ); DECLARE_ONE( DebugLineReloadOverlayScreens ); DECLARE_ONE( DebugLineToggleErrors ); DECLARE_ONE( DebugLineClearErrors ); -DECLARE_ONE( DebugLineConvrtXML ); +DECLARE_ONE( DebugLineConvertXML ); DECLARE_ONE( DebugLineWriteProfiles ); DECLARE_ONE( DebugLineWritePreferences ); DECLARE_ONE( DebugLineMenuTimer ); diff --git a/src/XmlToLua.cpp b/src/XmlToLua.cpp index d9a1cf405c..d7e6103d3d 100644 --- a/src/XmlToLua.cpp +++ b/src/XmlToLua.cpp @@ -245,6 +245,15 @@ void init_parser_helpers() chunks_to_replace["IsPlayerEnabled(1)"]= "IsPlayerEnabled(PLAYER_2)"; } +void convert_lua_chunk(RString& chunk_text) +{ + for(map::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 @@ -293,11 +302,7 @@ void actor_template_t::store_cmd(RString const& cmd_name, RString const& full_cm if(full_cmd.Left(1) == "%") { RString cmd_text= full_cmd.Right(full_cmd.size()-1); - for(map::iterator chunk= chunks_to_replace.begin(); - chunk != chunks_to_replace.end(); ++chunk) - { - cmd_text.Replace(chunk->first, chunk->second); - } + convert_lua_chunk(cmd_text); fields[cmd_name]= cmd_text; return; } @@ -739,7 +744,9 @@ void convert_xml_file(RString const& fname, RString const& dirname) for(condition_set_t::iterator cond= conditions.begin(); cond != conditions.end(); ++cond) { - file->Write("local " + cond->second + "_result= " + cond->first + "\n\n"); + RString cond_text= cond->first; + convert_lua_chunk(cond_text); + file->Write("local " + cond->second + "_result= " + cond_text + "\n\n"); } if(!conditions.empty()) { diff --git a/src/XmlToLua.h b/src/XmlToLua.h new file mode 100644 index 0000000000..02ba9219be --- /dev/null +++ b/src/XmlToLua.h @@ -0,0 +1,4 @@ +#ifndef XmlToLua_H +#define XmlToLua_H +void convert_xmls_in_dir(RString const& dirname); +#endif